[S390] dasd: add hyper PAV support to DASD device driver, part 1
[safe/jmp/linux-2.6] / drivers / s390 / block / dasd_proc.c
1 /*
2  * File...........: linux/drivers/s390/block/dasd_proc.c
3  * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
4  *                  Horst Hummel <Horst.Hummel@de.ibm.com>
5  *                  Carsten Otte <Cotte@de.ibm.com>
6  *                  Martin Schwidefsky <schwidefsky@de.ibm.com>
7  * Bugreports.to..: <Linux390@de.ibm.com>
8  * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2002
9  *
10  * /proc interface for the dasd driver.
11  *
12  */
13
14 #include <linux/ctype.h>
15 #include <linux/seq_file.h>
16 #include <linux/vmalloc.h>
17 #include <linux/proc_fs.h>
18
19 #include <asm/debug.h>
20 #include <asm/uaccess.h>
21
22 /* This is ugly... */
23 #define PRINTK_HEADER "dasd_proc:"
24
25 #include "dasd_int.h"
26
27 static struct proc_dir_entry *dasd_proc_root_entry = NULL;
28 static struct proc_dir_entry *dasd_devices_entry = NULL;
29 static struct proc_dir_entry *dasd_statistics_entry = NULL;
30
31 #ifdef CONFIG_DASD_PROFILE
32 static char *
33 dasd_get_user_string(const char __user *user_buf, size_t user_len)
34 {
35         char *buffer;
36
37         buffer = kmalloc(user_len + 1, GFP_KERNEL);
38         if (buffer == NULL)
39                 return ERR_PTR(-ENOMEM);
40         if (copy_from_user(buffer, user_buf, user_len) != 0) {
41                 kfree(buffer);
42                 return ERR_PTR(-EFAULT);
43         }
44         /* got the string, now strip linefeed. */
45         if (buffer[user_len - 1] == '\n')
46                 buffer[user_len - 1] = 0;
47         else
48                 buffer[user_len] = 0;
49         return buffer;
50 }
51 #endif /* CONFIG_DASD_PROFILE */
52
53 static int
54 dasd_devices_show(struct seq_file *m, void *v)
55 {
56         struct dasd_device *device;
57         struct dasd_block *block;
58         char *substr;
59
60         device = dasd_device_from_devindex((unsigned long) v - 1);
61         if (IS_ERR(device))
62                 return 0;
63         if (device->block)
64                 block = device->block;
65         else
66                 return 0;
67         /* Print device number. */
68         seq_printf(m, "%s", device->cdev->dev.bus_id);
69         /* Print discipline string. */
70         if (device != NULL && device->discipline != NULL)
71                 seq_printf(m, "(%s)", device->discipline->name);
72         else
73                 seq_printf(m, "(none)");
74         /* Print kdev. */
75         if (block->gdp)
76                 seq_printf(m, " at (%3d:%6d)",
77                            block->gdp->major, block->gdp->first_minor);
78         else
79                 seq_printf(m, "  at (???:??????)");
80         /* Print device name. */
81         if (block->gdp)
82                 seq_printf(m, " is %-8s", block->gdp->disk_name);
83         else
84                 seq_printf(m, " is ????????");
85         /* Print devices features. */
86         substr = (device->features & DASD_FEATURE_READONLY) ? "(ro)" : " ";
87         seq_printf(m, "%4s: ", substr);
88         /* Print device status information. */
89         switch ((device != NULL) ? device->state : -1) {
90         case -1:
91                 seq_printf(m, "unknown");
92                 break;
93         case DASD_STATE_NEW:
94                 seq_printf(m, "new");
95                 break;
96         case DASD_STATE_KNOWN:
97                 seq_printf(m, "detected");
98                 break;
99         case DASD_STATE_BASIC:
100                 seq_printf(m, "basic");
101                 break;
102         case DASD_STATE_UNFMT:
103                 seq_printf(m, "unformatted");
104                 break;
105         case DASD_STATE_READY:
106         case DASD_STATE_ONLINE:
107                 seq_printf(m, "active ");
108                 if (dasd_check_blocksize(block->bp_block))
109                         seq_printf(m, "n/f       ");
110                 else
111                         seq_printf(m,
112                                    "at blocksize: %d, %ld blocks, %ld MB",
113                                    block->bp_block, block->blocks,
114                                    ((block->bp_block >> 9) *
115                                     block->blocks) >> 11);
116                 break;
117         default:
118                 seq_printf(m, "no stat");
119                 break;
120         }
121         dasd_put_device(device);
122         if (dasd_probeonly)
123                 seq_printf(m, "(probeonly)");
124         seq_printf(m, "\n");
125         return 0;
126 }
127
128 static void *dasd_devices_start(struct seq_file *m, loff_t *pos)
129 {
130         if (*pos >= dasd_max_devindex)
131                 return NULL;
132         return (void *)((unsigned long) *pos + 1);
133 }
134
135 static void *dasd_devices_next(struct seq_file *m, void *v, loff_t *pos)
136 {
137         ++*pos;
138         return dasd_devices_start(m, pos);
139 }
140
141 static void dasd_devices_stop(struct seq_file *m, void *v)
142 {
143 }
144
145 static struct seq_operations dasd_devices_seq_ops = {
146         .start          = dasd_devices_start,
147         .next           = dasd_devices_next,
148         .stop           = dasd_devices_stop,
149         .show           = dasd_devices_show,
150 };
151
152 static int dasd_devices_open(struct inode *inode, struct file *file)
153 {
154         return seq_open(file, &dasd_devices_seq_ops);
155 }
156
157 static const struct file_operations dasd_devices_file_ops = {
158         .open           = dasd_devices_open,
159         .read           = seq_read,
160         .llseek         = seq_lseek,
161         .release        = seq_release,
162 };
163
164 static int
165 dasd_calc_metrics(char *page, char **start, off_t off,
166                   int count, int *eof, int len)
167 {
168         len = (len > off) ? len - off : 0;
169         if (len > count)
170                 len = count;
171         if (len < count)
172                 *eof = 1;
173         *start = page + off;
174         return len;
175 }
176
177 #ifdef CONFIG_DASD_PROFILE
178 static char *
179 dasd_statistics_array(char *str, unsigned int *array, int shift)
180 {
181         int i;
182
183         for (i = 0; i < 32; i++) {
184                 str += sprintf(str, "%7d ", array[i] >> shift);
185                 if (i == 15)
186                         str += sprintf(str, "\n");
187         }
188         str += sprintf(str,"\n");
189         return str;
190 }
191 #endif /* CONFIG_DASD_PROFILE */
192
193 static int
194 dasd_statistics_read(char *page, char **start, off_t off,
195                      int count, int *eof, void *data)
196 {
197         unsigned long len;
198 #ifdef CONFIG_DASD_PROFILE
199         struct dasd_profile_info_t *prof;
200         char *str;
201         int shift;
202
203         /* check for active profiling */
204         if (dasd_profile_level == DASD_PROFILE_OFF) {
205                 len = sprintf(page, "Statistics are off - they might be "
206                                     "switched on using 'echo set on > "
207                                     "/proc/dasd/statistics'\n");
208                 return dasd_calc_metrics(page, start, off, count, eof, len);
209         }
210
211         prof = &dasd_global_profile;
212         /* prevent couter 'overflow' on output */
213         for (shift = 0; (prof->dasd_io_reqs >> shift) > 9999999; shift++);
214
215         str = page;
216         str += sprintf(str, "%d dasd I/O requests\n", prof->dasd_io_reqs);
217         str += sprintf(str, "with %d sectors(512B each)\n",
218                        prof->dasd_io_sects);
219         str += sprintf(str,
220                        "   __<4    ___8    __16    __32    __64    _128 "
221                        "   _256    _512    __1k    __2k    __4k    __8k "
222                        "   _16k    _32k    _64k    128k\n");
223         str += sprintf(str,
224                        "   _256    _512    __1M    __2M    __4M    __8M "
225                        "   _16M    _32M    _64M    128M    256M    512M "
226                        "   __1G    __2G    __4G " "   _>4G\n");
227
228         str += sprintf(str, "Histogram of sizes (512B secs)\n");
229         str = dasd_statistics_array(str, prof->dasd_io_secs, shift);
230         str += sprintf(str, "Histogram of I/O times (microseconds)\n");
231         str = dasd_statistics_array(str, prof->dasd_io_times, shift);
232         str += sprintf(str, "Histogram of I/O times per sector\n");
233         str = dasd_statistics_array(str, prof->dasd_io_timps, shift);
234         str += sprintf(str, "Histogram of I/O time till ssch\n");
235         str = dasd_statistics_array(str, prof->dasd_io_time1, shift);
236         str += sprintf(str, "Histogram of I/O time between ssch and irq\n");
237         str = dasd_statistics_array(str, prof->dasd_io_time2, shift);
238         str += sprintf(str, "Histogram of I/O time between ssch "
239                             "and irq per sector\n");
240         str = dasd_statistics_array(str, prof->dasd_io_time2ps, shift);
241         str += sprintf(str, "Histogram of I/O time between irq and end\n");
242         str = dasd_statistics_array(str, prof->dasd_io_time3, shift);
243         str += sprintf(str, "# of req in chanq at enqueuing (1..32) \n");
244         str = dasd_statistics_array(str, prof->dasd_io_nr_req, shift);
245         len = str - page;
246 #else
247         len = sprintf(page, "Statistics are not activated in this kernel\n");
248 #endif
249         return dasd_calc_metrics(page, start, off, count, eof, len);
250 }
251
252 static int
253 dasd_statistics_write(struct file *file, const char __user *user_buf,
254                       unsigned long user_len, void *data)
255 {
256 #ifdef CONFIG_DASD_PROFILE
257         char *buffer, *str;
258
259         if (user_len > 65536)
260                 user_len = 65536;
261         buffer = dasd_get_user_string(user_buf, user_len);
262         if (IS_ERR(buffer))
263                 return PTR_ERR(buffer);
264         MESSAGE_LOG(KERN_INFO, "/proc/dasd/statictics: '%s'", buffer);
265
266         /* check for valid verbs */
267         for (str = buffer; isspace(*str); str++);
268         if (strncmp(str, "set", 3) == 0 && isspace(str[3])) {
269                 /* 'set xxx' was given */
270                 for (str = str + 4; isspace(*str); str++);
271                 if (strcmp(str, "on") == 0) {
272                         /* switch on statistics profiling */
273                         dasd_profile_level = DASD_PROFILE_ON;
274                         MESSAGE(KERN_INFO, "%s", "Statistics switched on");
275                 } else if (strcmp(str, "off") == 0) {
276                         /* switch off and reset statistics profiling */
277                         memset(&dasd_global_profile,
278                                0, sizeof (struct dasd_profile_info_t));
279                         dasd_profile_level = DASD_PROFILE_OFF;
280                         MESSAGE(KERN_INFO, "%s", "Statistics switched off");
281                 } else
282                         goto out_error;
283         } else if (strncmp(str, "reset", 5) == 0) {
284                 /* reset the statistics */
285                 memset(&dasd_global_profile, 0,
286                        sizeof (struct dasd_profile_info_t));
287                 MESSAGE(KERN_INFO, "%s", "Statistics reset");
288         } else
289                 goto out_error;
290         kfree(buffer);
291         return user_len;
292 out_error:
293         MESSAGE(KERN_WARNING, "%s",
294                 "/proc/dasd/statistics: only 'set on', 'set off' "
295                 "and 'reset' are supported verbs");
296         kfree(buffer);
297         return -EINVAL;
298 #else
299         MESSAGE(KERN_WARNING, "%s",
300                 "/proc/dasd/statistics: is not activated in this kernel");
301         return user_len;
302 #endif                          /* CONFIG_DASD_PROFILE */
303 }
304
305 /*
306  * Create dasd proc-fs entries.
307  * In case creation failed, cleanup and return -ENOENT.
308  */
309 int
310 dasd_proc_init(void)
311 {
312         dasd_proc_root_entry = proc_mkdir("dasd", &proc_root);
313         if (!dasd_proc_root_entry)
314                 goto out_nodasd;
315         dasd_proc_root_entry->owner = THIS_MODULE;
316         dasd_devices_entry = create_proc_entry("devices",
317                                                S_IFREG | S_IRUGO | S_IWUSR,
318                                                dasd_proc_root_entry);
319         if (!dasd_devices_entry)
320                 goto out_nodevices;
321         dasd_devices_entry->proc_fops = &dasd_devices_file_ops;
322         dasd_devices_entry->owner = THIS_MODULE;
323         dasd_statistics_entry = create_proc_entry("statistics",
324                                                   S_IFREG | S_IRUGO | S_IWUSR,
325                                                   dasd_proc_root_entry);
326         if (!dasd_statistics_entry)
327                 goto out_nostatistics;
328         dasd_statistics_entry->read_proc = dasd_statistics_read;
329         dasd_statistics_entry->write_proc = dasd_statistics_write;
330         dasd_statistics_entry->owner = THIS_MODULE;
331         return 0;
332
333  out_nostatistics:
334         remove_proc_entry("devices", dasd_proc_root_entry);
335  out_nodevices:
336         remove_proc_entry("dasd", &proc_root);
337  out_nodasd:
338         return -ENOENT;
339 }
340
341 void
342 dasd_proc_exit(void)
343 {
344         remove_proc_entry("devices", dasd_proc_root_entry);
345         remove_proc_entry("statistics", dasd_proc_root_entry);
346         remove_proc_entry("dasd", &proc_root);
347 }