Merge branch 'for-linus' of git://neil.brown.name/md
[deliverable/linux.git] / drivers / s390 / char / monreader.c
index 35fd8dfcaaa6d22d2083304ccdd9ebb4137d3af7..75a8831eebbc78dbf7b5bdd60903c2d9e1226355 100644 (file)
@@ -1,12 +1,14 @@
 /*
- * drivers/s390/char/monreader.c
- *
  * Character device driver for reading z/VM *MONITOR service records.
  *
- *   Copyright IBM Corp. 2004, 2008
- *   Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
+ * Copyright IBM Corp. 2004, 2009
+ *
+ * Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
  */
 
+#define KMSG_COMPONENT "monreader"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/init.h>
 #include <linux/spinlock.h>
 #include <linux/interrupt.h>
 #include <linux/poll.h>
+#include <linux/device.h>
 #include <net/iucv/iucv.h>
 #include <asm/uaccess.h>
 #include <asm/ebcdic.h>
 #include <asm/extmem.h>
 
-//#define MON_DEBUG                    /* Debug messages on/off */
-
-#define MON_NAME "monreader"
-
-#define P_INFO(x...)   printk(KERN_INFO MON_NAME " info: " x)
-#define P_ERROR(x...)  printk(KERN_ERR MON_NAME " error: " x)
-#define P_WARNING(x...)        printk(KERN_WARNING MON_NAME " warning: " x)
-
-#ifdef MON_DEBUG
-#define P_DEBUG(x...)   printk(KERN_DEBUG MON_NAME " debug: " x)
-#else
-#define P_DEBUG(x...)   do {} while (0)
-#endif
 
 #define MON_COLLECT_SAMPLE 0x80
 #define MON_COLLECT_EVENT  0x40
@@ -88,6 +78,7 @@ static u8 user_data_sever[16] = {
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 };
 
+static struct device *monreader_device;
 
 /******************************************************************************
  *                             helper functions                               *
@@ -172,7 +163,7 @@ static int mon_send_reply(struct mon_msg *monmsg,
        } else
                monmsg->replied_msglim = 1;
        if (rc) {
-               P_ERROR("read, IUCV reply failed with rc = %i\n\n", rc);
+               pr_err("Reading monitor data failed with rc=%i\n", rc);
                return -EIO;
        }
        return 0;
@@ -251,7 +242,8 @@ static void mon_iucv_path_severed(struct iucv_path *path, u8 ipuser[16])
 {
        struct mon_private *monpriv = path->private;
 
-       P_ERROR("IUCV connection severed with rc = 0x%X\n", ipuser[0]);
+       pr_err("z/VM *MONITOR system service disconnected with rc=%i\n",
+              ipuser[0]);
        iucv_path_sever(path, NULL);
        atomic_set(&monpriv->iucv_severed, 1);
        wake_up(&mon_conn_wait_queue);
@@ -266,8 +258,7 @@ static void mon_iucv_message_pending(struct iucv_path *path,
        memcpy(&monpriv->msg_array[monpriv->write_index]->msg,
               msg, sizeof(*msg));
        if (atomic_inc_return(&monpriv->msglim_count) == MON_MSGLIM) {
-               P_WARNING("IUCV message pending, message limit (%i) reached\n",
-                         MON_MSGLIM);
+               pr_warning("The read queue for monitor data is full\n");
                monpriv->msg_array[monpriv->write_index]->msglim_reached = 1;
        }
        monpriv->write_index = (monpriv->write_index + 1) % MON_MSGLIM;
@@ -311,8 +302,8 @@ static int mon_open(struct inode *inode, struct file *filp)
        rc = iucv_path_connect(monpriv->path, &monreader_iucv_handler,
                               MON_SERVICE, NULL, user_data_connect, monpriv);
        if (rc) {
-               P_ERROR("iucv connection to *MONITOR failed with "
-                       "IPUSER SEVER code = %i\n", rc);
+               pr_err("Connecting to the z/VM *MONITOR system service "
+                      "failed with rc=%i\n", rc);
                rc = -EIO;
                goto out_path;
        }
@@ -329,11 +320,12 @@ static int mon_open(struct inode *inode, struct file *filp)
                goto out_path;
        }
        filp->private_data = monpriv;
+       monreader_device->driver_data = monpriv;
        unlock_kernel();
        return nonseekable_open(inode, filp);
 
 out_path:
-       kfree(monpriv->path);
+       iucv_path_free(monpriv->path);
 out_priv:
        mon_free_mem(monpriv);
 out_use:
@@ -351,9 +343,13 @@ static int mon_close(struct inode *inode, struct file *filp)
        /*
         * Close IUCV connection and unregister
         */
-       rc = iucv_path_sever(monpriv->path, user_data_sever);
-       if (rc)
-               P_ERROR("close, iucv_sever failed with rc = %i\n", rc);
+       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);
@@ -461,6 +457,94 @@ static struct miscdevice mon_dev = {
        .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                              *
  *****************************************************************************/
@@ -469,7 +553,8 @@ static int __init mon_init(void)
        int rc;
 
        if (!MACHINE_IS_VM) {
-               P_ERROR("not running under z/VM, driver not loaded\n");
+               pr_err("The z/VM *MONITOR record device driver cannot be "
+                      "loaded without z/VM\n");
                return -ENODEV;
        }
 
@@ -478,20 +563,38 @@ static int __init mon_init(void)
         */
        rc = iucv_register(&monreader_iucv_handler, 1);
        if (rc) {
-               P_ERROR("failed to register with iucv driver\n");
+               pr_err("The z/VM *MONITOR record device driver failed to "
+                      "register with IUCV\n");
                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);
        if (rc < 0) {
                segment_warning(rc, mon_dcss_name);
-               goto out_iucv;
+               goto out_device;
        }
        if (rc != SEG_TYPE_SC) {
-               P_ERROR("segment %s has unsupported type, should be SC\n",
-                       mon_dcss_name);
+               pr_err("The specified *MONITOR DCSS %s does not have the "
+                      "required type SC\n", mon_dcss_name);
                rc = -EINVAL;
-               goto out_iucv;
+               goto out_device;
        }
 
        rc = segment_load(mon_dcss_name, SEGMENT_SHARED,
@@ -499,7 +602,7 @@ static int __init mon_init(void)
        if (rc < 0) {
                segment_warning(rc, mon_dcss_name);
                rc = -EINVAL;
-               goto out_iucv;
+               goto out_device;
        }
        dcss_mkname(mon_dcss_name, &user_data_connect[8]);
 
@@ -510,6 +613,10 @@ static int __init mon_init(void)
 
 out:
        segment_unload(mon_dcss_name);
+out_device:
+       device_unregister(monreader_device);
+out_driver:
+       driver_unregister(&monreader_driver);
 out_iucv:
        iucv_unregister(&monreader_iucv_handler, 1);
        return rc;
@@ -519,6 +626,8 @@ static void __exit mon_exit(void)
 {
        segment_unload(mon_dcss_name);
        WARN_ON(misc_deregister(&mon_dev) != 0);
+       device_unregister(monreader_device);
+       driver_unregister(&monreader_driver);
        iucv_unregister(&monreader_iucv_handler, 1);
        return;
 }
This page took 0.033018 seconds and 5 git commands to generate.