]>
Commit | Line | Data |
---|---|---|
c80d6cf3 | 1 | Index: linux-2.6.22-rc4/drivers/scsi/Kconfig |
2 | =================================================================== | |
3 | --- linux-2.6.22-rc4.orig/drivers/scsi/Kconfig 2007-06-11 20:23:32.000000000 +0200 | |
4 | +++ linux-2.6.22-rc4/drivers/scsi/Kconfig 2007-06-11 20:24:20.000000000 +0200 | |
5 | @@ -75,6 +75,14 @@ | |
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-2.6.22-rc4/drivers/scsi/sd.c | |
21 | =================================================================== | |
22 | --- linux-2.6.22-rc4.orig/drivers/scsi/sd.c 2007-06-11 20:23:32.000000000 +0200 | |
23 | +++ linux-2.6.22-rc4/drivers/scsi/sd.c 2007-06-11 20:33:35.000000000 +0200 | |
24 | @@ -244,6 +244,38 @@ | |
25 | .issue_flush = sd_issue_flush, | |
26 | }; | |
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 | * Device no to disk mapping: | |
62 | * | |
63 | @@ -347,6 +379,9 @@ | |
64 | (unsigned long long)block, | |
65 | this_count)); | |
66 | ||
67 | + sd_iostats_bump(scsi_disk(disk)->index, this_count, | |
68 | + rq_data_dir(SCpnt->request) == WRITE); | |
69 | + | |
70 | if (!sdp || !scsi_device_online(sdp) || | |
71 | block + rq->nr_sectors > get_capacity(disk)) { | |
72 | SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt, | |
73 | @@ -575,6 +610,8 @@ | |
74 | scsi_set_medium_removal(sdev, SCSI_REMOVAL_PREVENT); | |
75 | } | |
76 | ||
77 | + sd_iostats_init_disk(disk); | |
78 | + | |
79 | return 0; | |
80 | ||
81 | error_out: | |
82 | @@ -1002,6 +1039,7 @@ | |
83 | return 1; | |
84 | } | |
85 | ||
86 | +#define SD_STATS 256 | |
87 | /* | |
88 | * spinup disk - called only in sd_revalidate_disk() | |
89 | */ | |
90 | @@ -1684,6 +1722,327 @@ | |
91 | return error; | |
92 | } | |
93 | ||
94 | +#if (defined(CONFIG_SD_IOSTATS) && defined(CONFIG_PROC_FS)) | |
95 | +static int | |
96 | +sd_iostats_seq_show(struct seq_file *seq, void *v) | |
97 | +{ | |
98 | + struct timeval now; | |
99 | + struct gendisk *disk = seq->private; | |
100 | + iostat_stats_t *stats; | |
101 | + unsigned long long read_len; | |
102 | + unsigned long long read_len_tot; | |
103 | + unsigned long read_num; | |
104 | + unsigned long read_num_tot; | |
105 | + unsigned long long write_len; | |
106 | + unsigned long long write_len_tot; | |
107 | + unsigned long write_num; | |
108 | + unsigned long write_num_tot; | |
109 | + int i; | |
110 | + int maxi; | |
111 | + | |
112 | + if (sd_iostats == NULL) { | |
113 | + printk(KERN_ERR "sd_iostats_seq_show: NULL stats array\n"); | |
114 | + BUG(); | |
115 | + } | |
116 | + | |
117 | + stats = sd_iostats[scsi_disk(disk)->index]; | |
118 | + if (stats == NULL) { | |
119 | + printk(KERN_ERR "sd_iostats_seq_show: NULL stats entry\n"); | |
120 | + BUG(); | |
121 | + } | |
122 | + | |
123 | + do_gettimeofday(&now); | |
124 | + now.tv_sec -= stats->iostat_timeval.tv_sec; | |
125 | + now.tv_usec -= stats->iostat_timeval.tv_usec; | |
126 | + if (now.tv_usec < 0) { | |
127 | + now.tv_usec += 1000000; | |
128 | + now.tv_sec--; | |
129 | + } | |
130 | + | |
131 | + /* this sampling races with updates */ | |
132 | + seq_printf(seq, "index: %u snapshot_time: %lu.%06lu\n", | |
133 | + scsi_disk(disk)->index, now.tv_sec, now.tv_usec); | |
134 | + | |
135 | + for (i = IOSTAT_NCOUNTERS - 1; i > 0; i--) | |
136 | + if (stats->iostat_read_histogram[i].iostat_count != 0 || | |
137 | + stats->iostat_write_histogram[i].iostat_count != 0) | |
138 | + break; | |
139 | + maxi = i; | |
140 | + | |
141 | + seq_printf(seq, "%8s %8s %12s %8s %12s\n", "size", | |
142 | + "reads", "total", "writes", "total"); | |
143 | + | |
144 | + read_len_tot = write_len_tot = 0; | |
145 | + read_num_tot = write_num_tot = 0; | |
146 | + for (i = 0; i <= maxi; i++) { | |
147 | + read_len = stats->iostat_read_histogram[i].iostat_size; | |
148 | + read_len_tot += read_len; | |
149 | + read_num = stats->iostat_read_histogram[i].iostat_count; | |
150 | + read_num_tot += read_num; | |
151 | + | |
152 | + write_len = stats->iostat_write_histogram[i].iostat_size; | |
153 | + write_len_tot += write_len; | |
154 | + write_num = stats->iostat_write_histogram[i].iostat_count; | |
155 | + write_num_tot += write_num; | |
156 | + | |
157 | + seq_printf (seq, "%8d %8lu %12llu %8lu %12llu\n", | |
158 | + 512<<i, read_num, read_len, write_num, write_len); | |
159 | + } | |
160 | + | |
161 | + seq_printf(seq, "%8s %8lu %12llu %8lu %12llu\n", "total", | |
162 | + read_num_tot, read_len_tot, | |
163 | + write_num_tot, write_len_tot); | |
164 | + return 0; | |
165 | +} | |
166 | + | |
167 | +static void * | |
168 | +sd_iostats_seq_start(struct seq_file *p, loff_t *pos) | |
169 | +{ | |
170 | + return (*pos == 0) ? (void *)1 : NULL; | |
171 | +} | |
172 | + | |
173 | +static void * | |
174 | +sd_iostats_seq_next(struct seq_file *p, void *v, loff_t *pos) | |
175 | +{ | |
176 | + ++*pos; | |
177 | + return NULL; | |
178 | +} | |
179 | + | |
180 | +static void | |
181 | +sd_iostats_seq_stop(struct seq_file *p, void *v) | |
182 | +{ | |
183 | +} | |
184 | + | |
185 | +static struct seq_operations sd_iostats_seqops = { | |
186 | + .start = sd_iostats_seq_start, | |
187 | + .stop = sd_iostats_seq_stop, | |
188 | + .next = sd_iostats_seq_next, | |
189 | + .show = sd_iostats_seq_show, | |
190 | +}; | |
191 | + | |
192 | +static int | |
193 | +sd_iostats_seq_open (struct inode *inode, struct file *file) | |
194 | +{ | |
195 | + int rc; | |
196 | + | |
197 | + rc = seq_open(file, &sd_iostats_seqops); | |
198 | + if (rc != 0) | |
199 | + return rc; | |
200 | + | |
201 | + ((struct seq_file *)file->private_data)->private = PDE(inode)->data; | |
202 | + return 0; | |
203 | +} | |
204 | + | |
205 | +static ssize_t | |
206 | +sd_iostats_seq_write(struct file *file, const char *buffer, | |
207 | + size_t len, loff_t *off) | |
208 | +{ | |
209 | + struct seq_file *seq = file->private_data; | |
210 | + struct gendisk *disk = seq->private; | |
211 | + iostat_stats_t *stats = sd_iostats[scsi_disk(disk)->index]; | |
212 | + unsigned long flags; | |
213 | + | |
214 | + | |
215 | + spin_lock_irqsave (&sd_iostats_lock, flags); | |
216 | + memset (stats, 0, sizeof(*stats)); | |
217 | + do_gettimeofday(&stats->iostat_timeval); | |
218 | + spin_unlock_irqrestore (&sd_iostats_lock, flags); | |
219 | + | |
220 | + return len; | |
221 | +} | |
222 | + | |
223 | +static struct file_operations sd_iostats_proc_fops = { | |
224 | + .owner = THIS_MODULE, | |
225 | + .open = sd_iostats_seq_open, | |
226 | + .read = seq_read, | |
227 | + .write = sd_iostats_seq_write, | |
228 | + .llseek = seq_lseek, | |
229 | + .release = seq_release, | |
230 | +}; | |
231 | + | |
232 | +extern struct proc_dir_entry *proc_scsi; | |
233 | + | |
234 | +void | |
235 | +sd_iostats_init(void) | |
236 | +{ | |
237 | + int i; | |
238 | + | |
239 | + spin_lock_init(&sd_iostats_lock); | |
240 | + | |
241 | + sd_iostats = kmalloc(SD_STATS * sizeof(iostat_stats_t *), GFP_KERNEL); | |
242 | + if (sd_iostats == NULL) { | |
243 | + printk(KERN_WARNING "Can't keep sd iostats: " | |
244 | + "ENOMEM allocating stats array size %ld\n", | |
245 | + SD_STATS * sizeof(iostat_stats_t *)); | |
246 | + return; | |
247 | + } | |
248 | + | |
249 | + for (i = 0; i < SD_STATS; i++) | |
250 | + sd_iostats[i] = NULL; | |
251 | + | |
252 | + if (proc_scsi == NULL) { | |
253 | + printk(KERN_WARNING "No access to sd iostats: " | |
254 | + "proc_scsi is NULL\n"); | |
255 | + return; | |
256 | + } | |
257 | + | |
258 | + sd_iostats_procdir = create_proc_entry(sd_iostats_procdir_name, | |
259 | + S_IFDIR | S_IRUGO | S_IXUGO, | |
260 | + proc_scsi); | |
261 | + if (sd_iostats_procdir == NULL) { | |
262 | + printk(KERN_WARNING "No access to sd iostats: " | |
263 | + "can't create /proc/scsi/%s\n", sd_iostats_procdir_name); | |
264 | + return; | |
265 | + } | |
266 | +} | |
267 | + | |
268 | +void | |
269 | +sd_iostats_init_disk(struct gendisk *disk) | |
270 | +{ | |
271 | + struct proc_dir_entry *pde; | |
272 | + unsigned long flags; | |
273 | + iostat_stats_t *stats; | |
274 | + | |
275 | + if (sd_iostats == NULL || | |
276 | + sd_iostats_procdir == NULL) | |
277 | + return; | |
278 | + | |
279 | + if (scsi_disk(disk)->index > SD_STATS) { | |
280 | + printk(KERN_ERR "sd_iostats_init_disk: " | |
281 | + "unexpected disk index %d(%d)\n", | |
282 | + scsi_disk(disk)->index, SD_STATS); | |
283 | + return; | |
284 | + } | |
285 | + | |
286 | + if (sd_iostats[scsi_disk(disk)->index] != NULL) | |
287 | + return; | |
288 | + | |
289 | + stats = kmalloc(sizeof(*stats), GFP_KERNEL); | |
290 | + if (stats == NULL) { | |
291 | + printk(KERN_WARNING "Can't keep %s iostats: " | |
292 | + "ENOMEM allocating stats size %ld\n", | |
293 | + disk->disk_name, sizeof(*stats)); | |
294 | + return; | |
295 | + } | |
296 | + | |
297 | + memset (stats, 0, sizeof(*stats)); | |
298 | + do_gettimeofday(&stats->iostat_timeval); | |
299 | + | |
300 | + spin_lock_irqsave(&sd_iostats_lock, flags); | |
301 | + | |
302 | + if (sd_iostats[scsi_disk(disk)->index] != NULL) { | |
303 | + spin_unlock_irqrestore(&sd_iostats_lock, flags); | |
304 | + kfree (stats); | |
305 | + return; | |
306 | + } | |
307 | + | |
308 | + sd_iostats[scsi_disk(disk)->index] = stats; | |
309 | + | |
310 | + spin_unlock_irqrestore(&sd_iostats_lock, flags); | |
311 | + | |
312 | + pde = create_proc_entry(disk->disk_name, S_IRUGO | S_IWUSR, | |
313 | + sd_iostats_procdir); | |
314 | + if (pde == NULL) { | |
315 | + printk(KERN_WARNING "Can't create /proc/scsi/%s/%s\n", | |
316 | + sd_iostats_procdir_name, disk->disk_name); | |
317 | + } else { | |
318 | + pde->proc_fops = &sd_iostats_proc_fops; | |
319 | + pde->data = disk; | |
320 | + } | |
321 | +} | |
322 | + | |
323 | +static void sd_devname(unsigned int disknum, char *buffer) | |
324 | +{ | |
325 | + if (disknum < 26) | |
326 | + sprintf(buffer, "sd%c", 'a' + disknum); | |
327 | + else { | |
328 | + unsigned int min1; | |
329 | + unsigned int min2; | |
330 | + /* | |
331 | + * For larger numbers of disks, we need to go to a new | |
332 | + * naming scheme. | |
333 | + */ | |
334 | + min1 = disknum / 26; | |
335 | + min2 = disknum % 26; | |
336 | + sprintf(buffer, "sd%c%c", 'a' + min1 - 1, 'a' + min2); | |
337 | + } | |
338 | +} | |
339 | + | |
340 | +void | |
341 | +sd_iostats_fini(void) | |
342 | +{ | |
343 | + char name[6]; | |
344 | + int i; | |
345 | + | |
346 | + if (sd_iostats_procdir != NULL) { | |
347 | + for (i = 0; i < SD_STATS; i++) { | |
348 | + sd_devname(i, name); | |
349 | + remove_proc_entry(name, sd_iostats_procdir); | |
350 | + } | |
351 | + | |
352 | + if (proc_scsi == NULL) { | |
353 | + printk(KERN_ERR "sd_iostats_fini: proc_scsi NULL\n"); | |
354 | + BUG(); | |
355 | + } | |
356 | + remove_proc_entry(sd_iostats_procdir_name, | |
357 | + proc_scsi); | |
358 | + | |
359 | + sd_iostats_procdir = NULL; | |
360 | + } | |
361 | + | |
362 | + if (sd_iostats != NULL) { | |
363 | + for (i = 0; i < SD_STATS; i++) { | |
364 | + if (sd_iostats[i] != NULL) | |
365 | + kfree (sd_iostats[i]); | |
366 | + } | |
367 | + | |
368 | + kfree(sd_iostats); | |
369 | + sd_iostats = NULL; | |
370 | + } | |
371 | +} | |
372 | + | |
373 | +void | |
374 | +sd_iostats_bump(int disk, unsigned int nsect, int iswrite) | |
375 | +{ | |
376 | + iostat_stats_t *stats; | |
377 | + iostat_counter_t *counter; | |
378 | + int bucket; | |
379 | + int tmp; | |
380 | + unsigned long irqflags; | |
381 | + | |
382 | + if (sd_iostats == NULL) | |
383 | + return; | |
384 | + | |
385 | + if (disk < 0 || disk >= SD_STATS) { | |
386 | + printk(KERN_ERR "sd_iostats_bump: unexpected disk index %d([0-%d])\n", | |
387 | + disk, SD_STATS); | |
388 | + BUG(); | |
389 | + } | |
390 | + | |
391 | + for (bucket = 0, tmp = nsect; tmp > 1; bucket++) | |
392 | + tmp /= 2; | |
393 | + | |
394 | + if (bucket >= IOSTAT_NCOUNTERS) { | |
395 | + printk (KERN_ERR "sd_iostats_bump: nsect %d too big\n", nsect); | |
396 | + BUG(); | |
397 | + } | |
398 | + | |
399 | + spin_lock_irqsave(&sd_iostats_lock, irqflags); | |
400 | + | |
401 | + stats = sd_iostats[disk]; | |
402 | + if (stats != NULL) { | |
403 | + counter = iswrite ? | |
404 | + &stats->iostat_write_histogram[bucket] : | |
405 | + &stats->iostat_read_histogram[bucket]; | |
406 | + | |
407 | + counter->iostat_size += nsect; | |
408 | + counter->iostat_count++; | |
409 | + } | |
410 | + | |
411 | + spin_unlock_irqrestore(&sd_iostats_lock, irqflags); | |
412 | +} | |
413 | +#endif | |
414 | + | |
415 | /** | |
416 | * sd_remove - called whenever a scsi disk (previously recognized by | |
417 | * sd_probe) is detached from the system. It is called (potentially | |
418 | @@ -1855,6 +2214,7 @@ | |
419 | if (err) | |
420 | goto err_out_class; | |
421 | ||
422 | + sd_iostats_init(); | |
423 | return 0; | |
424 | ||
425 | err_out_class: | |
426 | @@ -1876,6 +2236,7 @@ | |
427 | ||
428 | SCSI_LOG_HLQUEUE(3, printk("exit_sd: exiting sd driver\n")); | |
429 | ||
430 | + sd_iostats_fini(); | |
431 | scsi_unregister_driver(&sd_template.gendrv); | |
432 | class_unregister(&sd_disk_class); | |
433 | ||
434 | Index: linux-2.6.22-rc4/drivers/scsi/scsi_proc.c | |
435 | =================================================================== | |
436 | --- linux-2.6.22-rc4.orig/drivers/scsi/scsi_proc.c 2007-06-11 20:23:32.000000000 +0200 | |
437 | +++ linux-2.6.22-rc4/drivers/scsi/scsi_proc.c 2007-06-11 20:24:20.000000000 +0200 | |
438 | @@ -40,7 +40,8 @@ | |
439 | /* 4K page size, but our output routines, use some slack for overruns */ | |
440 | #define PROC_BLOCK_SIZE (3*1024) | |
441 | ||
442 | -static struct proc_dir_entry *proc_scsi; | |
443 | +struct proc_dir_entry *proc_scsi; | |
444 | +EXPORT_SYMBOL(proc_scsi); | |
445 | ||
446 | /* Protect sht->present and sht->proc_dir */ | |
447 | static DEFINE_MUTEX(global_host_template_mutex); |