2 * drivers/s390/cio/blacklist.c
3 * S/390 common I/O routines -- blacklisting of specific devices
6 * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
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>
23 #include <asm/uaccess.h>
25 #include "blacklist.h"
27 #include "cio_debug.h"
31 * "Blacklisting" of certain devices:
32 * Device numbers given in the commandline as cio_ignore=... won't be known
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)) / \
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
49 blacklist_range (range_action action, unsigned int from, unsigned int to,
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);
61 for (; from <= to; from++) {
63 set_bit (from, bl_dev[ssid]);
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.
75 blacklist_busid(char **str, int *id0, int *ssid, int *devno)
82 /* check for leading '0x' */
84 if ((*str)[0] == '0' && (*str)[1] == 'x') {
88 if (!isxdigit((*str)[0])) /* We require at least one hex digit */
90 val = simple_strtoul(*str, str, 16);
91 if (old_style || (*str)[0] != '.') {
93 if (val < 0 || val > 0xffff)
96 if ((*str)[0] != ',' && (*str)[0] != '-' &&
97 (*str)[0] != '\n' && (*str)[0] != '\0')
101 /* New style x.y.z busid */
102 if (val < 0 || val > 0xff)
106 if (!isxdigit((*str)[0])) /* We require at least one hex digit */
108 val = simple_strtoul(*str, str, 16);
109 if (val < 0 || val > 0xff || (*str)++[0] != '.')
112 if (!isxdigit((*str)[0])) /* We require at least one hex digit */
114 val = simple_strtoul(*str, str, 16);
115 if (val < 0 || val > 0xffff)
118 if ((*str)[0] != ',' && (*str)[0] != '-' &&
119 (*str)[0] != '\n' && (*str)[0] != '\0')
124 printk(KERN_WARNING "Invalid cio_ignore parameter '%s'\n", sav);
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;
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) {
151 for (j=0; j <= __MAX_SSID; j++)
152 blacklist_range(ra, 0, __MAX_SUBCHANNEL, j);
156 rc = blacklist_busid(&str, &from_id0,
165 rc = blacklist_busid(&str, &to_id0,
171 printk(KERN_WARNING "invalid cio_ignore "
173 strsep(&str, ",\n"));
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);
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);
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
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,
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
229 __s390_redo_validation(struct subchannel_id schid, void *data)
232 struct subchannel *sch;
234 sch = get_subchannel_by_schid(schid);
237 put_device(&sch->dev);
240 ret = css_probe_device(schid);
242 return ret; /* We're through. */
244 /* Stop validation for now. Bad, but no need for a panic. */
250 * Function: s390_redo_validation
251 * Look for no longer blacklisted devices
252 * FIXME: there must be a better way to do this */
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
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);
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");
285 s390_redo_validation ();
288 /* Iterator struct for all devices. */
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))
302 iter = kzalloc(sizeof(struct ccwdev_iter), GFP_KERNEL);
304 return ERR_PTR(-ENOMEM);
305 iter->ssid = *offset / (__MAX_SUBCHANNEL + 1);
306 iter->devno = *offset % (__MAX_SUBCHANNEL + 1);
311 cio_ignore_proc_seq_stop(struct seq_file *s, void *it)
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))
325 if (iter->devno == __MAX_SUBCHANNEL) {
328 if (iter->ssid > __MAX_SSID)
337 cio_ignore_proc_seq_show(struct seq_file *s, void *it)
339 struct ccwdev_iter *iter;
342 if (!is_blacklisted(iter->ssid, iter->devno))
343 /* Not blacklisted, nothing to output. */
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);
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. */
359 return seq_printf(s, "0.%x.%04x\n", iter->ssid, iter->devno);
365 cio_ignore_write(struct file *file, const char __user *user_buf,
366 size_t user_len, loff_t *offset)
372 if (user_len > 65536)
374 buf = vmalloc (user_len + 1); /* maybe better use the stack? */
377 if (strncpy_from_user (buf, user_buf, user_len) < 0) {
381 buf[user_len] = '\0';
383 blacklist_parse_proc_parameters (buf);
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,
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,
406 .release = seq_release,
407 .write = cio_ignore_write,
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,
420 entry->proc_fops = &cio_ignore_proc_fops;
425 __initcall (cio_ignore_proc_init);
427 #endif /* CONFIG_PROC_FS */