diff options
author | Sebastian Ott <sebott@linux.vnet.ibm.com> | 2009-09-23 00:58:38 +0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2009-09-23 00:58:42 +0400 |
commit | 703e5c9993639284bc0a8929b6de362424df7019 (patch) | |
tree | 13d6348ca6e8a00aefa302c6a776bf50180a6ac7 | |
parent | b0a285d31bd475fdd4312e457288be558b705e55 (diff) | |
download | linux-703e5c9993639284bc0a8929b6de362424df7019.tar.xz |
[S390] cio: introduce consistent subchannel scanning
Previously, there were multiple subchannel scanning mechanisms
which could potentially conflict with each other. Fix this problem
by moving blacklist and ccw driver triggered scanning to the
existing evaluation method.
Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r-- | drivers/s390/cio/css.c | 146 | ||||
-rw-r--r-- | drivers/s390/cio/idset.c | 10 | ||||
-rw-r--r-- | drivers/s390/cio/idset.h | 1 |
3 files changed, 51 insertions, 106 deletions
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 5e217bbf8797..91c25706fa83 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -31,7 +31,6 @@ #include "chp.h" int css_init_done = 0; -static int need_reprobe = 0; int max_ssid; struct channel_subsystem *channel_subsystems[__MAX_CSSID + 1]; @@ -315,12 +314,18 @@ int css_probe_device(struct subchannel_id schid) int ret; struct subchannel *sch; - sch = css_alloc_subchannel(schid); - if (IS_ERR(sch)) - return PTR_ERR(sch); + if (cio_is_console(schid)) + sch = cio_get_console_subchannel(); + else { + sch = css_alloc_subchannel(schid); + if (IS_ERR(sch)) + return PTR_ERR(sch); + } ret = css_register_subchannel(sch); - if (ret) - put_device(&sch->dev); + if (ret) { + if (!cio_is_console(schid)) + put_device(&sch->dev); + } return ret; } @@ -510,76 +515,48 @@ void css_schedule_eval_all(void) spin_unlock_irqrestore(&slow_subchannel_lock, flags); } -void css_wait_for_slow_path(void) +static int __unset_registered(struct device *dev, void *data) { - flush_workqueue(slow_path_wq); -} - -/* Reprobe subchannel if unregistered. */ -static int reprobe_subchannel(struct subchannel_id schid, void *data) -{ - int ret; - - CIO_MSG_EVENT(6, "cio: reprobe 0.%x.%04x\n", - schid.ssid, schid.sch_no); - if (need_reprobe) - return -EAGAIN; - - ret = css_probe_device(schid); - switch (ret) { - case 0: - break; - case -ENXIO: - case -ENOMEM: - case -EIO: - /* These should abort looping */ - break; - default: - ret = 0; - } - - return ret; -} + struct idset *set = data; + struct subchannel *sch = to_subchannel(dev); -static void reprobe_after_idle(struct work_struct *unused) -{ - /* Make sure initial subchannel scan is done. */ - wait_event(ccw_device_init_wq, - atomic_read(&ccw_device_init_count) == 0); - if (need_reprobe) - css_schedule_reprobe(); + idset_sch_del(set, sch->schid); + return 0; } -static DECLARE_WORK(reprobe_idle_work, reprobe_after_idle); - -/* Work function used to reprobe all unregistered subchannels. */ -static void reprobe_all(struct work_struct *unused) +void css_schedule_eval_all_unreg(void) { - int ret; - - CIO_MSG_EVENT(4, "reprobe start\n"); + unsigned long flags; + struct idset *unreg_set; - /* Make sure initial subchannel scan is done. */ - if (atomic_read(&ccw_device_init_count) != 0) { - queue_work(ccw_device_work, &reprobe_idle_work); + /* Find unregistered subchannels. */ + unreg_set = idset_sch_new(); + if (!unreg_set) { + /* Fallback. */ + css_schedule_eval_all(); return; } - need_reprobe = 0; - ret = for_each_subchannel_staged(NULL, reprobe_subchannel, NULL); - - CIO_MSG_EVENT(4, "reprobe done (rc=%d, need_reprobe=%d)\n", ret, - need_reprobe); + idset_fill(unreg_set); + bus_for_each_dev(&css_bus_type, NULL, unreg_set, __unset_registered); + /* Apply to slow_subchannel_set. */ + spin_lock_irqsave(&slow_subchannel_lock, flags); + idset_add_set(slow_subchannel_set, unreg_set); + atomic_set(&css_eval_scheduled, 1); + queue_work(slow_path_wq, &slow_path_work); + spin_unlock_irqrestore(&slow_subchannel_lock, flags); + idset_free(unreg_set); } -static DECLARE_WORK(css_reprobe_work, reprobe_all); +void css_wait_for_slow_path(void) +{ + flush_workqueue(slow_path_wq); +} /* Schedule reprobing of all unregistered subchannels. */ void css_schedule_reprobe(void) { - need_reprobe = 1; - queue_work(slow_path_wq, &css_reprobe_work); + css_schedule_eval_all_unreg(); } - EXPORT_SYMBOL_GPL(css_schedule_reprobe); /* @@ -615,48 +592,6 @@ static void css_process_crw(struct crw *crw0, struct crw *crw1, int overflow) css_evaluate_subchannel(mchk_schid, 0); } -static int __init setup_subchannel(struct subchannel_id schid, void *data) -{ - struct subchannel *sch; - int ret; - - if (cio_is_console(schid)) - sch = cio_get_console_subchannel(); - else { - sch = css_alloc_subchannel(schid); - if (IS_ERR(sch)) - ret = PTR_ERR(sch); - else - ret = 0; - switch (ret) { - case 0: - break; - case -ENOMEM: - panic("Out of memory in init_channel_subsystem\n"); - /* -ENXIO: no more subchannels. */ - case -ENXIO: - return ret; - /* -EIO: this subchannel set not supported. */ - case -EIO: - return ret; - default: - return 0; - } - } - /* - * We register ALL valid subchannels in ioinfo, even those - * that have been present before init_channel_subsystem. - * These subchannels can't have been registered yet (kmalloc - * not working) so we do it now. This is true e.g. for the - * console subchannel. - */ - if (css_register_subchannel(sch)) { - if (!cio_is_console(schid)) - put_device(&sch->dev); - } - return 0; -} - static void __init css_generate_pgid(struct channel_subsystem *css, u32 tod_high) { @@ -1028,11 +963,10 @@ static int css_settle(struct device_driver *drv, void *unused) */ static int __init channel_subsystem_init_sync(void) { - /* Allocate and register subchannels. */ - for_each_subchannel(setup_subchannel, NULL); + /* Start initial subchannel evaluation. */ + css_schedule_eval_all(); /* Wait for the evaluation of subchannels to finish. */ wait_event(css_eval_wq, atomic_read(&css_eval_scheduled) == 0); - /* Wait for the subchannel type specific initialization to finish */ return bus_for_each_drv(&css_bus_type, NULL, NULL, css_settle); } diff --git a/drivers/s390/cio/idset.c b/drivers/s390/cio/idset.c index 5c88faf5b897..4d10981c7cc1 100644 --- a/drivers/s390/cio/idset.c +++ b/drivers/s390/cio/idset.c @@ -120,3 +120,13 @@ int idset_is_empty(struct idset *set) return 1; return 0; } + +void idset_add_set(struct idset *to, struct idset *from) +{ + unsigned long i, len; + + len = min(__BITOPS_WORDS(to->num_ssid * to->num_id), + __BITOPS_WORDS(from->num_ssid * from->num_id)); + for (i = 0; i < len ; i++) + to->bitmap[i] |= from->bitmap[i]; +} diff --git a/drivers/s390/cio/idset.h b/drivers/s390/cio/idset.h index ca1398aadc7e..7543da4529f9 100644 --- a/drivers/s390/cio/idset.h +++ b/drivers/s390/cio/idset.h @@ -22,5 +22,6 @@ void idset_sch_del(struct idset *set, struct subchannel_id id); int idset_sch_contains(struct idset *set, struct subchannel_id id); int idset_sch_get_first(struct idset *set, struct subchannel_id *id); int idset_is_empty(struct idset *set); +void idset_add_set(struct idset *to, struct idset *from); #endif /* S390_IDSET_H */ |