xref: /openbmc/linux/drivers/s390/cio/blacklist.c (revision 87c2ce3b)
1 /*
2  *  drivers/s390/cio/blacklist.c
3  *   S/390 common I/O routines -- blacklisting of specific devices
4  *   $Revision: 1.39 $
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/seq_file.h>
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 
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;
43 
44 /*
45  * Function: blacklist_range
46  * (Un-)blacklist the devices from-to
47  */
48 static inline void
49 blacklist_range (range_action action, unsigned int from, unsigned int to,
50 		 unsigned int ssid)
51 {
52 	if (!to)
53 		to = from;
54 
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;
60 	}
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]);
66 	}
67 }
68 
69 /*
70  * Function: blacklist_busid
71  * Get devno/busid from given string.
72  * Shamelessly grabbed from dasd_devmap.c.
73  */
74 static inline int
75 blacklist_busid(char **str, int *id0, int *ssid, int *devno)
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] != '.') {
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;
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;
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;
126 }
127 
128 static inline int
129 blacklist_parse_parameters (char *str, range_action action)
130 {
131 	unsigned int from, to, from_id0, to_id0, from_ssid, to_ssid;
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) {
148 			int j;
149 
150 			str += 3;
151 			for (j=0; j <= __MAX_SSID; j++)
152 				blacklist_range(ra, 0, __MAX_SUBCHANNEL, j);
153 		} else {
154 			int rc;
155 
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;
169 			}
170 			if (*str == '-') {
171 				printk(KERN_WARNING "invalid cio_ignore "
172 					"parameter '%s'\n",
173 					strsep(&str, ",\n"));
174 				continue;
175 			}
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;
183 			}
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);
188 		}
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  */
204 static int __init
205 blacklist_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  */
221 int
222 is_blacklisted (int ssid, int devno)
223 {
224 	return test_bit (devno, bl_dev[ssid]);
225 }
226 
227 #ifdef CONFIG_PROC_FS
228 static 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 
249 /*
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)
255 {
256 	CIO_TRACE_EVENT (0, "redoval");
257 
258 	for_each_subchannel(__s390_redo_validation, NULL);
259 }
260 
261 /*
262  * Function: blacklist_parse_proc_parameters
263  * parse the stuff which is piped to /proc/cio_ignore
264  */
265 static inline void
266 blacklist_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 
288 /* Iterator struct for all devices. */
289 struct ccwdev_iter {
290 	int devno;
291 	int ssid;
292 	int in_range;
293 };
294 
295 static void *
296 cio_ignore_proc_seq_start(struct seq_file *s, loff_t *offset)
297 {
298 	struct ccwdev_iter *iter;
299 
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;
308 }
309 
310 static void
311 cio_ignore_proc_seq_stop(struct seq_file *s, void *it)
312 {
313 	if (!IS_ERR(it))
314 		kfree(it);
315 }
316 
317 static void *
318 cio_ignore_proc_seq_next(struct seq_file *s, void *it, loff_t *offset)
319 {
320 	struct ccwdev_iter *iter;
321 
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;
334 }
335 
336 static int
337 cio_ignore_proc_seq_show(struct seq_file *s, void *it)
338 {
339 	struct ccwdev_iter *iter;
340 
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);
354 	}
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);
360 	}
361 	return 0;
362 }
363 
364 static ssize_t
365 cio_ignore_write(struct file *file, const char __user *user_buf,
366 		 size_t user_len, loff_t *offset)
367 {
368 	char *buf;
369 
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;
380 	}
381 	buf[user_len] = '\0';
382 
383 	blacklist_parse_proc_parameters (buf);
384 
385 	vfree (buf);
386 	return user_len;
387 }
388 
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,
394 };
395 
396 static int
397 cio_ignore_proc_open(struct inode *inode, struct file *file)
398 {
399 	return seq_open(file, &cio_ignore_proc_seq_ops);
400 }
401 
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,
408 };
409 
410 static int
411 cio_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 
420 	entry->proc_fops = &cio_ignore_proc_fops;
421 
422 	return 1;
423 }
424 
425 __initcall (cio_ignore_proc_init);
426 
427 #endif /* CONFIG_PROC_FS */
428