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