1 Index: linux-2.6.22.5/drivers/scsi/Kconfig
2 ===================================================================
3 --- linux-2.6.22.5.orig/drivers/scsi/Kconfig
4 +++ linux-2.6.22.5/drivers/scsi/Kconfig
5 @@ -76,6 +76,14 @@ config BLK_DEV_SD
6 In this case, do not compile the driver for your SCSI host adapter
7 (below) as a module either.
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 tape support"
20 Index: linux+rhel4+chaos/drivers/scsi/sd.c
21 ===================================================================
22 --- linux+rhel4+chaos.orig/drivers/scsi/sd.c
23 +++ linux+rhel4+chaos/drivers/scsi/sd.c
26 #include "scsi_logging.h"
28 +#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
29 +# include <linux/proc_fs.h>
30 +# include <linux/seq_file.h>
33 + unsigned long long iostat_size;
34 + unsigned long long iostat_count;
37 +#define IOSTAT_NCOUNTERS 16
39 + iostat_counter_t iostat_read_histogram[IOSTAT_NCOUNTERS];
40 + iostat_counter_t iostat_write_histogram[IOSTAT_NCOUNTERS];
41 + struct timeval iostat_timeval;
44 +iostat_stats_t **sd_iostats;
45 +spinlock_t sd_iostats_lock;
46 +struct proc_dir_entry *sd_iostats_procdir;
47 +char sd_iostats_procdir_name[] = "sd_iostats";
49 +extern void sd_iostats_init(void);
50 +extern void sd_iostats_init_disk(struct gendisk *);
51 +extern void sd_iostats_fini(void);
52 +extern void sd_iostats_bump(int disk, unsigned int nsect, int iswrite);
54 +static inline void sd_iostats_init(void) {}
55 +static inline void sd_iostats_init_disk(struct gendisk *disk) {}
56 +static inline void sd_iostats_fini(void) {}
57 +static inline void sd_iostats_bump(int disk, unsigned int nsect, int iswrite) {}
60 MODULE_AUTHOR("Eric Youngdale");
61 MODULE_DESCRIPTION("SCSI disk (sd) driver");
62 MODULE_LICENSE("GPL");
63 @@ -89,6 +121,7 @@ MODULE_ALIAS_SCSI_DEVICE(TYPE_RBC);
64 static DEFINE_IDR(sd_index_idr);
65 static DEFINE_SPINLOCK(sd_index_lock);
68 /* This semaphore is used to mediate the 0->1 reference get in the
69 * face of object destruction (i.e. we can't allow a get on an
70 * object after last put) */
71 @@ -368,6 +401,9 @@ static int sd_init_command(struct scsi_c
72 SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt, "block=%llu\n",
73 (unsigned long long)block));
75 + sd_iostats_bump(scsi_disk(disk)->index, this_count,
76 + rq_data_dir(SCpnt->request) == WRITE);
79 * If we have a 1K hardware sectorsize, prevent access to single
80 * 512 byte sectors. In theory we could handle this - in fact
81 @@ -575,6 +611,7 @@ static int sd_open(struct inode *inode,
82 scsi_set_medium_removal(sdev, SCSI_REMOVAL_PREVENT);
85 + sd_iostats_init_disk(disk);
89 @@ -601,8 +638,20 @@ static int sd_release(struct inode *inod
91 SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_release\n"));
93 - if (!--sdkp->openers && sdev->removable) {
94 - if (scsi_block_when_processing_errors(sdev))
95 + if (!--sdkp->openers) {
97 + * Remove sd_iostats information about this disk
99 + if (sd_iostats_procdir != NULL) {
100 + remove_proc_entry(disk->disk_name, sd_iostats_procdir);
102 + if (sd_iostats != NULL) {
103 + if (sd_iostats[sdkp->index] != NULL) {
104 + kfree (sd_iostats[sdkp->index]);
105 + sd_iostats[sdkp->index] = NULL;
108 + if (sdev->removable && scsi_block_when_processing_errors(sdev))
109 scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW);
112 @@ -1563,6 +1612,342 @@ static int sd_revalidate_disk(struct gen
116 +#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
118 +sd_iostats_seq_show(struct seq_file *seq, void *v)
120 + struct timeval now;
121 + struct gendisk *disk;
122 + iostat_stats_t *stats;
123 + unsigned long long read_len;
124 + unsigned long long read_len_tot;
125 + unsigned long read_num;
126 + unsigned long read_num_tot;
127 + unsigned long long write_len;
128 + unsigned long long write_len_tot;
129 + unsigned long write_num;
130 + unsigned long write_num_tot;
134 + if (seq == NULL || seq->private == NULL) {
135 + printk(KERN_ERR "sd_iostats_seq_show: NULL disk\n");
139 + disk = seq->private;
141 + if (scsi_disk(disk) == NULL || (disk->flags & GENHD_FL_UP) == 0) {
142 + seq_printf(seq, "sd_iostats_seq_show: Device %s "
143 + "does not exist\n", disk->disk_name);
147 + if (sd_iostats == NULL) {
148 + printk(KERN_ERR "sd_iostats_seq_show: NULL stats array\n");
152 + stats = sd_iostats[scsi_disk(disk)->index];
153 + if (stats == NULL) {
154 + seq_printf(seq, "sd_iostats_seq_show: sd_iostats "
155 + "entry %d does not exist\n",
156 + scsi_disk(disk)->index);
160 + do_gettimeofday(&now);
161 + now.tv_sec -= stats->iostat_timeval.tv_sec;
162 + now.tv_usec -= stats->iostat_timeval.tv_usec;
163 + if (now.tv_usec < 0) {
164 + now.tv_usec += 1000000;
168 + /* this sampling races with updates */
169 + seq_printf(seq, "index: %lu snapshot_time: %lu.%06lu\n",
170 + scsi_disk(disk)->index, now.tv_sec, now.tv_usec);
172 + for (i = IOSTAT_NCOUNTERS - 1; i > 0; i--)
173 + if (stats->iostat_read_histogram[i].iostat_count != 0 ||
174 + stats->iostat_write_histogram[i].iostat_count != 0)
178 + seq_printf(seq, "%8s %8s %12s %8s %12s\n", "size",
179 + "reads", "total", "writes", "total");
181 + read_len_tot = write_len_tot = 0;
182 + read_num_tot = write_num_tot = 0;
183 + for (i = 0; i <= maxi; i++) {
184 + read_len = stats->iostat_read_histogram[i].iostat_size;
185 + read_len_tot += read_len;
186 + read_num = stats->iostat_read_histogram[i].iostat_count;
187 + read_num_tot += read_num;
189 + write_len = stats->iostat_write_histogram[i].iostat_size;
190 + write_len_tot += write_len;
191 + write_num = stats->iostat_write_histogram[i].iostat_count;
192 + write_num_tot += write_num;
194 + seq_printf (seq, "%8d %8lu %12llu %8lu %12llu\n",
195 + 512<<i, read_num, read_len, write_num, write_len);
198 + seq_printf(seq, "%8s %8lu %12llu %8lu %12llu\n", "total",
199 + read_num_tot, read_len_tot,
200 + write_num_tot, write_len_tot);
205 +sd_iostats_seq_start(struct seq_file *p, loff_t *pos)
207 + return (*pos == 0) ? (void *)1 : NULL;
211 +sd_iostats_seq_next(struct seq_file *p, void *v, loff_t *pos)
218 +sd_iostats_seq_stop(struct seq_file *p, void *v)
222 +static struct seq_operations sd_iostats_seqops = {
223 + .start = sd_iostats_seq_start,
224 + .stop = sd_iostats_seq_stop,
225 + .next = sd_iostats_seq_next,
226 + .show = sd_iostats_seq_show,
230 +sd_iostats_seq_open (struct inode *inode, struct file *file)
234 + rc = seq_open(file, &sd_iostats_seqops);
238 + ((struct seq_file *)file->private_data)->private = PDE(inode)->data;
243 +sd_iostats_seq_write(struct file *file, const char *buffer,
244 + size_t len, loff_t *off)
246 + struct seq_file *seq = file->private_data;
247 + struct gendisk *disk = seq->private;
248 + iostat_stats_t *stats = sd_iostats[scsi_disk(disk)->index];
249 + unsigned long flags;
252 + spin_lock_irqsave (&sd_iostats_lock, flags);
253 + memset (stats, 0, sizeof(*stats));
254 + do_gettimeofday(&stats->iostat_timeval);
255 + spin_unlock_irqrestore (&sd_iostats_lock, flags);
260 +static struct file_operations sd_iostats_proc_fops = {
261 + .owner = THIS_MODULE,
262 + .open = sd_iostats_seq_open,
264 + .write = sd_iostats_seq_write,
265 + .llseek = seq_lseek,
266 + .release = seq_release,
269 +extern struct proc_dir_entry *proc_scsi;
272 +sd_iostats_init(void)
276 + spin_lock_init(&sd_iostats_lock);
278 + sd_iostats = kmalloc(SD_STATS * sizeof(iostat_stats_t *), GFP_KERNEL);
279 + if (sd_iostats == NULL) {
280 + printk(KERN_WARNING "Can't keep sd iostats: "
281 + "ENOMEM allocating stats array size %ld\n",
282 + SD_STATS * sizeof(iostat_stats_t *));
286 + for (i = 0; i < SD_STATS; i++)
287 + sd_iostats[i] = NULL;
289 + if (proc_scsi == NULL) {
290 + printk(KERN_WARNING "No access to sd iostats: "
291 + "proc_scsi is NULL\n");
295 + sd_iostats_procdir = create_proc_entry(sd_iostats_procdir_name,
296 + S_IFDIR | S_IRUGO | S_IXUGO,
298 + if (sd_iostats_procdir == NULL) {
299 + printk(KERN_WARNING "No access to sd iostats: "
300 + "can't create /proc/scsi/%s\n", sd_iostats_procdir_name);
306 +sd_iostats_init_disk(struct gendisk *disk)
308 + struct proc_dir_entry *pde;
309 + unsigned long flags;
310 + iostat_stats_t *stats;
312 + if (sd_iostats == NULL ||
313 + sd_iostats_procdir == NULL)
316 + if (scsi_disk(disk)->index > SD_STATS) {
317 + printk(KERN_ERR "sd_iostats_init_disk: "
318 + "unexpected disk index %d(%d)\n",
319 + scsi_disk(disk)->index, SD_STATS);
323 + if (sd_iostats[scsi_disk(disk)->index] != NULL)
326 + stats = kmalloc(sizeof(*stats), GFP_KERNEL);
327 + if (stats == NULL) {
328 + printk(KERN_WARNING "Can't keep %s iostats: "
329 + "ENOMEM allocating stats size %ld\n",
330 + disk->disk_name, sizeof(*stats));
334 + memset (stats, 0, sizeof(*stats));
335 + do_gettimeofday(&stats->iostat_timeval);
337 + spin_lock_irqsave(&sd_iostats_lock, flags);
339 + if (sd_iostats[scsi_disk(disk)->index] != NULL) {
340 + spin_unlock_irqrestore(&sd_iostats_lock, flags);
345 + sd_iostats[scsi_disk(disk)->index] = stats;
347 + spin_unlock_irqrestore(&sd_iostats_lock, flags);
349 + pde = create_proc_entry(disk->disk_name, S_IRUGO | S_IWUSR,
350 + sd_iostats_procdir);
352 + printk(KERN_WARNING "Can't create /proc/scsi/%s/%s\n",
353 + sd_iostats_procdir_name, disk->disk_name);
355 + pde->proc_fops = &sd_iostats_proc_fops;
360 +static void sd_devname(unsigned int disknum, char *buffer)
363 + sprintf(buffer, "sd%c", 'a' + disknum);
368 + * For larger numbers of disks, we need to go to a new
371 + min1 = disknum / 26;
372 + min2 = disknum % 26;
373 + sprintf(buffer, "sd%c%c", 'a' + min1 - 1, 'a' + min2);
378 +sd_iostats_fini(void)
383 + if (sd_iostats_procdir != NULL) {
384 + for (i = 0; i < SD_STATS; i++) {
385 + sd_devname(i, name);
386 + remove_proc_entry(name, sd_iostats_procdir);
389 + if (proc_scsi == NULL) {
390 + printk(KERN_ERR "sd_iostats_fini: proc_scsi NULL\n");
393 + remove_proc_entry(sd_iostats_procdir_name,
396 + sd_iostats_procdir = NULL;
399 + if (sd_iostats != NULL) {
400 + for (i = 0; i < SD_STATS; i++) {
401 + if (sd_iostats[i] != NULL)
402 + kfree (sd_iostats[i]);
411 +sd_iostats_bump(int disk, unsigned int nsect, int iswrite)
413 + iostat_stats_t *stats;
414 + iostat_counter_t *counter;
417 + unsigned long irqflags;
419 + if (sd_iostats == NULL)
422 + if (disk < 0 || disk >= SD_STATS) {
423 + printk(KERN_ERR "sd_iostats_bump: unexpected disk index %d([0-%d])\n",
428 + for (bucket = 0, tmp = nsect; tmp > 1; bucket++)
431 + if (bucket >= IOSTAT_NCOUNTERS) {
432 + printk (KERN_ERR "sd_iostats_bump: nsect %d too big\n", nsect);
436 + spin_lock_irqsave(&sd_iostats_lock, irqflags);
438 + stats = sd_iostats[disk];
439 + if (stats != NULL) {
440 + counter = iswrite ?
441 + &stats->iostat_write_histogram[bucket] :
442 + &stats->iostat_read_histogram[bucket];
444 + counter->iostat_size += nsect;
445 + counter->iostat_count++;
448 + spin_unlock_irqrestore(&sd_iostats_lock, irqflags);
453 * sd_probe - called during driver initialization and whenever a
454 * new scsi device is attached to the system. It is called once
455 @@ -1854,6 +2239,7 @@ static int __init init_sd(void)
456 err = scsi_register_driver(&sd_template.gendrv);
463 @@ -1876,6 +2262,7 @@ static void __exit exit_sd(void)
465 SCSI_LOG_HLQUEUE(3, printk("exit_sd: exiting sd driver\n"));
468 scsi_unregister_driver(&sd_template.gendrv);
469 class_unregister(&sd_disk_class);
471 Index: linux-2.6.22.5/drivers/scsi/scsi_proc.c
472 ===================================================================
473 --- linux-2.6.22.5.orig/drivers/scsi/scsi_proc.c
474 +++ linux-2.6.22.5/drivers/scsi/scsi_proc.c
476 /* 4K page size, but our output routines, use some slack for overruns */
477 #define PROC_BLOCK_SIZE (3*1024)
479 -static struct proc_dir_entry *proc_scsi;
480 +struct proc_dir_entry *proc_scsi;
481 +EXPORT_SYMBOL(proc_scsi);
483 /* Protect sht->present and sht->proc_dir */
484 static DEFINE_MUTEX(global_host_template_mutex);