]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/s390/cio/device.c
[S390] bus_id -> dev_name conversions
[linux-2.6-omap-h63xx.git] / drivers / s390 / cio / device.c
index e818d0c54c0949f5a21ae134ccf426da5d079d9c..95b92dfec5a815435630b1e7692591aa2dedad0c 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);
@@ -150,7 +151,6 @@ static struct css_driver io_subchannel_driver = {
 };
 
 struct workqueue_struct *ccw_device_work;
-struct workqueue_struct *ccw_device_notify_work;
 wait_queue_head_t ccw_device_init_wq;
 atomic_t ccw_device_init_count;
 
@@ -168,11 +168,6 @@ init_ccw_bus_type (void)
        ccw_device_work = create_singlethread_workqueue("cio");
        if (!ccw_device_work)
                return -ENOMEM; /* FIXME: better errno ? */
-       ccw_device_notify_work = create_singlethread_workqueue("cio_notify");
-       if (!ccw_device_notify_work) {
-               ret = -ENOMEM; /* FIXME: better errno ? */
-               goto out_err;
-       }
        slow_path_wq = create_singlethread_workqueue("kslowcrw");
        if (!slow_path_wq) {
                ret = -ENOMEM; /* FIXME: better errno ? */
@@ -192,8 +187,6 @@ init_ccw_bus_type (void)
 out_err:
        if (ccw_device_work)
                destroy_workqueue(ccw_device_work);
-       if (ccw_device_notify_work)
-               destroy_workqueue(ccw_device_notify_work);
        if (slow_path_wq)
                destroy_workqueue(slow_path_wq);
        return ret;
@@ -204,7 +197,6 @@ cleanup_ccw_bus_type (void)
 {
        css_driver_unregister(&io_subchannel_driver);
        bus_unregister(&ccw_bus_type);
-       destroy_workqueue(ccw_device_notify_work);
        destroy_workqueue(ccw_device_work);
 }
 
@@ -305,36 +297,33 @@ static void ccw_device_unregister(struct ccw_device *cdev)
                device_del(&cdev->dev);
 }
 
-static void ccw_device_remove_orphan_cb(struct device *dev)
+static void ccw_device_remove_orphan_cb(struct work_struct *work)
 {
-       struct ccw_device *cdev = to_ccwdev(dev);
+       struct ccw_device_private *priv;
+       struct ccw_device *cdev;
 
+       priv = container_of(work, struct ccw_device_private, kick_work);
+       cdev = priv->cdev;
        ccw_device_unregister(cdev);
        put_device(&cdev->dev);
+       /* Release cdev reference for workqueue processing. */
+       put_device(&cdev->dev);
 }
 
-static void ccw_device_remove_sch_cb(struct device *dev)
-{
-       struct subchannel *sch;
-
-       sch = to_subchannel(dev);
-       css_sch_device_unregister(sch);
-       /* Reset intparm to zeroes. */
-       sch->schib.pmcw.intparm = 0;
-       cio_modify(sch);
-       put_device(&sch->dev);
-}
+static void ccw_device_call_sch_unregister(struct work_struct *work);
 
 static void
 ccw_device_remove_disconnected(struct ccw_device *cdev)
 {
        unsigned long flags;
-       int rc;
 
        /*
         * Forced offline in disconnected state means
         * 'throw away device'.
         */
+       /* Get cdev reference for workqueue processing. */
+       if (!get_device(&cdev->dev))
+               return;
        if (ccw_device_is_orphan(cdev)) {
                /*
                 * Deregister ccw device.
@@ -344,23 +333,13 @@ ccw_device_remove_disconnected(struct ccw_device *cdev)
                spin_lock_irqsave(cdev->ccwlock, flags);
                cdev->private->state = DEV_STATE_NOT_OPER;
                spin_unlock_irqrestore(cdev->ccwlock, flags);
-               rc = device_schedule_callback(&cdev->dev,
-                                             ccw_device_remove_orphan_cb);
-               if (rc)
-                       CIO_MSG_EVENT(0, "Couldn't unregister orphan "
-                                     "0.%x.%04x\n",
-                                     cdev->private->dev_id.ssid,
-                                     cdev->private->dev_id.devno);
-               return;
-       }
-       /* Deregister subchannel, which will kill the ccw device. */
-       rc = device_schedule_callback(cdev->dev.parent,
-                                     ccw_device_remove_sch_cb);
-       if (rc)
-               CIO_MSG_EVENT(0, "Couldn't unregister disconnected device "
-                             "0.%x.%04x\n",
-                             cdev->private->dev_id.ssid,
-                             cdev->private->dev_id.devno);
+               PREPARE_WORK(&cdev->private->kick_work,
+                               ccw_device_remove_orphan_cb);
+       } else
+               /* Deregister subchannel, which will kill the ccw device. */
+               PREPARE_WORK(&cdev->private->kick_work,
+                               ccw_device_call_sch_unregister);
+       queue_work(slow_path_wq, &cdev->private->kick_work);
 }
 
 /**
@@ -979,12 +958,17 @@ static void ccw_device_call_sch_unregister(struct work_struct *work)
 
        priv = container_of(work, struct ccw_device_private, kick_work);
        cdev = priv->cdev;
+       /* Get subchannel reference for local processing. */
+       if (!get_device(cdev->dev.parent))
+               return;
        sch = to_subchannel(cdev->dev.parent);
        css_sch_device_unregister(sch);
        /* Reset intparm to zeroes. */
        sch->schib.pmcw.intparm = 0;
        cio_modify(sch);
+       /* Release cdev reference for workqueue processing.*/
        put_device(&cdev->dev);
+       /* Release subchannel reference for local processing. */
        put_device(&sch->dev);
 }
 
@@ -1010,6 +994,8 @@ io_subchannel_recog_done(struct ccw_device *cdev)
                PREPARE_WORK(&cdev->private->kick_work,
                             ccw_device_call_sch_unregister);
                queue_work(slow_path_wq, &cdev->private->kick_work);
+               /* Release subchannel reference for asynchronous recognition. */
+               put_device(&sch->dev);
                if (atomic_dec_and_test(&ccw_device_init_count))
                        wake_up(&ccw_device_init_wq);
                break;
@@ -1115,7 +1101,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);
 }
@@ -1485,6 +1471,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)
@@ -1496,11 +1521,22 @@ static void device_set_disconnected(struct ccw_device *cdev)
                ccw_device_schedule_recovery();
 }
 
+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);
+       ccw_device_set_timeout(cdev, 0);
+       cio_disable_subchannel(sch);
+       cdev->private->state = DEV_STATE_NOT_OPER;
+}
+
 static int io_subchannel_sch_event(struct subchannel *sch, int slow)
 {
        int event, ret, disc;
        unsigned long flags;
-       enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE } action;
+       enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE, DISC } action;
        struct ccw_device *cdev;
 
        spin_lock_irqsave(sch->lock, flags);
@@ -1535,16 +1571,11 @@ static int io_subchannel_sch_event(struct subchannel *sch, int slow)
                }
                /* fall through */
        case CIO_GONE:
-               /* Prevent unwanted effects when opening lock. */
-               cio_disable_subchannel(sch);
-               device_set_disconnected(cdev);
                /* Ask driver what to do with device. */
-               action = UNREGISTER;
-               spin_unlock_irqrestore(sch->lock, flags);
-               ret = io_subchannel_notify(sch, event);
-               spin_lock_irqsave(sch->lock, flags);
-               if (ret)
-                       action = NONE;
+               if (io_subchannel_notify(sch, event))
+                       action = DISC;
+               else
+                       action = UNREGISTER;
                break;
        case CIO_REVALIDATE:
                /* Device will be removed, so no notify necessary. */
@@ -1565,6 +1596,7 @@ static int io_subchannel_sch_event(struct subchannel *sch, int slow)
        switch (action) {
        case UNREGISTER:
        case UNREGISTER_PROBE:
+               ccw_device_set_notoper(cdev);
                /* Unregister device (will use subchannel lock). */
                spin_unlock_irqrestore(sch->lock, flags);
                css_sch_device_unregister(sch);
@@ -1577,6 +1609,9 @@ static int io_subchannel_sch_event(struct subchannel *sch, int slow)
        case REPROBE:
                ccw_device_trigger_reprobe(cdev);
                break;
+       case DISC:
+               device_set_disconnected(cdev);
+               break;
        default:
                break;
        }
@@ -1672,7 +1707,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);
 }
 
 
@@ -1828,5 +1863,4 @@ EXPORT_SYMBOL(ccw_driver_unregister);
 EXPORT_SYMBOL(get_ccwdev_by_busid);
 EXPORT_SYMBOL(ccw_bus_type);
 EXPORT_SYMBOL(ccw_device_work);
-EXPORT_SYMBOL(ccw_device_notify_work);
 EXPORT_SYMBOL_GPL(ccw_device_get_subchannel_id);