/home/lenb/src/to-akpm branch 'acpi-2.6.12'
[linux-2.6] / 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.34 $
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 (cohuck@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/ctype.h>
19 #include <linux/device.h>
20
21 #include <asm/cio.h>
22 #include <asm/uaccess.h>
23
24 #include "blacklist.h"
25 #include "cio.h"
26 #include "cio_debug.h"
27 #include "css.h"
28
29 /*
30  * "Blacklisting" of certain devices:
31  * Device numbers given in the commandline as cio_ignore=... won't be known
32  * to Linux.
33  *
34  * These can be single devices or ranges of devices
35  */
36
37 /* 65536 bits to indicate if a devno is blacklisted or not */
38 #define __BL_DEV_WORDS (__MAX_SUBCHANNELS + (8*sizeof(long) - 1) / \
39                          (8*sizeof(long)))
40 static unsigned long bl_dev[__BL_DEV_WORDS];
41 typedef enum {add, free} range_action;
42
43 /*
44  * Function: blacklist_range
45  * (Un-)blacklist the devices from-to
46  */
47 static inline void
48 blacklist_range (range_action action, unsigned int from, unsigned int to)
49 {
50         if (!to)
51                 to = from;
52
53         if (from > to || to > __MAX_SUBCHANNELS) {
54                 printk (KERN_WARNING "Invalid blacklist range "
55                         "0x%04x to 0x%04x, skipping\n", from, to);
56                 return;
57         }
58         for (; from <= to; from++) {
59                 if (action == add)
60                         set_bit (from, bl_dev);
61                 else
62                         clear_bit (from, bl_dev);
63         }
64 }
65
66 /*
67  * Function: blacklist_busid
68  * Get devno/busid from given string.
69  * Shamelessly grabbed from dasd_devmap.c.
70  */
71 static inline int
72 blacklist_busid(char **str, int *id0, int *id1, int *devno)
73 {
74         int val, old_style;
75         char *sav;
76
77         sav = *str;
78
79         /* check for leading '0x' */
80         old_style = 0;
81         if ((*str)[0] == '0' && (*str)[1] == 'x') {
82                 *str += 2;
83                 old_style = 1;
84         }
85         if (!isxdigit((*str)[0]))       /* We require at least one hex digit */
86                 goto confused;
87         val = simple_strtoul(*str, str, 16);
88         if (old_style || (*str)[0] != '.') {
89                 *id0 = *id1 = 0;
90                 if (val < 0 || val > 0xffff)
91                         goto confused;
92                 *devno = val;
93                 if ((*str)[0] != ',' && (*str)[0] != '-' &&
94                     (*str)[0] != '\n' && (*str)[0] != '\0')
95                         goto confused;
96                 return 0;
97         }
98         /* New style x.y.z busid */
99         if (val < 0 || val > 0xff)
100                 goto confused;
101         *id0 = val;
102         (*str)++;
103         if (!isxdigit((*str)[0]))       /* We require at least one hex digit */
104                 goto confused;
105         val = simple_strtoul(*str, str, 16);
106         if (val < 0 || val > 0xff || (*str)++[0] != '.')
107                 goto confused;
108         *id1 = val;
109         if (!isxdigit((*str)[0]))       /* We require at least one hex digit */
110                 goto confused;
111         val = simple_strtoul(*str, str, 16);
112         if (val < 0 || val > 0xffff)
113                 goto confused;
114         *devno = val;
115         if ((*str)[0] != ',' && (*str)[0] != '-' &&
116             (*str)[0] != '\n' && (*str)[0] != '\0')
117                 goto confused;
118         return 0;
119 confused:
120         strsep(str, ",\n");
121         printk(KERN_WARNING "Invalid cio_ignore parameter '%s'\n", sav);
122         return 1;
123 }
124
125 static inline int
126 blacklist_parse_parameters (char *str, range_action action)
127 {
128         unsigned int from, to, from_id0, to_id0, from_id1, to_id1;
129
130         while (*str != 0 && *str != '\n') {
131                 range_action ra = action;
132                 while(*str == ',')
133                         str++;
134                 if (*str == '!') {
135                         ra = !action;
136                         ++str;
137                 }
138
139                 /*
140                  * Since we have to parse the proc commands and the
141                  * kernel arguments we have to check four cases
142                  */
143                 if (strncmp(str,"all,",4) == 0 || strcmp(str,"all") == 0 ||
144                     strncmp(str,"all\n",4) == 0 || strncmp(str,"all ",4) == 0) {
145                         from = 0;
146                         to = __MAX_SUBCHANNELS;
147                         str += 3;
148                 } else {
149                         int rc;
150
151                         rc = blacklist_busid(&str, &from_id0,
152                                              &from_id1, &from);
153                         if (rc)
154                                 continue;
155                         to = from;
156                         to_id0 = from_id0;
157                         to_id1 = from_id1;
158                         if (*str == '-') {
159                                 str++;
160                                 rc = blacklist_busid(&str, &to_id0,
161                                                      &to_id1, &to);
162                                 if (rc)
163                                         continue;
164                         }
165                         if (*str == '-') {
166                                 printk(KERN_WARNING "invalid cio_ignore "
167                                         "parameter '%s'\n",
168                                         strsep(&str, ",\n"));
169                                 continue;
170                         }
171                         if ((from_id0 != to_id0) || (from_id1 != to_id1)) {
172                                 printk(KERN_WARNING "invalid cio_ignore range "
173                                         "%x.%x.%04x-%x.%x.%04x\n",
174                                         from_id0, from_id1, from,
175                                         to_id0, to_id1, to);
176                                 continue;
177                         }
178                 }
179                 /* FIXME: ignoring id0 and id1 here. */
180                 pr_debug("blacklist_setup: adding range "
181                          "from 0.0.%04x to 0.0.%04x\n", from, to);
182                 blacklist_range (ra, from, to);
183         }
184         return 1;
185 }
186
187 /* Parsing the commandline for blacklist parameters, e.g. to blacklist
188  * bus ids 0.0.1234, 0.0.1235 and 0.0.1236, you could use any of:
189  * - cio_ignore=1234-1236
190  * - cio_ignore=0x1234-0x1235,1236
191  * - cio_ignore=0x1234,1235-1236
192  * - cio_ignore=1236 cio_ignore=1234-0x1236
193  * - cio_ignore=1234 cio_ignore=1236 cio_ignore=0x1235
194  * - cio_ignore=0.0.1234-0.0.1236
195  * - cio_ignore=0.0.1234,0x1235,1236
196  * - ...
197  */
198 static int __init
199 blacklist_setup (char *str)
200 {
201         CIO_MSG_EVENT(6, "Reading blacklist parameters\n");
202         return blacklist_parse_parameters (str, add);
203 }
204
205 __setup ("cio_ignore=", blacklist_setup);
206
207 /* Checking if devices are blacklisted */
208
209 /*
210  * Function: is_blacklisted
211  * Returns 1 if the given devicenumber can be found in the blacklist,
212  * otherwise 0.
213  * Used by validate_subchannel()
214  */
215 int
216 is_blacklisted (int devno)
217 {
218         return test_bit (devno, bl_dev);
219 }
220
221 #ifdef CONFIG_PROC_FS
222 /*
223  * Function: s390_redo_validation
224  * Look for no longer blacklisted devices
225  * FIXME: there must be a better way to do this */
226 static inline void
227 s390_redo_validation (void)
228 {
229         unsigned int irq;
230
231         CIO_TRACE_EVENT (0, "redoval");
232         for (irq = 0; irq < __MAX_SUBCHANNELS; irq++) {
233                 int ret;
234                 struct subchannel *sch;
235
236                 sch = get_subchannel_by_schid(irq);
237                 if (sch) {
238                         /* Already known. */
239                         put_device(&sch->dev);
240                         continue;
241                 }
242                 ret = css_probe_device(irq);
243                 if (ret == -ENXIO)
244                         break; /* We're through. */
245                 if (ret == -ENOMEM)
246                         /*
247                          * Stop validation for now. Bad, but no need for a
248                          * panic.
249                          */
250                         break;
251         }
252 }
253
254 /*
255  * Function: blacklist_parse_proc_parameters
256  * parse the stuff which is piped to /proc/cio_ignore
257  */
258 static inline void
259 blacklist_parse_proc_parameters (char *buf)
260 {
261         if (strncmp (buf, "free ", 5) == 0) {
262                 blacklist_parse_parameters (buf + 5, free);
263         } else if (strncmp (buf, "add ", 4) == 0) {
264                 /* 
265                  * We don't need to check for known devices since
266                  * css_probe_device will handle this correctly. 
267                  */
268                 blacklist_parse_parameters (buf + 4, add);
269         } else {
270                 printk (KERN_WARNING "cio_ignore: Parse error; \n"
271                         KERN_WARNING "try using 'free all|<devno-range>,"
272                                      "<devno-range>,...'\n"
273                         KERN_WARNING "or 'add <devno-range>,"
274                                      "<devno-range>,...'\n");
275                 return;
276         }
277
278         s390_redo_validation ();
279 }
280
281 /* FIXME: These should be real bus ids and not home-grown ones! */
282 static int cio_ignore_read (char *page, char **start, off_t off,
283                             int count, int *eof, void *data)
284 {
285         const unsigned int entry_size = 18; /* "0.0.ABCD-0.0.EFGH\n" */
286         long devno;
287         int len;
288
289         len = 0;
290         for (devno = off; /* abuse the page variable
291                            * as counter, see fs/proc/generic.c */
292              devno < __MAX_SUBCHANNELS && len + entry_size < count; devno++) {
293                 if (!test_bit(devno, bl_dev))
294                         continue;
295                 len += sprintf(page + len, "0.0.%04lx", devno);
296                 if (test_bit(devno + 1, bl_dev)) { /* print range */
297                         while (++devno < __MAX_SUBCHANNELS)
298                                 if (!test_bit(devno, bl_dev))
299                                         break;
300                         len += sprintf(page + len, "-0.0.%04lx", --devno);
301                 }
302                 len += sprintf(page + len, "\n");
303         }
304
305         if (devno < __MAX_SUBCHANNELS)
306                 *eof = 1;
307         *start = (char *) (devno - off); /* number of checked entries */
308         return len;
309 }
310
311 static int cio_ignore_write(struct file *file, const char __user *user_buf,
312                              unsigned long user_len, void *data)
313 {
314         char *buf;
315
316         if (user_len > 65536)
317                 user_len = 65536;
318         buf = vmalloc (user_len + 1); /* maybe better use the stack? */
319         if (buf == NULL)
320                 return -ENOMEM;
321         if (strncpy_from_user (buf, user_buf, user_len) < 0) {
322                 vfree (buf);
323                 return -EFAULT;
324         }
325         buf[user_len] = '\0';
326
327         blacklist_parse_proc_parameters (buf);
328
329         vfree (buf);
330         return user_len;
331 }
332
333 static int
334 cio_ignore_proc_init (void)
335 {
336         struct proc_dir_entry *entry;
337
338         entry = create_proc_entry ("cio_ignore", S_IFREG | S_IRUGO | S_IWUSR,
339                                    &proc_root);
340         if (!entry)
341                 return 0;
342
343         entry->read_proc  = cio_ignore_read;
344         entry->write_proc = cio_ignore_write;
345
346         return 1;
347 }
348
349 __initcall (cio_ignore_proc_init);
350
351 #endif /* CONFIG_PROC_FS */