Whamcloud - gitweb
Branch HEAD
[fs/lustre-release.git] / lustre / kernel_patches / patches / sd_iostats-2.6-rhel4.patch
1 Index: linux-2.6.9/drivers/scsi/Kconfig
2 ===================================================================
3 --- linux-2.6.9.orig/drivers/scsi/Kconfig       2007-08-02 09:26:17.000000000 +0200
4 +++ linux-2.6.9/drivers/scsi/Kconfig    2007-08-02 09:26:21.000000000 +0200
5 @@ -61,6 +61,14 @@ config SCSI_DUMP
6         help
7            SCSI dump support
8  
9 +config SD_IOSTATS
10 +   bool "Enable SCSI disk I/O stats"
11 +   depends on BLK_DEV_SD
12 +   default y
13 +   ---help---
14 +     This enables SCSI disk I/O stats collection.  You must also enable
15 +     /proc file system support if you want this feature.
16 +
17  config CHR_DEV_ST
18         tristate "SCSI tape support"
19         depends on SCSI
20 Index: linux-2.6.9/drivers/scsi/sd.c
21 ===================================================================
22 --- linux-2.6.9.orig/drivers/scsi/sd.c  2007-08-02 09:26:17.000000000 +0200
23 +++ linux-2.6.9/drivers/scsi/sd.c       2007-08-02 09:29:31.000000000 +0200
24 @@ -63,6 +63,38 @@
25  
26  #include "scsi_logging.h"
27  
28 +#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
29 +# include <linux/proc_fs.h>
30 +# include <linux/seq_file.h>
31 +
32 +typedef struct {
33 +        unsigned long long iostat_size;
34 +        unsigned long long iostat_count;
35 +} iostat_counter_t;
36 +
37 +#define IOSTAT_NCOUNTERS 16
38 +typedef struct {
39 +        iostat_counter_t        iostat_read_histogram[IOSTAT_NCOUNTERS];
40 +        iostat_counter_t        iostat_write_histogram[IOSTAT_NCOUNTERS];
41 +        struct timeval          iostat_timeval;
42 +} iostat_stats_t;
43 +
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";
48 +
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);
53 +#else
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) {}
58 +#endif
59 +
60  /*
61   * More than enough for everybody ;)  The huge number of majors
62   * is a leftover from 16bit dev_t days, we don't really need that
63 @@ -71,10 +103,18 @@
64  #define SD_MAJORS      16
65  
66  /*
67 - * This is limited by the naming scheme enforced in sd_probe,
68 + * SD_MAX_DISKS is limited by the naming scheme enforced in sd_probe,
69   * add another character to it if you really need more disks.
70 + *
71 + * SD_STATS must not be less than SD_MAX_DISKS and, is limited by 131072/8
72 + * for sd_iostats kmalloc (see kmalloc_sizes.h)
73   */
74 +#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
75 +#define SD_STATS       16384
76 +#define SD_MAX_DISKS   SD_STATS
77 +#else
78  #define SD_MAX_DISKS   (((26 * 26) + 26 + 1) * 26)
79 +#endif
80  
81  /*
82   * Time out in seconds for disks and Magneto-opticals (which are slower).
83 @@ -278,6 +318,9 @@ static int sd_init_command(struct scsi_c
84         SCSI_LOG_HLQUEUE(2, printk("%s : block=%llu\n",
85                                    disk->disk_name, (unsigned long long)block));
86  
87 +   sd_iostats_bump(scsi_disk(disk)->index, this_count,
88 +                   rq_data_dir(SCpnt->request) == WRITE);
89 +
90         /*
91          * If we have a 1K hardware sectorsize, prevent access to single
92          * 512 byte sectors.  In theory we could handle this - in fact
93 @@ -474,6 +517,7 @@ static int sd_open(struct inode *inode, 
94                         scsi_set_medium_removal(sdev, SCSI_REMOVAL_PREVENT);
95         }
96  
97 +   sd_iostats_init_disk(disk);
98         return 0;
99  
100  error_out:
101 @@ -500,8 +544,20 @@ static int sd_release(struct inode *inod
102  
103         SCSI_LOG_HLQUEUE(3, printk("sd_release: disk=%s\n", disk->disk_name));
104  
105 -       if (!--sdkp->openers && sdev->removable) {
106 -               if (scsi_block_when_processing_errors(sdev))
107 +       if (!--sdkp->openers) {
108 +               /*
109 +                * Remove sd_iostats information about this disk
110 +                */
111 +               if (sd_iostats_procdir != NULL) {
112 +                       remove_proc_entry(disk->disk_name, sd_iostats_procdir);
113 +               }
114 +               if (sd_iostats != NULL) {
115 +                       if (sd_iostats[sdkp->index] != NULL) {
116 +                               kfree (sd_iostats[sdkp->index]);
117 +                               sd_iostats[sdkp->index] = NULL;
118 +                       }
119 +               }
120 +               if (sdev->removable && scsi_block_when_processing_errors(sdev))
121                         scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW);
122         }
123  
124 @@ -1575,6 +1631,342 @@ static void sd_shutdown(struct device *d
125         sd_sync_cache(sdp);
126  }      
127  
128 +#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
129 +static int
130 +sd_iostats_seq_show(struct seq_file *seq, void *v)
131 +{
132 +        struct timeval     now;
133 +        struct gendisk *disk;
134 +        iostat_stats_t    *stats;
135 +        unsigned long long read_len;
136 +        unsigned long long read_len_tot;
137 +        unsigned long      read_num;
138 +        unsigned long      read_num_tot;
139 +        unsigned long long write_len;
140 +        unsigned long long write_len_tot;
141 +        unsigned long      write_num;
142 +        unsigned long      write_num_tot;
143 +        int                i;
144 +        int                maxi;
145 +
146 +       if (seq == NULL || seq->private == NULL) {
147 +               printk(KERN_ERR "sd_iostats_seq_show: NULL disk\n");
148 +               BUG();
149 +       }
150 +
151 +       disk = seq->private;
152 +
153 +       if (scsi_disk(disk) == NULL || (disk->flags & GENHD_FL_UP) == 0) {
154 +               seq_printf(seq, "sd_iostats_seq_show: Device %s "
155 +                               "does not exist\n", disk->disk_name);
156 +               return 0;
157 +       }
158 +
159 +        if (sd_iostats == NULL) {
160 +                printk(KERN_ERR "sd_iostats_seq_show: NULL stats array\n");
161 +                BUG();
162 +        }
163 +
164 +        stats = sd_iostats[scsi_disk(disk)->index];
165 +        if (stats == NULL) {
166 +                seq_printf(seq, "sd_iostats_seq_show: sd_iostats "
167 +                               "entry %d does not exist\n",
168 +                               scsi_disk(disk)->index);
169 +               return 0;
170 +        }
171 +
172 +        do_gettimeofday(&now);
173 +        now.tv_sec -= stats->iostat_timeval.tv_sec;
174 +        now.tv_usec -= stats->iostat_timeval.tv_usec;
175 +        if (now.tv_usec < 0) {
176 +                now.tv_usec += 1000000;
177 +                now.tv_sec--;
178 +        }
179 +
180 +        /* this sampling races with updates */
181 +        seq_printf(seq, "index:        %lu   snapshot_time:         %lu.%06lu\n",
182 +                   scsi_disk(disk)->index, now.tv_sec, now.tv_usec);
183 +
184 +        for (i = IOSTAT_NCOUNTERS - 1; i > 0; i--)
185 +                if (stats->iostat_read_histogram[i].iostat_count != 0 ||
186 +                    stats->iostat_write_histogram[i].iostat_count != 0)
187 +                        break;
188 +        maxi = i;
189 +
190 +        seq_printf(seq, "%8s %8s %12s %8s %12s\n", "size", 
191 +                   "reads", "total", "writes", "total");
192 +
193 +        read_len_tot = write_len_tot = 0;
194 +        read_num_tot = write_num_tot = 0;
195 +        for (i = 0; i <= maxi; i++) {
196 +                read_len = stats->iostat_read_histogram[i].iostat_size;
197 +                read_len_tot += read_len;
198 +                read_num = stats->iostat_read_histogram[i].iostat_count;
199 +                read_num_tot += read_num;
200 +
201 +                write_len = stats->iostat_write_histogram[i].iostat_size;
202 +                write_len_tot += write_len;
203 +                write_num = stats->iostat_write_histogram[i].iostat_count;
204 +                write_num_tot += write_num;
205 +
206 +                seq_printf (seq, "%8d %8lu %12llu %8lu %12llu\n", 
207 +                            512<<i, read_num, read_len, write_num, write_len);
208 +        }
209 +        
210 +        seq_printf(seq, "%8s %8lu %12llu %8lu %12llu\n", "total",
211 +                   read_num_tot, read_len_tot, 
212 +                   write_num_tot, write_len_tot);
213 +        return 0;
214 +}
215 +
216 +static void *
217 +sd_iostats_seq_start(struct seq_file *p, loff_t *pos)
218 +{
219 +        return (*pos == 0) ? (void *)1 : NULL;
220 +}
221 +
222 +static void *
223 +sd_iostats_seq_next(struct seq_file *p, void *v, loff_t *pos)
224 +{
225 +        ++*pos;
226 +        return NULL;
227 +}
228 +
229 +static void
230 +sd_iostats_seq_stop(struct seq_file *p, void *v)
231 +{
232 +}
233 +
234 +static struct seq_operations sd_iostats_seqops = {
235 +        .start = sd_iostats_seq_start,
236 +        .stop  = sd_iostats_seq_stop,
237 +        .next  = sd_iostats_seq_next,
238 +        .show  = sd_iostats_seq_show,
239 +};
240 +
241 +static int
242 +sd_iostats_seq_open (struct inode *inode, struct file *file)
243 +{
244 +        int                    rc;
245 +
246 +        rc = seq_open(file, &sd_iostats_seqops);
247 +        if (rc != 0)
248 +                return rc;
249 +
250 +        ((struct seq_file *)file->private_data)->private = PDE(inode)->data;
251 +        return 0;
252 +}
253 +
254 +static ssize_t
255 +sd_iostats_seq_write(struct file *file, const char *buffer,
256 +                     size_t len, loff_t *off)
257 +{
258 +        struct seq_file   *seq = file->private_data;
259 +        struct gendisk *disk = seq->private;
260 +        iostat_stats_t    *stats = sd_iostats[scsi_disk(disk)->index];
261 +        unsigned long      flags;
262 +        
263 +        
264 +        spin_lock_irqsave (&sd_iostats_lock, flags);
265 +        memset (stats, 0, sizeof(*stats));
266 +        do_gettimeofday(&stats->iostat_timeval);
267 +        spin_unlock_irqrestore (&sd_iostats_lock, flags);
268 +
269 +        return len;
270 +}
271 +
272 +static struct file_operations sd_iostats_proc_fops = {
273 +        .owner   = THIS_MODULE,
274 +        .open    = sd_iostats_seq_open,
275 +        .read    = seq_read,
276 +        .write   = sd_iostats_seq_write,
277 +        .llseek  = seq_lseek,
278 +        .release = seq_release,
279 +};
280 +
281 +extern struct proc_dir_entry *proc_scsi;
282 +
283 +void
284 +sd_iostats_init(void)
285 +{
286 +        int    i;
287 +
288 +        spin_lock_init(&sd_iostats_lock);
289 +
290 +        sd_iostats = kmalloc(SD_STATS * sizeof(iostat_stats_t *), GFP_KERNEL);
291 +        if (sd_iostats == NULL) {
292 +                printk(KERN_WARNING "Can't keep sd iostats: "
293 +                       "ENOMEM allocating stats array size %ld\n",
294 +                       SD_STATS * sizeof(iostat_stats_t *));
295 +                return;
296 +        }
297 +
298 +        for (i = 0; i < SD_STATS; i++)
299 +                sd_iostats[i] = NULL;
300 +
301 +        if (proc_scsi == NULL) {
302 +                printk(KERN_WARNING "No access to sd iostats: "
303 +                       "proc_scsi is NULL\n");
304 +                return;
305 +        }
306 +
307 +        sd_iostats_procdir = create_proc_entry(sd_iostats_procdir_name,
308 +                                               S_IFDIR | S_IRUGO | S_IXUGO,
309 +                                               proc_scsi);
310 +        if (sd_iostats_procdir == NULL) {
311 +                printk(KERN_WARNING "No access to sd iostats: "
312 +                       "can't create /proc/scsi/%s\n", sd_iostats_procdir_name);
313 +                return;
314 +        }
315 +}
316 +
317 +void
318 +sd_iostats_init_disk(struct gendisk *disk)
319 +{
320 +        struct proc_dir_entry *pde;
321 +        unsigned long          flags;
322 +        iostat_stats_t        *stats;
323 +
324 +        if (sd_iostats == NULL ||
325 +            sd_iostats_procdir == NULL)
326 +                return;
327 +
328 +        if (scsi_disk(disk)->index > SD_STATS) {
329 +                printk(KERN_ERR "sd_iostats_init_disk: "
330 +                       "unexpected disk index %d(%d)\n",
331 +                       scsi_disk(disk)->index, SD_STATS);
332 +                                   return;
333 +        }
334 +
335 +        if (sd_iostats[scsi_disk(disk)->index] != NULL)
336 +                return;
337 +
338 +        stats = kmalloc(sizeof(*stats), GFP_KERNEL);
339 +        if (stats == NULL) {
340 +                printk(KERN_WARNING "Can't keep %s iostats: "
341 +                       "ENOMEM allocating stats size %ld\n", 
342 +                       disk->disk_name, sizeof(*stats));
343 +                return;
344 +        }
345 +
346 +        memset (stats, 0, sizeof(*stats));
347 +        do_gettimeofday(&stats->iostat_timeval);
348 +
349 +        spin_lock_irqsave(&sd_iostats_lock, flags);
350 +
351 +        if (sd_iostats[scsi_disk(disk)->index] != NULL) {
352 +                spin_unlock_irqrestore(&sd_iostats_lock, flags);
353 +                kfree (stats);
354 +                return;
355 +        }
356 +
357 +        sd_iostats[scsi_disk(disk)->index] = stats;
358 +        
359 +        spin_unlock_irqrestore(&sd_iostats_lock, flags);
360 +        
361 +        pde = create_proc_entry(disk->disk_name, S_IRUGO | S_IWUSR, 
362 +                                sd_iostats_procdir);
363 +        if (pde == NULL) {
364 +                printk(KERN_WARNING "Can't create /proc/scsi/%s/%s\n",
365 +                       sd_iostats_procdir_name, disk->disk_name);
366 +        } else {
367 +                pde->proc_fops = &sd_iostats_proc_fops;
368 +                pde->data = disk;
369 +        }
370 +}
371 +
372 +static void sd_devname(unsigned int disknum, char *buffer)
373 +{
374 +        if (disknum < 26)
375 +                sprintf(buffer, "sd%c", 'a' + disknum);
376 +        else {
377 +                unsigned int min1;
378 +                unsigned int min2;
379 +                /*
380 +                 * For larger numbers of disks, we need to go to a new
381 +                 * naming scheme.
382 +                 */
383 +                min1 = disknum / 26;
384 +                min2 = disknum % 26;
385 +                sprintf(buffer, "sd%c%c", 'a' + min1 - 1, 'a' + min2);
386 +        }
387 +}
388 +
389 +void
390 +sd_iostats_fini(void)
391 +{
392 +        char name[6];
393 +        int  i;
394 +        
395 +        if (sd_iostats_procdir != NULL) {
396 +                for (i = 0; i < SD_STATS; i++) {
397 +                        sd_devname(i, name);
398 +                        remove_proc_entry(name, sd_iostats_procdir);
399 +                }
400 +
401 +                if (proc_scsi == NULL) {
402 +                        printk(KERN_ERR "sd_iostats_fini: proc_scsi NULL\n");
403 +                        BUG();
404 +                }
405 +                remove_proc_entry(sd_iostats_procdir_name,
406 +                                  proc_scsi);
407 +
408 +                sd_iostats_procdir = NULL;
409 +        }
410 +        
411 +        if (sd_iostats != NULL) {
412 +                for (i = 0; i < SD_STATS; i++) {
413 +                        if (sd_iostats[i] != NULL)
414 +                                kfree (sd_iostats[i]);
415 +                }
416 +                
417 +                kfree(sd_iostats);
418 +                sd_iostats = NULL;
419 +        }
420 +}
421 +
422 +void
423 +sd_iostats_bump(int disk, unsigned int nsect, int iswrite)
424 +{
425 +        iostat_stats_t    *stats;
426 +        iostat_counter_t  *counter;
427 +        int                bucket;
428 +        int                tmp;
429 +        unsigned long      irqflags;
430 +
431 +        if (sd_iostats == NULL)
432 +                return;
433 +
434 +        if (disk < 0 || disk >= SD_STATS) {
435 +                printk(KERN_ERR "sd_iostats_bump: unexpected disk index %d([0-%d])\n",
436 +                       disk, SD_STATS);
437 +                BUG();
438 +        }
439 +
440 +        for (bucket = 0, tmp = nsect; tmp > 1; bucket++)
441 +                tmp /= 2;
442 +
443 +        if (bucket >= IOSTAT_NCOUNTERS) {
444 +                printk (KERN_ERR "sd_iostats_bump: nsect %d too big\n", nsect);
445 +                BUG();
446 +        }
447 +
448 +        spin_lock_irqsave(&sd_iostats_lock, irqflags);
449 +        
450 +        stats = sd_iostats[disk];
451 +        if (stats != NULL) {
452 +                counter = iswrite ? 
453 +                          &stats->iostat_write_histogram[bucket] :
454 +                          &stats->iostat_read_histogram[bucket];
455 +
456 +                counter->iostat_size += nsect;
457 +                counter->iostat_count++;
458 +        }
459 +
460 +        spin_unlock_irqrestore(&sd_iostats_lock, irqflags);
461 +}
462 +#endif
463 +
464  /**
465   *     init_sd - entry point for this driver (both when built in or when
466   *     a module).
467 @@ -1584,6 +1976,7 @@ static void sd_shutdown(struct device *d
468  static int __init init_sd(void)
469  {
470         int majors = 0, i;
471 +   int rc = 0;
472  
473         SCSI_LOG_HLQUEUE(3, printk("init_sd: sd driver entry point\n"));
474  
475 @@ -1594,7 +1987,10 @@ static int __init init_sd(void)
476         if (!majors)
477                 return -ENODEV;
478  
479 -       return scsi_register_driver(&sd_template.gendrv);
480 +   rc = scsi_register_driver(&sd_template.gendrv);
481 +   if (rc == 0)
482 +      sd_iostats_init();
483 +   return rc;
484  }
485  
486  /**
487 @@ -1608,6 +2004,7 @@ static void __exit exit_sd(void)
488  
489         SCSI_LOG_HLQUEUE(3, printk("exit_sd: exiting sd driver\n"));
490  
491 +   sd_iostats_fini();
492         scsi_unregister_driver(&sd_template.gendrv);
493         for (i = 0; i < SD_MAJORS; i++)
494                 unregister_blkdev(sd_major(i), "sd");
495 Index: linux-2.6.9/drivers/scsi/scsi_proc.c
496 ===================================================================
497 --- linux-2.6.9.orig/drivers/scsi/scsi_proc.c   2007-08-02 09:26:17.000000000 +0200
498 +++ linux-2.6.9/drivers/scsi/scsi_proc.c        2007-08-02 09:26:21.000000000 +0200
499 @@ -38,7 +38,8 @@
500  /* 4K page size, but our output routines, use some slack for overruns */
501  #define PROC_BLOCK_SIZE (3*1024)
502  
503 -static struct proc_dir_entry *proc_scsi;
504 +struct proc_dir_entry *proc_scsi;
505 +EXPORT_SYMBOL(proc_scsi);
506  
507  /* Protect sht->present and sht->proc_dir */
508  static DECLARE_MUTEX(global_host_template_sem);