Whamcloud - gitweb
b=16437
[fs/lustre-release.git] / lustre / kernel_patches / patches / sd_iostats-2.6.22-vanilla.patch
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.
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+rhel4+chaos/drivers/scsi/sd.c
21 ===================================================================
22 --- linux+rhel4+chaos.orig/drivers/scsi/sd.c
23 +++ linux+rhel4+chaos/drivers/scsi/sd.c
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  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);
66  
67 +#define SD_STATS 256
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));
74  
75 +   sd_iostats_bump(scsi_disk(disk)->index, this_count,
76 +                   rq_data_dir(SCpnt->request) == WRITE);
77 +
78         /*
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);
83         }
84  
85 +   sd_iostats_init_disk(disk);
86         return 0;
87  
88  error_out:
89 @@ -601,8 +638,20 @@ static int sd_release(struct inode *inod
90  
91         SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_release\n"));
92  
93 -       if (!--sdkp->openers && sdev->removable) {
94 -               if (scsi_block_when_processing_errors(sdev))
95 +       if (!--sdkp->openers) {
96 +               /*
97 +                * Remove sd_iostats information about this disk
98 +                */
99 +               if (sd_iostats_procdir != NULL) {
100 +                       remove_proc_entry(disk->disk_name, sd_iostats_procdir);
101 +               }
102 +               if (sd_iostats != NULL) {
103 +                       if (sd_iostats[sdkp->index] != NULL) {
104 +                               kfree (sd_iostats[sdkp->index]);
105 +                               sd_iostats[sdkp->index] = NULL;
106 +                       }
107 +               }
108 +               if (sdev->removable && scsi_block_when_processing_errors(sdev))
109                         scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW);
110         }
111  
112 @@ -1563,6 +1612,342 @@ static int sd_revalidate_disk(struct gen
113         return 0;
114  }
115  
116 +#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
117 +static int
118 +sd_iostats_seq_show(struct seq_file *seq, void *v)
119 +{
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;
131 +        int                i;
132 +        int                maxi;
133 +
134 +       if (seq == NULL || seq->private == NULL) {
135 +               printk(KERN_ERR "sd_iostats_seq_show: NULL disk\n");
136 +               BUG();
137 +       }
138 +
139 +       disk = seq->private;
140 +
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);
144 +               return 0;
145 +       }
146 +
147 +        if (sd_iostats == NULL) {
148 +                printk(KERN_ERR "sd_iostats_seq_show: NULL stats array\n");
149 +                BUG();
150 +        }
151 +
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);
157 +               return 0;
158 +        }
159 +
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;
165 +                now.tv_sec--;
166 +        }
167 +
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);
171 +
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)
175 +                        break;
176 +        maxi = i;
177 +
178 +        seq_printf(seq, "%8s %8s %12s %8s %12s\n", "size", 
179 +                   "reads", "total", "writes", "total");
180 +
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;
188 +
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;
193 +
194 +                seq_printf (seq, "%8d %8lu %12llu %8lu %12llu\n", 
195 +                            512<<i, read_num, read_len, write_num, write_len);
196 +        }
197 +        
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);
201 +        return 0;
202 +}
203 +
204 +static void *
205 +sd_iostats_seq_start(struct seq_file *p, loff_t *pos)
206 +{
207 +        return (*pos == 0) ? (void *)1 : NULL;
208 +}
209 +
210 +static void *
211 +sd_iostats_seq_next(struct seq_file *p, void *v, loff_t *pos)
212 +{
213 +        ++*pos;
214 +        return NULL;
215 +}
216 +
217 +static void
218 +sd_iostats_seq_stop(struct seq_file *p, void *v)
219 +{
220 +}
221 +
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,
227 +};
228 +
229 +static int
230 +sd_iostats_seq_open (struct inode *inode, struct file *file)
231 +{
232 +        int                    rc;
233 +
234 +        rc = seq_open(file, &sd_iostats_seqops);
235 +        if (rc != 0)
236 +                return rc;
237 +
238 +        ((struct seq_file *)file->private_data)->private = PDE(inode)->data;
239 +        return 0;
240 +}
241 +
242 +static ssize_t
243 +sd_iostats_seq_write(struct file *file, const char *buffer,
244 +                     size_t len, loff_t *off)
245 +{
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;
250 +        
251 +        
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);
256 +
257 +        return len;
258 +}
259 +
260 +static struct file_operations sd_iostats_proc_fops = {
261 +        .owner   = THIS_MODULE,
262 +        .open    = sd_iostats_seq_open,
263 +        .read    = seq_read,
264 +        .write   = sd_iostats_seq_write,
265 +        .llseek  = seq_lseek,
266 +        .release = seq_release,
267 +};
268 +
269 +extern struct proc_dir_entry *proc_scsi;
270 +
271 +void
272 +sd_iostats_init(void)
273 +{
274 +        int    i;
275 +
276 +        spin_lock_init(&sd_iostats_lock);
277 +
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 *));
283 +                return;
284 +        }
285 +
286 +        for (i = 0; i < SD_STATS; i++)
287 +                sd_iostats[i] = NULL;
288 +
289 +        if (proc_scsi == NULL) {
290 +                printk(KERN_WARNING "No access to sd iostats: "
291 +                       "proc_scsi is NULL\n");
292 +                return;
293 +        }
294 +
295 +        sd_iostats_procdir = create_proc_entry(sd_iostats_procdir_name,
296 +                                               S_IFDIR | S_IRUGO | S_IXUGO,
297 +                                               proc_scsi);
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);
301 +                return;
302 +        }
303 +}
304 +
305 +void
306 +sd_iostats_init_disk(struct gendisk *disk)
307 +{
308 +        struct proc_dir_entry *pde;
309 +        unsigned long          flags;
310 +        iostat_stats_t        *stats;
311 +
312 +        if (sd_iostats == NULL ||
313 +            sd_iostats_procdir == NULL)
314 +                return;
315 +
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);
320 +                                   return;
321 +        }
322 +
323 +        if (sd_iostats[scsi_disk(disk)->index] != NULL)
324 +                return;
325 +
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));
331 +                return;
332 +        }
333 +
334 +        memset (stats, 0, sizeof(*stats));
335 +        do_gettimeofday(&stats->iostat_timeval);
336 +
337 +        spin_lock_irqsave(&sd_iostats_lock, flags);
338 +
339 +        if (sd_iostats[scsi_disk(disk)->index] != NULL) {
340 +                spin_unlock_irqrestore(&sd_iostats_lock, flags);
341 +                kfree (stats);
342 +                return;
343 +        }
344 +
345 +        sd_iostats[scsi_disk(disk)->index] = stats;
346 +        
347 +        spin_unlock_irqrestore(&sd_iostats_lock, flags);
348 +        
349 +        pde = create_proc_entry(disk->disk_name, S_IRUGO | S_IWUSR, 
350 +                                sd_iostats_procdir);
351 +        if (pde == NULL) {
352 +                printk(KERN_WARNING "Can't create /proc/scsi/%s/%s\n",
353 +                       sd_iostats_procdir_name, disk->disk_name);
354 +        } else {
355 +                pde->proc_fops = &sd_iostats_proc_fops;
356 +                pde->data = disk;
357 +        }
358 +}
359 +
360 +static void sd_devname(unsigned int disknum, char *buffer)
361 +{
362 +        if (disknum < 26)
363 +                sprintf(buffer, "sd%c", 'a' + disknum);
364 +        else {
365 +                unsigned int min1;
366 +                unsigned int min2;
367 +                /*
368 +                 * For larger numbers of disks, we need to go to a new
369 +                 * naming scheme.
370 +                 */
371 +                min1 = disknum / 26;
372 +                min2 = disknum % 26;
373 +                sprintf(buffer, "sd%c%c", 'a' + min1 - 1, 'a' + min2);
374 +        }
375 +}
376 +
377 +void
378 +sd_iostats_fini(void)
379 +{
380 +        char name[6];
381 +        int  i;
382 +        
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);
387 +                }
388 +
389 +                if (proc_scsi == NULL) {
390 +                        printk(KERN_ERR "sd_iostats_fini: proc_scsi NULL\n");
391 +                        BUG();
392 +                }
393 +                remove_proc_entry(sd_iostats_procdir_name,
394 +                                  proc_scsi);
395 +
396 +                sd_iostats_procdir = NULL;
397 +        }
398 +        
399 +        if (sd_iostats != NULL) {
400 +                for (i = 0; i < SD_STATS; i++) {
401 +                        if (sd_iostats[i] != NULL)
402 +                                kfree (sd_iostats[i]);
403 +                }
404 +                
405 +                kfree(sd_iostats);
406 +                sd_iostats = NULL;
407 +        }
408 +}
409 +
410 +void
411 +sd_iostats_bump(int disk, unsigned int nsect, int iswrite)
412 +{
413 +        iostat_stats_t    *stats;
414 +        iostat_counter_t  *counter;
415 +        int                bucket;
416 +        int                tmp;
417 +        unsigned long      irqflags;
418 +
419 +        if (sd_iostats == NULL)
420 +                return;
421 +
422 +        if (disk < 0 || disk >= SD_STATS) {
423 +                printk(KERN_ERR "sd_iostats_bump: unexpected disk index %d([0-%d])\n",
424 +                       disk, SD_STATS);
425 +                BUG();
426 +        }
427 +
428 +        for (bucket = 0, tmp = nsect; tmp > 1; bucket++)
429 +                tmp /= 2;
430 +
431 +        if (bucket >= IOSTAT_NCOUNTERS) {
432 +                printk (KERN_ERR "sd_iostats_bump: nsect %d too big\n", nsect);
433 +                BUG();
434 +        }
435 +
436 +        spin_lock_irqsave(&sd_iostats_lock, irqflags);
437 +        
438 +        stats = sd_iostats[disk];
439 +        if (stats != NULL) {
440 +                counter = iswrite ? 
441 +                          &stats->iostat_write_histogram[bucket] :
442 +                          &stats->iostat_read_histogram[bucket];
443 +
444 +                counter->iostat_size += nsect;
445 +                counter->iostat_count++;
446 +        }
447 +
448 +        spin_unlock_irqrestore(&sd_iostats_lock, irqflags);
449 +}
450 +#endif
451 +
452  /**
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);
457         if (err)
458                 goto err_out_class;
459 +       sd_iostats_init();
460  
461         return 0;
462  
463 @@ -1876,6 +2262,7 @@ static void __exit exit_sd(void)
464  
465         SCSI_LOG_HLQUEUE(3, printk("exit_sd: exiting sd driver\n"));
466  
467 +       sd_iostats_fini();
468         scsi_unregister_driver(&sd_template.gendrv);
469         class_unregister(&sd_disk_class);
470  
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
475 @@ -40,7 +40,8 @@
476  /* 4K page size, but our output routines, use some slack for overruns */
477  #define PROC_BLOCK_SIZE (3*1024)
478  
479 -static struct proc_dir_entry *proc_scsi;
480 +struct proc_dir_entry *proc_scsi;
481 +EXPORT_SYMBOL(proc_scsi);
482  
483  /* Protect sht->present and sht->proc_dir */
484  static DEFINE_MUTEX(global_host_template_mutex);