s390/cio: reorder initialization of ccw consoles
authorSebastian Ott <sebott@linux.vnet.ibm.com>
Mon, 27 Jan 2014 12:28:10 +0000 (13:28 +0100)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Fri, 21 Feb 2014 07:50:12 +0000 (08:50 +0100)
Drivers for ccw consoles use ccw_device_probe_console to receive
an initialized ccw device which is already enabled for interrupts.
After that the device driver does the initialization of its private
data. This can race with unsolicited interrupts which can happen
once the device is enabled for interrupts.

Split ccw_device_probe_console into ccw_device_create_console and
ccw_device_enable_console and reorder the initialization of the ccw
console drivers.

While at it mark these functions as __init.

Reviewed-by: Peter Oberparleiter <oberpar@linux.vnet.ibm.com>
Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/include/asm/ccwdev.h
drivers/s390/char/con3215.c
drivers/s390/char/raw3270.c
drivers/s390/cio/device.c

index 31b5ca8f8c3dcee17ff3008d4ace3d8c177a4334..a9c2c06861772f63b52988cc98afa80058fc0f22 100644 (file)
@@ -219,7 +219,9 @@ extern void ccw_device_get_id(struct ccw_device *, struct ccw_dev_id *);
 #define to_ccwdev(n) container_of(n, struct ccw_device, dev)
 #define to_ccwdrv(n) container_of(n, struct ccw_driver, driver)
 
-extern struct ccw_device *ccw_device_probe_console(struct ccw_driver *);
+extern struct ccw_device *ccw_device_create_console(struct ccw_driver *);
+extern void ccw_device_destroy_console(struct ccw_device *);
+extern int ccw_device_enable_console(struct ccw_device *);
 extern void ccw_device_wait_idle(struct ccw_device *);
 extern int ccw_device_force_console(struct ccw_device *);
 
index bb86494e2b7b7878aecddf25b9df4b53fa443260..5af7f0bd6125702358cb1fbab5830c5a77506e7b 100644 (file)
@@ -922,7 +922,7 @@ static int __init con3215_init(void)
                raw3215_freelist = req;
        }
 
-       cdev = ccw_device_probe_console(&raw3215_ccw_driver);
+       cdev = ccw_device_create_console(&raw3215_ccw_driver);
        if (IS_ERR(cdev))
                return -ENODEV;
 
@@ -932,6 +932,12 @@ static int __init con3215_init(void)
        cdev->handler = raw3215_irq;
 
        raw->flags |= RAW3215_FIXED;
+       if (ccw_device_enable_console(cdev)) {
+               ccw_device_destroy_console(cdev);
+               raw3215_free_info(raw);
+               raw3215[0] = NULL;
+               return -ENODEV;
+       }
 
        /* Request the console irq */
        if (raw3215_startup(raw) != 0) {
index de2c0483949f7a5728e54ae4a94ec581f8bea3c4..041c65bc7bb17cf0b483ef40511b33ac4b61b900 100644 (file)
@@ -790,7 +790,7 @@ struct raw3270 __init *raw3270_setup_console(void)
        char *ascebc;
        int rc;
 
-       cdev = ccw_device_probe_console(&raw3270_ccw_driver);
+       cdev = ccw_device_create_console(&raw3270_ccw_driver);
        if (IS_ERR(cdev))
                return ERR_CAST(cdev);
 
@@ -800,6 +800,13 @@ struct raw3270 __init *raw3270_setup_console(void)
        if (rc)
                return ERR_PTR(rc);
        set_bit(RAW3270_FLAGS_CONSOLE, &rp->flags);
+
+       rc = ccw_device_enable_console(cdev);
+       if (rc) {
+               ccw_device_destroy_console(cdev);
+               return ERR_PTR(rc);
+       }
+
        spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags);
        do {
                __raw3270_reset_device(rp);
index 4283dd3cdd4909ea924e478b9aafae3853d69652..da431992fd8e6ca7054bbf50c81dfa0bab6723a9 100644 (file)
@@ -1572,11 +1572,14 @@ out:
 }
 
 #ifdef CONFIG_CCW_CONSOLE
-static int ccw_device_console_enable(struct ccw_device *cdev,
-                                    struct subchannel *sch)
+int __init ccw_device_enable_console(struct ccw_device *cdev)
 {
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
        int rc;
 
+       if (!cdev->drv || !cdev->handler)
+               return -EINVAL;
+
        io_subchannel_init_fields(sch);
        rc = cio_commit_config(sch);
        if (rc)
@@ -1609,12 +1612,11 @@ out_unlock:
        return rc;
 }
 
-struct ccw_device *ccw_device_probe_console(struct ccw_driver *drv)
+struct ccw_device * __init ccw_device_create_console(struct ccw_driver *drv)
 {
        struct io_subchannel_private *io_priv;
        struct ccw_device *cdev;
        struct subchannel *sch;
-       int ret;
 
        sch = cio_probe_console();
        if (IS_ERR(sch))
@@ -1633,17 +1635,20 @@ struct ccw_device *ccw_device_probe_console(struct ccw_driver *drv)
        }
        cdev->drv = drv;
        set_io_private(sch, io_priv);
-       ret = ccw_device_console_enable(cdev, sch);
-       if (ret) {
-               set_io_private(sch, NULL);
-               put_device(&sch->dev);
-               put_device(&cdev->dev);
-               kfree(io_priv);
-               return ERR_PTR(ret);
-       }
        return cdev;
 }
 
+void __init ccw_device_destroy_console(struct ccw_device *cdev)
+{
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+       struct io_subchannel_private *io_priv = to_io_private(sch);
+
+       set_io_private(sch, NULL);
+       put_device(&sch->dev);
+       put_device(&cdev->dev);
+       kfree(io_priv);
+}
+
 /**
  * ccw_device_wait_idle() - busy wait for device to become idle
  * @cdev: ccw device