Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / drivers / s390 / cio / css.c
index 74ea8aa..13eeea3 100644 (file)
 #include "cio_debug.h"
 #include "ioasm.h"
 #include "chsc.h"
+#include "device.h"
 
 int need_rescan = 0;
 int css_init_done = 0;
+static int need_reprobe = 0;
 static int max_ssid = 0;
 
 struct channel_subsystem *css[__MAX_CSSID + 1];
@@ -106,6 +108,24 @@ css_subchannel_release(struct device *dev)
 
 extern int css_get_ssd_info(struct subchannel *sch);
 
+
+int css_sch_device_register(struct subchannel *sch)
+{
+       int ret;
+
+       mutex_lock(&sch->reg_mutex);
+       ret = device_register(&sch->dev);
+       mutex_unlock(&sch->reg_mutex);
+       return ret;
+}
+
+void css_sch_device_unregister(struct subchannel *sch)
+{
+       mutex_lock(&sch->reg_mutex);
+       device_unregister(&sch->dev);
+       mutex_unlock(&sch->reg_mutex);
+}
+
 static int
 css_register_subchannel(struct subchannel *sch)
 {
@@ -117,7 +137,7 @@ css_register_subchannel(struct subchannel *sch)
        sch->dev.release = &css_subchannel_release;
        
        /* make it known to the system */
-       ret = device_register(&sch->dev);
+       ret = css_sch_device_register(sch);
        if (ret)
                printk (KERN_WARNING "%s: could not register %s\n",
                        __func__, sch->dev.bus_id);
@@ -248,7 +268,7 @@ css_evaluate_subchannel(struct subchannel_id schid, int slow)
                 * The device will be killed automatically.
                 */
                cio_disable_subchannel(sch);
-               device_unregister(&sch->dev);
+               css_sch_device_unregister(sch);
                /* Reset intparm to zeroes. */
                sch->schib.pmcw.intparm = 0;
                cio_modify(sch);
@@ -262,7 +282,7 @@ css_evaluate_subchannel(struct subchannel_id schid, int slow)
                 * away in any case.
                 */
                if (!disc) {
-                       device_unregister(&sch->dev);
+                       css_sch_device_unregister(sch);
                        /* Reset intparm to zeroes. */
                        sch->schib.pmcw.intparm = 0;
                        cio_modify(sch);
@@ -339,6 +359,67 @@ typedef void (*workfunc)(void *);
 DECLARE_WORK(slow_path_work, (workfunc)css_trigger_slow_path, NULL);
 struct workqueue_struct *slow_path_wq;
 
+/* Reprobe subchannel if unregistered. */
+static int reprobe_subchannel(struct subchannel_id schid, void *data)
+{
+       struct subchannel *sch;
+       int ret;
+
+       CIO_DEBUG(KERN_INFO, 6, "cio: reprobe 0.%x.%04x\n",
+                 schid.ssid, schid.sch_no);
+       if (need_reprobe)
+               return -EAGAIN;
+
+       sch = get_subchannel_by_schid(schid);
+       if (sch) {
+               /* Already known. */
+               put_device(&sch->dev);
+               return 0;
+       }
+
+       ret = css_probe_device(schid);
+       switch (ret) {
+       case 0:
+               break;
+       case -ENXIO:
+       case -ENOMEM:
+               /* These should abort looping */
+               break;
+       default:
+               ret = 0;
+       }
+
+       return ret;
+}
+
+/* Work function used to reprobe all unregistered subchannels. */
+static void reprobe_all(void *data)
+{
+       int ret;
+
+       CIO_MSG_EVENT(2, "reprobe start\n");
+
+       need_reprobe = 0;
+       /* Make sure initial subchannel scan is done. */
+       wait_event(ccw_device_init_wq,
+                  atomic_read(&ccw_device_init_count) == 0);
+       ret = for_each_subchannel(reprobe_subchannel, NULL);
+
+       CIO_MSG_EVENT(2, "reprobe done (rc=%d, need_reprobe=%d)\n", ret,
+                     need_reprobe);
+}
+
+DECLARE_WORK(css_reprobe_work, reprobe_all, NULL);
+
+/* Schedule reprobing of all unregistered subchannels. */
+void css_schedule_reprobe(void)
+{
+       need_reprobe = 1;
+       queue_work(ccw_device_work, &css_reprobe_work);
+}
+
+EXPORT_SYMBOL_GPL(css_schedule_reprobe);
+
 /*
  * Rescan for new devices. FIXME: This is slow.
  * This function is called when we have lost CRWs due to overflows and we have
@@ -542,9 +623,13 @@ init_channel_subsystem (void)
                ret = device_register(&css[i]->device);
                if (ret)
                        goto out_free;
-               if (css_characteristics_avail && css_chsc_characteristics.secm)
-                       device_create_file(&css[i]->device,
-                                          &dev_attr_cm_enable);
+               if (css_characteristics_avail &&
+                   css_chsc_characteristics.secm) {
+                       ret = device_create_file(&css[i]->device,
+                                                &dev_attr_cm_enable);
+                       if (ret)
+                               goto out_device;
+               }
        }
        css_init_done = 1;
 
@@ -552,6 +637,8 @@ init_channel_subsystem (void)
 
        for_each_subchannel(__init_channel_subsystem, NULL);
        return 0;
+out_device:
+       device_unregister(&css[i]->device);
 out_free:
        kfree(css[i]);
 out_unregister: