[PATCH] s390: email-address change
[linux-2.6-block.git] / drivers / s390 / cio / blacklist.c
CommitLineData
1da177e4
LT
1/*
2 * drivers/s390/cio/blacklist.c
3 * S/390 common I/O routines -- blacklisting of specific devices
4ce3b30c 4 * $Revision: 1.42 $
1da177e4
LT
5 *
6 * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
7 * IBM Corporation
8 * Author(s): Ingo Adlung (adlung@de.ibm.com)
4ce3b30c 9 * Cornelia Huck (cornelia.huck@de.ibm.com)
1da177e4
LT
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>
678a395b 18#include <linux/seq_file.h>
1da177e4
LT
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
fb6958a5 38/* 65536 bits for each set to indicate if a devno is blacklisted or not */
a8237fc4 39#define __BL_DEV_WORDS ((__MAX_SUBCHANNEL + (8*sizeof(long) - 1)) / \
1da177e4 40 (8*sizeof(long)))
fb6958a5 41static unsigned long bl_dev[__MAX_SSID + 1][__BL_DEV_WORDS];
1da177e4
LT
42typedef enum {add, free} range_action;
43
44/*
45 * Function: blacklist_range
46 * (Un-)blacklist the devices from-to
47 */
48static inline void
fb6958a5
CH
49blacklist_range (range_action action, unsigned int from, unsigned int to,
50 unsigned int ssid)
1da177e4
LT
51{
52 if (!to)
53 to = from;
54
fb6958a5 55 if (from > to || to > __MAX_SUBCHANNEL || ssid > __MAX_SSID) {
1da177e4 56 printk (KERN_WARNING "Invalid blacklist range "
fb6958a5
CH
57 "0.%x.%04x to 0.%x.%04x, skipping\n",
58 ssid, from, ssid, to);
1da177e4
LT
59 return;
60 }
61 for (; from <= to; from++) {
62 if (action == add)
fb6958a5 63 set_bit (from, bl_dev[ssid]);
1da177e4 64 else
fb6958a5 65 clear_bit (from, bl_dev[ssid]);
1da177e4
LT
66 }
67}
68
69/*
70 * Function: blacklist_busid
71 * Get devno/busid from given string.
72 * Shamelessly grabbed from dasd_devmap.c.
73 */
74static inline int
fb6958a5 75blacklist_busid(char **str, int *id0, int *ssid, int *devno)
1da177e4
LT
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] != '.') {
fb6958a5 92 *id0 = *ssid = 0;
1da177e4
LT
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;
fb6958a5 111 *ssid = val;
1da177e4
LT
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;
122confused:
123 strsep(str, ",\n");
124 printk(KERN_WARNING "Invalid cio_ignore parameter '%s'\n", sav);
125 return 1;
126}
127
128static inline int
129blacklist_parse_parameters (char *str, range_action action)
130{
fb6958a5 131 unsigned int from, to, from_id0, to_id0, from_ssid, to_ssid;
1da177e4
LT
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) {
fb6958a5
CH
148 int j;
149
1da177e4 150 str += 3;
fb6958a5
CH
151 for (j=0; j <= __MAX_SSID; j++)
152 blacklist_range(ra, 0, __MAX_SUBCHANNEL, j);
1da177e4
LT
153 } else {
154 int rc;
155
156 rc = blacklist_busid(&str, &from_id0,
fb6958a5 157 &from_ssid, &from);
1da177e4
LT
158 if (rc)
159 continue;
160 to = from;
161 to_id0 = from_id0;
fb6958a5 162 to_ssid = from_ssid;
1da177e4
LT
163 if (*str == '-') {
164 str++;
165 rc = blacklist_busid(&str, &to_id0,
fb6958a5 166 &to_ssid, &to);
1da177e4
LT
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 }
fb6958a5
CH
176 if ((from_id0 != to_id0) ||
177 (from_ssid != to_ssid)) {
1da177e4
LT
178 printk(KERN_WARNING "invalid cio_ignore range "
179 "%x.%x.%04x-%x.%x.%04x\n",
fb6958a5
CH
180 from_id0, from_ssid, from,
181 to_id0, to_ssid, to);
1da177e4
LT
182 continue;
183 }
fb6958a5
CH
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);
1da177e4 188 }
1da177e4
LT
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 */
204static int __init
205blacklist_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 */
221int
fb6958a5 222is_blacklisted (int ssid, int devno)
1da177e4 223{
fb6958a5 224 return test_bit (devno, bl_dev[ssid]);
1da177e4
LT
225}
226
227#ifdef CONFIG_PROC_FS
f97a56fb
CH
228static 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
1da177e4
LT
249/*
250 * Function: s390_redo_validation
251 * Look for no longer blacklisted devices
252 * FIXME: there must be a better way to do this */
253static inline void
254s390_redo_validation (void)
255{
1da177e4 256 CIO_TRACE_EVENT (0, "redoval");
1da177e4 257
f97a56fb 258 for_each_subchannel(__s390_redo_validation, NULL);
1da177e4
LT
259}
260
261/*
262 * Function: blacklist_parse_proc_parameters
263 * parse the stuff which is piped to /proc/cio_ignore
264 */
265static inline void
266blacklist_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
678a395b
CH
288/* Iterator struct for all devices. */
289struct ccwdev_iter {
290 int devno;
fb6958a5 291 int ssid;
678a395b
CH
292 int in_range;
293};
294
295static void *
296cio_ignore_proc_seq_start(struct seq_file *s, loff_t *offset)
1da177e4 297{
678a395b
CH
298 struct ccwdev_iter *iter;
299
fb6958a5 300 if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1))
678a395b 301 return NULL;
3b793060 302 iter = kzalloc(sizeof(struct ccwdev_iter), GFP_KERNEL);
678a395b
CH
303 if (!iter)
304 return ERR_PTR(-ENOMEM);
fb6958a5
CH
305 iter->ssid = *offset / (__MAX_SUBCHANNEL + 1);
306 iter->devno = *offset % (__MAX_SUBCHANNEL + 1);
678a395b
CH
307 return iter;
308}
309
310static void
311cio_ignore_proc_seq_stop(struct seq_file *s, void *it)
312{
313 if (!IS_ERR(it))
314 kfree(it);
315}
1da177e4 316
678a395b
CH
317static void *
318cio_ignore_proc_seq_next(struct seq_file *s, void *it, loff_t *offset)
319{
320 struct ccwdev_iter *iter;
321
fb6958a5 322 if (*offset >= (__MAX_SUBCHANNEL + 1) * (__MAX_SSID + 1))
678a395b 323 return NULL;
3b793060 324 iter = it;
fb6958a5
CH
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++;
678a395b
CH
332 (*offset)++;
333 return iter;
1da177e4
LT
334}
335
678a395b
CH
336static int
337cio_ignore_proc_seq_show(struct seq_file *s, void *it)
338{
339 struct ccwdev_iter *iter;
340
3b793060 341 iter = it;
fb6958a5 342 if (!is_blacklisted(iter->ssid, iter->devno))
678a395b
CH
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) ||
fb6958a5 348 !is_blacklisted(iter->ssid, iter->devno + 1))
678a395b 349 /* Singular device. */
fb6958a5
CH
350 return seq_printf(s, "0.%x.%04x\n",
351 iter->ssid, iter->devno);
678a395b 352 iter->in_range = 1;
fb6958a5 353 return seq_printf(s, "0.%x.%04x-", iter->ssid, iter->devno);
678a395b
CH
354 }
355 if ((iter->devno == __MAX_SUBCHANNEL) ||
fb6958a5 356 !is_blacklisted(iter->ssid, iter->devno + 1)) {
678a395b
CH
357 /* Last device in range. */
358 iter->in_range = 0;
fb6958a5 359 return seq_printf(s, "0.%x.%04x\n", iter->ssid, iter->devno);
678a395b
CH
360 }
361 return 0;
362}
363
364static ssize_t
365cio_ignore_write(struct file *file, const char __user *user_buf,
366 size_t user_len, loff_t *offset)
1da177e4
LT
367{
368 char *buf;
369
678a395b
CH
370 if (*offset)
371 return -EINVAL;
1da177e4
LT
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
678a395b
CH
389static 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
396static int
397cio_ignore_proc_open(struct inode *inode, struct file *file)
398{
399 return seq_open(file, &cio_ignore_proc_seq_ops);
400}
401
402static 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
1da177e4
LT
410static int
411cio_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
678a395b 420 entry->proc_fops = &cio_ignore_proc_fops;
1da177e4
LT
421
422 return 1;
423}
424
425__initcall (cio_ignore_proc_init);
426
427#endif /* CONFIG_PROC_FS */