diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/s390/cio/blacklist.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/s390/cio/blacklist.c')
-rw-r--r-- | drivers/s390/cio/blacklist.c | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c new file mode 100644 index 000000000000..4a06c7d0e5e4 --- /dev/null +++ b/drivers/s390/cio/blacklist.c | |||
@@ -0,0 +1,351 @@ | |||
1 | /* | ||
2 | * drivers/s390/cio/blacklist.c | ||
3 | * S/390 common I/O routines -- blacklisting of specific devices | ||
4 | * $Revision: 1.33 $ | ||
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 */ | ||
38 | #define __BL_DEV_WORDS (__MAX_SUBCHANNELS + (8*sizeof(long) - 1) / \ | ||
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 | |||
53 | if (from > to || to > __MAX_SUBCHANNELS) { | ||
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; | ||
146 | to = __MAX_SUBCHANNELS; | ||
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 | ||
222 | /* | ||
223 | * Function: s390_redo_validation | ||
224 | * Look for no longer blacklisted devices | ||
225 | * FIXME: there must be a better way to do this */ | ||
226 | static inline void | ||
227 | s390_redo_validation (void) | ||
228 | { | ||
229 | unsigned int irq; | ||
230 | |||
231 | CIO_TRACE_EVENT (0, "redoval"); | ||
232 | for (irq = 0; irq < __MAX_SUBCHANNELS; irq++) { | ||
233 | int ret; | ||
234 | struct subchannel *sch; | ||
235 | |||
236 | sch = get_subchannel_by_schid(irq); | ||
237 | if (sch) { | ||
238 | /* Already known. */ | ||
239 | put_device(&sch->dev); | ||
240 | continue; | ||
241 | } | ||
242 | ret = css_probe_device(irq); | ||
243 | if (ret == -ENXIO) | ||
244 | break; /* We're through. */ | ||
245 | if (ret == -ENOMEM) | ||
246 | /* | ||
247 | * Stop validation for now. Bad, but no need for a | ||
248 | * panic. | ||
249 | */ | ||
250 | break; | ||
251 | } | ||
252 | } | ||
253 | |||
254 | /* | ||
255 | * Function: blacklist_parse_proc_parameters | ||
256 | * parse the stuff which is piped to /proc/cio_ignore | ||
257 | */ | ||
258 | static inline void | ||
259 | blacklist_parse_proc_parameters (char *buf) | ||
260 | { | ||
261 | if (strncmp (buf, "free ", 5) == 0) { | ||
262 | blacklist_parse_parameters (buf + 5, free); | ||
263 | } else if (strncmp (buf, "add ", 4) == 0) { | ||
264 | /* | ||
265 | * We don't need to check for known devices since | ||
266 | * css_probe_device will handle this correctly. | ||
267 | */ | ||
268 | blacklist_parse_parameters (buf + 4, add); | ||
269 | } else { | ||
270 | printk (KERN_WARNING "cio_ignore: Parse error; \n" | ||
271 | KERN_WARNING "try using 'free all|<devno-range>," | ||
272 | "<devno-range>,...'\n" | ||
273 | KERN_WARNING "or 'add <devno-range>," | ||
274 | "<devno-range>,...'\n"); | ||
275 | return; | ||
276 | } | ||
277 | |||
278 | s390_redo_validation (); | ||
279 | } | ||
280 | |||
281 | /* FIXME: These should be real bus ids and not home-grown ones! */ | ||
282 | static int cio_ignore_read (char *page, char **start, off_t off, | ||
283 | int count, int *eof, void *data) | ||
284 | { | ||
285 | const unsigned int entry_size = 18; /* "0.0.ABCD-0.0.EFGH\n" */ | ||
286 | long devno; | ||
287 | int len; | ||
288 | |||
289 | len = 0; | ||
290 | for (devno = off; /* abuse the page variable | ||
291 | * as counter, see fs/proc/generic.c */ | ||
292 | devno <= __MAX_SUBCHANNELS && len + entry_size < count; devno++) { | ||
293 | if (!test_bit(devno, bl_dev)) | ||
294 | continue; | ||
295 | len += sprintf(page + len, "0.0.%04lx", devno); | ||
296 | if (test_bit(devno + 1, bl_dev)) { /* print range */ | ||
297 | while (++devno < __MAX_SUBCHANNELS) | ||
298 | if (!test_bit(devno, bl_dev)) | ||
299 | break; | ||
300 | len += sprintf(page + len, "-0.0.%04lx", --devno); | ||
301 | } | ||
302 | len += sprintf(page + len, "\n"); | ||
303 | } | ||
304 | |||
305 | if (devno <= __MAX_SUBCHANNELS) | ||
306 | *eof = 1; | ||
307 | *start = (char *) (devno - off); /* number of checked entries */ | ||
308 | return len; | ||
309 | } | ||
310 | |||
311 | static int cio_ignore_write(struct file *file, const char __user *user_buf, | ||
312 | unsigned long user_len, void *data) | ||
313 | { | ||
314 | char *buf; | ||
315 | |||
316 | if (user_len > 65536) | ||
317 | user_len = 65536; | ||
318 | buf = vmalloc (user_len + 1); /* maybe better use the stack? */ | ||
319 | if (buf == NULL) | ||
320 | return -ENOMEM; | ||
321 | if (strncpy_from_user (buf, user_buf, user_len) < 0) { | ||
322 | vfree (buf); | ||
323 | return -EFAULT; | ||
324 | } | ||
325 | buf[user_len] = '\0'; | ||
326 | |||
327 | blacklist_parse_proc_parameters (buf); | ||
328 | |||
329 | vfree (buf); | ||
330 | return user_len; | ||
331 | } | ||
332 | |||
333 | static int | ||
334 | cio_ignore_proc_init (void) | ||
335 | { | ||
336 | struct proc_dir_entry *entry; | ||
337 | |||
338 | entry = create_proc_entry ("cio_ignore", S_IFREG | S_IRUGO | S_IWUSR, | ||
339 | &proc_root); | ||
340 | if (!entry) | ||
341 | return 0; | ||
342 | |||
343 | entry->read_proc = cio_ignore_read; | ||
344 | entry->write_proc = cio_ignore_write; | ||
345 | |||
346 | return 1; | ||
347 | } | ||
348 | |||
349 | __initcall (cio_ignore_proc_init); | ||
350 | |||
351 | #endif /* CONFIG_PROC_FS */ | ||