]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/s390/cio/css.c
Driver core: change add_uevent_var to use a struct
[linux-2.6-omap-h63xx.git] / drivers / s390 / cio / css.c
index fe0ace7aece8e65edeaac816637ab135af8ef6f7..5635e656c1a369b92bd0027c0e8beca261baed3c 100644 (file)
@@ -20,8 +20,9 @@
 #include "ioasm.h"
 #include "chsc.h"
 #include "device.h"
+#include "idset.h"
+#include "chp.h"
 
-int need_rescan = 0;
 int css_init_done = 0;
 static int need_reprobe = 0;
 static int max_ssid = 0;
@@ -78,6 +79,7 @@ css_alloc_subchannel(struct subchannel_id schid)
        sch->schib.pmcw.intparm = (__u32)(unsigned long)sch;
        ret = cio_modify(sch);
        if (ret) {
+               kfree(sch->lock);
                kfree(sch);
                return ERR_PTR(ret);
        }
@@ -108,7 +110,7 @@ css_subchannel_release(struct device *dev)
        }
 }
 
-int css_sch_device_register(struct subchannel *sch)
+static int css_sch_device_register(struct subchannel *sch)
 {
        int ret;
 
@@ -125,8 +127,52 @@ void css_sch_device_unregister(struct subchannel *sch)
        mutex_unlock(&sch->reg_mutex);
 }
 
-static int
-css_register_subchannel(struct subchannel *sch)
+static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw)
+{
+       int i;
+       int mask;
+
+       memset(ssd, 0, sizeof(struct chsc_ssd_info));
+       ssd->path_mask = pmcw->pim;
+       for (i = 0; i < 8; i++) {
+               mask = 0x80 >> i;
+               if (pmcw->pim & mask) {
+                       chp_id_init(&ssd->chpid[i]);
+                       ssd->chpid[i].id = pmcw->chpid[i];
+               }
+       }
+}
+
+static void ssd_register_chpids(struct chsc_ssd_info *ssd)
+{
+       int i;
+       int mask;
+
+       for (i = 0; i < 8; i++) {
+               mask = 0x80 >> i;
+               if (ssd->path_mask & mask)
+                       if (!chp_is_registered(ssd->chpid[i]))
+                               chp_new(ssd->chpid[i]);
+       }
+}
+
+void css_update_ssd_info(struct subchannel *sch)
+{
+       int ret;
+
+       if (cio_is_console(sch->schid)) {
+               /* Console is initialized too early for functions requiring
+                * memory allocation. */
+               ssd_from_pmcw(&sch->ssd_info, &sch->schib.pmcw);
+       } else {
+               ret = chsc_get_ssd_info(sch->schid, &sch->ssd_info);
+               if (ret)
+                       ssd_from_pmcw(&sch->ssd_info, &sch->schib.pmcw);
+               ssd_register_chpids(&sch->ssd_info);
+       }
+}
+
+static int css_register_subchannel(struct subchannel *sch)
 {
        int ret;
 
@@ -135,21 +181,18 @@ css_register_subchannel(struct subchannel *sch)
        sch->dev.bus = &css_bus_type;
        sch->dev.release = &css_subchannel_release;
        sch->dev.groups = subch_attr_groups;
-
-       css_get_ssd_info(sch);
-
+       css_update_ssd_info(sch);
        /* make it known to the system */
        ret = css_sch_device_register(sch);
        if (ret) {
-               printk (KERN_WARNING "%s: could not register %s\n",
-                       __func__, sch->dev.bus_id);
+               CIO_MSG_EVENT(0, "Could not register sch 0.%x.%04x: %d\n",
+                             sch->schid.ssid, sch->schid.sch_no, ret);
                return ret;
        }
        return ret;
 }
 
-int
-css_probe_device(struct subchannel_id schid)
+static int css_probe_device(struct subchannel_id schid)
 {
        int ret;
        struct subchannel *sch;
@@ -306,7 +349,7 @@ static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow)
        return css_probe_device(schid);
 }
 
-static int css_evaluate_subchannel(struct subchannel_id schid, int slow)
+static void css_evaluate_subchannel(struct subchannel_id schid, int slow)
 {
        struct subchannel *sch;
        int ret;
@@ -317,61 +360,71 @@ static int css_evaluate_subchannel(struct subchannel_id schid, int slow)
                put_device(&sch->dev);
        } else
                ret = css_evaluate_new_subchannel(schid, slow);
-
-       return ret;
+       if (ret == -EAGAIN)
+               css_schedule_eval(schid);
 }
 
-static int
-css_rescan_devices(struct subchannel_id schid, void *data)
+static struct idset *slow_subchannel_set;
+static spinlock_t slow_subchannel_lock;
+
+static int __init slow_subchannel_init(void)
 {
-       return css_evaluate_subchannel(schid, 1);
+       spin_lock_init(&slow_subchannel_lock);
+       slow_subchannel_set = idset_sch_new();
+       if (!slow_subchannel_set) {
+               CIO_MSG_EVENT(0, "could not allocate slow subchannel set\n");
+               return -ENOMEM;
+       }
+       return 0;
 }
 
-struct slow_subchannel {
-       struct list_head slow_list;
+static void css_slow_path_func(struct work_struct *unused)
+{
        struct subchannel_id schid;
-};
-
-static LIST_HEAD(slow_subchannels_head);
-static DEFINE_SPINLOCK(slow_subchannel_lock);
 
-static void
-css_trigger_slow_path(struct work_struct *unused)
-{
        CIO_TRACE_EVENT(4, "slowpath");
-
-       if (need_rescan) {
-               need_rescan = 0;
-               for_each_subchannel(css_rescan_devices, NULL);
-               return;
-       }
-
        spin_lock_irq(&slow_subchannel_lock);
-       while (!list_empty(&slow_subchannels_head)) {
-               struct slow_subchannel *slow_sch =
-                       list_entry(slow_subchannels_head.next,
-                                  struct slow_subchannel, slow_list);
-
-               list_del_init(slow_subchannels_head.next);
+       init_subchannel_id(&schid);
+       while (idset_sch_get_first(slow_subchannel_set, &schid)) {
+               idset_sch_del(slow_subchannel_set, schid);
                spin_unlock_irq(&slow_subchannel_lock);
-               css_evaluate_subchannel(slow_sch->schid, 1);
+               css_evaluate_subchannel(schid, 1);
                spin_lock_irq(&slow_subchannel_lock);
-               kfree(slow_sch);
        }
        spin_unlock_irq(&slow_subchannel_lock);
 }
 
-DECLARE_WORK(slow_path_work, css_trigger_slow_path);
+static DECLARE_WORK(slow_path_work, css_slow_path_func);
 struct workqueue_struct *slow_path_wq;
 
+void css_schedule_eval(struct subchannel_id schid)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&slow_subchannel_lock, flags);
+       idset_sch_add(slow_subchannel_set, schid);
+       queue_work(slow_path_wq, &slow_path_work);
+       spin_unlock_irqrestore(&slow_subchannel_lock, flags);
+}
+
+void css_schedule_eval_all(void)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&slow_subchannel_lock, flags);
+       idset_fill(slow_subchannel_set);
+       queue_work(slow_path_wq, &slow_path_work);
+       spin_unlock_irqrestore(&slow_subchannel_lock, flags);
+}
+
 /* 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);
+       CIO_MSG_EVENT(6, "cio: reprobe 0.%x.%04x\n",
+                     schid.ssid, schid.sch_no);
        if (need_reprobe)
                return -EAGAIN;
 
@@ -425,34 +478,15 @@ void css_schedule_reprobe(void)
 
 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
- * to do subchannel housekeeping.
- */
-void
-css_reiterate_subchannels(void)
-{
-       css_clear_subchannel_slow_list();
-       need_rescan = 1;
-}
-
 /*
  * Called from the machine check handler for subchannel report words.
  */
-int
-css_process_crw(int rsid1, int rsid2)
+void css_process_crw(int rsid1, int rsid2)
 {
-       int ret;
        struct subchannel_id mchk_schid;
 
        CIO_CRW_EVENT(2, "source is subchannel %04X, subsystem id %x\n",
                      rsid1, rsid2);
-
-       if (need_rescan)
-               /* We need to iterate all subchannels anyway. */
-               return -EAGAIN;
-
        init_subchannel_id(&mchk_schid);
        mchk_schid.sch_no = rsid1;
        if (rsid2 != 0)
@@ -463,14 +497,7 @@ css_process_crw(int rsid1, int rsid2)
         * use stsch() to find out if the subchannel in question has come
         * or gone.
         */
-       ret = css_evaluate_subchannel(mchk_schid, 0);
-       if (ret == -EAGAIN) {
-               if (css_enqueue_subchannel_slow(mchk_schid)) {
-                       css_clear_subchannel_slow_list();
-                       need_rescan = 1;
-               }
-       }
-       return ret;
+       css_evaluate_subchannel(mchk_schid, 0);
 }
 
 static int __init
@@ -613,9 +640,20 @@ init_channel_subsystem (void)
 {
        int ret, i;
 
-       if (chsc_determine_css_characteristics() == 0)
+       ret = chsc_determine_css_characteristics();
+       if (ret == -ENOMEM)
+               goto out; /* No need to continue. */
+       if (ret == 0)
                css_characteristics_avail = 1;
 
+       ret = chsc_alloc_sei_area();
+       if (ret)
+               goto out;
+
+       ret = slow_subchannel_init();
+       if (ret)
+               goto out;
+
        if ((ret = bus_register(&css_bus_type)))
                goto out;
 
@@ -681,6 +719,10 @@ out_unregister:
 out_bus:
        bus_unregister(&css_bus_type);
 out:
+       chsc_free_sei_area();
+       kfree(slow_subchannel_set);
+       printk(KERN_WARNING"cio: failed to initialize css driver (%d)!\n",
+              ret);
        return ret;
 }
 
@@ -745,47 +787,6 @@ struct bus_type css_bus_type = {
 
 subsys_initcall(init_channel_subsystem);
 
-int
-css_enqueue_subchannel_slow(struct subchannel_id schid)
-{
-       struct slow_subchannel *new_slow_sch;
-       unsigned long flags;
-
-       new_slow_sch = kzalloc(sizeof(struct slow_subchannel), GFP_ATOMIC);
-       if (!new_slow_sch)
-               return -ENOMEM;
-       new_slow_sch->schid = schid;
-       spin_lock_irqsave(&slow_subchannel_lock, flags);
-       list_add_tail(&new_slow_sch->slow_list, &slow_subchannels_head);
-       spin_unlock_irqrestore(&slow_subchannel_lock, flags);
-       return 0;
-}
-
-void
-css_clear_subchannel_slow_list(void)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(&slow_subchannel_lock, flags);
-       while (!list_empty(&slow_subchannels_head)) {
-               struct slow_subchannel *slow_sch =
-                       list_entry(slow_subchannels_head.next,
-                                  struct slow_subchannel, slow_list);
-
-               list_del_init(slow_subchannels_head.next);
-               kfree(slow_sch);
-       }
-       spin_unlock_irqrestore(&slow_subchannel_lock, flags);
-}
-
-
-
-int
-css_slow_subchannels_exist(void)
-{
-       return (!list_empty(&slow_subchannels_head));
-}
-
 MODULE_LICENSE("GPL");
 EXPORT_SYMBOL(css_bus_type);
 EXPORT_SYMBOL_GPL(css_characteristics_avail);