diff options
Diffstat (limited to 'drivers/s390/cio')
-rw-r--r-- | drivers/s390/cio/blacklist.c | 122 |
1 files changed, 92 insertions, 30 deletions
diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c index 25e98483d4e4..daea41c63329 100644 --- a/drivers/s390/cio/blacklist.c +++ b/drivers/s390/cio/blacklist.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/vmalloc.h> | 15 | #include <linux/vmalloc.h> |
16 | #include <linux/slab.h> | 16 | #include <linux/slab.h> |
17 | #include <linux/proc_fs.h> | 17 | #include <linux/proc_fs.h> |
18 | #include <linux/seq_file.h> | ||
18 | #include <linux/ctype.h> | 19 | #include <linux/ctype.h> |
19 | #include <linux/device.h> | 20 | #include <linux/device.h> |
20 | 21 | ||
@@ -279,41 +280,82 @@ blacklist_parse_proc_parameters (char *buf) | |||
279 | s390_redo_validation (); | 280 | s390_redo_validation (); |
280 | } | 281 | } |
281 | 282 | ||
282 | /* FIXME: These should be real bus ids and not home-grown ones! */ | 283 | /* Iterator struct for all devices. */ |
283 | static int cio_ignore_read (char *page, char **start, off_t off, | 284 | struct ccwdev_iter { |
284 | int count, int *eof, void *data) | 285 | int devno; |
286 | int in_range; | ||
287 | }; | ||
288 | |||
289 | static void * | ||
290 | cio_ignore_proc_seq_start(struct seq_file *s, loff_t *offset) | ||
285 | { | 291 | { |
286 | const unsigned int entry_size = 18; /* "0.0.ABCD-0.0.EFGH\n" */ | 292 | struct ccwdev_iter *iter; |
287 | long devno; | 293 | |
288 | int len; | 294 | if (*offset > __MAX_SUBCHANNEL) |
289 | 295 | return NULL; | |
290 | len = 0; | 296 | iter = kmalloc(sizeof(struct ccwdev_iter), GFP_KERNEL); |
291 | for (devno = off; /* abuse the page variable | 297 | if (!iter) |
292 | * as counter, see fs/proc/generic.c */ | 298 | return ERR_PTR(-ENOMEM); |
293 | devno < __MAX_SUBCHANNEL && len + entry_size < count; devno++) { | 299 | memset(iter, 0, sizeof(struct ccwdev_iter)); |
294 | if (!test_bit(devno, bl_dev)) | 300 | iter->devno = *offset; |
295 | continue; | 301 | return iter; |
296 | len += sprintf(page + len, "0.0.%04lx", devno); | 302 | } |
297 | if (test_bit(devno + 1, bl_dev)) { /* print range */ | 303 | |
298 | while (++devno < __MAX_SUBCHANNEL) | 304 | static void |
299 | if (!test_bit(devno, bl_dev)) | 305 | cio_ignore_proc_seq_stop(struct seq_file *s, void *it) |
300 | break; | 306 | { |
301 | len += sprintf(page + len, "-0.0.%04lx", --devno); | 307 | if (!IS_ERR(it)) |
302 | } | 308 | kfree(it); |
303 | len += sprintf(page + len, "\n"); | 309 | } |
304 | } | ||
305 | 310 | ||
306 | if (devno < __MAX_SUBCHANNEL) | 311 | static void * |
307 | *eof = 1; | 312 | cio_ignore_proc_seq_next(struct seq_file *s, void *it, loff_t *offset) |
308 | *start = (char *) (devno - off); /* number of checked entries */ | 313 | { |
309 | return len; | 314 | struct ccwdev_iter *iter; |
315 | |||
316 | if (*offset > __MAX_SUBCHANNEL) | ||
317 | return NULL; | ||
318 | iter = (struct ccwdev_iter *)it; | ||
319 | iter->devno++; | ||
320 | (*offset)++; | ||
321 | return iter; | ||
310 | } | 322 | } |
311 | 323 | ||
312 | static int cio_ignore_write(struct file *file, const char __user *user_buf, | 324 | static int |
313 | unsigned long user_len, void *data) | 325 | cio_ignore_proc_seq_show(struct seq_file *s, void *it) |
326 | { | ||
327 | struct ccwdev_iter *iter; | ||
328 | |||
329 | iter = (struct ccwdev_iter *)it; | ||
330 | if (!is_blacklisted(iter->devno)) | ||
331 | /* Not blacklisted, nothing to output. */ | ||
332 | return 0; | ||
333 | if (!iter->in_range) { | ||
334 | /* First device in range. */ | ||
335 | if ((iter->devno == __MAX_SUBCHANNEL) || | ||
336 | !is_blacklisted(iter->devno + 1)) | ||
337 | /* Singular device. */ | ||
338 | return seq_printf(s, "0.0.%04x\n", iter->devno); | ||
339 | iter->in_range = 1; | ||
340 | return seq_printf(s, "0.0.%04x-", iter->devno); | ||
341 | } | ||
342 | if ((iter->devno == __MAX_SUBCHANNEL) || | ||
343 | !is_blacklisted(iter->devno + 1)) { | ||
344 | /* Last device in range. */ | ||
345 | iter->in_range = 0; | ||
346 | return seq_printf(s, "0.0.%04x\n", iter->devno); | ||
347 | } | ||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | static ssize_t | ||
352 | cio_ignore_write(struct file *file, const char __user *user_buf, | ||
353 | size_t user_len, loff_t *offset) | ||
314 | { | 354 | { |
315 | char *buf; | 355 | char *buf; |
316 | 356 | ||
357 | if (*offset) | ||
358 | return -EINVAL; | ||
317 | if (user_len > 65536) | 359 | if (user_len > 65536) |
318 | user_len = 65536; | 360 | user_len = 65536; |
319 | buf = vmalloc (user_len + 1); /* maybe better use the stack? */ | 361 | buf = vmalloc (user_len + 1); /* maybe better use the stack? */ |
@@ -331,6 +373,27 @@ static int cio_ignore_write(struct file *file, const char __user *user_buf, | |||
331 | return user_len; | 373 | return user_len; |
332 | } | 374 | } |
333 | 375 | ||
376 | static struct seq_operations cio_ignore_proc_seq_ops = { | ||
377 | .start = cio_ignore_proc_seq_start, | ||
378 | .stop = cio_ignore_proc_seq_stop, | ||
379 | .next = cio_ignore_proc_seq_next, | ||
380 | .show = cio_ignore_proc_seq_show, | ||
381 | }; | ||
382 | |||
383 | static int | ||
384 | cio_ignore_proc_open(struct inode *inode, struct file *file) | ||
385 | { | ||
386 | return seq_open(file, &cio_ignore_proc_seq_ops); | ||
387 | } | ||
388 | |||
389 | static struct file_operations cio_ignore_proc_fops = { | ||
390 | .open = cio_ignore_proc_open, | ||
391 | .read = seq_read, | ||
392 | .llseek = seq_lseek, | ||
393 | .release = seq_release, | ||
394 | .write = cio_ignore_write, | ||
395 | }; | ||
396 | |||
334 | static int | 397 | static int |
335 | cio_ignore_proc_init (void) | 398 | cio_ignore_proc_init (void) |
336 | { | 399 | { |
@@ -341,8 +404,7 @@ cio_ignore_proc_init (void) | |||
341 | if (!entry) | 404 | if (!entry) |
342 | return 0; | 405 | return 0; |
343 | 406 | ||
344 | entry->read_proc = cio_ignore_read; | 407 | entry->proc_fops = &cio_ignore_proc_fops; |
345 | entry->write_proc = cio_ignore_write; | ||
346 | 408 | ||
347 | return 1; | 409 | return 1; |
348 | } | 410 | } |