[PATCH] s390: introduce struct subchannel_id
[safe/jmp/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.35 $
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_SUBCHANNEL + (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_SUBCHANNEL) {
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_SUBCHANNEL;
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         struct subchannel_id schid;
230
231         CIO_TRACE_EVENT (0, "redoval");
232         init_subchannel_id(&schid);
233         do {
234                 int ret;
235                 struct subchannel *sch;
236
237                 sch = get_subchannel_by_schid(schid);
238                 if (sch) {
239                         /* Already known. */
240                         put_device(&sch->dev);
241                         continue;
242                 }
243                 ret = css_probe_device(schid);
244                 if (ret == -ENXIO)
245                         break; /* We're through. */
246                 if (ret == -ENOMEM)
247                         /*
248                          * Stop validation for now. Bad, but no need for a
249                          * panic.
250                          */
251                         break;
252         } while (schid.sch_no++ < __MAX_SUBCHANNEL);
253 }
254
255 /*
256  * Function: blacklist_parse_proc_parameters
257  * parse the stuff which is piped to /proc/cio_ignore
258  */
259 static inline void
260 blacklist_parse_proc_parameters (char *buf)
261 {
262         if (strncmp (buf, "free ", 5) == 0) {
263                 blacklist_parse_parameters (buf + 5, free);
264         } else if (strncmp (buf, "add ", 4) == 0) {
265                 /* 
266                  * We don't need to check for known devices since
267                  * css_probe_device will handle this correctly. 
268                  */
269                 blacklist_parse_parameters (buf + 4, add);
270         } else {
271                 printk (KERN_WARNING "cio_ignore: Parse error; \n"
272                         KERN_WARNING "try using 'free all|<devno-range>,"
273                                      "<devno-range>,...'\n"
274                         KERN_WARNING "or 'add <devno-range>,"
275                                      "<devno-range>,...'\n");
276                 return;
277         }
278
279         s390_redo_validation ();
280 }
281
282 /* FIXME: These should be real bus ids and not home-grown ones! */
283 static int cio_ignore_read (char *page, char **start, off_t off,
284                             int count, int *eof, void *data)
285 {
286         const unsigned int entry_size = 18; /* "0.0.ABCD-0.0.EFGH\n" */
287         long devno;
288         int len;
289
290         len = 0;
291         for (devno = off; /* abuse the page variable
292                            * as counter, see fs/proc/generic.c */
293              devno < __MAX_SUBCHANNEL && len + entry_size < count; devno++) {
294                 if (!test_bit(devno, bl_dev))
295                         continue;
296                 len += sprintf(page + len, "0.0.%04lx", devno);
297                 if (test_bit(devno + 1, bl_dev)) { /* print range */
298                         while (++devno < __MAX_SUBCHANNEL)
299                                 if (!test_bit(devno, bl_dev))
300                                         break;
301                         len += sprintf(page + len, "-0.0.%04lx", --devno);
302                 }
303                 len += sprintf(page + len, "\n");
304         }
305
306         if (devno < __MAX_SUBCHANNEL)
307                 *eof = 1;
308         *start = (char *) (devno - off); /* number of checked entries */
309         return len;
310 }
311
312 static int cio_ignore_write(struct file *file, const char __user *user_buf,
313                              unsigned long user_len, void *data)
314 {
315         char *buf;
316
317         if (user_len > 65536)
318                 user_len = 65536;
319         buf = vmalloc (user_len + 1); /* maybe better use the stack? */
320         if (buf == NULL)
321                 return -ENOMEM;
322         if (strncpy_from_user (buf, user_buf, user_len) < 0) {
323                 vfree (buf);
324                 return -EFAULT;
325         }
326         buf[user_len] = '\0';
327
328         blacklist_parse_proc_parameters (buf);
329
330         vfree (buf);
331         return user_len;
332 }
333
334 static int
335 cio_ignore_proc_init (void)
336 {
337         struct proc_dir_entry *entry;
338
339         entry = create_proc_entry ("cio_ignore", S_IFREG | S_IRUGO | S_IWUSR,
340                                    &proc_root);
341         if (!entry)
342                 return 0;
343
344         entry->read_proc  = cio_ignore_read;
345         entry->write_proc = cio_ignore_write;
346
347         return 1;
348 }
349
350 __initcall (cio_ignore_proc_init);
351
352 #endif /* CONFIG_PROC_FS */