]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/s390/cio/device.c
Merge branch 'core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip...
[karo-tx-linux.git] / drivers / s390 / cio / device.c
index cba33aa1df7936dcaaf9c91f37b1fafaae3be55f..23d5752349b59d0f85b3274c206eea556123e10e 100644 (file)
@@ -751,8 +751,8 @@ static int io_subchannel_initialize_dev(struct subchannel *sch,
        /* Do first half of device_register. */
        device_initialize(&cdev->dev);
        if (!get_device(&sch->dev)) {
-               if (cdev->dev.release)
-                       cdev->dev.release(&cdev->dev);
+               /* Release reference from device_initialize(). */
+               put_device(&cdev->dev);
                return -ENODEV;
        }
        return 0;
@@ -950,6 +950,14 @@ io_subchannel_register(struct work_struct *work)
        priv = container_of(work, struct ccw_device_private, kick_work);
        cdev = priv->cdev;
        sch = to_subchannel(cdev->dev.parent);
+       /*
+        * Check if subchannel is still registered. It may have become
+        * unregistered if a machine check hit us after finishing
+        * device recognition but before the register work could be
+        * queued.
+        */
+       if (!device_is_registered(&sch->dev))
+               goto out_err;
        css_update_ssd_info(sch);
        /*
         * io_subchannel_register() will also be called after device
@@ -957,7 +965,7 @@ io_subchannel_register(struct work_struct *work)
         * be registered). We need to reprobe since we may now have sense id
         * information.
         */
-       if (klist_node_attached(&cdev->dev.knode_parent)) {
+       if (device_is_registered(&cdev->dev)) {
                if (!cdev->drv) {
                        ret = device_reprobe(&cdev->dev);
                        if (ret)
@@ -984,18 +992,16 @@ io_subchannel_register(struct work_struct *work)
                spin_lock_irqsave(sch->lock, flags);
                sch_set_cdev(sch, NULL);
                spin_unlock_irqrestore(sch->lock, flags);
-               /* Release reference for workqueue processing. */
-               put_device(&cdev->dev);
                /* Release initial device reference. */
                put_device(&cdev->dev);
-               if (atomic_dec_and_test(&ccw_device_init_count))
-                       wake_up(&ccw_device_init_wq);
-               return;
+               goto out_err;
        }
-       put_device(&cdev->dev);
 out:
        cdev->private->flags.recog_done = 1;
        wake_up(&cdev->private->wait_q);
+out_err:
+       /* Release reference for workqueue processing. */
+       put_device(&cdev->dev);
        if (atomic_dec_and_test(&ccw_device_init_count))
                wake_up(&ccw_device_init_wq);
 }
@@ -1014,8 +1020,8 @@ static void ccw_device_call_sch_unregister(struct work_struct *work)
        sch = to_subchannel(cdev->dev.parent);
        css_sch_device_unregister(sch);
        /* Reset intparm to zeroes. */
-       sch->schib.pmcw.intparm = 0;
-       cio_modify(sch);
+       sch->config.intparm = 0;
+       cio_commit_config(sch);
        /* Release cdev reference for workqueue processing.*/
        put_device(&cdev->dev);
        /* Release subchannel reference for local processing. */
@@ -1142,8 +1148,8 @@ static void ccw_device_move_to_sch(struct work_struct *work)
                spin_unlock_irq(former_parent->lock);
                css_sch_device_unregister(former_parent);
                /* Reset intparm to zeroes. */
-               former_parent->schib.pmcw.intparm = 0;
-               cio_modify(former_parent);
+               former_parent->config.intparm = 0;
+               cio_commit_config(former_parent);
        }
        sch_attach_device(sch, cdev);
 out:
@@ -1164,6 +1170,15 @@ static void io_subchannel_irq(struct subchannel *sch)
                dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);
 }
 
+void io_subchannel_init_config(struct subchannel *sch)
+{
+       memset(&sch->config, 0, sizeof(sch->config));
+       sch->config.csense = 1;
+       /* Use subchannel mp mode when there is more than 1 installed CHPID. */
+       if ((sch->schib.pmcw.pim & (sch->schib.pmcw.pim - 1)) != 0)
+               sch->config.mp = 1;
+}
+
 static void io_subchannel_init_fields(struct subchannel *sch)
 {
        if (cio_is_console(sch->schid))
@@ -1178,18 +1193,34 @@ static void io_subchannel_init_fields(struct subchannel *sch)
                      sch->schib.pmcw.dev, sch->schid.ssid,
                      sch->schid.sch_no, sch->schib.pmcw.pim,
                      sch->schib.pmcw.pam, sch->schib.pmcw.pom);
-       /* Initially set up some fields in the pmcw. */
-       sch->schib.pmcw.ena = 0;
-       sch->schib.pmcw.csense = 1;     /* concurrent sense */
-       if ((sch->lpm & (sch->lpm - 1)) != 0)
-               sch->schib.pmcw.mp = 1; /* multipath mode */
-       /* clean up possible residual cmf stuff */
-       sch->schib.pmcw.mme = 0;
-       sch->schib.pmcw.mbfc = 0;
-       sch->schib.pmcw.mbi = 0;
-       sch->schib.mba = 0;
+
+       io_subchannel_init_config(sch);
+}
+
+static void io_subchannel_do_unreg(struct work_struct *work)
+{
+       struct subchannel *sch;
+
+       sch = container_of(work, struct subchannel, work);
+       css_sch_device_unregister(sch);
+       /* Reset intparm to zeroes. */
+       sch->config.intparm = 0;
+       cio_commit_config(sch);
+       put_device(&sch->dev);
+}
+
+/* Schedule unregister if we have no cdev. */
+static void io_subchannel_schedule_removal(struct subchannel *sch)
+{
+       get_device(&sch->dev);
+       INIT_WORK(&sch->work, io_subchannel_do_unreg);
+       queue_work(slow_path_wq, &sch->work);
 }
 
+/*
+ * Note: We always return 0 so that we bind to the device even on error.
+ * This is needed so that our remove function is called on unregister.
+ */
 static int io_subchannel_probe(struct subchannel *sch)
 {
        struct ccw_device *cdev;
@@ -1229,17 +1260,18 @@ static int io_subchannel_probe(struct subchannel *sch)
                return 0;
        }
        io_subchannel_init_fields(sch);
+       rc = cio_commit_config(sch);
+       if (rc)
+               goto out_schedule;
        rc = sysfs_create_group(&sch->dev.kobj,
                                &io_subchannel_attr_group);
        if (rc)
-               return rc;
+               goto out_schedule;
        /* Allocate I/O subchannel private data. */
        sch->private = kzalloc(sizeof(struct io_subchannel_private),
                               GFP_KERNEL | GFP_DMA);
-       if (!sch->private) {
-               rc = -ENOMEM;
+       if (!sch->private)
                goto out_err;
-       }
        /*
         * First check if a fitting device may be found amongst the
         * disconnected devices or in the orphanage.
@@ -1263,24 +1295,21 @@ static int io_subchannel_probe(struct subchannel *sch)
                return 0;
        }
        cdev = io_subchannel_create_ccwdev(sch);
-       if (IS_ERR(cdev)) {
-               rc = PTR_ERR(cdev);
+       if (IS_ERR(cdev))
                goto out_err;
-       }
        rc = io_subchannel_recog(cdev, sch);
        if (rc) {
                spin_lock_irqsave(sch->lock, flags);
-               sch_set_cdev(sch, NULL);
+               io_subchannel_recog_done(cdev);
                spin_unlock_irqrestore(sch->lock, flags);
-               if (cdev->dev.release)
-                       cdev->dev.release(&cdev->dev);
-               goto out_err;
        }
        return 0;
 out_err:
        kfree(sch->private);
        sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group);
-       return rc;
+out_schedule:
+       io_subchannel_schedule_removal(sch);
+       return 0;
 }
 
 static int
@@ -1325,10 +1354,7 @@ static void io_subchannel_verify(struct subchannel *sch)
 
 static int check_for_io_on_path(struct subchannel *sch, int mask)
 {
-       int cc;
-
-       cc = stsch(sch->schid, &sch->schib);
-       if (cc)
+       if (cio_update_schib(sch))
                return 0;
        if (scsw_actl(&sch->schib.scsw) && sch->schib.pmcw.lpum == mask)
                return 1;
@@ -1397,15 +1423,13 @@ static int io_subchannel_chp_event(struct subchannel *sch,
                io_subchannel_verify(sch);
                break;
        case CHP_OFFLINE:
-               if (stsch(sch->schid, &sch->schib))
-                       return -ENXIO;
-               if (!css_sch_is_valid(&sch->schib))
+               if (cio_update_schib(sch))
                        return -ENODEV;
                io_subchannel_terminate_path(sch, mask);
                break;
        case CHP_ONLINE:
-               if (stsch(sch->schid, &sch->schib))
-                       return -ENXIO;
+               if (cio_update_schib(sch))
+                       return -ENODEV;
                sch->lpm |= mask & sch->opm;
                io_subchannel_verify(sch);
                break;
@@ -1660,8 +1684,8 @@ static int io_subchannel_sch_event(struct subchannel *sch, int slow)
                spin_lock_irqsave(sch->lock, flags);
 
                /* Reset intparm to zeroes. */
-               sch->schib.pmcw.intparm = 0;
-               cio_modify(sch);
+               sch->config.intparm = 0;
+               cio_commit_config(sch);
                break;
        case REPROBE:
                ccw_device_trigger_reprobe(cdev);
@@ -1702,6 +1726,9 @@ static int ccw_device_console_enable(struct ccw_device *cdev,
        sch->private = cio_get_console_priv();
        memset(sch->private, 0, sizeof(struct io_subchannel_private));
        io_subchannel_init_fields(sch);
+       rc = cio_commit_config(sch);
+       if (rc)
+               return rc;
        sch->driver = &io_subchannel_driver;
        /* Initialize the ccw_device structure. */
        cdev->dev.parent= &sch->dev;