Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=e82a1567e4b22eb035da2499d20ddd573c9acf75
Commit:     e82a1567e4b22eb035da2499d20ddd573c9acf75
Parent:     4beee64685e116b01c47655daf6d88df87e053c8
Author:     Peter Oberparleiter <[EMAIL PROTECTED]>
AuthorDate: Sat Jan 26 14:10:48 2008 +0100
Committer:  Martin Schwidefsky <[EMAIL PROTECTED]>
CommitDate: Sat Jan 26 14:11:03 2008 +0100

    [S390] cio: reduce cpu utilization during device scan
    
    Minimize calls to cpu intensive function get_subchannel_by_schid()
    by introducing function for_each_subchannel_staged() which
    temporarily caches the information about registered subchannels
    in a bitmap.
    
    Signed-off-by: Peter Oberparleiter <[EMAIL PROTECTED]>
    Signed-off-by: Martin Schwidefsky <[EMAIL PROTECTED]>
---
 drivers/s390/cio/chsc.c |   98 ++++++++++++--------------------------
 drivers/s390/cio/css.c  |  120 ++++++++++++++++++++++++++++++++++++++++-------
 drivers/s390/cio/css.h  |    3 +
 3 files changed, 137 insertions(+), 84 deletions(-)

diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 12a344c..93e6f74 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -136,17 +136,13 @@ static void terminate_internal_io(struct subchannel *sch)
                sch->driver->termination(sch);
 }
 
-static int
-s390_subchannel_remove_chpid(struct device *dev, void *data)
+static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data)
 {
        int j;
        int mask;
-       struct subchannel *sch;
-       struct chp_id *chpid;
+       struct chp_id *chpid = data;
        struct schib schib;
 
-       sch = to_subchannel(dev);
-       chpid = data;
        for (j = 0; j < 8; j++) {
                mask = 0x80 >> j;
                if ((sch->schib.pmcw.pim & mask) &&
@@ -202,12 +198,10 @@ void chsc_chp_offline(struct chp_id chpid)
 
        if (chp_get_status(chpid) <= 0)
                return;
-       bus_for_each_dev(&css_bus_type, NULL, &chpid,
-                        s390_subchannel_remove_chpid);
+       for_each_subchannel_staged(s390_subchannel_remove_chpid, NULL, &chpid);
 }
 
-static int
-s390_process_res_acc_new_sch(struct subchannel_id schid)
+static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data)
 {
        struct schib schib;
        /*
@@ -253,18 +247,10 @@ static int get_res_chpid_mask(struct chsc_ssd_info *ssd,
        return 0;
 }
 
-static int
-__s390_process_res_acc(struct subchannel_id schid, void *data)
+static int __s390_process_res_acc(struct subchannel *sch, void *data)
 {
        int chp_mask, old_lpm;
-       struct res_acc_data *res_data;
-       struct subchannel *sch;
-
-       res_data = data;
-       sch = get_subchannel_by_schid(schid);
-       if (!sch)
-               /* Check if a subchannel is newly available. */
-               return s390_process_res_acc_new_sch(schid);
+       struct res_acc_data *res_data = data;
 
        spin_lock_irq(sch->lock);
        chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data);
@@ -283,7 +269,7 @@ __s390_process_res_acc(struct subchannel_id schid, void 
*data)
                sch->driver->verify(sch);
 out:
        spin_unlock_irq(sch->lock);
-       put_device(&sch->dev);
+
        return 0;
 }
 
@@ -306,7 +292,8 @@ static void s390_process_res_acc (struct res_acc_data 
*res_data)
         * The more information we have (info), the less scanning
         * will we have to do.
         */
-       for_each_subchannel(__s390_process_res_acc, res_data);
+       for_each_subchannel_staged(__s390_process_res_acc,
+                                  s390_process_res_acc_new_sch, res_data);
 }
 
 static int
@@ -500,8 +487,7 @@ void chsc_process_crw(void)
        } while (sei_area->flags & 0x80);
 }
 
-static int
-__chp_add_new_sch(struct subchannel_id schid)
+static int __chp_add_new_sch(struct subchannel_id schid, void *data)
 {
        struct schib schib;
 
@@ -515,35 +501,27 @@ __chp_add_new_sch(struct subchannel_id schid)
 }
 
 
-static int
-__chp_add(struct subchannel_id schid, void *data)
+static int __chp_add(struct subchannel *sch, void *data)
 {
        int i, mask;
-       struct chp_id *chpid;
-       struct subchannel *sch;
-
-       chpid = data;
-       sch = get_subchannel_by_schid(schid);
-       if (!sch)
-               /* Check if the subchannel is now available. */
-               return __chp_add_new_sch(schid);
+       struct chp_id *chpid = data;
+
        spin_lock_irq(sch->lock);
        for (i=0; i<8; i++) {
                mask = 0x80 >> i;
                if ((sch->schib.pmcw.pim & mask) &&
-                   (sch->schib.pmcw.chpid[i] == chpid->id)) {
-                       if (stsch(sch->schid, &sch->schib) != 0) {
-                               /* Endgame. */
-                               spin_unlock_irq(sch->lock);
-                               return -ENXIO;
-                       }
+                   (sch->schib.pmcw.chpid[i] == chpid->id))
                        break;
-               }
        }
        if (i==8) {
                spin_unlock_irq(sch->lock);
                return 0;
        }
+       if (stsch(sch->schid, &sch->schib)) {
+               spin_unlock_irq(sch->lock);
+               css_schedule_eval(sch->schid);
+               return 0;
+       }
        sch->lpm = ((sch->schib.pmcw.pim &
                     sch->schib.pmcw.pam &
                     sch->schib.pmcw.pom)
@@ -553,7 +531,7 @@ __chp_add(struct subchannel_id schid, void *data)
                sch->driver->verify(sch);
 
        spin_unlock_irq(sch->lock);
-       put_device(&sch->dev);
+
        return 0;
 }
 
@@ -565,7 +543,8 @@ void chsc_chp_online(struct chp_id chpid)
        CIO_TRACE_EVENT(2, dbf_txt);
 
        if (chp_get_status(chpid) != 0)
-               for_each_subchannel(__chp_add, &chpid);
+               for_each_subchannel_staged(__chp_add, __chp_add_new_sch,
+                                          &chpid);
 }
 
 static void __s390_subchannel_vary_chpid(struct subchannel *sch,
@@ -616,25 +595,17 @@ static void __s390_subchannel_vary_chpid(struct 
subchannel *sch,
        spin_unlock_irqrestore(sch->lock, flags);
 }
 
-static int s390_subchannel_vary_chpid_off(struct device *dev, void *data)
+static int s390_subchannel_vary_chpid_off(struct subchannel *sch, void *data)
 {
-       struct subchannel *sch;
-       struct chp_id *chpid;
-
-       sch = to_subchannel(dev);
-       chpid = data;
+       struct chp_id *chpid = data;
 
        __s390_subchannel_vary_chpid(sch, *chpid, 0);
        return 0;
 }
 
-static int s390_subchannel_vary_chpid_on(struct device *dev, void *data)
+static int s390_subchannel_vary_chpid_on(struct subchannel *sch, void *data)
 {
-       struct subchannel *sch;
-       struct chp_id *chpid;
-
-       sch = to_subchannel(dev);
-       chpid = data;
+       struct chp_id *chpid = data;
 
        __s390_subchannel_vary_chpid(sch, *chpid, 1);
        return 0;
@@ -644,13 +615,7 @@ static int
 __s390_vary_chpid_on(struct subchannel_id schid, void *data)
 {
        struct schib schib;
-       struct subchannel *sch;
 
-       sch = get_subchannel_by_schid(schid);
-       if (sch) {
-               put_device(&sch->dev);
-               return 0;
-       }
        if (stsch_err(schid, &schib))
                /* We're through */
                return -ENXIO;
@@ -670,12 +635,13 @@ int chsc_chp_vary(struct chp_id chpid, int on)
         * Redo PathVerification on the devices the chpid connects to
         */
 
-       bus_for_each_dev(&css_bus_type, NULL, &chpid, on ?
-                        s390_subchannel_vary_chpid_on :
-                        s390_subchannel_vary_chpid_off);
        if (on)
-               /* Scan for new devices on varied on path. */
-               for_each_subchannel(__s390_vary_chpid_on, NULL);
+               for_each_subchannel_staged(s390_subchannel_vary_chpid_on,
+                                          __s390_vary_chpid_on, &chpid);
+       else
+               for_each_subchannel_staged(s390_subchannel_vary_chpid_off,
+                                          NULL, &chpid);
+
        return 0;
 }
 
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 69d56c7..3b45bbe 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -51,6 +51,62 @@ for_each_subchannel(int(*fn)(struct subchannel_id, void *), 
void *data)
        return ret;
 }
 
+struct cb_data {
+       void *data;
+       struct idset *set;
+       int (*fn_known_sch)(struct subchannel *, void *);
+       int (*fn_unknown_sch)(struct subchannel_id, void *);
+};
+
+static int call_fn_known_sch(struct device *dev, void *data)
+{
+       struct subchannel *sch = to_subchannel(dev);
+       struct cb_data *cb = data;
+       int rc = 0;
+
+       idset_sch_del(cb->set, sch->schid);
+       if (cb->fn_known_sch)
+               rc = cb->fn_known_sch(sch, cb->data);
+       return rc;
+}
+
+static int call_fn_unknown_sch(struct subchannel_id schid, void *data)
+{
+       struct cb_data *cb = data;
+       int rc = 0;
+
+       if (idset_sch_contains(cb->set, schid))
+               rc = cb->fn_unknown_sch(schid, cb->data);
+       return rc;
+}
+
+int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *),
+                              int (*fn_unknown)(struct subchannel_id,
+                              void *), void *data)
+{
+       struct cb_data cb;
+       int rc;
+
+       cb.set = idset_sch_new();
+       if (!cb.set)
+               return -ENOMEM;
+       idset_fill(cb.set);
+       cb.data = data;
+       cb.fn_known_sch = fn_known;
+       cb.fn_unknown_sch = fn_unknown;
+       /* Process registered subchannels. */
+       rc = bus_for_each_dev(&css_bus_type, NULL, &cb, call_fn_known_sch);
+       if (rc)
+               goto out;
+       /* Process unregistered subchannels. */
+       if (fn_unknown)
+               rc = for_each_subchannel(call_fn_unknown_sch, &cb);
+out:
+       idset_free(cb.set);
+
+       return rc;
+}
+
 static struct subchannel *
 css_alloc_subchannel(struct subchannel_id schid)
 {
@@ -402,20 +458,56 @@ static int __init slow_subchannel_init(void)
        return 0;
 }
 
-static void css_slow_path_func(struct work_struct *unused)
+static int slow_eval_known_fn(struct subchannel *sch, void *data)
 {
-       struct subchannel_id schid;
+       int eval;
+       int rc;
 
-       CIO_TRACE_EVENT(4, "slowpath");
        spin_lock_irq(&slow_subchannel_lock);
-       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(schid, 1);
-               spin_lock_irq(&slow_subchannel_lock);
+       eval = idset_sch_contains(slow_subchannel_set, sch->schid);
+       idset_sch_del(slow_subchannel_set, sch->schid);
+       spin_unlock_irq(&slow_subchannel_lock);
+       if (eval) {
+               rc = css_evaluate_known_subchannel(sch, 1);
+               if (rc == -EAGAIN)
+                       css_schedule_eval(sch->schid);
        }
+       return 0;
+}
+
+static int slow_eval_unknown_fn(struct subchannel_id schid, void *data)
+{
+       int eval;
+       int rc = 0;
+
+       spin_lock_irq(&slow_subchannel_lock);
+       eval = idset_sch_contains(slow_subchannel_set, schid);
+       idset_sch_del(slow_subchannel_set, schid);
        spin_unlock_irq(&slow_subchannel_lock);
+       if (eval) {
+               rc = css_evaluate_new_subchannel(schid, 1);
+               switch (rc) {
+               case -EAGAIN:
+                       css_schedule_eval(schid);
+                       rc = 0;
+                       break;
+               case -ENXIO:
+               case -ENOMEM:
+               case -EIO:
+                       /* These should abort looping */
+                       break;
+               default:
+                       rc = 0;
+               }
+       }
+       return rc;
+}
+
+static void css_slow_path_func(struct work_struct *unused)
+{
+       CIO_TRACE_EVENT(4, "slowpath");
+       for_each_subchannel_staged(slow_eval_known_fn, slow_eval_unknown_fn,
+                                  NULL);
 }
 
 static DECLARE_WORK(slow_path_work, css_slow_path_func);
@@ -444,7 +536,6 @@ void css_schedule_eval_all(void)
 /* Reprobe subchannel if unregistered. */
 static int reprobe_subchannel(struct subchannel_id schid, void *data)
 {
-       struct subchannel *sch;
        int ret;
 
        CIO_MSG_EVENT(6, "cio: reprobe 0.%x.%04x\n",
@@ -452,13 +543,6 @@ static int reprobe_subchannel(struct subchannel_id schid, 
void *data)
        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:
@@ -486,7 +570,7 @@ static void reprobe_all(struct work_struct *unused)
        /* 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);
+       ret = for_each_subchannel_staged(NULL, reprobe_subchannel, NULL);
 
        CIO_MSG_EVENT(2, "reprobe done (rc=%d, need_reprobe=%d)\n", ret,
                      need_reprobe);
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h
index 40598b8..b705545 100644
--- a/drivers/s390/cio/css.h
+++ b/drivers/s390/cio/css.h
@@ -91,6 +91,9 @@ extern void css_driver_unregister(struct css_driver *);
 extern void css_sch_device_unregister(struct subchannel *);
 extern struct subchannel * get_subchannel_by_schid(struct subchannel_id);
 extern int css_init_done;
+int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *),
+                              int (*fn_unknown)(struct subchannel_id,
+                              void *), void *data);
 extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *);
 extern void css_process_crw(int, int);
 extern void css_reiterate_subchannels(void);
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to