diff options
author | Seymour, Shane M <shane.seymour@hp.com> | 2015-05-05 21:37:20 -0400 |
---|---|---|
committer | James Bottomley <JBottomley@Odin.com> | 2015-06-02 11:03:25 -0400 |
commit | 05545c92db9637318a98d3d59d400beb819decc7 (patch) | |
tree | 56c911568dd7dd6507aac24f7c8c90b657f7bda0 | |
parent | ba929992522b6d1f866b7021bc50da66f8fdd743 (diff) |
st: implement tape statistics
This patch implements tape statistics in the st module via
sysfs. Current no statistics are available for tape I/O and there
is no easy way to reuse the block layer statistics for tape
as tape is a character device and does not have perform I/O in
sector sized chunks (the size of the data written to tape
can change). For tapes we also need extra stats related to
things like tape movement (via other I/O).
There have been multiple end users requesting statistics
including AT&T (and some HP customers who have not given
permission to be named). It is impossible for them
to investigate any issues related to tape performance
in a non-invasive way.
[jejb: eliminate PRId64]
Signed-off-by: Shane Seymour <shane.seymour@hp.com>
Tested-by: Shane Seymour <shane.seymour@hp.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: James Bottomley <JBottomley@Odin.com>
-rw-r--r-- | Documentation/ABI/testing/sysfs-class-scsi_tape | 109 | ||||
-rw-r--r-- | Documentation/scsi/st.txt | 59 | ||||
-rw-r--r-- | drivers/scsi/st.c | 272 | ||||
-rw-r--r-- | drivers/scsi/st.h | 22 |
4 files changed, 461 insertions, 1 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-scsi_tape b/Documentation/ABI/testing/sysfs-class-scsi_tape new file mode 100644 index 000000000000..9be398b87ee9 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-scsi_tape | |||
@@ -0,0 +1,109 @@ | |||
1 | What: /sys/class/scsi_tape/*/stats/in_flight | ||
2 | Date: Apr 2015 | ||
3 | KernelVersion: 4.2 | ||
4 | Contact: Shane Seymour <shane.seymour@hp.com> | ||
5 | Description: | ||
6 | Show the number of I/Os currently in-flight between the st | ||
7 | module and the SCSI mid-layer. | ||
8 | Users: | ||
9 | |||
10 | |||
11 | What: /sys/class/scsi_tape/*/stats/io_ns | ||
12 | Date: Apr 2015 | ||
13 | KernelVersion: 4.2 | ||
14 | Contact: Shane Seymour <shane.seymour@hp.com> | ||
15 | Description: | ||
16 | Shows the total amount of time spent waiting for all I/O | ||
17 | to and from the tape drive to complete. This includes all | ||
18 | reads, writes, and other SCSI commands issued to the tape | ||
19 | drive. An example of other SCSI commands would be tape | ||
20 | movement such as a rewind when a rewind tape device is | ||
21 | closed. This item is measured in nanoseconds. | ||
22 | |||
23 | To determine the amount of time spent waiting for other I/O | ||
24 | to complete subtract read_ns and write_ns from this value. | ||
25 | Users: | ||
26 | |||
27 | |||
28 | What: /sys/class/scsi_tape/*/stats/other_cnt | ||
29 | Date: Apr 2015 | ||
30 | KernelVersion: 4.2 | ||
31 | Contact: Shane Seymour <shane.seymour@hp.com> | ||
32 | Description: | ||
33 | The number of I/O requests issued to the tape drive other | ||
34 | than SCSI read/write requests. | ||
35 | Users: | ||
36 | |||
37 | |||
38 | What: /sys/class/scsi_tape/*/stats/read_byte_cnt | ||
39 | Date: Apr 2015 | ||
40 | KernelVersion: 4.2 | ||
41 | Contact: Shane Seymour <shane.seymour@hp.com> | ||
42 | Description: | ||
43 | Shows the total number of bytes requested from the tape drive. | ||
44 | This value is presented in bytes because tape drives support | ||
45 | variable length block sizes. | ||
46 | Users: | ||
47 | |||
48 | |||
49 | What: /sys/class/scsi_tape/*/stats/read_cnt | ||
50 | Date: Apr 2015 | ||
51 | KernelVersion: 4.2 | ||
52 | Contact: Shane Seymour <shane.seymour@hp.com> | ||
53 | Description: | ||
54 | Shows the total number of read requests issued to the tape | ||
55 | drive. | ||
56 | Users: | ||
57 | |||
58 | |||
59 | What: /sys/class/scsi_tape/*/stats/read_ns | ||
60 | Date: Apr 2015 | ||
61 | KernelVersion: 4.2 | ||
62 | Contact: Shane Seymour <shane.seymour@hp.com> | ||
63 | Description: | ||
64 | Shows the total amount of time in nanoseconds waiting for | ||
65 | read I/O requests to complete. | ||
66 | Users: | ||
67 | |||
68 | |||
69 | What: /sys/class/scsi_tape/*/stats/write_byte_cnt | ||
70 | Date: Apr 2015 | ||
71 | KernelVersion: 4.2 | ||
72 | Contact: Shane Seymour <shane.seymour@hp.com> | ||
73 | Description: | ||
74 | Shows the total number of bytes written to the tape drive. | ||
75 | This value is presented in bytes because tape drives support | ||
76 | variable length block sizes. | ||
77 | Users: | ||
78 | |||
79 | |||
80 | What: /sys/class/scsi_tape/*/stats/write_cnt | ||
81 | Date: Apr 2015 | ||
82 | KernelVersion: 4.2 | ||
83 | Contact: Shane Seymour <shane.seymour@hp.com> | ||
84 | Description: | ||
85 | Shows the total number of write requests issued to the tape | ||
86 | drive. | ||
87 | Users: | ||
88 | |||
89 | |||
90 | What: /sys/class/scsi_tape/*/stats/write_ms | ||
91 | Date: Apr 2015 | ||
92 | KernelVersion: 4.2 | ||
93 | Contact: Shane Seymour <shane.seymour@hp.com> | ||
94 | Description: | ||
95 | Shows the total amount of time in nanoseconds waiting for | ||
96 | write I/O requests to complete. | ||
97 | Users: | ||
98 | |||
99 | |||
100 | What: /sys/class/scsi_tape/*/stats/resid_cnt | ||
101 | Date: Apr 2015 | ||
102 | KernelVersion: 4.2 | ||
103 | Contact: Shane Seymour <shane.seymour@hp.com> | ||
104 | Description: | ||
105 | Shows the number of times we found that a residual >0 | ||
106 | was found when the SCSI midlayer indicated that there was | ||
107 | an error. For reads this may be a case of someone issuing | ||
108 | reads greater than the block size. | ||
109 | Users: | ||
diff --git a/Documentation/scsi/st.txt b/Documentation/scsi/st.txt index 0d5bdb153d3b..f29fa550665a 100644 --- a/Documentation/scsi/st.txt +++ b/Documentation/scsi/st.txt | |||
@@ -151,6 +151,65 @@ A link named 'tape' is made from the SCSI device directory to the class | |||
151 | directory corresponding to the mode 0 auto-rewind device (e.g., st0). | 151 | directory corresponding to the mode 0 auto-rewind device (e.g., st0). |
152 | 152 | ||
153 | 153 | ||
154 | SYSFS AND STATISTICS FOR TAPE DEVICES | ||
155 | |||
156 | The st driver maintains statistics for tape drives inside the sysfs filesystem. | ||
157 | The following method can be used to locate the statistics that are | ||
158 | available (assuming that sysfs is mounted at /sys): | ||
159 | |||
160 | 1. Use opendir(3) on the directory /sys/class/scsi_tape | ||
161 | 2. Use readdir(3) to read the directory contents | ||
162 | 3. Use regcomp(3)/regexec(3) to match directory entries to the extended | ||
163 | regular expression "^st[0-9]+$" | ||
164 | 4. Access the statistics from the /sys/class/scsi_tape/<match>/stats | ||
165 | directory (where <match> is a directory entry from /sys/class/scsi_tape | ||
166 | that matched the extended regular expression) | ||
167 | |||
168 | The reason for using this approach is that all the character devices | ||
169 | pointing to the same tape drive use the same statistics. That means | ||
170 | that st0 would have the same statistics as nst0. | ||
171 | |||
172 | The directory contains the following statistics files: | ||
173 | |||
174 | 1. in_flight - The number of I/Os currently outstanding to this device. | ||
175 | 2. io_ns - The amount of time spent waiting (in nanoseconds) for all I/O | ||
176 | to complete (including read and write). This includes tape movement | ||
177 | commands such as seeking between file or set marks and implicit tape | ||
178 | movement such as when rewind on close tape devices are used. | ||
179 | 3. other_cnt - The number of I/Os issued to the tape drive other than read or | ||
180 | write commands. The time taken to complete these commands uses the | ||
181 | following calculation io_ms-read_ms-write_ms. | ||
182 | 4. read_byte_cnt - The number of bytes read from the tape drive. | ||
183 | 5. read_cnt - The number of read requests issued to the tape drive. | ||
184 | 6. read_ns - The amount of time (in nanoseconds) spent waiting for read | ||
185 | requests to complete. | ||
186 | 7. write_byte_cnt - The number of bytes written to the tape drive. | ||
187 | 8. write_cnt - The number of write requests issued to the tape drive. | ||
188 | 9. write_ns - The amount of time (in nanoseconds) spent waiting for write | ||
189 | requests to complete. | ||
190 | 10. resid_cnt - The number of times during a read or write we found | ||
191 | the residual amount to be non-zero. This should mean that a program | ||
192 | is issuing a read larger thean the block size on tape. For write | ||
193 | not all data made it to tape. | ||
194 | |||
195 | Note: The in_flight value is incremented when an I/O starts the I/O | ||
196 | itself is not added to the statistics until it completes. | ||
197 | |||
198 | The total of read_cnt, write_cnt, and other_cnt may not total to the same | ||
199 | value as iodone_cnt at the device level. The tape statistics only count | ||
200 | I/O issued via the st module. | ||
201 | |||
202 | When read the statistics may not be temporally consistent while I/O is in | ||
203 | progress. The individual values are read and written to atomically however | ||
204 | when reading them back via sysfs they may be in the process of being | ||
205 | updated when starting an I/O or when it is completed. | ||
206 | |||
207 | The value shown in in_flight is incremented before any statstics are | ||
208 | updated and decremented when an I/O completes after updating statistics. | ||
209 | The value of in_flight is 0 when there are no I/Os outstanding that are | ||
210 | issued by the st driver. Tape statistics do not take into account any | ||
211 | I/O performed via the sg device. | ||
212 | |||
154 | BSD AND SYS V SEMANTICS | 213 | BSD AND SYS V SEMANTICS |
155 | 214 | ||
156 | The user can choose between these two behaviours of the tape driver by | 215 | The user can choose between these two behaviours of the tape driver by |
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) | |||
471 | kfree(streq); | 471 | kfree(streq); |
472 | } | 472 | } |
473 | 473 | ||
474 | static void st_do_stats(struct scsi_tape *STp, struct request *req) | ||
475 | { | ||
476 | ktime_t now; | ||
477 | |||
478 | now = ktime_get(); | ||
479 | if (req->cmd[0] == WRITE_6) { | ||
480 | now = ktime_sub(now, STp->stats->write_time); | ||
481 | atomic64_add(ktime_to_ns(now), &STp->stats->tot_write_time); | ||
482 | atomic64_add(ktime_to_ns(now), &STp->stats->tot_io_time); | ||
483 | atomic64_inc(&STp->stats->write_cnt); | ||
484 | if (req->errors) { | ||
485 | atomic64_add(atomic_read(&STp->stats->last_write_size) | ||
486 | - STp->buffer->cmdstat.residual, | ||
487 | &STp->stats->write_byte_cnt); | ||
488 | if (STp->buffer->cmdstat.residual > 0) | ||
489 | atomic64_inc(&STp->stats->resid_cnt); | ||
490 | } else | ||
491 | atomic64_add(atomic_read(&STp->stats->last_write_size), | ||
492 | &STp->stats->write_byte_cnt); | ||
493 | } else if (req->cmd[0] == READ_6) { | ||
494 | now = ktime_sub(now, STp->stats->read_time); | ||
495 | atomic64_add(ktime_to_ns(now), &STp->stats->tot_read_time); | ||
496 | atomic64_add(ktime_to_ns(now), &STp->stats->tot_io_time); | ||
497 | atomic64_inc(&STp->stats->read_cnt); | ||
498 | if (req->errors) { | ||
499 | atomic64_add(atomic_read(&STp->stats->last_read_size) | ||
500 | - STp->buffer->cmdstat.residual, | ||
501 | &STp->stats->read_byte_cnt); | ||
502 | if (STp->buffer->cmdstat.residual > 0) | ||
503 | atomic64_inc(&STp->stats->resid_cnt); | ||
504 | } else | ||
505 | atomic64_add(atomic_read(&STp->stats->last_read_size), | ||
506 | &STp->stats->read_byte_cnt); | ||
507 | } else { | ||
508 | now = ktime_sub(now, STp->stats->other_time); | ||
509 | atomic64_add(ktime_to_ns(now), &STp->stats->tot_io_time); | ||
510 | atomic64_inc(&STp->stats->other_cnt); | ||
511 | } | ||
512 | atomic64_dec(&STp->stats->in_flight); | ||
513 | } | ||
514 | |||
474 | static void st_scsi_execute_end(struct request *req, int uptodate) | 515 | static void st_scsi_execute_end(struct request *req, int uptodate) |
475 | { | 516 | { |
476 | struct st_request *SRpnt = req->end_io_data; | 517 | struct st_request *SRpnt = req->end_io_data; |
@@ -480,6 +521,8 @@ static void st_scsi_execute_end(struct request *req, int uptodate) | |||
480 | STp->buffer->cmdstat.midlevel_result = SRpnt->result = req->errors; | 521 | STp->buffer->cmdstat.midlevel_result = SRpnt->result = req->errors; |
481 | STp->buffer->cmdstat.residual = req->resid_len; | 522 | STp->buffer->cmdstat.residual = req->resid_len; |
482 | 523 | ||
524 | st_do_stats(STp, req); | ||
525 | |||
483 | tmp = SRpnt->bio; | 526 | tmp = SRpnt->bio; |
484 | if (SRpnt->waiting) | 527 | if (SRpnt->waiting) |
485 | complete(SRpnt->waiting); | 528 | complete(SRpnt->waiting); |
@@ -496,6 +539,7 @@ static int st_scsi_execute(struct st_request *SRpnt, const unsigned char *cmd, | |||
496 | struct rq_map_data *mdata = &SRpnt->stp->buffer->map_data; | 539 | struct rq_map_data *mdata = &SRpnt->stp->buffer->map_data; |
497 | int err = 0; | 540 | int err = 0; |
498 | int write = (data_direction == DMA_TO_DEVICE); | 541 | int write = (data_direction == DMA_TO_DEVICE); |
542 | struct scsi_tape *STp = SRpnt->stp; | ||
499 | 543 | ||
500 | req = blk_get_request(SRpnt->stp->device->request_queue, write, | 544 | req = blk_get_request(SRpnt->stp->device->request_queue, write, |
501 | GFP_KERNEL); | 545 | GFP_KERNEL); |
@@ -516,6 +560,17 @@ static int st_scsi_execute(struct st_request *SRpnt, const unsigned char *cmd, | |||
516 | } | 560 | } |
517 | } | 561 | } |
518 | 562 | ||
563 | atomic64_inc(&STp->stats->in_flight); | ||
564 | if (cmd[0] == WRITE_6) { | ||
565 | atomic_set(&STp->stats->last_write_size, bufflen); | ||
566 | STp->stats->write_time = ktime_get(); | ||
567 | } else if (cmd[0] == READ_6) { | ||
568 | atomic_set(&STp->stats->last_read_size, bufflen); | ||
569 | STp->stats->read_time = ktime_get(); | ||
570 | } else { | ||
571 | STp->stats->other_time = ktime_get(); | ||
572 | } | ||
573 | |||
519 | SRpnt->bio = req->bio; | 574 | SRpnt->bio = req->bio; |
520 | req->cmd_len = COMMAND_SIZE(cmd[0]); | 575 | req->cmd_len = COMMAND_SIZE(cmd[0]); |
521 | memset(req->cmd, 0, BLK_MAX_CDB); | 576 | memset(req->cmd, 0, BLK_MAX_CDB); |
@@ -4222,6 +4277,12 @@ static int st_probe(struct device *dev) | |||
4222 | } | 4277 | } |
4223 | tpnt->index = error; | 4278 | tpnt->index = error; |
4224 | sprintf(disk->disk_name, "st%d", tpnt->index); | 4279 | sprintf(disk->disk_name, "st%d", tpnt->index); |
4280 | tpnt->stats = kzalloc(sizeof(struct scsi_tape_stats), GFP_KERNEL); | ||
4281 | if (tpnt->stats == NULL) { | ||
4282 | sdev_printk(KERN_ERR, SDp, | ||
4283 | "st: Can't allocate statistics.\n"); | ||
4284 | goto out_idr_remove; | ||
4285 | } | ||
4225 | 4286 | ||
4226 | dev_set_drvdata(dev, tpnt); | 4287 | dev_set_drvdata(dev, tpnt); |
4227 | 4288 | ||
@@ -4241,6 +4302,8 @@ static int st_probe(struct device *dev) | |||
4241 | 4302 | ||
4242 | out_remove_devs: | 4303 | out_remove_devs: |
4243 | remove_cdevs(tpnt); | 4304 | remove_cdevs(tpnt); |
4305 | kfree(tpnt->stats); | ||
4306 | out_idr_remove: | ||
4244 | spin_lock(&st_index_lock); | 4307 | spin_lock(&st_index_lock); |
4245 | idr_remove(&st_index_idr, tpnt->index); | 4308 | idr_remove(&st_index_idr, tpnt->index); |
4246 | spin_unlock(&st_index_lock); | 4309 | spin_unlock(&st_index_lock); |
@@ -4298,6 +4361,7 @@ static void scsi_tape_release(struct kref *kref) | |||
4298 | 4361 | ||
4299 | disk->private_data = NULL; | 4362 | disk->private_data = NULL; |
4300 | put_disk(disk); | 4363 | put_disk(disk); |
4364 | kfree(tpnt->stats); | ||
4301 | kfree(tpnt); | 4365 | kfree(tpnt); |
4302 | return; | 4366 | return; |
4303 | } | 4367 | } |
@@ -4513,6 +4577,184 @@ options_show(struct device *dev, struct device_attribute *attr, char *buf) | |||
4513 | } | 4577 | } |
4514 | static DEVICE_ATTR_RO(options); | 4578 | static DEVICE_ATTR_RO(options); |
4515 | 4579 | ||
4580 | /* Support for tape stats */ | ||
4581 | |||
4582 | /** | ||
4583 | * read_cnt_show - return read count - count of reads made from tape drive | ||
4584 | * @dev: struct device | ||
4585 | * @attr: attribute structure | ||
4586 | * @buf: buffer to return formatted data in | ||
4587 | */ | ||
4588 | static ssize_t read_cnt_show(struct device *dev, | ||
4589 | struct device_attribute *attr, char *buf) | ||
4590 | { | ||
4591 | struct st_modedef *STm = dev_get_drvdata(dev); | ||
4592 | |||
4593 | return sprintf(buf, "%lld", | ||
4594 | (long long)atomic64_read(&STm->tape->stats->read_cnt)); | ||
4595 | } | ||
4596 | static DEVICE_ATTR_RO(read_cnt); | ||
4597 | |||
4598 | /** | ||
4599 | * read_byte_cnt_show - return read byte count - tape drives | ||
4600 | * may use blocks less than 512 bytes this gives the raw byte count of | ||
4601 | * of data read from the tape drive. | ||
4602 | * @dev: struct device | ||
4603 | * @attr: attribute structure | ||
4604 | * @buf: buffer to return formatted data in | ||
4605 | */ | ||
4606 | static ssize_t read_byte_cnt_show(struct device *dev, | ||
4607 | struct device_attribute *attr, char *buf) | ||
4608 | { | ||
4609 | struct st_modedef *STm = dev_get_drvdata(dev); | ||
4610 | |||
4611 | return sprintf(buf, "%lld", | ||
4612 | (long long)atomic64_read(&STm->tape->stats->read_byte_cnt)); | ||
4613 | } | ||
4614 | static DEVICE_ATTR_RO(read_byte_cnt); | ||
4615 | |||
4616 | /** | ||
4617 | * read_us_show - return read us - overall time spent waiting on reads in ns. | ||
4618 | * @dev: struct device | ||
4619 | * @attr: attribute structure | ||
4620 | * @buf: buffer to return formatted data in | ||
4621 | */ | ||
4622 | static ssize_t read_ns_show(struct device *dev, | ||
4623 | struct device_attribute *attr, char *buf) | ||
4624 | { | ||
4625 | struct st_modedef *STm = dev_get_drvdata(dev); | ||
4626 | |||
4627 | return sprintf(buf, "%lld", | ||
4628 | (long long)atomic64_read(&STm->tape->stats->tot_read_time)); | ||
4629 | } | ||
4630 | static DEVICE_ATTR_RO(read_ns); | ||
4631 | |||
4632 | /** | ||
4633 | * write_cnt_show - write count - number of user calls | ||
4634 | * to write(2) that have written data to tape. | ||
4635 | * @dev: struct device | ||
4636 | * @attr: attribute structure | ||
4637 | * @buf: buffer to return formatted data in | ||
4638 | */ | ||
4639 | static ssize_t write_cnt_show(struct device *dev, | ||
4640 | struct device_attribute *attr, char *buf) | ||
4641 | { | ||
4642 | struct st_modedef *STm = dev_get_drvdata(dev); | ||
4643 | |||
4644 | return sprintf(buf, "%lld", | ||
4645 | (long long)atomic64_read(&STm->tape->stats->write_cnt)); | ||
4646 | } | ||
4647 | static DEVICE_ATTR_RO(write_cnt); | ||
4648 | |||
4649 | /** | ||
4650 | * write_byte_cnt_show - write byte count - raw count of | ||
4651 | * bytes written to tape. | ||
4652 | * @dev: struct device | ||
4653 | * @attr: attribute structure | ||
4654 | * @buf: buffer to return formatted data in | ||
4655 | */ | ||
4656 | static ssize_t write_byte_cnt_show(struct device *dev, | ||
4657 | struct device_attribute *attr, char *buf) | ||
4658 | { | ||
4659 | struct st_modedef *STm = dev_get_drvdata(dev); | ||
4660 | |||
4661 | return sprintf(buf, "%lld", | ||
4662 | (long long)atomic64_read(&STm->tape->stats->write_byte_cnt)); | ||
4663 | } | ||
4664 | static DEVICE_ATTR_RO(write_byte_cnt); | ||
4665 | |||
4666 | /** | ||
4667 | * write_ns_show - write ns - number of nanoseconds waiting on write | ||
4668 | * requests to complete. | ||
4669 | * @dev: struct device | ||
4670 | * @attr: attribute structure | ||
4671 | * @buf: buffer to return formatted data in | ||
4672 | */ | ||
4673 | static ssize_t write_ns_show(struct device *dev, | ||
4674 | struct device_attribute *attr, char *buf) | ||
4675 | { | ||
4676 | struct st_modedef *STm = dev_get_drvdata(dev); | ||
4677 | |||
4678 | return sprintf(buf, "%lld", | ||
4679 | (long long)atomic64_read(&STm->tape->stats->tot_write_time)); | ||
4680 | } | ||
4681 | static DEVICE_ATTR_RO(write_ns); | ||
4682 | |||
4683 | /** | ||
4684 | * in_flight_show - number of I/Os currently in flight - | ||
4685 | * in most cases this will be either 0 or 1. It may be higher if someone | ||
4686 | * has also issued other SCSI commands such as via an ioctl. | ||
4687 | * @dev: struct device | ||
4688 | * @attr: attribute structure | ||
4689 | * @buf: buffer to return formatted data in | ||
4690 | */ | ||
4691 | static ssize_t in_flight_show(struct device *dev, | ||
4692 | struct device_attribute *attr, char *buf) | ||
4693 | { | ||
4694 | struct st_modedef *STm = dev_get_drvdata(dev); | ||
4695 | |||
4696 | return sprintf(buf, "%lld", | ||
4697 | (long long)atomic64_read(&STm->tape->stats->in_flight)); | ||
4698 | } | ||
4699 | static DEVICE_ATTR_RO(in_flight); | ||
4700 | |||
4701 | /** | ||
4702 | * io_ns_show - io wait ns - this is the number of ns spent | ||
4703 | * waiting on all I/O to complete. This includes tape movement commands | ||
4704 | * such as rewinding, seeking to end of file or tape, it also includes | ||
4705 | * read and write. To determine the time spent on tape movement | ||
4706 | * subtract the read and write ns from this value. | ||
4707 | * @dev: struct device | ||
4708 | * @attr: attribute structure | ||
4709 | * @buf: buffer to return formatted data in | ||
4710 | */ | ||
4711 | static ssize_t io_ns_show(struct device *dev, | ||
4712 | struct device_attribute *attr, char *buf) | ||
4713 | { | ||
4714 | struct st_modedef *STm = dev_get_drvdata(dev); | ||
4715 | |||
4716 | return sprintf(buf, "%lld", | ||
4717 | (long long)atomic64_read(&STm->tape->stats->tot_io_time)); | ||
4718 | } | ||
4719 | static DEVICE_ATTR_RO(io_ns); | ||
4720 | |||
4721 | /** | ||
4722 | * other_cnt_show - other io count - this is the number of | ||
4723 | * I/O requests other than read and write requests. | ||
4724 | * Typically these are tape movement requests but will include driver | ||
4725 | * tape movement. This includes only requests issued by the st driver. | ||
4726 | * @dev: struct device | ||
4727 | * @attr: attribute structure | ||
4728 | * @buf: buffer to return formatted data in | ||
4729 | */ | ||
4730 | static ssize_t other_cnt_show(struct device *dev, | ||
4731 | struct device_attribute *attr, char *buf) | ||
4732 | { | ||
4733 | struct st_modedef *STm = dev_get_drvdata(dev); | ||
4734 | |||
4735 | return sprintf(buf, "%lld", | ||
4736 | (long long)atomic64_read(&STm->tape->stats->other_cnt)); | ||
4737 | } | ||
4738 | static DEVICE_ATTR_RO(other_cnt); | ||
4739 | |||
4740 | /** | ||
4741 | * resid_cnt_show - A count of the number of times we get a residual | ||
4742 | * count - this should indicate someone issuing reads larger than the | ||
4743 | * block size on tape. | ||
4744 | * @dev: struct device | ||
4745 | * @attr: attribute structure | ||
4746 | * @buf: buffer to return formatted data in | ||
4747 | */ | ||
4748 | static ssize_t resid_cnt_show(struct device *dev, | ||
4749 | struct device_attribute *attr, char *buf) | ||
4750 | { | ||
4751 | struct st_modedef *STm = dev_get_drvdata(dev); | ||
4752 | |||
4753 | return sprintf(buf, "%lld", | ||
4754 | (long long)atomic64_read(&STm->tape->stats->resid_cnt)); | ||
4755 | } | ||
4756 | static DEVICE_ATTR_RO(resid_cnt); | ||
4757 | |||
4516 | static struct attribute *st_dev_attrs[] = { | 4758 | static struct attribute *st_dev_attrs[] = { |
4517 | &dev_attr_defined.attr, | 4759 | &dev_attr_defined.attr, |
4518 | &dev_attr_default_blksize.attr, | 4760 | &dev_attr_default_blksize.attr, |
@@ -4521,7 +4763,35 @@ static struct attribute *st_dev_attrs[] = { | |||
4521 | &dev_attr_options.attr, | 4763 | &dev_attr_options.attr, |
4522 | NULL, | 4764 | NULL, |
4523 | }; | 4765 | }; |
4524 | ATTRIBUTE_GROUPS(st_dev); | 4766 | |
4767 | static struct attribute *st_stats_attrs[] = { | ||
4768 | &dev_attr_read_cnt.attr, | ||
4769 | &dev_attr_read_byte_cnt.attr, | ||
4770 | &dev_attr_read_ns.attr, | ||
4771 | &dev_attr_write_cnt.attr, | ||
4772 | &dev_attr_write_byte_cnt.attr, | ||
4773 | &dev_attr_write_ns.attr, | ||
4774 | &dev_attr_in_flight.attr, | ||
4775 | &dev_attr_io_ns.attr, | ||
4776 | &dev_attr_other_cnt.attr, | ||
4777 | &dev_attr_resid_cnt.attr, | ||
4778 | NULL, | ||
4779 | }; | ||
4780 | |||
4781 | static struct attribute_group stats_group = { | ||
4782 | .name = "stats", | ||
4783 | .attrs = st_stats_attrs, | ||
4784 | }; | ||
4785 | |||
4786 | static struct attribute_group st_group = { | ||
4787 | .attrs = st_dev_attrs, | ||
4788 | }; | ||
4789 | |||
4790 | static const struct attribute_group *st_dev_groups[] = { | ||
4791 | &st_group, | ||
4792 | &stats_group, | ||
4793 | NULL, | ||
4794 | }; | ||
4525 | 4795 | ||
4526 | /* The following functions may be useful for a larger audience. */ | 4796 | /* The following functions may be useful for a larger audience. */ |
4527 | static int sgl_map_user_pages(struct st_buffer *STbp, | 4797 | static int sgl_map_user_pages(struct st_buffer *STbp, |
diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h index f3eee0f9f40c..b6486b5d8681 100644 --- a/drivers/scsi/st.h +++ b/drivers/scsi/st.h | |||
@@ -92,6 +92,27 @@ struct st_partstat { | |||
92 | int drv_file; | 92 | int drv_file; |
93 | }; | 93 | }; |
94 | 94 | ||
95 | /* Tape statistics */ | ||
96 | struct scsi_tape_stats { | ||
97 | atomic64_t read_byte_cnt; /* bytes read */ | ||
98 | atomic64_t write_byte_cnt; /* bytes written */ | ||
99 | atomic64_t in_flight; /* Number of I/Os in flight */ | ||
100 | atomic64_t read_cnt; /* Count of read requests */ | ||
101 | atomic64_t write_cnt; /* Count of write requests */ | ||
102 | atomic64_t other_cnt; /* Count of other requests either | ||
103 | * implicit or from user space | ||
104 | * ioctl. */ | ||
105 | atomic64_t resid_cnt; /* Count of resid_len > 0 */ | ||
106 | atomic64_t tot_read_time; /* ktime spent completing reads */ | ||
107 | atomic64_t tot_write_time; /* ktime spent completing writes */ | ||
108 | atomic64_t tot_io_time; /* ktime spent doing any I/O */ | ||
109 | ktime_t read_time; /* holds ktime request was queued */ | ||
110 | ktime_t write_time; /* holds ktime request was queued */ | ||
111 | ktime_t other_time; /* holds ktime request was queued */ | ||
112 | atomic_t last_read_size; /* Number of bytes issued for last read */ | ||
113 | atomic_t last_write_size; /* Number of bytes issued for last write */ | ||
114 | }; | ||
115 | |||
95 | #define ST_NBR_PARTITIONS 4 | 116 | #define ST_NBR_PARTITIONS 4 |
96 | 117 | ||
97 | /* The tape drive descriptor */ | 118 | /* The tape drive descriptor */ |
@@ -171,6 +192,7 @@ struct scsi_tape { | |||
171 | #endif | 192 | #endif |
172 | struct gendisk *disk; | 193 | struct gendisk *disk; |
173 | struct kref kref; | 194 | struct kref kref; |
195 | struct scsi_tape_stats *stats; | ||
174 | }; | 196 | }; |
175 | 197 | ||
176 | /* Bit masks for use_pf */ | 198 | /* Bit masks for use_pf */ |