]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/s390/cio/device.c
[S390] cio: Fix refcount after moving devices.
[linux-2.6-omap-h63xx.git] / drivers / s390 / cio / device.c
index 38a79ecfc743edff2eaee6b746c4a16c2ec18796..4e4008325e281edc58f11b3a211506b3f8bac35a 100644 (file)
@@ -31,6 +31,7 @@
 #include "device.h"
 #include "ioasm.h"
 #include "io_sch.h"
+#include "blacklist.h"
 
 static struct timer_list recovery_timer;
 static DEFINE_SPINLOCK(recovery_lock);
@@ -873,11 +874,15 @@ void ccw_device_move_to_orphanage(struct work_struct *work)
        replacing_cdev = get_disc_ccwdev_by_dev_id(&dev_id, cdev);
        if (replacing_cdev) {
                sch_attach_disconnected_device(sch, replacing_cdev);
+               /* Release reference from get_disc_ccwdev_by_dev_id() */
+               put_device(&cdev->dev);
                return;
        }
        replacing_cdev = get_orphaned_ccwdev_by_dev_id(css, &dev_id);
        if (replacing_cdev) {
                sch_attach_orphaned_device(sch, replacing_cdev);
+               /* Release reference from get_orphaned_ccwdev_by_dev_id() */
+               put_device(&cdev->dev);
                return;
        }
        sch_create_and_recog_new_device(sch);
@@ -1034,8 +1039,11 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
        init_timer(&priv->timer);
 
        /* Set an initial name for the device. */
-       snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x",
-                 sch->schid.ssid, sch->schib.pmcw.dev);
+       if (cio_is_console(sch->schid))
+               cdev->dev.init_name = cio_get_console_cdev_name(sch);
+       else
+               dev_set_name(&cdev->dev, "0.%x.%04x",
+                            sch->schid.ssid, sch->schib.pmcw.dev);
 
        /* Increase counter of devices currently in recognition. */
        atomic_inc(&ccw_device_init_count);
@@ -1100,7 +1108,7 @@ static void io_subchannel_irq(struct subchannel *sch)
        cdev = sch_get_cdev(sch);
 
        CIO_TRACE_EVENT(3, "IRQ");
-       CIO_TRACE_EVENT(3, sch->dev.bus_id);
+       CIO_TRACE_EVENT(3, dev_name(&sch->dev));
        if (cdev)
                dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);
 }
@@ -1470,6 +1478,45 @@ static void ccw_device_schedule_recovery(void)
        spin_unlock_irqrestore(&recovery_lock, flags);
 }
 
+static int purge_fn(struct device *dev, void *data)
+{
+       struct ccw_device *cdev = to_ccwdev(dev);
+       struct ccw_device_private *priv = cdev->private;
+       int unreg;
+
+       spin_lock_irq(cdev->ccwlock);
+       unreg = is_blacklisted(priv->dev_id.ssid, priv->dev_id.devno) &&
+               (priv->state == DEV_STATE_OFFLINE);
+       spin_unlock_irq(cdev->ccwlock);
+       if (!unreg)
+               goto out;
+       if (!get_device(&cdev->dev))
+               goto out;
+       CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", priv->dev_id.ssid,
+                     priv->dev_id.devno);
+       PREPARE_WORK(&cdev->private->kick_work, ccw_device_call_sch_unregister);
+       queue_work(slow_path_wq, &cdev->private->kick_work);
+
+out:
+       /* Abort loop in case of pending signal. */
+       if (signal_pending(current))
+               return -EINTR;
+
+       return 0;
+}
+
+/**
+ * ccw_purge_blacklisted - purge unused, blacklisted devices
+ *
+ * Unregister all ccw devices that are offline and on the blacklist.
+ */
+int ccw_purge_blacklisted(void)
+{
+       CIO_MSG_EVENT(2, "ccw: purging blacklisted devices\n");
+       bus_for_each_dev(&ccw_bus_type, NULL, NULL, purge_fn);
+       return 0;
+}
+
 static void device_set_disconnected(struct ccw_device *cdev)
 {
        if (!cdev)
@@ -1486,7 +1533,7 @@ void ccw_device_set_notoper(struct ccw_device *cdev)
        struct subchannel *sch = to_subchannel(cdev->dev.parent);
 
        CIO_TRACE_EVENT(2, "notoper");
-       CIO_TRACE_EVENT(2, sch->dev.bus_id);
+       CIO_TRACE_EVENT(2, dev_name(&sch->dev));
        ccw_device_set_timeout(cdev, 0);
        cio_disable_subchannel(sch);
        cdev->private->state = DEV_STATE_NOT_OPER;
@@ -1585,6 +1632,7 @@ static int io_subchannel_sch_event(struct subchannel *sch, int slow)
 
 #ifdef CONFIG_CCW_CONSOLE
 static struct ccw_device console_cdev;
+static char console_cdev_name[10] = "0.x.xxxx";
 static struct ccw_device_private console_private;
 static int console_cdev_in_use;
 
@@ -1655,6 +1703,14 @@ ccw_device_probe_console(void)
        console_cdev.online = 1;
        return &console_cdev;
 }
+
+
+const char *cio_get_console_cdev_name(struct subchannel *sch)
+{
+       snprintf(console_cdev_name, 10, "0.%x.%04x",
+                sch->schid.ssid, sch->schib.pmcw.dev);
+       return (const char *)console_cdev_name;
+}
 #endif
 
 /*
@@ -1667,7 +1723,7 @@ __ccwdev_check_busid(struct device *dev, void *id)
 
        bus_id = id;
 
-       return (strncmp(bus_id, dev->bus_id, BUS_ID_SIZE) == 0);
+       return (strncmp(bus_id, dev_name(dev), BUS_ID_SIZE) == 0);
 }