1 Index: linux-2.6.16.60-0.37/drivers/scsi/Kconfig
2 ===================================================================
3 --- linux-2.6.16.60-0.37.orig/drivers/scsi/Kconfig 2009-03-24 05:46:32.000000000 -0700
4 +++ linux-2.6.16.60-0.37/drivers/scsi/Kconfig 2009-06-02 23:33:14.000000000 -0600
6 To compile this driver as a module, choose M here and read
7 <file:Documentation/scsi/scsi.txt>. The module will be called st.
10 + bool "Enable SCSI disk I/O stats"
11 + depends on BLK_DEV_SD
14 + This enables SCSI disk I/O stats collection. You must also enable
15 + /proc file system support if you want this feature.
18 tristate "SCSI OnStream SC-x0 tape support"
20 Index: linux-2.6.16.60-0.37/drivers/scsi/scsi_proc.c
21 ===================================================================
22 --- linux-2.6.16.60-0.37.orig/drivers/scsi/scsi_proc.c 2009-03-24 05:46:25.000000000 -0700
23 +++ linux-2.6.16.60-0.37/drivers/scsi/scsi_proc.c 2009-06-02 23:33:14.000000000 -0600
25 /* 4K page size, but our output routines, use some slack for overruns */
26 #define PROC_BLOCK_SIZE (3*1024)
28 -static struct proc_dir_entry *proc_scsi;
29 +struct proc_dir_entry *proc_scsi;
30 +EXPORT_SYMBOL(proc_scsi);
32 /* Protect sht->present and sht->proc_dir */
33 static DEFINE_MUTEX(global_host_template_mutex);
34 Index: linux-2.6.16.60-0.37/drivers/scsi/sd.c
35 ===================================================================
36 --- linux-2.6.16.60-0.37.orig/drivers/scsi/sd.c 2009-03-24 05:46:25.000000000 -0700
37 +++ linux-2.6.16.60-0.37/drivers/scsi/sd.c 2009-06-02 23:33:14.000000000 -0600
40 #include "scsi_logging.h"
42 +#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
43 +# include <linux/proc_fs.h>
44 +# include <linux/seq_file.h>
47 + unsigned long long iostat_size;
48 + unsigned long long iostat_count;
51 +#define IOSTAT_NCOUNTERS 16
53 + iostat_counter_t iostat_read_histogram[IOSTAT_NCOUNTERS];
54 + iostat_counter_t iostat_write_histogram[IOSTAT_NCOUNTERS];
55 + struct timeval iostat_timeval;
57 + /* queue depth: how well the pipe is filled up */
58 + unsigned long long iostat_queue_ticks[IOSTAT_NCOUNTERS];
59 + unsigned long long iostat_queue_ticks_sum;
60 + unsigned long iostat_queue_depth;
61 + unsigned long iostat_queue_stamp;
63 + /* seeks: how linear the traffic is */
64 + unsigned long long iostat_next_sector;
65 + unsigned long long iostat_seek_sectors;
66 + unsigned long long iostat_seeks;
67 + unsigned long long iostat_sectors;
68 + unsigned long long iostat_reqs;
69 + unsigned long iostat_read_reqs;
70 + unsigned long iostat_write_reqs;
72 + /* process time: how long it takes to process requests */
73 + unsigned long iostat_rtime[IOSTAT_NCOUNTERS];
74 + unsigned long iostat_wtime[IOSTAT_NCOUNTERS];
76 + /* queue time: how long process spent in elevator's queue */
77 + unsigned long iostat_rtime_in_queue[IOSTAT_NCOUNTERS];
78 + unsigned long iostat_wtime_in_queue[IOSTAT_NCOUNTERS];
80 + /* must be the last field, as it's used to know size to be memset'ed */
81 + spinlock_t iostat_lock;
82 +} ____cacheline_aligned_in_smp iostat_stats_t;
84 +struct proc_dir_entry *sd_iostats_procdir = NULL;
85 +char sd_iostats_procdir_name[] = "sd_iostats";
86 +static struct file_operations sd_iostats_proc_fops;
88 +extern void sd_iostats_init(void);
89 +extern void sd_iostats_fini(void);
90 +void sd_iostats_start_req(struct scsi_cmnd *SCpnt);
91 +void sd_iostats_finish_req(struct scsi_cmnd *SCpnt);
93 +static inline void sd_iostats_init(void) {}
94 +static inline void sd_iostats_fini(void) {}
95 +static inline void sd_iostats_start_req(struct scsi_cmnd *SCpnt) {}
96 +static inline void sd_iostats_finish_req(struct scsi_cmnd *SCpnt) {}
100 * More than enough for everybody ;) The huge number of majors
101 * is a leftover from 16bit dev_t days, we don't really need that
103 unsigned WCE : 1; /* state of disk WCE bit */
104 unsigned RCD : 1; /* state of disk RCD bit, unused */
105 unsigned DPOFUA : 1; /* state of disk DPOFUA bit */
106 +#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
107 + iostat_stats_t *stats; /* scsi disk statistics */
110 #define to_scsi_disk(obj) container_of(obj,struct scsi_disk,cdev)
114 SCpnt->done = sd_rw_intr;
116 + sd_iostats_start_req(SCpnt);
119 * This indicates that the command is ready from our end to be
121 @@ -1014,6 +1076,7 @@
125 + sd_iostats_finish_req(SCpnt);
126 scsi_io_completion(SCpnt, good_bytes);
129 @@ -1713,6 +1776,36 @@
131 gd->flags |= GENHD_FL_REMOVABLE;
133 +#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
134 + sdkp->stats = kzalloc(sizeof(iostat_stats_t), GFP_KERNEL);
135 + if (!sdkp->stats) {
136 + printk(KERN_WARNING "cannot allocate iostat structure for"
137 + "%s\n", gd->disk_name);
139 + do_gettimeofday(&sdkp->stats->iostat_timeval);
140 + sdkp->stats->iostat_queue_stamp = jiffies;
141 + spin_lock_init(&sdkp->stats->iostat_lock);
142 + if (sd_iostats_procdir) {
143 + struct proc_dir_entry *pde;
144 + pde = create_proc_entry(gd->disk_name, S_IRUGO | S_IWUSR,
145 + sd_iostats_procdir);
147 + printk(KERN_WARNING "Can't create /proc/scsi/"
149 + sd_iostats_procdir_name,
151 + kfree(sdkp->stats);
152 + sdkp->stats = NULL;
154 + pde->proc_fops = &sd_iostats_proc_fops;
158 + kfree(sdkp->stats);
159 + sdkp->stats = NULL;
163 dev_set_drvdata(dev, sdkp);
166 @@ -1756,6 +1849,366 @@
170 +#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
172 +sd_iostats_seq_show(struct seq_file *seq, void *v)
174 + struct timeval now;
175 + struct gendisk *disk = seq->private;
176 + iostat_stats_t *stats;
177 + unsigned long long read_len;
178 + unsigned long long read_len_tot;
179 + unsigned long read_num;
180 + unsigned long read_num_tot;
181 + unsigned long long write_len;
182 + unsigned long long write_len_tot;
183 + unsigned long write_num;
184 + unsigned long write_num_tot;
188 + stats = scsi_disk(disk)->stats;
189 + if (stats == NULL) {
190 + printk(KERN_ERR "sd_iostats_seq_show: NULL stats entry\n");
194 + do_gettimeofday(&now);
195 + now.tv_sec -= stats->iostat_timeval.tv_sec;
196 + now.tv_usec -= stats->iostat_timeval.tv_usec;
197 + if (now.tv_usec < 0) {
198 + now.tv_usec += 1000000;
202 + /* this sampling races with updates */
203 + seq_printf(seq, "index: %lu snapshot_time: %lu.%06lu\n",
204 + (unsigned long) scsi_disk(disk)->index,
205 + now.tv_sec, now.tv_usec);
207 + for (i = IOSTAT_NCOUNTERS - 1; i > 0; i--)
208 + if (stats->iostat_read_histogram[i].iostat_count != 0 ||
209 + stats->iostat_write_histogram[i].iostat_count != 0)
213 + seq_printf(seq, "%8s %8s %12s %8s %12s\n", "size",
214 + "reads", "total", "writes", "total");
216 + read_len_tot = write_len_tot = 0;
217 + read_num_tot = write_num_tot = 0;
218 + for (i = 0; i <= maxi; i++) {
219 + read_len = stats->iostat_read_histogram[i].iostat_size;
220 + read_len_tot += read_len;
221 + read_num = stats->iostat_read_histogram[i].iostat_count;
222 + read_num_tot += read_num;
224 + write_len = stats->iostat_write_histogram[i].iostat_size;
225 + write_len_tot += write_len;
226 + write_num = stats->iostat_write_histogram[i].iostat_count;
227 + write_num_tot += write_num;
229 + seq_printf (seq, "%8d %8lu %12llu %8lu %12llu\n",
230 + 512<<i, read_num, read_len, write_num, write_len);
233 + seq_printf(seq, "%8s %8lu %12llu %8lu %12llu\n\n", "total",
234 + read_num_tot, read_len_tot,
235 + write_num_tot, write_len_tot);
237 + seq_printf(seq, "%8s %8s %8s\n", "qdepth", "ticks", "%");
238 + for (i = 0; i < IOSTAT_NCOUNTERS; i++) {
239 + unsigned long long ticks, percent;
240 + ticks = stats->iostat_queue_ticks[i];
243 + percent = stats->iostat_queue_ticks[i] * 100;
244 + do_div(percent, stats->iostat_queue_ticks_sum);
245 + seq_printf(seq, "%8d %8llu %8llu\n", i, ticks, percent);
248 + if (stats->iostat_reqs != 0) {
249 + unsigned long long aveseek = 0, percent = 0;
251 + if (stats->iostat_seeks) {
252 + aveseek = stats->iostat_seek_sectors;
253 + do_div(aveseek, stats->iostat_seeks);
254 + percent = stats->iostat_seeks * 100;
255 + do_div(percent, stats->iostat_reqs);
258 + seq_printf(seq, "\n%llu sectors in %llu reqs: %llu seek(s) over "
259 + "%llu sectors in ave, %llu%% of all reqs\n",
260 + stats->iostat_sectors, stats->iostat_reqs,
261 + stats->iostat_seeks, aveseek, percent);
264 + seq_printf(seq, "\n%16s %8s %8s %8s %8s\n", "process time", "reads",
265 + "%%", "writes", "%%");
266 + for (i = 0; i < IOSTAT_NCOUNTERS; i++) {
267 + unsigned long read_percent = 0, write_percent = 0;
268 + if (stats->iostat_wtime[i] == 0 &&
269 + stats->iostat_rtime[i] == 0)
271 + if (stats->iostat_read_reqs)
272 + read_percent = stats->iostat_rtime[i] * 100 /
273 + stats->iostat_read_reqs;
274 + if (stats->iostat_write_reqs)
275 + write_percent = stats->iostat_wtime[i] * 100 /
276 + stats->iostat_write_reqs;
277 + seq_printf(seq, "%16u %8lu %8lu %8lu %8lu\n",
278 + jiffies_to_msecs(((1UL << i) >> 1) << 1),
279 + stats->iostat_rtime[i], read_percent,
280 + stats->iostat_wtime[i], write_percent);
283 + seq_printf(seq, "\n%16s %8s %8s %8s %8s\n", "time in queue", "reads",
284 + "%%", "writes", "%%");
285 + for (i = 0; i < IOSTAT_NCOUNTERS; i++) {
286 + unsigned long read_percent = 0, write_percent = 0;
287 + if (stats->iostat_wtime_in_queue[i] == 0 &&
288 + stats->iostat_rtime_in_queue[i] == 0)
290 + if (stats->iostat_read_reqs)
291 + read_percent = stats->iostat_rtime_in_queue[i] * 100 /
292 + stats->iostat_read_reqs;
293 + if (stats->iostat_write_reqs)
294 + write_percent = stats->iostat_wtime_in_queue[i] * 100 /
295 + stats->iostat_write_reqs;
296 + seq_printf(seq, "%16u %8lu %8lu %8lu %8lu\n",
297 + jiffies_to_msecs(((1UL << i) >> 1) << 1),
298 + stats->iostat_rtime_in_queue[i],
300 + stats->iostat_wtime_in_queue[i],
308 +sd_iostats_seq_start(struct seq_file *p, loff_t *pos)
310 + return (*pos == 0) ? (void *)1 : NULL;
314 +sd_iostats_seq_next(struct seq_file *p, void *v, loff_t *pos)
321 +sd_iostats_seq_stop(struct seq_file *p, void *v)
325 +static struct seq_operations sd_iostats_seqops = {
326 + .start = sd_iostats_seq_start,
327 + .stop = sd_iostats_seq_stop,
328 + .next = sd_iostats_seq_next,
329 + .show = sd_iostats_seq_show,
333 +sd_iostats_seq_open (struct inode *inode, struct file *file)
337 + rc = seq_open(file, &sd_iostats_seqops);
341 + ((struct seq_file *)file->private_data)->private = PDE(inode)->data;
346 +sd_iostats_seq_write(struct file *file, const char *buffer,
347 + size_t len, loff_t *off)
349 + struct seq_file *seq = file->private_data;
350 + struct gendisk *disk = seq->private;
351 + iostat_stats_t *stats = scsi_disk(disk)->stats;
352 + unsigned long flags;
353 + unsigned long qdepth;
356 + spin_lock_irqsave (&stats->iostat_lock, flags);
357 + qdepth = stats->iostat_queue_depth;
358 + memset (stats, 0, offsetof(iostat_stats_t, iostat_lock));
359 + do_gettimeofday(&stats->iostat_timeval);
360 + stats->iostat_queue_stamp = jiffies;
361 + stats->iostat_queue_depth = qdepth;
362 + spin_unlock_irqrestore (&stats->iostat_lock, flags);
367 +static struct file_operations sd_iostats_proc_fops = {
368 + .owner = THIS_MODULE,
369 + .open = sd_iostats_seq_open,
371 + .write = sd_iostats_seq_write,
372 + .llseek = seq_lseek,
373 + .release = seq_release,
376 +extern struct proc_dir_entry *proc_scsi;
379 +sd_iostats_init(void)
381 + if (proc_scsi == NULL) {
382 + printk(KERN_WARNING "No access to sd iostats: "
383 + "proc_scsi is NULL\n");
387 + sd_iostats_procdir = create_proc_entry(sd_iostats_procdir_name,
388 + S_IFDIR | S_IRUGO | S_IXUGO,
390 + if (sd_iostats_procdir == NULL) {
391 + printk(KERN_WARNING "No access to sd iostats: "
392 + "can't create /proc/scsi/%s\n", sd_iostats_procdir_name);
397 +void sd_iostats_fini(void)
399 + if (proc_scsi != NULL && sd_iostats_procdir != NULL)
400 + remove_proc_entry(sd_iostats_procdir_name, proc_scsi);
402 + sd_iostats_procdir = NULL;
405 +void sd_iostats_finish_req(struct scsi_cmnd *SCpnt)
407 + struct request *rq = SCpnt->request;
408 + iostat_stats_t *stats;
409 + unsigned long *tcounter;
412 + unsigned long irqflags;
415 + stats = scsi_disk(rq->rq_disk)->stats;
419 + tmp = jiffies - rq->start_time;
420 + for (tbucket = 0; tmp > 1; tbucket++)
422 + if (tbucket >= IOSTAT_NCOUNTERS)
423 + tbucket = IOSTAT_NCOUNTERS - 1;
424 + //printk("%u ticks in D to %u\n", jiffies - rq->start_time, tbucket);
426 + tcounter = rq_data_dir(rq) == WRITE ?
427 + &stats->iostat_wtime[tbucket] : &stats->iostat_rtime[tbucket];
429 + spin_lock_irqsave(&stats->iostat_lock, irqflags);
431 + /* update delay stats */
434 + /* update queue depth stats */
435 + i = stats->iostat_queue_depth;
436 + if (i >= IOSTAT_NCOUNTERS)
437 + i = IOSTAT_NCOUNTERS - 1;
438 + stats->iostat_queue_ticks[i] += jiffies - stats->iostat_queue_stamp;
439 + stats->iostat_queue_ticks_sum += jiffies - stats->iostat_queue_stamp;
440 + BUG_ON(stats->iostat_queue_depth == 0);
441 + stats->iostat_queue_depth--;
443 + /* update seek stats. XXX: not sure about nr_sectors */
444 + stats->iostat_sectors += rq->nr_sectors;
445 + stats->iostat_reqs++;
446 + if (rq->sector != stats->iostat_next_sector) {
447 + stats->iostat_seek_sectors +=
448 + rq->sector > stats->iostat_next_sector ?
449 + rq->sector - stats->iostat_next_sector :
450 + stats->iostat_next_sector - rq->sector;
451 + stats->iostat_seeks++;
453 + stats->iostat_next_sector = rq->sector + rq->nr_sectors;
455 + stats->iostat_queue_stamp = jiffies;
457 + spin_unlock_irqrestore(&stats->iostat_lock, irqflags);
460 +void sd_iostats_start_req(struct scsi_cmnd *SCpnt)
462 + struct request *rq = SCpnt->request;
463 + iostat_stats_t *stats;
464 + iostat_counter_t *counter;
468 + unsigned long irqflags;
472 + stats = scsi_disk(rq->rq_disk)->stats;
476 + nsect = SCpnt->request_bufflen >> 9;
477 + for (bucket = 0, tmp = nsect; tmp > 1; bucket++)
480 + if (bucket >= IOSTAT_NCOUNTERS) {
481 + printk (KERN_ERR "sd_iostats_bump: nsect %d too big\n", nsect);
485 + counter = rq_data_dir(rq) == WRITE ?
486 + &stats->iostat_write_histogram[bucket] :
487 + &stats->iostat_read_histogram[bucket];
489 + tmp = jiffies - rq->start_time;
490 + for (tbucket = 0; tmp > 1; tbucket++)
492 + if (tbucket >= IOSTAT_NCOUNTERS)
493 + tbucket = IOSTAT_NCOUNTERS - 1;
494 + //printk("%u ticks in Q to %u\n", jiffies - rq->start_time, tbucket);
496 + /* an ugly hack to know exact processing time. the right
497 + * solution is to add one more field to struct request
498 + * hopefully it will break nothing ... */
499 + rq->start_time = jiffies;
501 + spin_lock_irqsave(&stats->iostat_lock, irqflags);
503 + /* update queue depth stats */
504 + i = stats->iostat_queue_depth;
505 + if (i >= IOSTAT_NCOUNTERS)
506 + i = IOSTAT_NCOUNTERS - 1;
507 + stats->iostat_queue_ticks[i] += jiffies - stats->iostat_queue_stamp;
508 + stats->iostat_queue_ticks_sum += jiffies - stats->iostat_queue_stamp;
509 + stats->iostat_queue_depth++;
511 + /* update delay stats */
512 + if (rq_data_dir(rq) == WRITE) {
513 + stats->iostat_wtime_in_queue[tbucket]++;
514 + stats->iostat_write_reqs++;
516 + stats->iostat_rtime_in_queue[tbucket]++;
517 + stats->iostat_read_reqs++;
520 + /* update size stats */
521 + counter->iostat_size += nsect;
522 + counter->iostat_count++;
524 + stats->iostat_queue_stamp = jiffies;
526 + spin_unlock_irqrestore(&stats->iostat_lock, irqflags);
531 * scsi_disk_release - Called to free the scsi_disk structure
532 * @cdev: pointer to embedded class device
533 @@ -1774,10 +2227,16 @@
534 idr_remove(&sd_index_idr, sdkp->index);
535 spin_unlock(&sd_index_lock);
537 +#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
539 + remove_proc_entry(disk->disk_name, sd_iostats_procdir);
540 + kfree(sdkp->stats);
541 + sdkp->stats = NULL;
544 disk->private_data = NULL;
546 put_device(&sdkp->device->sdev_gendev);
551 @@ -1844,6 +2303,7 @@
552 static int __init init_sd(void)
557 SCSI_LOG_HLQUEUE(3, printk("init_sd: sd driver entry point\n"));
559 @@ -1854,9 +2314,13 @@
564 class_register(&sd_disk_class);
566 - return scsi_register_driver(&sd_template.gendrv);
567 + rc = scsi_register_driver(&sd_template.gendrv);
574 @@ -1875,6 +2339,7 @@
575 unregister_blkdev(sd_major(i), "sd");
577 class_unregister(&sd_disk_class);
581 module_init(init_sd);