Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core-2.6
[pandora-kernel.git] / drivers / s390 / char / vmur.c
index 9020eba..7d9e67c 100644 (file)
@@ -2,12 +2,15 @@
  * Linux driver for System z and s390 unit record devices
  * (z/VM virtual punch, reader, printer)
  *
- * Copyright IBM Corp. 2001, 2007
+ * Copyright IBM Corp. 2001, 2009
  * Authors: Malcolm Beattie <beattiem@uk.ibm.com>
  *         Michael Holzheu <holzheu@de.ibm.com>
  *         Frank Munzert <munzert@de.ibm.com>
  */
 
+#define KMSG_COMPONENT "vmur"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
 #include <linux/cdev.h>
 #include <linux/smp_lock.h>
 
@@ -40,8 +43,6 @@ MODULE_AUTHOR("IBM Corporation");
 MODULE_DESCRIPTION("s390 z/VM virtual unit record device driver");
 MODULE_LICENSE("GPL");
 
-#define PRINTK_HEADER "vmur: "
-
 static dev_t ur_first_dev_maj_min;
 static struct class *vmur_class;
 static struct debug_info *vmur_dbf;
@@ -59,6 +60,7 @@ static int ur_probe(struct ccw_device *cdev);
 static void ur_remove(struct ccw_device *cdev);
 static int ur_set_online(struct ccw_device *cdev);
 static int ur_set_offline(struct ccw_device *cdev);
+static int ur_pm_suspend(struct ccw_device *cdev);
 
 static struct ccw_driver ur_driver = {
        .name           = "vmur",
@@ -68,6 +70,7 @@ static struct ccw_driver ur_driver = {
        .remove         = ur_remove,
        .set_online     = ur_set_online,
        .set_offline    = ur_set_offline,
+       .freeze         = ur_pm_suspend,
 };
 
 static DEFINE_MUTEX(vmur_mutex);
@@ -77,11 +80,11 @@ static DEFINE_MUTEX(vmur_mutex);
  *
  * Each ur device (urd) contains a reference to its corresponding ccw device
  * (cdev) using the urd->cdev pointer. Each ccw device has a reference to the
- * ur device using the cdev->dev.driver_data pointer.
+ * ur device using dev_get_drvdata(&cdev->dev) pointer.
  *
  * urd references:
  * - ur_probe gets a urd reference, ur_remove drops the reference
- *   (cdev->dev.driver_data)
+ *   dev_get_drvdata(&cdev->dev)
  * - ur_open gets a urd reference, ur_relase drops the reference
  *   (urf->urd)
  *
@@ -89,7 +92,7 @@ static DEFINE_MUTEX(vmur_mutex);
  * - urdev_alloc get a cdev reference (urd->cdev)
  * - urdev_free drops the cdev reference (urd->cdev)
  *
- * Setting and clearing of cdev->dev.driver_data is protected by the ccwdev lock
+ * Setting and clearing of dev_get_drvdata(&cdev->dev) is protected by the ccwdev lock
  */
 static struct urdev *urdev_alloc(struct ccw_device *cdev)
 {
@@ -128,7 +131,7 @@ static struct urdev *urdev_get_from_cdev(struct ccw_device *cdev)
        unsigned long flags;
 
        spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
-       urd = cdev->dev.driver_data;
+       urd = dev_get_drvdata(&cdev->dev);
        if (urd)
                urdev_get(urd);
        spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
@@ -156,6 +159,28 @@ static void urdev_put(struct urdev *urd)
                urdev_free(urd);
 }
 
+/*
+ * State and contents of ur devices can be changed by class D users issuing
+ * CP commands such as PURGE or TRANSFER, while the Linux guest is suspended.
+ * Also the Linux guest might be logged off, which causes all active spool
+ * files to be closed.
+ * So we cannot guarantee that spool files are still the same when the Linux
+ * guest is resumed. In order to avoid unpredictable results at resume time
+ * we simply refuse to suspend if a ur device node is open.
+ */
+static int ur_pm_suspend(struct ccw_device *cdev)
+{
+       struct urdev *urd = cdev->dev.driver_data;
+
+       TRACE("ur_pm_suspend: cdev=%p\n", cdev);
+       if (urd->open_flag) {
+               pr_err("Unit record device %s is busy, %s refusing to "
+                      "suspend.\n", dev_name(&cdev->dev), ur_banner);
+               return -EBUSY;
+       }
+       return 0;
+}
+
 /*
  * Low-level functions to do I/O to a ur device.
  *     alloc_chan_prog
@@ -285,7 +310,7 @@ static void ur_int_handler(struct ccw_device *cdev, unsigned long intparm,
                TRACE("ur_int_handler: unsolicited interrupt\n");
                return;
        }
-       urd = cdev->dev.driver_data;
+       urd = dev_get_drvdata(&cdev->dev);
        BUG_ON(!urd);
        /* On special conditions irb is an error pointer */
        if (IS_ERR(irb))
@@ -831,7 +856,7 @@ static int ur_probe(struct ccw_device *cdev)
                goto fail_remove_attr;
        }
        spin_lock_irq(get_ccwdev_lock(cdev));
-       cdev->dev.driver_data = urd;
+       dev_set_drvdata(&cdev->dev, urd);
        spin_unlock_irq(get_ccwdev_lock(cdev));
 
        mutex_unlock(&vmur_mutex);
@@ -971,8 +996,8 @@ static void ur_remove(struct ccw_device *cdev)
        ur_remove_attributes(&cdev->dev);
 
        spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
-       urdev_put(cdev->dev.driver_data);
-       cdev->dev.driver_data = NULL;
+       urdev_put(dev_get_drvdata(&cdev->dev));
+       dev_set_drvdata(&cdev->dev, NULL);
        spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
 
        mutex_unlock(&vmur_mutex);
@@ -987,7 +1012,8 @@ static int __init ur_init(void)
        dev_t dev;
 
        if (!MACHINE_IS_VM) {
-               PRINT_ERR("%s is only available under z/VM.\n", ur_banner);
+               pr_err("The %s cannot be loaded without z/VM\n",
+                      ur_banner);
                return -ENODEV;
        }
 
@@ -1006,7 +1032,8 @@ static int __init ur_init(void)
 
        rc = alloc_chrdev_region(&dev, 0, NUM_MINORS, "vmur");
        if (rc) {
-               PRINT_ERR("alloc_chrdev_region failed: err = %d\n", rc);
+               pr_err("Kernel function alloc_chrdev_region failed with "
+                      "error code %d\n", rc);
                goto fail_unregister_driver;
        }
        ur_first_dev_maj_min = MKDEV(MAJOR(dev), 0);
@@ -1016,7 +1043,7 @@ static int __init ur_init(void)
                rc = PTR_ERR(vmur_class);
                goto fail_unregister_region;
        }
-       PRINT_INFO("%s loaded.\n", ur_banner);
+       pr_info("%s loaded.\n", ur_banner);
        return 0;
 
 fail_unregister_region:
@@ -1034,7 +1061,7 @@ static void __exit ur_exit(void)
        unregister_chrdev_region(ur_first_dev_maj_min, NUM_MINORS);
        ccw_driver_unregister(&ur_driver);
        debug_unregister(vmur_dbf);
-       PRINT_INFO("%s unloaded.\n", ur_banner);
+       pr_info("%s unloaded.\n", ur_banner);
 }
 
 module_init(ur_init);