aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/st.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-06-23 15:55:44 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2015-06-23 15:55:44 -0700
commitacd53127c4adbd34570b221e7ea1f7fc94aea923 (patch)
tree5e24adc30e91db14bc47ef4287319f38eb1b2108 /drivers/scsi/st.c
parentMerge tag 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dledford/rdma (diff)
parentsnic: driver for Cisco SCSI HBA (diff)
downloadlinux-dev-acd53127c4adbd34570b221e7ea1f7fc94aea923.tar.xz
linux-dev-acd53127c4adbd34570b221e7ea1f7fc94aea923.zip
Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
Pull SCSI updates from James Bottomley: "This is the usual grab bag of driver updates (lpfc, hpsa, megaraid_sas, cxgbi, be2iscsi) plus an assortment of minor updates. There is also one new driver: the Cisco snic. The advansys driver has been rewritten to get rid of the warning about converting it to the DMA API, the tape statistics patch got in and finally, there's a resuffle of SCSI header files to separate more cleanly initiator from target mode (and better share the common definitions)" * tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi: (156 commits) snic: driver for Cisco SCSI HBA qla2xxx: Fix indentation qla2xxx: Comment out unreachable code fusion: remove dead MTRR code advansys: fix compilation errors and warnings when CONFIG_PCI is not set mptsas: fix depth param in scsi_track_queue_full megaraid: fix irq setup process regression lpfc: Update version to 10.7.0.0 for upstream patch set. lpfc: Fix to drop PLOGIs from fabric node till LOGO processing completes lpfc: Fix scsi task management error message. lpfc: Fix cq_id masking problem. lpfc: Fix scsi prep dma buf error. lpfc: Add support for using block multi-queue lpfc: Devices are not discovered during takeaway/giveback testing lpfc: Fix vport deletion failure. lpfc: Check for active portpeerbeacon. lpfc: Update driver version for upstream patch set 10.6.0.1. lpfc: Change buffer pool empty message to miscellaneous category lpfc: Fix incorrect log message reported for empty FCF record. lpfc: Fix rport leak. ...
Diffstat (limited to 'drivers/scsi/st.c')
-rw-r--r--drivers/scsi/st.c272
1 files changed, 271 insertions, 1 deletions
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index 9a1c34205254..3f25b8fa921d 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -471,6 +471,47 @@ static void st_release_request(struct st_request *streq)
kfree(streq);
}
+static void st_do_stats(struct scsi_tape *STp, struct request *req)
+{
+ ktime_t now;
+
+ now = ktime_get();
+ if (req->cmd[0] == WRITE_6) {
+ now = ktime_sub(now, STp->stats->write_time);
+ atomic64_add(ktime_to_ns(now), &STp->stats->tot_write_time);
+ atomic64_add(ktime_to_ns(now), &STp->stats->tot_io_time);
+ atomic64_inc(&STp->stats->write_cnt);
+ if (req->errors) {
+ atomic64_add(atomic_read(&STp->stats->last_write_size)
+ - STp->buffer->cmdstat.residual,
+ &STp->stats->write_byte_cnt);
+ if (STp->buffer->cmdstat.residual > 0)
+ atomic64_inc(&STp->stats->resid_cnt);
+ } else
+ atomic64_add(atomic_read(&STp->stats->last_write_size),
+ &STp->stats->write_byte_cnt);
+ } else if (req->cmd[0] == READ_6) {
+ now = ktime_sub(now, STp->stats->read_time);
+ atomic64_add(ktime_to_ns(now), &STp->stats->tot_read_time);
+ atomic64_add(ktime_to_ns(now), &STp->stats->tot_io_time);
+ atomic64_inc(&STp->stats->read_cnt);
+ if (req->errors) {
+ atomic64_add(atomic_read(&STp->stats->last_read_size)
+ - STp->buffer->cmdstat.residual,
+ &STp->stats->read_byte_cnt);
+ if (STp->buffer->cmdstat.residual > 0)
+ atomic64_inc(&STp->stats->resid_cnt);
+ } else
+ atomic64_add(atomic_read(&STp->stats->last_read_size),
+ &STp->stats->read_byte_cnt);
+ } else {
+ now = ktime_sub(now, STp->stats->other_time);
+ atomic64_add(ktime_to_ns(now), &STp->stats->tot_io_time);
+ atomic64_inc(&STp->stats->other_cnt);
+ }
+ atomic64_dec(&STp->stats->in_flight);
+}
+
static void st_scsi_execute_end(struct request *req, int uptodate)
{
struct st_request *SRpnt = req->end_io_data;
@@ -480,6 +521,8 @@ static void st_scsi_execute_end(struct request *req, int uptodate)
STp->buffer->cmdstat.midlevel_result = SRpnt->result = req->errors;
STp->buffer->cmdstat.residual = req->resid_len;
+ st_do_stats(STp, req);
+
tmp = SRpnt->bio;
if (SRpnt->waiting)
complete(SRpnt->waiting);
@@ -496,6 +539,7 @@ static int st_scsi_execute(struct st_request *SRpnt, const unsigned char *cmd,
struct rq_map_data *mdata = &SRpnt->stp->buffer->map_data;
int err = 0;
int write = (data_direction == DMA_TO_DEVICE);
+ struct scsi_tape *STp = SRpnt->stp;
req = blk_get_request(SRpnt->stp->device->request_queue, write,
GFP_KERNEL);
@@ -516,6 +560,17 @@ static int st_scsi_execute(struct st_request *SRpnt, const unsigned char *cmd,
}
}
+ atomic64_inc(&STp->stats->in_flight);
+ if (cmd[0] == WRITE_6) {
+ atomic_set(&STp->stats->last_write_size, bufflen);
+ STp->stats->write_time = ktime_get();
+ } else if (cmd[0] == READ_6) {
+ atomic_set(&STp->stats->last_read_size, bufflen);
+ STp->stats->read_time = ktime_get();
+ } else {
+ STp->stats->other_time = ktime_get();
+ }
+
SRpnt->bio = req->bio;
req->cmd_len = COMMAND_SIZE(cmd[0]);
memset(req->cmd, 0, BLK_MAX_CDB);
@@ -4222,6 +4277,12 @@ static int st_probe(struct device *dev)
}
tpnt->index = error;
sprintf(disk->disk_name, "st%d", tpnt->index);
+ tpnt->stats = kzalloc(sizeof(struct scsi_tape_stats), GFP_KERNEL);
+ if (tpnt->stats == NULL) {
+ sdev_printk(KERN_ERR, SDp,
+ "st: Can't allocate statistics.\n");
+ goto out_idr_remove;
+ }
dev_set_drvdata(dev, tpnt);
@@ -4241,6 +4302,8 @@ static int st_probe(struct device *dev)
out_remove_devs:
remove_cdevs(tpnt);
+ kfree(tpnt->stats);
+out_idr_remove:
spin_lock(&st_index_lock);
idr_remove(&st_index_idr, tpnt->index);
spin_unlock(&st_index_lock);
@@ -4298,6 +4361,7 @@ static void scsi_tape_release(struct kref *kref)
disk->private_data = NULL;
put_disk(disk);
+ kfree(tpnt->stats);
kfree(tpnt);
return;
}
@@ -4513,6 +4577,184 @@ options_show(struct device *dev, struct device_attribute *attr, char *buf)
}
static DEVICE_ATTR_RO(options);
+/* Support for tape stats */
+
+/**
+ * read_cnt_show - return read count - count of reads made from tape drive
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t read_cnt_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct st_modedef *STm = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lld",
+ (long long)atomic64_read(&STm->tape->stats->read_cnt));
+}
+static DEVICE_ATTR_RO(read_cnt);
+
+/**
+ * read_byte_cnt_show - return read byte count - tape drives
+ * may use blocks less than 512 bytes this gives the raw byte count of
+ * of data read from the tape drive.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t read_byte_cnt_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct st_modedef *STm = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lld",
+ (long long)atomic64_read(&STm->tape->stats->read_byte_cnt));
+}
+static DEVICE_ATTR_RO(read_byte_cnt);
+
+/**
+ * read_us_show - return read us - overall time spent waiting on reads in ns.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t read_ns_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct st_modedef *STm = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lld",
+ (long long)atomic64_read(&STm->tape->stats->tot_read_time));
+}
+static DEVICE_ATTR_RO(read_ns);
+
+/**
+ * write_cnt_show - write count - number of user calls
+ * to write(2) that have written data to tape.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t write_cnt_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct st_modedef *STm = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lld",
+ (long long)atomic64_read(&STm->tape->stats->write_cnt));
+}
+static DEVICE_ATTR_RO(write_cnt);
+
+/**
+ * write_byte_cnt_show - write byte count - raw count of
+ * bytes written to tape.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t write_byte_cnt_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct st_modedef *STm = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lld",
+ (long long)atomic64_read(&STm->tape->stats->write_byte_cnt));
+}
+static DEVICE_ATTR_RO(write_byte_cnt);
+
+/**
+ * write_ns_show - write ns - number of nanoseconds waiting on write
+ * requests to complete.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t write_ns_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct st_modedef *STm = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lld",
+ (long long)atomic64_read(&STm->tape->stats->tot_write_time));
+}
+static DEVICE_ATTR_RO(write_ns);
+
+/**
+ * in_flight_show - number of I/Os currently in flight -
+ * in most cases this will be either 0 or 1. It may be higher if someone
+ * has also issued other SCSI commands such as via an ioctl.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t in_flight_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct st_modedef *STm = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lld",
+ (long long)atomic64_read(&STm->tape->stats->in_flight));
+}
+static DEVICE_ATTR_RO(in_flight);
+
+/**
+ * io_ns_show - io wait ns - this is the number of ns spent
+ * waiting on all I/O to complete. This includes tape movement commands
+ * such as rewinding, seeking to end of file or tape, it also includes
+ * read and write. To determine the time spent on tape movement
+ * subtract the read and write ns from this value.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t io_ns_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct st_modedef *STm = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lld",
+ (long long)atomic64_read(&STm->tape->stats->tot_io_time));
+}
+static DEVICE_ATTR_RO(io_ns);
+
+/**
+ * other_cnt_show - other io count - this is the number of
+ * I/O requests other than read and write requests.
+ * Typically these are tape movement requests but will include driver
+ * tape movement. This includes only requests issued by the st driver.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t other_cnt_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct st_modedef *STm = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lld",
+ (long long)atomic64_read(&STm->tape->stats->other_cnt));
+}
+static DEVICE_ATTR_RO(other_cnt);
+
+/**
+ * resid_cnt_show - A count of the number of times we get a residual
+ * count - this should indicate someone issuing reads larger than the
+ * block size on tape.
+ * @dev: struct device
+ * @attr: attribute structure
+ * @buf: buffer to return formatted data in
+ */
+static ssize_t resid_cnt_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct st_modedef *STm = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lld",
+ (long long)atomic64_read(&STm->tape->stats->resid_cnt));
+}
+static DEVICE_ATTR_RO(resid_cnt);
+
static struct attribute *st_dev_attrs[] = {
&dev_attr_defined.attr,
&dev_attr_default_blksize.attr,
@@ -4521,7 +4763,35 @@ static struct attribute *st_dev_attrs[] = {
&dev_attr_options.attr,
NULL,
};
-ATTRIBUTE_GROUPS(st_dev);
+
+static struct attribute *st_stats_attrs[] = {
+ &dev_attr_read_cnt.attr,
+ &dev_attr_read_byte_cnt.attr,
+ &dev_attr_read_ns.attr,
+ &dev_attr_write_cnt.attr,
+ &dev_attr_write_byte_cnt.attr,
+ &dev_attr_write_ns.attr,
+ &dev_attr_in_flight.attr,
+ &dev_attr_io_ns.attr,
+ &dev_attr_other_cnt.attr,
+ &dev_attr_resid_cnt.attr,
+ NULL,
+};
+
+static struct attribute_group stats_group = {
+ .name = "stats",
+ .attrs = st_stats_attrs,
+};
+
+static struct attribute_group st_group = {
+ .attrs = st_dev_attrs,
+};
+
+static const struct attribute_group *st_dev_groups[] = {
+ &st_group,
+ &stats_group,
+ NULL,
+};
/* The following functions may be useful for a larger audience. */
static int sgl_map_user_pages(struct st_buffer *STbp,