diff options
| author | Gerald Schaefer <gerald.schaefer@de.ibm.com> | 2009-06-16 04:30:50 -0400 |
|---|---|---|
| committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2009-06-16 04:31:21 -0400 |
| commit | 2b1e3e5558b9de0f85ed9183a7adb2d61aab363b (patch) | |
| tree | 5a03549da9bdb356617bbc1ad8361569c6ff394c | |
| parent | fb78140cedcd2805e5cfec12af59c0a6dfa112db (diff) | |
[S390] pm: monreader power management callbacks.
Signed-off-by: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
| -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 | } |
