Whamcloud - gitweb
b=20595
[fs/lustre-release.git] / lustre / kernel_patches / patches / sd_iostats-2.6-rhel5.patch
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
5 @@ -78,6 +78,14 @@
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.
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_OSST
18         tristate "SCSI OnStream SC-x0 tape support"
19         depends on SCSI
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
24 @@ -40,7 +40,8 @@
25  /* 4K page size, but our output routines, use some slack for overruns */
26  #define PROC_BLOCK_SIZE (3*1024)
27  
28 -static struct proc_dir_entry *proc_scsi;
29 +struct proc_dir_entry *proc_scsi;
30 +EXPORT_SYMBOL(proc_scsi);
31  
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
38 @@ -63,6 +63,63 @@
39  
40  #include "scsi_logging.h"
41  
42 +#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
43 +# include <linux/proc_fs.h>
44 +# include <linux/seq_file.h>
45 +
46 +typedef struct {
47 +       unsigned long long iostat_size;
48 +       unsigned long long iostat_count;
49 +} iostat_counter_t;
50 +
51 +#define IOSTAT_NCOUNTERS 16
52 +typedef struct {
53 +       iostat_counter_t        iostat_read_histogram[IOSTAT_NCOUNTERS];
54 +       iostat_counter_t        iostat_write_histogram[IOSTAT_NCOUNTERS];
55 +       struct timeval          iostat_timeval;
56 +
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;
62 +
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;
71 +
72 +       /* process time: how long it takes to process requests */
73 +       unsigned long           iostat_rtime[IOSTAT_NCOUNTERS];
74 +       unsigned long           iostat_wtime[IOSTAT_NCOUNTERS];
75 +
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];
79 +
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;
83 +
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;
87 +
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);
92 +#else
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) {}
97 +#endif
98 +
99  /*
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
102 @@ -127,6 +184,9 @@
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 */
108 +#endif
109  };
110  #define to_scsi_disk(obj) container_of(obj,struct scsi_disk,cdev)
111  
112 @@ -520,6 +580,8 @@
113          */
114         SCpnt->done = sd_rw_intr;
115  
116 +       sd_iostats_start_req(SCpnt);
117 +
118         /*
119          * This indicates that the command is ready from our end to be
120          * queued.
121 @@ -1014,6 +1076,7 @@
122                 break;
123         }
124   out:
125 +       sd_iostats_finish_req(SCpnt);
126         scsi_io_completion(SCpnt, good_bytes);
127  }
128  
129 @@ -1713,6 +1776,36 @@
130         if (sdp->removable)
131                 gd->flags |= GENHD_FL_REMOVABLE;
132  
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);
138 +       } else {
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);
146 +                       if (!pde) {
147 +                               printk(KERN_WARNING "Can't create /proc/scsi/"
148 +                                                   "%s/%s\n",
149 +                                                   sd_iostats_procdir_name,
150 +                                                   gd->disk_name);
151 +                               kfree(sdkp->stats);
152 +                               sdkp->stats = NULL;
153 +                       } else {
154 +                               pde->proc_fops = &sd_iostats_proc_fops;
155 +                               pde->data = gd;
156 +                       }
157 +               } else {
158 +                       kfree(sdkp->stats);
159 +                       sdkp->stats = NULL;
160 +               }
161 +       }
162 +#endif
163         dev_set_drvdata(dev, sdkp);
164         add_disk(gd);
165  
166 @@ -1756,6 +1849,366 @@
167         return 0;
168  }
169  
170 +#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
171 +static int
172 +sd_iostats_seq_show(struct seq_file *seq, void *v)
173 +{
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;
185 +       int                i;
186 +       int                maxi;
187 +
188 +       stats = scsi_disk(disk)->stats;
189 +       if (stats == NULL) {
190 +               printk(KERN_ERR "sd_iostats_seq_show: NULL stats entry\n");
191 +               BUG();
192 +       }
193 +
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;
199 +               now.tv_sec--;
200 +       }
201 +
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);
206 +
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)
210 +                       break;
211 +       maxi = i;
212 +
213 +       seq_printf(seq, "%8s %8s %12s %8s %12s\n", "size", 
214 +                       "reads", "total", "writes", "total");
215 +
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;
223 +
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;
228 +
229 +               seq_printf (seq, "%8d %8lu %12llu %8lu %12llu\n", 
230 +                               512<<i, read_num, read_len, write_num, write_len);
231 +       }
232 +
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);
236 +
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];
241 +               if (ticks == 0)
242 +                       continue;
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);
246 +       }
247 +
248 +       if (stats->iostat_reqs != 0) {
249 +               unsigned long long aveseek = 0, percent = 0;
250 +
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);
256 +               }
257 +
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);
262 +       }
263 +
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)
270 +                       continue;
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);
281 +       }
282 +
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)
289 +                       continue;
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],
299 +                               read_percent,
300 +                               stats->iostat_wtime_in_queue[i],
301 +                               write_percent);
302 +       }
303 +
304 +       return 0;
305 +}
306 +
307 +static void *
308 +sd_iostats_seq_start(struct seq_file *p, loff_t *pos)
309 +{
310 +       return (*pos == 0) ? (void *)1 : NULL;
311 +}
312 +
313 +static void *
314 +sd_iostats_seq_next(struct seq_file *p, void *v, loff_t *pos)
315 +{
316 +       ++*pos;
317 +       return NULL;
318 +}
319 +
320 +static void
321 +sd_iostats_seq_stop(struct seq_file *p, void *v)
322 +{
323 +}
324 +
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,
330 +};
331 +
332 +static int
333 +sd_iostats_seq_open (struct inode *inode, struct file *file)
334 +{
335 +       int rc;
336 +
337 +       rc = seq_open(file, &sd_iostats_seqops);
338 +       if (rc != 0)
339 +               return rc;
340 +
341 +       ((struct seq_file *)file->private_data)->private = PDE(inode)->data;
342 +       return 0;
343 +}
344 +
345 +static ssize_t
346 +sd_iostats_seq_write(struct file *file, const char *buffer,
347 +                    size_t len, loff_t *off)
348 +{
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;
354 +
355 +
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);
363 +
364 +       return len;
365 +}
366 +
367 +static struct file_operations sd_iostats_proc_fops = {
368 +       .owner   = THIS_MODULE,
369 +       .open    = sd_iostats_seq_open,
370 +       .read    = seq_read,
371 +       .write   = sd_iostats_seq_write,
372 +       .llseek  = seq_lseek,
373 +       .release = seq_release,
374 +};
375 +
376 +extern struct proc_dir_entry *proc_scsi;
377 +
378 +void
379 +sd_iostats_init(void)
380 +{
381 +       if (proc_scsi == NULL) {
382 +               printk(KERN_WARNING "No access to sd iostats: "
383 +                       "proc_scsi is NULL\n");
384 +               return;
385 +       }
386 +
387 +       sd_iostats_procdir = create_proc_entry(sd_iostats_procdir_name,
388 +                                              S_IFDIR | S_IRUGO | S_IXUGO,
389 +                                               proc_scsi);
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);
393 +               return;
394 +       }
395 +}
396 +
397 +void sd_iostats_fini(void)
398 +{
399 +       if (proc_scsi != NULL && sd_iostats_procdir != NULL)
400 +               remove_proc_entry(sd_iostats_procdir_name, proc_scsi);
401 +
402 +       sd_iostats_procdir = NULL;
403 +}
404 +
405 +void sd_iostats_finish_req(struct scsi_cmnd *SCpnt)
406 +{
407 +       struct request          *rq = SCpnt->request;
408 +       iostat_stats_t          *stats;
409 +       unsigned long           *tcounter;
410 +       int                     tbucket;
411 +       int                     tmp;
412 +       unsigned long           irqflags;
413 +       unsigned long           i;
414 +
415 +       stats = scsi_disk(rq->rq_disk)->stats;
416 +       if (stats == NULL)
417 +               return;
418 +
419 +       tmp = jiffies - rq->start_time;
420 +       for (tbucket = 0; tmp > 1; tbucket++)
421 +               tmp >>= 1;
422 +       if (tbucket >= IOSTAT_NCOUNTERS)
423 +               tbucket = IOSTAT_NCOUNTERS - 1;
424 +       //printk("%u ticks in D to %u\n", jiffies - rq->start_time, tbucket);
425 +
426 +       tcounter = rq_data_dir(rq) == WRITE ?
427 +               &stats->iostat_wtime[tbucket] : &stats->iostat_rtime[tbucket];
428 +
429 +       spin_lock_irqsave(&stats->iostat_lock, irqflags);
430 +
431 +       /* update delay stats */
432 +       (*tcounter)++;
433 +
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--;
442 +
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++;
452 +       }
453 +       stats->iostat_next_sector = rq->sector + rq->nr_sectors;
454 +
455 +       stats->iostat_queue_stamp = jiffies;
456 +
457 +       spin_unlock_irqrestore(&stats->iostat_lock, irqflags);
458 +}
459 +
460 +void sd_iostats_start_req(struct scsi_cmnd *SCpnt)
461 +{
462 +       struct request          *rq = SCpnt->request;
463 +       iostat_stats_t          *stats;
464 +       iostat_counter_t        *counter;
465 +       int                     bucket;
466 +       int                     tbucket;
467 +       int                     tmp;
468 +       unsigned long           irqflags;
469 +       unsigned long           i;
470 +       int                     nsect;
471 +
472 +       stats = scsi_disk(rq->rq_disk)->stats;
473 +       if (stats == NULL)
474 +               return;
475 +
476 +       nsect = SCpnt->request_bufflen >> 9;
477 +       for (bucket = 0, tmp = nsect; tmp > 1; bucket++)
478 +               tmp >>= 1;
479 +
480 +       if (bucket >= IOSTAT_NCOUNTERS) {
481 +               printk (KERN_ERR "sd_iostats_bump: nsect %d too big\n", nsect);
482 +               BUG();
483 +       }
484 +
485 +       counter = rq_data_dir(rq) == WRITE ?
486 +               &stats->iostat_write_histogram[bucket] :
487 +               &stats->iostat_read_histogram[bucket];
488 +
489 +       tmp = jiffies - rq->start_time;
490 +       for (tbucket = 0; tmp > 1; tbucket++)
491 +               tmp >>= 1;
492 +       if (tbucket >= IOSTAT_NCOUNTERS)
493 +               tbucket = IOSTAT_NCOUNTERS - 1;
494 +       //printk("%u ticks in Q to %u\n", jiffies - rq->start_time, tbucket);
495 +
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;
500 +
501 +       spin_lock_irqsave(&stats->iostat_lock, irqflags);
502 +
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++;
510 +
511 +       /* update delay stats */
512 +       if (rq_data_dir(rq) == WRITE) {
513 +               stats->iostat_wtime_in_queue[tbucket]++;
514 +               stats->iostat_write_reqs++;
515 +       } else {
516 +               stats->iostat_rtime_in_queue[tbucket]++;
517 +               stats->iostat_read_reqs++;
518 +       }
519 +
520 +       /* update size stats */
521 +       counter->iostat_size += nsect;
522 +       counter->iostat_count++;
523 +
524 +       stats->iostat_queue_stamp = jiffies;
525 +
526 +       spin_unlock_irqrestore(&stats->iostat_lock, irqflags);
527 +}
528 +#endif
529 +
530  /**
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);
536  
537 +#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS))
538 +       if (sdkp->stats) {
539 +               remove_proc_entry(disk->disk_name, sd_iostats_procdir);
540 +               kfree(sdkp->stats);
541 +               sdkp->stats = NULL;
542 +       }
543 +#endif
544         disk->private_data = NULL;
545         put_disk(disk);
546         put_device(&sdkp->device->sdev_gendev);
547 -
548         kfree(sdkp);
549  }
550  
551 @@ -1844,6 +2303,7 @@
552  static int __init init_sd(void)
553  {
554         int majors = 0, i;
555 +       int rc = 0;
556  
557         SCSI_LOG_HLQUEUE(3, printk("init_sd: sd driver entry point\n"));
558  
559 @@ -1854,9 +2314,13 @@
560         if (!majors)
561                 return -ENODEV;
562  
563 +       sd_iostats_init();
564         class_register(&sd_disk_class);
565  
566 -       return scsi_register_driver(&sd_template.gendrv);
567 +       rc = scsi_register_driver(&sd_template.gendrv);
568 +       if (rc)
569 +               sd_iostats_fini();
570 +       return rc;
571  }
572  
573  /**
574 @@ -1875,6 +2339,7 @@
575                 unregister_blkdev(sd_major(i), "sd");
576  
577         class_unregister(&sd_disk_class);
578 +       sd_iostats_fini();
579  }
580  
581  module_init(init_sd);