Commit 2b1e3e55 authored by Gerald Schaefer's avatar Gerald Schaefer Committed by Martin Schwidefsky

[S390] pm: monreader power management callbacks.

Signed-off-by: default avatarGerald Schaefer <gerald.schaefer@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent fb78140c
/* /*
* drivers/s390/char/monreader.c
*
* Character device driver for reading z/VM *MONITOR service records. * Character device driver for reading z/VM *MONITOR service records.
* *
* Copyright IBM Corp. 2004, 2008 * Copyright IBM Corp. 2004, 2009
*
* Author: Gerald Schaefer <gerald.schaefer@de.ibm.com> * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
*/ */
...@@ -22,6 +21,7 @@ ...@@ -22,6 +21,7 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/device.h>
#include <net/iucv/iucv.h> #include <net/iucv/iucv.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/ebcdic.h> #include <asm/ebcdic.h>
...@@ -78,6 +78,7 @@ static u8 user_data_sever[16] = { ...@@ -78,6 +78,7 @@ static u8 user_data_sever[16] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
}; };
static struct device *monreader_device;
/****************************************************************************** /******************************************************************************
* helper functions * * helper functions *
...@@ -319,11 +320,12 @@ static int mon_open(struct inode *inode, struct file *filp) ...@@ -319,11 +320,12 @@ static int mon_open(struct inode *inode, struct file *filp)
goto out_path; goto out_path;
} }
filp->private_data = monpriv; filp->private_data = monpriv;
monreader_device->driver_data = monpriv;
unlock_kernel(); unlock_kernel();
return nonseekable_open(inode, filp); return nonseekable_open(inode, filp);
out_path: out_path:
kfree(monpriv->path); iucv_path_free(monpriv->path);
out_priv: out_priv:
mon_free_mem(monpriv); mon_free_mem(monpriv);
out_use: out_use:
...@@ -341,10 +343,13 @@ static int mon_close(struct inode *inode, struct file *filp) ...@@ -341,10 +343,13 @@ static int mon_close(struct inode *inode, struct file *filp)
/* /*
* Close IUCV connection and unregister * Close IUCV connection and unregister
*/ */
if (monpriv->path) {
rc = iucv_path_sever(monpriv->path, user_data_sever); rc = iucv_path_sever(monpriv->path, user_data_sever);
if (rc) if (rc)
pr_warning("Disconnecting the z/VM *MONITOR system service " pr_warning("Disconnecting the z/VM *MONITOR system "
"failed with rc=%i\n", rc); "service failed with rc=%i\n", rc);
iucv_path_free(monpriv->path);
}
atomic_set(&monpriv->iucv_severed, 0); atomic_set(&monpriv->iucv_severed, 0);
atomic_set(&monpriv->iucv_connected, 0); atomic_set(&monpriv->iucv_connected, 0);
...@@ -452,6 +457,94 @@ static struct miscdevice mon_dev = { ...@@ -452,6 +457,94 @@ static struct miscdevice mon_dev = {
.minor = MISC_DYNAMIC_MINOR, .minor = MISC_DYNAMIC_MINOR,
}; };
/******************************************************************************
* suspend / resume *
*****************************************************************************/
static int monreader_freeze(struct device *dev)
{
struct mon_private *monpriv = dev->driver_data;
int rc;
if (!monpriv)
return 0;
if (monpriv->path) {
rc = iucv_path_sever(monpriv->path, user_data_sever);
if (rc)
pr_warning("Disconnecting the z/VM *MONITOR system "
"service failed with rc=%i\n", rc);
iucv_path_free(monpriv->path);
}
atomic_set(&monpriv->iucv_severed, 0);
atomic_set(&monpriv->iucv_connected, 0);
atomic_set(&monpriv->read_ready, 0);
atomic_set(&monpriv->msglim_count, 0);
monpriv->write_index = 0;
monpriv->read_index = 0;
monpriv->path = NULL;
return 0;
}
static int monreader_thaw(struct device *dev)
{
struct mon_private *monpriv = dev->driver_data;
int rc;
if (!monpriv)
return 0;
rc = -ENOMEM;
monpriv->path = iucv_path_alloc(MON_MSGLIM, IUCV_IPRMDATA, GFP_KERNEL);
if (!monpriv->path)
goto out;
rc = iucv_path_connect(monpriv->path, &monreader_iucv_handler,
MON_SERVICE, NULL, user_data_connect, monpriv);
if (rc) {
pr_err("Connecting to the z/VM *MONITOR system service "
"failed with rc=%i\n", rc);
goto out_path;
}
wait_event(mon_conn_wait_queue,
atomic_read(&monpriv->iucv_connected) ||
atomic_read(&monpriv->iucv_severed));
if (atomic_read(&monpriv->iucv_severed))
goto out_path;
return 0;
out_path:
rc = -EIO;
iucv_path_free(monpriv->path);
monpriv->path = NULL;
out:
atomic_set(&monpriv->iucv_severed, 1);
return rc;
}
static int monreader_restore(struct device *dev)
{
int rc;
segment_unload(mon_dcss_name);
rc = segment_load(mon_dcss_name, SEGMENT_SHARED,
&mon_dcss_start, &mon_dcss_end);
if (rc < 0) {
segment_warning(rc, mon_dcss_name);
panic("fatal monreader resume error: no monitor dcss\n");
}
return monreader_thaw(dev);
}
static struct dev_pm_ops monreader_pm_ops = {
.freeze = monreader_freeze,
.thaw = monreader_thaw,
.restore = monreader_restore,
};
static struct device_driver monreader_driver = {
.name = "monreader",
.bus = &iucv_bus,
.pm = &monreader_pm_ops,
};
/****************************************************************************** /******************************************************************************
* module init/exit * * module init/exit *
*****************************************************************************/ *****************************************************************************/
...@@ -475,16 +568,33 @@ static int __init mon_init(void) ...@@ -475,16 +568,33 @@ static int __init mon_init(void)
return rc; return rc;
} }
rc = driver_register(&monreader_driver);
if (rc)
goto out_iucv;
monreader_device = kzalloc(sizeof(struct device), GFP_KERNEL);
if (!monreader_device)
goto out_driver;
dev_set_name(monreader_device, "monreader-dev");
monreader_device->bus = &iucv_bus;
monreader_device->parent = iucv_root;
monreader_device->driver = &monreader_driver;
monreader_device->release = (void (*)(struct device *))kfree;
rc = device_register(monreader_device);
if (rc) {
kfree(monreader_device);
goto out_driver;
}
rc = segment_type(mon_dcss_name); rc = segment_type(mon_dcss_name);
if (rc < 0) { if (rc < 0) {
segment_warning(rc, mon_dcss_name); segment_warning(rc, mon_dcss_name);
goto out_iucv; goto out_device;
} }
if (rc != SEG_TYPE_SC) { if (rc != SEG_TYPE_SC) {
pr_err("The specified *MONITOR DCSS %s does not have the " pr_err("The specified *MONITOR DCSS %s does not have the "
"required type SC\n", mon_dcss_name); "required type SC\n", mon_dcss_name);
rc = -EINVAL; rc = -EINVAL;
goto out_iucv; goto out_device;
} }
rc = segment_load(mon_dcss_name, SEGMENT_SHARED, rc = segment_load(mon_dcss_name, SEGMENT_SHARED,
...@@ -492,7 +602,7 @@ static int __init mon_init(void) ...@@ -492,7 +602,7 @@ static int __init mon_init(void)
if (rc < 0) { if (rc < 0) {
segment_warning(rc, mon_dcss_name); segment_warning(rc, mon_dcss_name);
rc = -EINVAL; rc = -EINVAL;
goto out_iucv; goto out_device;
} }
dcss_mkname(mon_dcss_name, &user_data_connect[8]); dcss_mkname(mon_dcss_name, &user_data_connect[8]);
...@@ -503,6 +613,10 @@ static int __init mon_init(void) ...@@ -503,6 +613,10 @@ static int __init mon_init(void)
out: out:
segment_unload(mon_dcss_name); segment_unload(mon_dcss_name);
out_device:
device_unregister(monreader_device);
out_driver:
driver_unregister(&monreader_driver);
out_iucv: out_iucv:
iucv_unregister(&monreader_iucv_handler, 1); iucv_unregister(&monreader_iucv_handler, 1);
return rc; return rc;
...@@ -512,6 +626,8 @@ static void __exit mon_exit(void) ...@@ -512,6 +626,8 @@ static void __exit mon_exit(void)
{ {
segment_unload(mon_dcss_name); segment_unload(mon_dcss_name);
WARN_ON(misc_deregister(&mon_dev) != 0); WARN_ON(misc_deregister(&mon_dev) != 0);
device_unregister(monreader_device);
driver_unregister(&monreader_driver);
iucv_unregister(&monreader_iucv_handler, 1); iucv_unregister(&monreader_iucv_handler, 1);
return; return;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment