Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * drivers/s390/cio/blacklist.c | |
3 | * S/390 common I/O routines -- blacklisting of specific devices | |
d176795b | 4 | * $Revision: 1.35 $ |
1da177e4 LT |
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 */ | |
a8237fc4 | 38 | #define __BL_DEV_WORDS ((__MAX_SUBCHANNEL + (8*sizeof(long) - 1)) / \ |
1da177e4 LT |
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 | ||
a8237fc4 | 53 | if (from > to || to > __MAX_SUBCHANNEL) { |
1da177e4 LT |
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; | |
a8237fc4 | 146 | to = __MAX_SUBCHANNEL; |
1da177e4 LT |
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 | |
f97a56fb CH |
222 | static int |
223 | __s390_redo_validation(struct subchannel_id schid, void *data) | |
224 | { | |
225 | int ret; | |
226 | struct subchannel *sch; | |
227 | ||
228 | sch = get_subchannel_by_schid(schid); | |
229 | if (sch) { | |
230 | /* Already known. */ | |
231 | put_device(&sch->dev); | |
232 | return 0; | |
233 | } | |
234 | ret = css_probe_device(schid); | |
235 | if (ret == -ENXIO) | |
236 | return ret; /* We're through. */ | |
237 | if (ret == -ENOMEM) | |
238 | /* Stop validation for now. Bad, but no need for a panic. */ | |
239 | return ret; | |
240 | return 0; | |
241 | } | |
242 | ||
1da177e4 LT |
243 | /* |
244 | * Function: s390_redo_validation | |
245 | * Look for no longer blacklisted devices | |
246 | * FIXME: there must be a better way to do this */ | |
247 | static inline void | |
248 | s390_redo_validation (void) | |
249 | { | |
1da177e4 | 250 | CIO_TRACE_EVENT (0, "redoval"); |
1da177e4 | 251 | |
f97a56fb | 252 | for_each_subchannel(__s390_redo_validation, NULL); |
1da177e4 LT |
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 */ | |
a8237fc4 | 293 | devno < __MAX_SUBCHANNEL && len + entry_size < count; devno++) { |
1da177e4 LT |
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 */ | |
a8237fc4 | 298 | while (++devno < __MAX_SUBCHANNEL) |
1da177e4 LT |
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 | ||
a8237fc4 | 306 | if (devno < __MAX_SUBCHANNEL) |
1da177e4 LT |
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 */ |