upload tizen1.0 source
[kernel/linux-2.6.36.git] / drivers / s390 / cio / blacklist.c
1 /*
2  *  drivers/s390/cio/blacklist.c
3  *   S/390 common I/O routines -- blacklisting of specific devices
4  *
5  *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
6  *                            IBM Corporation
7  *    Author(s): Ingo Adlung (adlung@de.ibm.com)
8  *               Cornelia Huck (cornelia.huck@de.ibm.com)
9  *               Arnd Bergmann (arndb@de.ibm.com)
10  */
11
12 #define KMSG_COMPONENT "cio"
13 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
14
15 #include <linux/init.h>
16 #include <linux/vmalloc.h>
17 #include <linux/proc_fs.h>
18 #include <linux/seq_file.h>
19 #include <linux/ctype.h>
20 #include <linux/device.h>
21
22 #include <asm/cio.h>
23 #include <asm/uaccess.h>
24
25 #include "blacklist.h"
26 #include "cio.h"
27 #include "cio_debug.h"
28 #include "css.h"
29 #include "device.h"
30
31 /*
32  * "Blacklisting" of certain devices:
33  * Device numbers given in the commandline as cio_ignore=... won't be known
34  * to Linux.
35  *
36  * These can be single devices or ranges of devices
37  */
38
39 /* 65536 bits for each set to indicate if a devno is blacklisted or not */
40 #define __BL_DEV_WORDS ((__MAX_SUBCHANNEL + (8*sizeof(long) - 1)) / \
41                          (8*sizeof(long)))
42 static unsigned long bl_dev[__MAX_SSID + 1][__BL_DEV_WORDS];
43 typedef enum {add, free} range_action;
44
45 /*
46  * Function: blacklist_range
47  * (Un-)blacklist the devices from-to
48  */
49 static int blacklist_range(range_action action, unsigned int from_ssid,
50                            unsigned int to_ssid, unsigned int from,
51                            unsigned int to, int msgtrigger)
52 {
53         if ((from_ssid > to_ssid) || ((from_ssid == to_ssid) && (from > to))) {
54                 if (msgtrigger)
55                         pr_warning("0.%x.%04x to 0.%x.%04x is not a valid "
56                                    "range for cio_ignore\n", from_ssid, from,
57                                    to_ssid, to);
58
59                 return 1;
60         }
61
62         while ((from_ssid < to_ssid) || ((from_ssid == to_ssid) &&
63                (from <= to))) {
64                 if (action == add)
65                         set_bit(from, bl_dev[from_ssid]);
66                 else
67                         clear_bit(from, bl_dev[from_ssid]);
68                 from++;
69                 if (from > __MAX_SUBCHANNEL) {
70                         from_ssid++;
71                         from = 0;
72                 }
73         }
74
75         return 0;
76 }
77
78 static int pure_hex(char **cp, unsigned int *val, int min_digit,
79                     int max_digit, int max_val)
80 {
81         int diff;
82         unsigned int value;
83
84         diff = 0;
85         *val = 0;
86
87         while (isxdigit(**cp) && (diff <= max_digit)) {
88
89                 if (isdigit(**cp))
90                         value = **cp - '0';
91                 else
92                         value = tolower(**cp) - 'a' + 10;
93                 *val = *val * 16 + value;
94                 (*cp)++;
95                 diff++;
96         }
97
98         if ((diff < min_digit) || (diff > max_digit) || (*val > max_val))
99                 return 1;
100
101         return 0;
102 }
103
104 static int parse_busid(char *str, unsigned int *cssid, unsigned int *ssid,
105                        unsigned int *devno, int msgtrigger)
106 {
107         char *str_work;
108         int val, rc, ret;
109
110         rc = 1;
111
112         if (*str == '\0')
113                 goto out;
114
115         /* old style */
116         str_work = str;
117         val = simple_strtoul(str, &str_work, 16);
118
119         if (*str_work == '\0') {
120                 if (val <= __MAX_SUBCHANNEL) {
121                         *devno = val;
122                         *ssid = 0;
123                         *cssid = 0;
124                         rc = 0;
125                 }
126                 goto out;
127         }
128
129         /* new style */
130         str_work = str;
131         ret = pure_hex(&str_work, cssid, 1, 2, __MAX_CSSID);
132         if (ret || (str_work[0] != '.'))
133                 goto out;
134         str_work++;
135         ret = pure_hex(&str_work, ssid, 1, 1, __MAX_SSID);
136         if (ret || (str_work[0] != '.'))
137                 goto out;
138         str_work++;
139         ret = pure_hex(&str_work, devno, 4, 4, __MAX_SUBCHANNEL);
140         if (ret || (str_work[0] != '\0'))
141                 goto out;
142
143         rc = 0;
144 out:
145         if (rc && msgtrigger)
146                 pr_warning("%s is not a valid device for the cio_ignore "
147                            "kernel parameter\n", str);
148
149         return rc;
150 }
151
152 static int blacklist_parse_parameters(char *str, range_action action,
153                                       int msgtrigger)
154 {
155         unsigned int from_cssid, to_cssid, from_ssid, to_ssid, from, to;
156         int rc, totalrc;
157         char *parm;
158         range_action ra;
159
160         totalrc = 0;
161
162         while ((parm = strsep(&str, ","))) {
163                 rc = 0;
164                 ra = action;
165                 if (*parm == '!') {
166                         if (ra == add)
167                                 ra = free;
168                         else
169                                 ra = add;
170                         parm++;
171                 }
172                 if (strcmp(parm, "all") == 0) {
173                         from_cssid = 0;
174                         from_ssid = 0;
175                         from = 0;
176                         to_cssid = __MAX_CSSID;
177                         to_ssid = __MAX_SSID;
178                         to = __MAX_SUBCHANNEL;
179                 } else {
180                         rc = parse_busid(strsep(&parm, "-"), &from_cssid,
181                                          &from_ssid, &from, msgtrigger);
182                         if (!rc) {
183                                 if (parm != NULL)
184                                         rc = parse_busid(parm, &to_cssid,
185                                                          &to_ssid, &to,
186                                                          msgtrigger);
187                                 else {
188                                         to_cssid = from_cssid;
189                                         to_ssid = from_ssid;
190                                         to = from;
191                                 }
192                         }
193                 }
194                 if (!rc) {
195                         rc = blacklist_range(ra, from_ssid, to_ssid, from, to,
196                                              msgtrigger);
197                         if (rc)
198                                 totalrc = -EINVAL;
199                 } else
200                         totalrc = -EINVAL;
201         }
202
203         return totalrc;
204 }
205
206 static int __init
207 blacklist_setup (char *str)
208 {
209         CIO_MSG_EVENT(6, "Reading blacklist parameters\n");
210         if (blacklist_parse_parameters(str, add, 1))
211                 return 0;
212         return 1;
213 }
214
215 __setup ("cio_ignore=", blacklist_setup);
216
217 /* Checking if devices are blacklisted */
218
219 /*
220  * Function: is_blacklisted
221  * Returns 1 if the given devicenumber can be found in the blacklist,
222  * otherwise 0.
223  * Used by validate_subchannel()
224  */
225 int
226 is_blacklisted (int ssid, int devno)
227 {
228         return test_bit (devno, bl_dev[ssid]);
229 }
230
231 #ifdef CONFIG_PROC_FS
232 /*
233  * Function: blacklist_parse_proc_parameters
234  * parse the stuff which is piped to /proc/cio_ignore
235  */
236 static int blacklist_parse_proc_parameters(char *buf)
237 {
238         int rc;
239         char *parm;
240
241         parm = strsep(&buf, " ");
242
243         if (strcmp("free", parm) == 0)
244                 rc = blacklist_parse_parameters(buf, free, 0);
245         else if (strcmp("add", parm) == 0)
246                 rc = blacklist_parse_parameters(buf, add, 0);
247         else if (strcmp("purge", parm) == 0)
248                 return ccw_purge_blacklisted();
249         else
250                 return -EINVAL;
251
252         css_schedule_reprobe();
253
254         return rc;
255 }
256
257 /* Iterator struct for all devices. */
258 struct ccwdev_iter {
259         int devno;
260         int ssid;
261         int in_range;
262 };
263
264 static void *
265 cio_ignore_proc_seq_start(struct seq_file *s, loff_t *offset)
266 {
267         struct ccwdev_iter *iter = s->private;
268
269         if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1))
270                 return NULL;
271         memset(iter, 0, sizeof(*iter));
272         iter->ssid = *offset / (__MAX_SUBCHANNEL + 1);
273         iter->devno = *offset % (__MAX_SUBCHANNEL + 1);
274         return iter;
275 }
276
277 static void
278 cio_ignore_proc_seq_stop(struct seq_file *s, void *it)
279 {
280 }
281
282 static void *
283 cio_ignore_proc_seq_next(struct seq_file *s, void *it, loff_t *offset)
284 {
285         struct ccwdev_iter *iter;
286
287         if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1))
288                 return NULL;
289         iter = it;
290         if (iter->devno == __MAX_SUBCHANNEL) {
291                 iter->devno = 0;
292                 iter->ssid++;
293                 if (iter->ssid > __MAX_SSID)
294                         return NULL;
295         } else
296                 iter->devno++;
297         (*offset)++;
298         return iter;
299 }
300
301 static int
302 cio_ignore_proc_seq_show(struct seq_file *s, void *it)
303 {
304         struct ccwdev_iter *iter;
305
306         iter = it;
307         if (!is_blacklisted(iter->ssid, iter->devno))
308                 /* Not blacklisted, nothing to output. */
309                 return 0;
310         if (!iter->in_range) {
311                 /* First device in range. */
312                 if ((iter->devno == __MAX_SUBCHANNEL) ||
313                     !is_blacklisted(iter->ssid, iter->devno + 1))
314                         /* Singular device. */
315                         return seq_printf(s, "0.%x.%04x\n",
316                                           iter->ssid, iter->devno);
317                 iter->in_range = 1;
318                 return seq_printf(s, "0.%x.%04x-", iter->ssid, iter->devno);
319         }
320         if ((iter->devno == __MAX_SUBCHANNEL) ||
321             !is_blacklisted(iter->ssid, iter->devno + 1)) {
322                 /* Last device in range. */
323                 iter->in_range = 0;
324                 return seq_printf(s, "0.%x.%04x\n", iter->ssid, iter->devno);
325         }
326         return 0;
327 }
328
329 static ssize_t
330 cio_ignore_write(struct file *file, const char __user *user_buf,
331                  size_t user_len, loff_t *offset)
332 {
333         char *buf;
334         ssize_t rc, ret, i;
335
336         if (*offset)
337                 return -EINVAL;
338         if (user_len > 65536)
339                 user_len = 65536;
340         buf = vmalloc (user_len + 1); /* maybe better use the stack? */
341         if (buf == NULL)
342                 return -ENOMEM;
343         memset(buf, 0, user_len + 1);
344
345         if (strncpy_from_user (buf, user_buf, user_len) < 0) {
346                 rc = -EFAULT;
347                 goto out_free;
348         }
349
350         i = user_len - 1;
351         while ((i >= 0) && (isspace(buf[i]) || (buf[i] == 0))) {
352                 buf[i] = '\0';
353                 i--;
354         }
355         ret = blacklist_parse_proc_parameters(buf);
356         if (ret)
357                 rc = ret;
358         else
359                 rc = user_len;
360
361 out_free:
362         vfree (buf);
363         return rc;
364 }
365
366 static const struct seq_operations cio_ignore_proc_seq_ops = {
367         .start = cio_ignore_proc_seq_start,
368         .stop  = cio_ignore_proc_seq_stop,
369         .next  = cio_ignore_proc_seq_next,
370         .show  = cio_ignore_proc_seq_show,
371 };
372
373 static int
374 cio_ignore_proc_open(struct inode *inode, struct file *file)
375 {
376         return seq_open_private(file, &cio_ignore_proc_seq_ops,
377                                 sizeof(struct ccwdev_iter));
378 }
379
380 static const struct file_operations cio_ignore_proc_fops = {
381         .open    = cio_ignore_proc_open,
382         .read    = seq_read,
383         .llseek  = seq_lseek,
384         .release = seq_release_private,
385         .write   = cio_ignore_write,
386 };
387
388 static int
389 cio_ignore_proc_init (void)
390 {
391         struct proc_dir_entry *entry;
392
393         entry = proc_create("cio_ignore", S_IFREG | S_IRUGO | S_IWUSR, NULL,
394                             &cio_ignore_proc_fops);
395         if (!entry)
396                 return -ENOENT;
397         return 0;
398 }
399
400 __initcall (cio_ignore_proc_init);
401
402 #endif /* CONFIG_PROC_FS */