Merge git://git.kernel.org/pub/scm/linux/kernel/git/sam/kbuild
[pandora-kernel.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  *   $Revision: 1.42 $
5  *
6  *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
7  *                            IBM Corporation
8  *    Author(s): Ingo Adlung (adlung@de.ibm.com)
9  *               Cornelia Huck (cornelia.huck@de.ibm.com)
10  *               Arnd Bergmann (arndb@de.ibm.com)
11  */
12
13 #include <linux/config.h>
14 #include <linux/init.h>
15 #include <linux/vmalloc.h>
16 #include <linux/slab.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
30 /*
31  * "Blacklisting" of certain devices:
32  * Device numbers given in the commandline as cio_ignore=... won't be known
33  * to Linux.
34  *
35  * These can be single devices or ranges of devices
36  */
37
38 /* 65536 bits for each set to indicate if a devno is blacklisted or not */
39 #define __BL_DEV_WORDS ((__MAX_SUBCHANNEL + (8*sizeof(long) - 1)) / \
40                          (8*sizeof(long)))
41 static unsigned long bl_dev[__MAX_SSID + 1][__BL_DEV_WORDS];
42 typedef enum {add, free} range_action;
43
44 /*
45  * Function: blacklist_range
46  * (Un-)blacklist the devices from-to
47  */
48 static inline void
49 blacklist_range (range_action action, unsigned int from, unsigned int to,
50                  unsigned int ssid)
51 {
52         if (!to)
53                 to = from;
54
55         if (from > to || to > __MAX_SUBCHANNEL || ssid > __MAX_SSID) {
56                 printk (KERN_WARNING "Invalid blacklist range "
57                         "0.%x.%04x to 0.%x.%04x, skipping\n",
58                         ssid, from, ssid, to);
59                 return;
60         }
61         for (; from <= to; from++) {
62                 if (action == add)
63                         set_bit (from, bl_dev[ssid]);
64                 else
65                         clear_bit (from, bl_dev[ssid]);
66         }
67 }
68
69 /*
70  * Function: blacklist_busid
71  * Get devno/busid from given string.
72  * Shamelessly grabbed from dasd_devmap.c.
73  */
74 static inline int
75 blacklist_busid(char **str, int *id0, int *ssid, int *devno)
76 {
77         int val, old_style;
78         char *sav;
79
80         sav = *str;
81
82         /* check for leading '0x' */
83         old_style = 0;
84         if ((*str)[0] == '0' && (*str)[1] == 'x') {
85                 *str += 2;
86                 old_style = 1;
87         }
88         if (!isxdigit((*str)[0]))       /* We require at least one hex digit */
89                 goto confused;
90         val = simple_strtoul(*str, str, 16);
91         if (old_style || (*str)[0] != '.') {
92                 *id0 = *ssid = 0;
93                 if (val < 0 || val > 0xffff)
94                         goto confused;
95                 *devno = val;
96                 if ((*str)[0] != ',' && (*str)[0] != '-' &&
97                     (*str)[0] != '\n' && (*str)[0] != '\0')
98                         goto confused;
99                 return 0;
100         }
101         /* New style x.y.z busid */
102         if (val < 0 || val > 0xff)
103                 goto confused;
104         *id0 = val;
105         (*str)++;
106         if (!isxdigit((*str)[0]))       /* We require at least one hex digit */
107                 goto confused;
108         val = simple_strtoul(*str, str, 16);
109         if (val < 0 || val > 0xff || (*str)++[0] != '.')
110                 goto confused;
111         *ssid = val;
112         if (!isxdigit((*str)[0]))       /* We require at least one hex digit */
113                 goto confused;
114         val = simple_strtoul(*str, str, 16);
115         if (val < 0 || val > 0xffff)
116                 goto confused;
117         *devno = val;
118         if ((*str)[0] != ',' && (*str)[0] != '-' &&
119             (*str)[0] != '\n' && (*str)[0] != '\0')
120                 goto confused;
121         return 0;
122 confused:
123         strsep(str, ",\n");
124         printk(KERN_WARNING "Invalid cio_ignore parameter '%s'\n", sav);
125         return 1;
126 }
127
128 static inline int
129 blacklist_parse_parameters (char *str, range_action action)
130 {
131         unsigned int from, to, from_id0, to_id0, from_ssid, to_ssid;
132
133         while (*str != 0 && *str != '\n') {
134                 range_action ra = action;
135                 while(*str == ',')
136                         str++;
137                 if (*str == '!') {
138                         ra = !action;
139                         ++str;
140                 }
141
142                 /*
143                  * Since we have to parse the proc commands and the
144                  * kernel arguments we have to check four cases
145                  */
146                 if (strncmp(str,"all,",4) == 0 || strcmp(str,"all") == 0 ||
147                     strncmp(str,"all\n",4) == 0 || strncmp(str,"all ",4) == 0) {
148                         int j;
149
150                         str += 3;
151                         for (j=0; j <= __MAX_SSID; j++)
152                                 blacklist_range(ra, 0, __MAX_SUBCHANNEL, j);
153                 } else {
154                         int rc;
155
156                         rc = blacklist_busid(&str, &from_id0,
157                                              &from_ssid, &from);
158                         if (rc)
159                                 continue;
160                         to = from;
161                         to_id0 = from_id0;
162                         to_ssid = from_ssid;
163                         if (*str == '-') {
164                                 str++;
165                                 rc = blacklist_busid(&str, &to_id0,
166                                                      &to_ssid, &to);
167                                 if (rc)
168                                         continue;
169                         }
170                         if (*str == '-') {
171                                 printk(KERN_WARNING "invalid cio_ignore "
172                                         "parameter '%s'\n",
173                                         strsep(&str, ",\n"));
174                                 continue;
175                         }
176                         if ((from_id0 != to_id0) ||
177                             (from_ssid != to_ssid)) {
178                                 printk(KERN_WARNING "invalid cio_ignore range "
179                                         "%x.%x.%04x-%x.%x.%04x\n",
180                                         from_id0, from_ssid, from,
181                                         to_id0, to_ssid, to);
182                                 continue;
183                         }
184                         pr_debug("blacklist_setup: adding range "
185                                  "from %x.%x.%04x to %x.%x.%04x\n",
186                                  from_id0, from_ssid, from, to_id0, to_ssid, to);
187                         blacklist_range (ra, from, to, to_ssid);
188                 }
189         }
190         return 1;
191 }
192
193 /* Parsing the commandline for blacklist parameters, e.g. to blacklist
194  * bus ids 0.0.1234, 0.0.1235 and 0.0.1236, you could use any of:
195  * - cio_ignore=1234-1236
196  * - cio_ignore=0x1234-0x1235,1236
197  * - cio_ignore=0x1234,1235-1236
198  * - cio_ignore=1236 cio_ignore=1234-0x1236
199  * - cio_ignore=1234 cio_ignore=1236 cio_ignore=0x1235
200  * - cio_ignore=0.0.1234-0.0.1236
201  * - cio_ignore=0.0.1234,0x1235,1236
202  * - ...
203  */
204 static int __init
205 blacklist_setup (char *str)
206 {
207         CIO_MSG_EVENT(6, "Reading blacklist parameters\n");
208         return blacklist_parse_parameters (str, add);
209 }
210
211 __setup ("cio_ignore=", blacklist_setup);
212
213 /* Checking if devices are blacklisted */
214
215 /*
216  * Function: is_blacklisted
217  * Returns 1 if the given devicenumber can be found in the blacklist,
218  * otherwise 0.
219  * Used by validate_subchannel()
220  */
221 int
222 is_blacklisted (int ssid, int devno)
223 {
224         return test_bit (devno, bl_dev[ssid]);
225 }
226
227 #ifdef CONFIG_PROC_FS
228 static int
229 __s390_redo_validation(struct subchannel_id schid, void *data)
230 {
231         int ret;
232         struct subchannel *sch;
233
234         sch = get_subchannel_by_schid(schid);
235         if (sch) {
236                 /* Already known. */
237                 put_device(&sch->dev);
238                 return 0;
239         }
240         ret = css_probe_device(schid);
241         if (ret == -ENXIO)
242                 return ret; /* We're through. */
243         if (ret == -ENOMEM)
244                 /* Stop validation for now. Bad, but no need for a panic. */
245                 return ret;
246         return 0;
247 }
248
249 /*
250  * Function: s390_redo_validation
251  * Look for no longer blacklisted devices
252  * FIXME: there must be a better way to do this */
253 static inline void
254 s390_redo_validation (void)
255 {
256         CIO_TRACE_EVENT (0, "redoval");
257
258         for_each_subchannel(__s390_redo_validation, NULL);
259 }
260
261 /*
262  * Function: blacklist_parse_proc_parameters
263  * parse the stuff which is piped to /proc/cio_ignore
264  */
265 static inline void
266 blacklist_parse_proc_parameters (char *buf)
267 {
268         if (strncmp (buf, "free ", 5) == 0) {
269                 blacklist_parse_parameters (buf + 5, free);
270         } else if (strncmp (buf, "add ", 4) == 0) {
271                 /* 
272                  * We don't need to check for known devices since
273                  * css_probe_device will handle this correctly. 
274                  */
275                 blacklist_parse_parameters (buf + 4, add);
276         } else {
277                 printk (KERN_WARNING "cio_ignore: Parse error; \n"
278                         KERN_WARNING "try using 'free all|<devno-range>,"
279                                      "<devno-range>,...'\n"
280                         KERN_WARNING "or 'add <devno-range>,"
281                                      "<devno-range>,...'\n");
282                 return;
283         }
284
285         s390_redo_validation ();
286 }
287
288 /* Iterator struct for all devices. */
289 struct ccwdev_iter {
290         int devno;
291         int ssid;
292         int in_range;
293 };
294
295 static void *
296 cio_ignore_proc_seq_start(struct seq_file *s, loff_t *offset)
297 {
298         struct ccwdev_iter *iter;
299
300         if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1))
301                 return NULL;
302         iter = kzalloc(sizeof(struct ccwdev_iter), GFP_KERNEL);
303         if (!iter)
304                 return ERR_PTR(-ENOMEM);
305         iter->ssid = *offset / (__MAX_SUBCHANNEL + 1);
306         iter->devno = *offset % (__MAX_SUBCHANNEL + 1);
307         return iter;
308 }
309
310 static void
311 cio_ignore_proc_seq_stop(struct seq_file *s, void *it)
312 {
313         if (!IS_ERR(it))
314                 kfree(it);
315 }
316
317 static void *
318 cio_ignore_proc_seq_next(struct seq_file *s, void *it, loff_t *offset)
319 {
320         struct ccwdev_iter *iter;
321
322         if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1))
323                 return NULL;
324         iter = it;
325         if (iter->devno == __MAX_SUBCHANNEL) {
326                 iter->devno = 0;
327                 iter->ssid++;
328                 if (iter->ssid > __MAX_SSID)
329                         return NULL;
330         } else
331                 iter->devno++;
332         (*offset)++;
333         return iter;
334 }
335
336 static int
337 cio_ignore_proc_seq_show(struct seq_file *s, void *it)
338 {
339         struct ccwdev_iter *iter;
340
341         iter = it;
342         if (!is_blacklisted(iter->ssid, iter->devno))
343                 /* Not blacklisted, nothing to output. */
344                 return 0;
345         if (!iter->in_range) {
346                 /* First device in range. */
347                 if ((iter->devno == __MAX_SUBCHANNEL) ||
348                     !is_blacklisted(iter->ssid, iter->devno + 1))
349                         /* Singular device. */
350                         return seq_printf(s, "0.%x.%04x\n",
351                                           iter->ssid, iter->devno);
352                 iter->in_range = 1;
353                 return seq_printf(s, "0.%x.%04x-", iter->ssid, iter->devno);
354         }
355         if ((iter->devno == __MAX_SUBCHANNEL) ||
356             !is_blacklisted(iter->ssid, iter->devno + 1)) {
357                 /* Last device in range. */
358                 iter->in_range = 0;
359                 return seq_printf(s, "0.%x.%04x\n", iter->ssid, iter->devno);
360         }
361         return 0;
362 }
363
364 static ssize_t
365 cio_ignore_write(struct file *file, const char __user *user_buf,
366                  size_t user_len, loff_t *offset)
367 {
368         char *buf;
369
370         if (*offset)
371                 return -EINVAL;
372         if (user_len > 65536)
373                 user_len = 65536;
374         buf = vmalloc (user_len + 1); /* maybe better use the stack? */
375         if (buf == NULL)
376                 return -ENOMEM;
377         if (strncpy_from_user (buf, user_buf, user_len) < 0) {
378                 vfree (buf);
379                 return -EFAULT;
380         }
381         buf[user_len] = '\0';
382
383         blacklist_parse_proc_parameters (buf);
384
385         vfree (buf);
386         return user_len;
387 }
388
389 static struct seq_operations cio_ignore_proc_seq_ops = {
390         .start = cio_ignore_proc_seq_start,
391         .stop  = cio_ignore_proc_seq_stop,
392         .next  = cio_ignore_proc_seq_next,
393         .show  = cio_ignore_proc_seq_show,
394 };
395
396 static int
397 cio_ignore_proc_open(struct inode *inode, struct file *file)
398 {
399         return seq_open(file, &cio_ignore_proc_seq_ops);
400 }
401
402 static struct file_operations cio_ignore_proc_fops = {
403         .open    = cio_ignore_proc_open,
404         .read    = seq_read,
405         .llseek  = seq_lseek,
406         .release = seq_release,
407         .write   = cio_ignore_write,
408 };
409
410 static int
411 cio_ignore_proc_init (void)
412 {
413         struct proc_dir_entry *entry;
414
415         entry = create_proc_entry ("cio_ignore", S_IFREG | S_IRUGO | S_IWUSR,
416                                    &proc_root);
417         if (!entry)
418                 return 0;
419
420         entry->proc_fops = &cio_ignore_proc_fops;
421
422         return 1;
423 }
424
425 __initcall (cio_ignore_proc_init);
426
427 #endif /* CONFIG_PROC_FS */