Merge master.kernel.org:/pub/scm/linux/kernel/git/dtor/input
[linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git] / drivers / s390 / cio / blacklist.c
blobdaf21e03b21d43b5e632713f03e0bb9794624558
1 /*
2 * drivers/s390/cio/blacklist.c
3 * S/390 common I/O routines -- blacklisting of specific devices
4 * $Revision: 1.39 $
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)
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>
22 #include <asm/cio.h>
23 #include <asm/uaccess.h>
25 #include "blacklist.h"
26 #include "cio.h"
27 #include "cio_debug.h"
28 #include "css.h"
31 * "Blacklisting" of certain devices:
32 * Device numbers given in the commandline as cio_ignore=... won't be known
33 * to Linux.
35 * These can be single devices or ranges of devices
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;
45 * Function: blacklist_range
46 * (Un-)blacklist the devices from-to
48 static inline void
49 blacklist_range (range_action action, unsigned int from, unsigned int to,
50 unsigned int ssid)
52 if (!to)
53 to = from;
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;
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]);
70 * Function: blacklist_busid
71 * Get devno/busid from given string.
72 * Shamelessly grabbed from dasd_devmap.c.
74 static inline int
75 blacklist_busid(char **str, int *id0, int *ssid, int *devno)
77 int val, old_style;
78 char *sav;
80 sav = *str;
82 /* check for leading '0x' */
83 old_style = 0;
84 if ((*str)[0] == '0' && (*str)[1] == 'x') {
85 *str += 2;
86 old_style = 1;
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;
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;
128 static inline int
129 blacklist_parse_parameters (char *str, range_action action)
131 unsigned int from, to, from_id0, to_id0, from_ssid, to_ssid;
133 while (*str != 0 && *str != '\n') {
134 range_action ra = action;
135 while(*str == ',')
136 str++;
137 if (*str == '!') {
138 ra = !action;
139 ++str;
143 * Since we have to parse the proc commands and the
144 * kernel arguments we have to check four cases
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;
150 str += 3;
151 for (j=0; j <= __MAX_SSID; j++)
152 blacklist_range(ra, 0, __MAX_SUBCHANNEL, j);
153 } else {
154 int rc;
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;
170 if (*str == '-') {
171 printk(KERN_WARNING "invalid cio_ignore "
172 "parameter '%s'\n",
173 strsep(&str, ",\n"));
174 continue;
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;
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);
190 return 1;
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 * - ...
204 static int __init
205 blacklist_setup (char *str)
207 CIO_MSG_EVENT(6, "Reading blacklist parameters\n");
208 return blacklist_parse_parameters (str, add);
211 __setup ("cio_ignore=", blacklist_setup);
213 /* Checking if devices are blacklisted */
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()
222 is_blacklisted (int ssid, int devno)
224 return test_bit (devno, bl_dev[ssid]);
227 #ifdef CONFIG_PROC_FS
228 static int
229 __s390_redo_validation(struct subchannel_id schid, void *data)
231 int ret;
232 struct subchannel *sch;
234 sch = get_subchannel_by_schid(schid);
235 if (sch) {
236 /* Already known. */
237 put_device(&sch->dev);
238 return 0;
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;
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)
256 CIO_TRACE_EVENT (0, "redoval");
258 for_each_subchannel(__s390_redo_validation, NULL);
262 * Function: blacklist_parse_proc_parameters
263 * parse the stuff which is piped to /proc/cio_ignore
265 static inline void
266 blacklist_parse_proc_parameters (char *buf)
268 if (strncmp (buf, "free ", 5) == 0) {
269 blacklist_parse_parameters (buf + 5, free);
270 } else if (strncmp (buf, "add ", 4) == 0) {
272 * We don't need to check for known devices since
273 * css_probe_device will handle this correctly.
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;
285 s390_redo_validation ();
288 /* Iterator struct for all devices. */
289 struct ccwdev_iter {
290 int devno;
291 int ssid;
292 int in_range;
295 static void *
296 cio_ignore_proc_seq_start(struct seq_file *s, loff_t *offset)
298 struct ccwdev_iter *iter;
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;
310 static void
311 cio_ignore_proc_seq_stop(struct seq_file *s, void *it)
313 if (!IS_ERR(it))
314 kfree(it);
317 static void *
318 cio_ignore_proc_seq_next(struct seq_file *s, void *it, loff_t *offset)
320 struct ccwdev_iter *iter;
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;
336 static int
337 cio_ignore_proc_seq_show(struct seq_file *s, void *it)
339 struct ccwdev_iter *iter;
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);
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);
361 return 0;
364 static ssize_t
365 cio_ignore_write(struct file *file, const char __user *user_buf,
366 size_t user_len, loff_t *offset)
368 char *buf;
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;
381 buf[user_len] = '\0';
383 blacklist_parse_proc_parameters (buf);
385 vfree (buf);
386 return user_len;
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,
396 static int
397 cio_ignore_proc_open(struct inode *inode, struct file *file)
399 return seq_open(file, &cio_ignore_proc_seq_ops);
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,
410 static int
411 cio_ignore_proc_init (void)
413 struct proc_dir_entry *entry;
415 entry = create_proc_entry ("cio_ignore", S_IFREG | S_IRUGO | S_IWUSR,
416 &proc_root);
417 if (!entry)
418 return 0;
420 entry->proc_fops = &cio_ignore_proc_fops;
422 return 1;
425 __initcall (cio_ignore_proc_init);
427 #endif /* CONFIG_PROC_FS */