diff options
-rw-r--r-- | drivers/s390/char/monreader.c | 140 |
1 files changed, 128 insertions, 12 deletions
diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c index 97e63cf46944..75a8831eebbc 100644 --- a/drivers/s390/char/monreader.c +++ b/drivers/s390/char/monreader.c | |||
@@ -1,10 +1,9 @@ | |||
1 | /* | 1 | /* |
2 | * drivers/s390/char/monreader.c | ||
3 | * | ||
4 | * Character device driver for reading z/VM *MONITOR service records. | 2 | * Character device driver for reading z/VM *MONITOR service records. |
5 | * | 3 | * |
6 | * Copyright IBM Corp. 2004, 2008 | 4 | * Copyright IBM Corp. 2004, 2009 |
7 | * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com> | 5 | * |
6 | * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com> | ||
8 | */ | 7 | */ |
9 | 8 | ||
10 | #define KMSG_COMPONENT "monreader" | 9 | #define KMSG_COMPONENT "monreader" |
@@ -22,6 +21,7 @@ | |||
22 | #include <linux/spinlock.h> | 21 | #include <linux/spinlock.h> |
23 | #include <linux/interrupt.h> | 22 | #include <linux/interrupt.h> |
24 | #include <linux/poll.h> | 23 | #include <linux/poll.h> |
24 | #include <linux/device.h> | ||
25 | #include <net/iucv/iucv.h> | 25 | #include <net/iucv/iucv.h> |
26 | #include <asm/uaccess.h> | 26 | #include <asm/uaccess.h> |
27 | #include <asm/ebcdic.h> | 27 | #include <asm/ebcdic.h> |
@@ -78,6 +78,7 @@ static u8 user_data_sever[16] = { | |||
78 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | 78 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
79 | }; | 79 | }; |
80 | 80 | ||
81 | static struct device *monreader_device; | ||
81 | 82 | ||
82 | /****************************************************************************** | 83 | /****************************************************************************** |
83 | * helper functions * | 84 | * helper functions * |
@@ -319,11 +320,12 @@ static int mon_open(struct inode *inode, struct file *filp) | |||
319 | goto out_path; | 320 | goto out_path; |
320 | } | 321 | } |
321 | filp->private_data = monpriv; | 322 | filp->private_data = monpriv; |
323 | monreader_device->driver_data = monpriv; | ||
322 | unlock_kernel(); | 324 | unlock_kernel(); |
323 | return nonseekable_open(inode, filp); | 325 | return nonseekable_open(inode, filp); |
324 | 326 | ||
325 | out_path: | 327 | out_path: |
326 | kfree(monpriv->path); | 328 | iucv_path_free(monpriv->path); |
327 | out_priv: | 329 | out_priv: |
328 | mon_free_mem(monpriv); | 330 | mon_free_mem(monpriv); |
329 | out_use: | 331 | out_use: |
@@ -341,10 +343,13 @@ static int mon_close(struct inode *inode, struct file *filp) | |||
341 | /* | 343 | /* |
342 | * Close IUCV connection and unregister | 344 | * Close IUCV connection and unregister |
343 | */ | 345 | */ |
344 | rc = iucv_path_sever(monpriv->path, user_data_sever); | 346 | if (monpriv->path) { |
345 | if (rc) | 347 | rc = iucv_path_sever(monpriv->path, user_data_sever); |
346 | pr_warning("Disconnecting the z/VM *MONITOR system service " | 348 | if (rc) |
347 | "failed with rc=%i\n", rc); | 349 | pr_warning("Disconnecting the z/VM *MONITOR system " |
350 | "service failed with rc=%i\n", rc); | ||
351 | iucv_path_free(monpriv->path); | ||
352 | } | ||
348 | 353 | ||
349 | atomic_set(&monpriv->iucv_severed, 0); | 354 | atomic_set(&monpriv->iucv_severed, 0); |
350 | atomic_set(&monpriv->iucv_connected, 0); | 355 | atomic_set(&monpriv->iucv_connected, 0); |
@@ -452,6 +457,94 @@ static struct miscdevice mon_dev = { | |||
452 | .minor = MISC_DYNAMIC_MINOR, | 457 | .minor = MISC_DYNAMIC_MINOR, |
453 | }; | 458 | }; |
454 | 459 | ||
460 | |||
461 | /****************************************************************************** | ||
462 | * suspend / resume * | ||
463 | *****************************************************************************/ | ||
464 | static int monreader_freeze(struct device *dev) | ||
465 | { | ||
466 | struct mon_private *monpriv = dev->driver_data; | ||
467 | int rc; | ||
468 | |||
469 | if (!monpriv) | ||
470 | return 0; | ||
471 | if (monpriv->path) { | ||
472 | rc = iucv_path_sever(monpriv->path, user_data_sever); | ||
473 | if (rc) | ||
474 | pr_warning("Disconnecting the z/VM *MONITOR system " | ||
475 | "service failed with rc=%i\n", rc); | ||
476 | iucv_path_free(monpriv->path); | ||
477 | } | ||
478 | atomic_set(&monpriv->iucv_severed, 0); | ||
479 | atomic_set(&monpriv->iucv_connected, 0); | ||
480 | atomic_set(&monpriv->read_ready, 0); | ||
481 | atomic_set(&monpriv->msglim_count, 0); | ||
482 | monpriv->write_index = 0; | ||
483 | monpriv->read_index = 0; | ||
484 | monpriv->path = NULL; | ||
485 | return 0; | ||
486 | } | ||
487 | |||
488 | static int monreader_thaw(struct device *dev) | ||
489 | { | ||
490 | struct mon_private *monpriv = dev->driver_data; | ||
491 | int rc; | ||
492 | |||
493 | if (!monpriv) | ||
494 | return 0; | ||
495 | rc = -ENOMEM; | ||
496 | monpriv->path = iucv_path_alloc(MON_MSGLIM, IUCV_IPRMDATA, GFP_KERNEL); | ||
497 | if (!monpriv->path) | ||
498 | goto out; | ||
499 | rc = iucv_path_connect(monpriv->path, &monreader_iucv_handler, | ||
500 | MON_SERVICE, NULL, user_data_connect, monpriv); | ||
501 | if (rc) { | ||
502 | pr_err("Connecting to the z/VM *MONITOR system service " | ||
503 | "failed with rc=%i\n", rc); | ||
504 | goto out_path; | ||
505 | } | ||
506 | wait_event(mon_conn_wait_queue, | ||
507 | atomic_read(&monpriv->iucv_connected) || | ||
508 | atomic_read(&monpriv->iucv_severed)); | ||
509 | if (atomic_read(&monpriv->iucv_severed)) | ||
510 | goto out_path; | ||
511 | return 0; | ||
512 | out_path: | ||
513 | rc = -EIO; | ||
514 | iucv_path_free(monpriv->path); | ||
515 | monpriv->path = NULL; | ||
516 | out: | ||
517 | atomic_set(&monpriv->iucv_severed, 1); | ||
518 | return rc; | ||
519 | } | ||
520 | |||
521 | static int monreader_restore(struct device *dev) | ||
522 | { | ||
523 | int rc; | ||
524 | |||
525 | segment_unload(mon_dcss_name); | ||
526 | rc = segment_load(mon_dcss_name, SEGMENT_SHARED, | ||
527 | &mon_dcss_start, &mon_dcss_end); | ||
528 | if (rc < 0) { | ||
529 | segment_warning(rc, mon_dcss_name); | ||
530 | panic("fatal monreader resume error: no monitor dcss\n"); | ||
531 | } | ||
532 | return monreader_thaw(dev); | ||
533 | } | ||
534 | |||
535 | static struct dev_pm_ops monreader_pm_ops = { | ||
536 | .freeze = monreader_freeze, | ||
537 | .thaw = monreader_thaw, | ||
538 | .restore = monreader_restore, | ||
539 | }; | ||
540 | |||
541 | static struct device_driver monreader_driver = { | ||
542 | .name = "monreader", | ||
543 | .bus = &iucv_bus, | ||
544 | .pm = &monreader_pm_ops, | ||
545 | }; | ||
546 | |||
547 | |||
455 | /****************************************************************************** | 548 | /****************************************************************************** |
456 | * module init/exit * | 549 | * module init/exit * |
457 | *****************************************************************************/ | 550 | *****************************************************************************/ |
@@ -475,16 +568,33 @@ static int __init mon_init(void) | |||
475 | return rc; | 568 | return rc; |
476 | } | 569 | } |
477 | 570 | ||
571 | rc = driver_register(&monreader_driver); | ||
572 | if (rc) | ||
573 | goto out_iucv; | ||
574 | monreader_device = kzalloc(sizeof(struct device), GFP_KERNEL); | ||
575 | if (!monreader_device) | ||
576 | goto out_driver; | ||
577 | dev_set_name(monreader_device, "monreader-dev"); | ||
578 | monreader_device->bus = &iucv_bus; | ||
579 | monreader_device->parent = iucv_root; | ||
580 | monreader_device->driver = &monreader_driver; | ||
581 | monreader_device->release = (void (*)(struct device *))kfree; | ||
582 | rc = device_register(monreader_device); | ||
583 | if (rc) { | ||
584 | kfree(monreader_device); | ||
585 | goto out_driver; | ||
586 | } | ||
587 | |||
478 | rc = segment_type(mon_dcss_name); | 588 | rc = segment_type(mon_dcss_name); |
479 | if (rc < 0) { | 589 | if (rc < 0) { |
480 | segment_warning(rc, mon_dcss_name); | 590 | segment_warning(rc, mon_dcss_name); |
481 | goto out_iucv; | 591 | goto out_device; |
482 | } | 592 | } |
483 | if (rc != SEG_TYPE_SC) { | 593 | if (rc != SEG_TYPE_SC) { |
484 | pr_err("The specified *MONITOR DCSS %s does not have the " | 594 | pr_err("The specified *MONITOR DCSS %s does not have the " |
485 | "required type SC\n", mon_dcss_name); | 595 | "required type SC\n", mon_dcss_name); |
486 | rc = -EINVAL; | 596 | rc = -EINVAL; |
487 | goto out_iucv; | 597 | goto out_device; |
488 | } | 598 | } |
489 | 599 | ||
490 | rc = segment_load(mon_dcss_name, SEGMENT_SHARED, | 600 | rc = segment_load(mon_dcss_name, SEGMENT_SHARED, |
@@ -492,7 +602,7 @@ static int __init mon_init(void) | |||
492 | if (rc < 0) { | 602 | if (rc < 0) { |
493 | segment_warning(rc, mon_dcss_name); | 603 | segment_warning(rc, mon_dcss_name); |
494 | rc = -EINVAL; | 604 | rc = -EINVAL; |
495 | goto out_iucv; | 605 | goto out_device; |
496 | } | 606 | } |
497 | dcss_mkname(mon_dcss_name, &user_data_connect[8]); | 607 | dcss_mkname(mon_dcss_name, &user_data_connect[8]); |
498 | 608 | ||
@@ -503,6 +613,10 @@ static int __init mon_init(void) | |||
503 | 613 | ||
504 | out: | 614 | out: |
505 | segment_unload(mon_dcss_name); | 615 | segment_unload(mon_dcss_name); |
616 | out_device: | ||
617 | device_unregister(monreader_device); | ||
618 | out_driver: | ||
619 | driver_unregister(&monreader_driver); | ||
506 | out_iucv: | 620 | out_iucv: |
507 | iucv_unregister(&monreader_iucv_handler, 1); | 621 | iucv_unregister(&monreader_iucv_handler, 1); |
508 | return rc; | 622 | return rc; |
@@ -512,6 +626,8 @@ static void __exit mon_exit(void) | |||
512 | { | 626 | { |
513 | segment_unload(mon_dcss_name); | 627 | segment_unload(mon_dcss_name); |
514 | WARN_ON(misc_deregister(&mon_dev) != 0); | 628 | WARN_ON(misc_deregister(&mon_dev) != 0); |
629 | device_unregister(monreader_device); | ||
630 | driver_unregister(&monreader_driver); | ||
515 | iucv_unregister(&monreader_iucv_handler, 1); | 631 | iucv_unregister(&monreader_iucv_handler, 1); |
516 | return; | 632 | return; |
517 | } | 633 | } |