Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
1da177e4 LT |
2 | * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> |
3 | * Horst Hummel <Horst.Hummel@de.ibm.com> | |
4 | * Carsten Otte <Cotte@de.ibm.com> | |
5 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | |
6 | * Bugreports.to..: <Linux390@de.ibm.com> | |
a53c8fab | 7 | * Coypright IBM Corp. 1999, 2002 |
1da177e4 LT |
8 | * |
9 | * /proc interface for the dasd driver. | |
10 | * | |
1da177e4 LT |
11 | */ |
12 | ||
fc19f381 SH |
13 | #define KMSG_COMPONENT "dasd" |
14 | ||
1da177e4 | 15 | #include <linux/ctype.h> |
5a0e3ad6 | 16 | #include <linux/slab.h> |
e7d2860b | 17 | #include <linux/string.h> |
1da177e4 LT |
18 | #include <linux/seq_file.h> |
19 | #include <linux/vmalloc.h> | |
66a464db | 20 | #include <linux/proc_fs.h> |
1da177e4 LT |
21 | |
22 | #include <asm/debug.h> | |
23 | #include <asm/uaccess.h> | |
24 | ||
25 | /* This is ugly... */ | |
26 | #define PRINTK_HEADER "dasd_proc:" | |
27 | ||
28 | #include "dasd_int.h" | |
29 | ||
30 | static struct proc_dir_entry *dasd_proc_root_entry = NULL; | |
31 | static struct proc_dir_entry *dasd_devices_entry = NULL; | |
32 | static struct proc_dir_entry *dasd_statistics_entry = NULL; | |
33 | ||
1da177e4 LT |
34 | static int |
35 | dasd_devices_show(struct seq_file *m, void *v) | |
36 | { | |
37 | struct dasd_device *device; | |
8e09f215 | 38 | struct dasd_block *block; |
1da177e4 LT |
39 | char *substr; |
40 | ||
41 | device = dasd_device_from_devindex((unsigned long) v - 1); | |
42 | if (IS_ERR(device)) | |
43 | return 0; | |
8e09f215 SW |
44 | if (device->block) |
45 | block = device->block; | |
a5e23839 SW |
46 | else { |
47 | dasd_put_device(device); | |
8e09f215 | 48 | return 0; |
a5e23839 | 49 | } |
1da177e4 | 50 | /* Print device number. */ |
2a0217d5 | 51 | seq_printf(m, "%s", dev_name(&device->cdev->dev)); |
1da177e4 | 52 | /* Print discipline string. */ |
294001a8 | 53 | if (device->discipline != NULL) |
1da177e4 LT |
54 | seq_printf(m, "(%s)", device->discipline->name); |
55 | else | |
56 | seq_printf(m, "(none)"); | |
57 | /* Print kdev. */ | |
8e09f215 | 58 | if (block->gdp) |
1da177e4 | 59 | seq_printf(m, " at (%3d:%6d)", |
f331c029 TH |
60 | MAJOR(disk_devt(block->gdp)), |
61 | MINOR(disk_devt(block->gdp))); | |
1da177e4 LT |
62 | else |
63 | seq_printf(m, " at (???:??????)"); | |
64 | /* Print device name. */ | |
8e09f215 SW |
65 | if (block->gdp) |
66 | seq_printf(m, " is %-8s", block->gdp->disk_name); | |
1da177e4 LT |
67 | else |
68 | seq_printf(m, " is ????????"); | |
69 | /* Print devices features. */ | |
c6eb7b77 | 70 | substr = (device->features & DASD_FEATURE_READONLY) ? "(ro)" : " "; |
1da177e4 LT |
71 | seq_printf(m, "%4s: ", substr); |
72 | /* Print device status information. */ | |
294001a8 | 73 | switch (device->state) { |
1da177e4 LT |
74 | case DASD_STATE_NEW: |
75 | seq_printf(m, "new"); | |
76 | break; | |
77 | case DASD_STATE_KNOWN: | |
78 | seq_printf(m, "detected"); | |
79 | break; | |
80 | case DASD_STATE_BASIC: | |
81 | seq_printf(m, "basic"); | |
82 | break; | |
90f0094d | 83 | case DASD_STATE_UNFMT: |
b707dbe6 | 84 | seq_printf(m, "unformatted"); |
90f0094d | 85 | break; |
1da177e4 LT |
86 | case DASD_STATE_READY: |
87 | case DASD_STATE_ONLINE: | |
88 | seq_printf(m, "active "); | |
8e09f215 | 89 | if (dasd_check_blocksize(block->bp_block)) |
1da177e4 LT |
90 | seq_printf(m, "n/f "); |
91 | else | |
92 | seq_printf(m, | |
b44b0ab3 | 93 | "at blocksize: %d, %lld blocks, %lld MB", |
8e09f215 SW |
94 | block->bp_block, block->blocks, |
95 | ((block->bp_block >> 9) * | |
96 | block->blocks) >> 11); | |
1da177e4 LT |
97 | break; |
98 | default: | |
99 | seq_printf(m, "no stat"); | |
100 | break; | |
101 | } | |
102 | dasd_put_device(device); | |
103 | if (dasd_probeonly) | |
104 | seq_printf(m, "(probeonly)"); | |
105 | seq_printf(m, "\n"); | |
106 | return 0; | |
107 | } | |
108 | ||
109 | static void *dasd_devices_start(struct seq_file *m, loff_t *pos) | |
110 | { | |
111 | if (*pos >= dasd_max_devindex) | |
112 | return NULL; | |
113 | return (void *)((unsigned long) *pos + 1); | |
114 | } | |
115 | ||
116 | static void *dasd_devices_next(struct seq_file *m, void *v, loff_t *pos) | |
117 | { | |
118 | ++*pos; | |
119 | return dasd_devices_start(m, pos); | |
120 | } | |
121 | ||
122 | static void dasd_devices_stop(struct seq_file *m, void *v) | |
123 | { | |
124 | } | |
125 | ||
5c81cdbe | 126 | static const struct seq_operations dasd_devices_seq_ops = { |
1da177e4 LT |
127 | .start = dasd_devices_start, |
128 | .next = dasd_devices_next, | |
129 | .stop = dasd_devices_stop, | |
130 | .show = dasd_devices_show, | |
131 | }; | |
132 | ||
133 | static int dasd_devices_open(struct inode *inode, struct file *file) | |
134 | { | |
135 | return seq_open(file, &dasd_devices_seq_ops); | |
136 | } | |
137 | ||
d54b1fdb | 138 | static const struct file_operations dasd_devices_file_ops = { |
8b594007 | 139 | .owner = THIS_MODULE, |
1da177e4 LT |
140 | .open = dasd_devices_open, |
141 | .read = seq_read, | |
142 | .llseek = seq_lseek, | |
143 | .release = seq_release, | |
144 | }; | |
145 | ||
3d62149f | 146 | #ifdef CONFIG_DASD_PROFILE |
4fa52aa7 SW |
147 | static int dasd_stats_all_block_on(void) |
148 | { | |
149 | int i, rc; | |
150 | struct dasd_device *device; | |
151 | ||
152 | rc = 0; | |
153 | for (i = 0; i < dasd_max_devindex; ++i) { | |
154 | device = dasd_device_from_devindex(i); | |
155 | if (IS_ERR(device)) | |
156 | continue; | |
157 | if (device->block) | |
158 | rc = dasd_profile_on(&device->block->profile); | |
159 | dasd_put_device(device); | |
160 | if (rc) | |
161 | return rc; | |
162 | } | |
163 | return 0; | |
164 | } | |
165 | ||
166 | static void dasd_stats_all_block_off(void) | |
167 | { | |
168 | int i; | |
169 | struct dasd_device *device; | |
170 | ||
171 | for (i = 0; i < dasd_max_devindex; ++i) { | |
172 | device = dasd_device_from_devindex(i); | |
173 | if (IS_ERR(device)) | |
174 | continue; | |
175 | if (device->block) | |
176 | dasd_profile_off(&device->block->profile); | |
177 | dasd_put_device(device); | |
178 | } | |
179 | } | |
180 | ||
181 | static void dasd_stats_all_block_reset(void) | |
182 | { | |
183 | int i; | |
184 | struct dasd_device *device; | |
185 | ||
186 | for (i = 0; i < dasd_max_devindex; ++i) { | |
187 | device = dasd_device_from_devindex(i); | |
188 | if (IS_ERR(device)) | |
189 | continue; | |
190 | if (device->block) | |
191 | dasd_profile_reset(&device->block->profile); | |
192 | dasd_put_device(device); | |
193 | } | |
194 | } | |
195 | ||
34b9243a | 196 | static void dasd_statistics_array(struct seq_file *m, unsigned int *array, int factor) |
1da177e4 LT |
197 | { |
198 | int i; | |
199 | ||
200 | for (i = 0; i < 32; i++) { | |
34b9243a | 201 | seq_printf(m, "%7d ", array[i] / factor); |
1da177e4 | 202 | if (i == 15) |
34b9243a | 203 | seq_putc(m, '\n'); |
1da177e4 | 204 | } |
34b9243a | 205 | seq_putc(m, '\n'); |
1da177e4 | 206 | } |
3d62149f | 207 | #endif /* CONFIG_DASD_PROFILE */ |
1da177e4 | 208 | |
34b9243a | 209 | static int dasd_stats_proc_show(struct seq_file *m, void *v) |
1da177e4 | 210 | { |
1da177e4 | 211 | #ifdef CONFIG_DASD_PROFILE |
4fa52aa7 | 212 | struct dasd_profile_info *prof; |
2bf373b3 | 213 | int factor; |
1da177e4 LT |
214 | |
215 | /* check for active profiling */ | |
4fa52aa7 | 216 | if (!dasd_global_profile_level) { |
34b9243a | 217 | seq_printf(m, "Statistics are off - they might be " |
1da177e4 LT |
218 | "switched on using 'echo set on > " |
219 | "/proc/dasd/statistics'\n"); | |
34b9243a | 220 | return 0; |
1da177e4 | 221 | } |
4fa52aa7 | 222 | prof = &dasd_global_profile_data; |
1da177e4 | 223 | |
fbfecd37 | 224 | /* prevent counter 'overflow' on output */ |
2bf373b3 SH |
225 | for (factor = 1; (prof->dasd_io_reqs / factor) > 9999999; |
226 | factor *= 10); | |
1da177e4 | 227 | |
34b9243a AD |
228 | seq_printf(m, "%d dasd I/O requests\n", prof->dasd_io_reqs); |
229 | seq_printf(m, "with %u sectors(512B each)\n", | |
1da177e4 | 230 | prof->dasd_io_sects); |
34b9243a AD |
231 | seq_printf(m, "Scale Factor is %d\n", factor); |
232 | seq_printf(m, | |
1da177e4 LT |
233 | " __<4 ___8 __16 __32 __64 _128 " |
234 | " _256 _512 __1k __2k __4k __8k " | |
235 | " _16k _32k _64k 128k\n"); | |
34b9243a | 236 | seq_printf(m, |
1da177e4 LT |
237 | " _256 _512 __1M __2M __4M __8M " |
238 | " _16M _32M _64M 128M 256M 512M " | |
239 | " __1G __2G __4G " " _>4G\n"); | |
240 | ||
34b9243a AD |
241 | seq_printf(m, "Histogram of sizes (512B secs)\n"); |
242 | dasd_statistics_array(m, prof->dasd_io_secs, factor); | |
243 | seq_printf(m, "Histogram of I/O times (microseconds)\n"); | |
244 | dasd_statistics_array(m, prof->dasd_io_times, factor); | |
245 | seq_printf(m, "Histogram of I/O times per sector\n"); | |
246 | dasd_statistics_array(m, prof->dasd_io_timps, factor); | |
247 | seq_printf(m, "Histogram of I/O time till ssch\n"); | |
248 | dasd_statistics_array(m, prof->dasd_io_time1, factor); | |
249 | seq_printf(m, "Histogram of I/O time between ssch and irq\n"); | |
250 | dasd_statistics_array(m, prof->dasd_io_time2, factor); | |
251 | seq_printf(m, "Histogram of I/O time between ssch " | |
1da177e4 | 252 | "and irq per sector\n"); |
34b9243a AD |
253 | dasd_statistics_array(m, prof->dasd_io_time2ps, factor); |
254 | seq_printf(m, "Histogram of I/O time between irq and end\n"); | |
255 | dasd_statistics_array(m, prof->dasd_io_time3, factor); | |
256 | seq_printf(m, "# of req in chanq at enqueuing (1..32) \n"); | |
257 | dasd_statistics_array(m, prof->dasd_io_nr_req, factor); | |
1da177e4 | 258 | #else |
34b9243a | 259 | seq_printf(m, "Statistics are not activated in this kernel\n"); |
1da177e4 | 260 | #endif |
34b9243a | 261 | return 0; |
1da177e4 LT |
262 | } |
263 | ||
34b9243a AD |
264 | static int dasd_stats_proc_open(struct inode *inode, struct file *file) |
265 | { | |
266 | return single_open(file, dasd_stats_proc_show, NULL); | |
267 | } | |
268 | ||
269 | static ssize_t dasd_stats_proc_write(struct file *file, | |
270 | const char __user *user_buf, size_t user_len, loff_t *pos) | |
1da177e4 LT |
271 | { |
272 | #ifdef CONFIG_DASD_PROFILE | |
273 | char *buffer, *str; | |
4fa52aa7 | 274 | int rc; |
1da177e4 LT |
275 | |
276 | if (user_len > 65536) | |
277 | user_len = 65536; | |
278 | buffer = dasd_get_user_string(user_buf, user_len); | |
279 | if (IS_ERR(buffer)) | |
280 | return PTR_ERR(buffer); | |
1da177e4 LT |
281 | |
282 | /* check for valid verbs */ | |
e7d2860b | 283 | str = skip_spaces(buffer); |
1da177e4 LT |
284 | if (strncmp(str, "set", 3) == 0 && isspace(str[3])) { |
285 | /* 'set xxx' was given */ | |
e7d2860b | 286 | str = skip_spaces(str + 4); |
1da177e4 LT |
287 | if (strcmp(str, "on") == 0) { |
288 | /* switch on statistics profiling */ | |
4fa52aa7 SW |
289 | rc = dasd_stats_all_block_on(); |
290 | if (rc) { | |
291 | dasd_stats_all_block_off(); | |
292 | goto out_error; | |
293 | } | |
294 | dasd_global_profile_reset(); | |
295 | dasd_global_profile_level = DASD_PROFILE_ON; | |
fc19f381 SH |
296 | pr_info("The statistics feature has been switched " |
297 | "on\n"); | |
1da177e4 LT |
298 | } else if (strcmp(str, "off") == 0) { |
299 | /* switch off and reset statistics profiling */ | |
4fa52aa7 SW |
300 | dasd_global_profile_level = DASD_PROFILE_OFF; |
301 | dasd_global_profile_reset(); | |
302 | dasd_stats_all_block_off(); | |
fc19f381 SH |
303 | pr_info("The statistics feature has been switched " |
304 | "off\n"); | |
1da177e4 | 305 | } else |
4fa52aa7 | 306 | goto out_parse_error; |
1da177e4 LT |
307 | } else if (strncmp(str, "reset", 5) == 0) { |
308 | /* reset the statistics */ | |
4fa52aa7 SW |
309 | dasd_global_profile_reset(); |
310 | dasd_stats_all_block_reset(); | |
fc19f381 | 311 | pr_info("The statistics have been reset\n"); |
1da177e4 | 312 | } else |
4fa52aa7 | 313 | goto out_parse_error; |
e4258d55 | 314 | vfree(buffer); |
1da177e4 | 315 | return user_len; |
4fa52aa7 SW |
316 | out_parse_error: |
317 | rc = -EINVAL; | |
fc19f381 SH |
318 | pr_warning("%s is not a supported value for /proc/dasd/statistics\n", |
319 | str); | |
4fa52aa7 | 320 | out_error: |
e4258d55 | 321 | vfree(buffer); |
4fa52aa7 | 322 | return rc; |
1da177e4 | 323 | #else |
fc19f381 | 324 | pr_warning("/proc/dasd/statistics: is not activated in this kernel\n"); |
1da177e4 LT |
325 | return user_len; |
326 | #endif /* CONFIG_DASD_PROFILE */ | |
327 | } | |
328 | ||
34b9243a AD |
329 | static const struct file_operations dasd_stats_proc_fops = { |
330 | .owner = THIS_MODULE, | |
331 | .open = dasd_stats_proc_open, | |
332 | .read = seq_read, | |
333 | .llseek = seq_lseek, | |
334 | .release = single_release, | |
335 | .write = dasd_stats_proc_write, | |
336 | }; | |
337 | ||
7220fe8b HH |
338 | /* |
339 | * Create dasd proc-fs entries. | |
340 | * In case creation failed, cleanup and return -ENOENT. | |
341 | */ | |
1da177e4 LT |
342 | int |
343 | dasd_proc_init(void) | |
344 | { | |
c74c120a | 345 | dasd_proc_root_entry = proc_mkdir("dasd", NULL); |
7220fe8b HH |
346 | if (!dasd_proc_root_entry) |
347 | goto out_nodasd; | |
8b594007 DL |
348 | dasd_devices_entry = proc_create("devices", |
349 | S_IFREG | S_IRUGO | S_IWUSR, | |
350 | dasd_proc_root_entry, | |
351 | &dasd_devices_file_ops); | |
7220fe8b HH |
352 | if (!dasd_devices_entry) |
353 | goto out_nodevices; | |
34b9243a AD |
354 | dasd_statistics_entry = proc_create("statistics", |
355 | S_IFREG | S_IRUGO | S_IWUSR, | |
356 | dasd_proc_root_entry, | |
357 | &dasd_stats_proc_fops); | |
7220fe8b HH |
358 | if (!dasd_statistics_entry) |
359 | goto out_nostatistics; | |
1da177e4 | 360 | return 0; |
7220fe8b HH |
361 | |
362 | out_nostatistics: | |
363 | remove_proc_entry("devices", dasd_proc_root_entry); | |
364 | out_nodevices: | |
c74c120a | 365 | remove_proc_entry("dasd", NULL); |
7220fe8b HH |
366 | out_nodasd: |
367 | return -ENOENT; | |
1da177e4 LT |
368 | } |
369 | ||
370 | void | |
371 | dasd_proc_exit(void) | |
372 | { | |
373 | remove_proc_entry("devices", dasd_proc_root_entry); | |
374 | remove_proc_entry("statistics", dasd_proc_root_entry); | |
c74c120a | 375 | remove_proc_entry("dasd", NULL); |
1da177e4 | 376 | } |