From e6b6e10ac1de116fc6d2288f185393014851cccf Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Fri, 27 Apr 2007 16:01:28 +0200 Subject: [S390] cio: Introduce separate files for channel-path related code. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens --- drivers/s390/cio/chp.h | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 drivers/s390/cio/chp.h (limited to 'drivers/s390/cio/chp.h') diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h new file mode 100644 index 000000000000..ac2b1a9c3bc2 --- /dev/null +++ b/drivers/s390/cio/chp.h @@ -0,0 +1,37 @@ +/* + * drivers/s390/cio/chp.h + * + * Copyright IBM Corp. 2007 + * Author(s): Peter Oberparleiter + */ + +#ifndef S390_CHP_H +#define S390_CHP_H S390_CHP_H + +#include +#include + +#include "chpid.h" +#include "chsc.h" + +struct channel_path { + struct chp_id chpid; + int state; + struct channel_path_desc desc; + /* Channel-measurement related stuff: */ + int cmg; + int shared; + void *cmg_chars; + struct device dev; +}; + +int chp_get_status(struct chp_id chpid); +u8 chp_get_sch_opm(struct subchannel *sch); +int chp_is_registered(struct chp_id chpid); +void *chp_get_chp_desc(struct chp_id chpid); +int chp_process_crw(int id, int available); +void chp_remove_cmg_attr(struct channel_path *chp); +int chp_add_cmg_attr(struct channel_path *chp); +int chp_new(struct chp_id chpid); + +#endif /* S390_CHP_H */ -- cgit v1.2.3 From e5854a5839fa426a7873f038080f63587de5f1f1 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Fri, 27 Apr 2007 16:01:31 +0200 Subject: [S390] cio: Channel-path configure function. Add a new attribute to the channel-path sysfs directory through which channel-path configure operations can be triggered. Also listen for hardware events requesting channel-path configure operations and process them accordingly. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens --- drivers/s390/char/Makefile | 2 +- drivers/s390/char/sclp_chp.c | 196 +++++++++++++++++++++++++++++++++ drivers/s390/cio/chp.c | 250 +++++++++++++++++++++++++++++++++++++++++- drivers/s390/cio/chp.h | 20 +++- drivers/s390/cio/chpid.h | 51 --------- drivers/s390/cio/chsc.c | 44 +++++++- drivers/s390/cio/chsc.h | 2 +- drivers/s390/cio/cio.c | 1 + drivers/s390/cio/cio.h | 1 + drivers/s390/cio/css.h | 4 +- drivers/s390/cio/device_fsm.c | 2 +- drivers/s390/cio/device_ops.c | 2 +- drivers/s390/cio/ioasm.h | 2 +- include/asm-s390/chpid.h | 53 +++++++++ include/asm-s390/cio.h | 1 + include/asm-s390/sclp.h | 12 ++ 16 files changed, 581 insertions(+), 62 deletions(-) create mode 100644 drivers/s390/char/sclp_chp.c delete mode 100644 drivers/s390/cio/chpid.h create mode 100644 include/asm-s390/chpid.h (limited to 'drivers/s390/cio/chp.h') diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index 293e667b50f2..5fd581c22db3 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -3,7 +3,7 @@ # obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \ - sclp_info.o + sclp_info.o sclp_chp.o obj-$(CONFIG_TN3270) += raw3270.o obj-$(CONFIG_TN3270_CONSOLE) += con3270.o diff --git a/drivers/s390/char/sclp_chp.c b/drivers/s390/char/sclp_chp.c new file mode 100644 index 000000000000..a66b914519b5 --- /dev/null +++ b/drivers/s390/char/sclp_chp.c @@ -0,0 +1,196 @@ +/* + * drivers/s390/char/sclp_chp.c + * + * Copyright IBM Corp. 2007 + * Author(s): Peter Oberparleiter + */ + +#include +#include +#include +#include +#include +#include + +#include "sclp.h" + +#define TAG "sclp_chp: " + +#define SCLP_CMDW_CONFIGURE_CHANNEL_PATH 0x000f0001 +#define SCLP_CMDW_DECONFIGURE_CHANNEL_PATH 0x000e0001 +#define SCLP_CMDW_READ_CHANNEL_PATH_INFORMATION 0x00030001 + +static inline sclp_cmdw_t get_configure_cmdw(struct chp_id chpid) +{ + return SCLP_CMDW_CONFIGURE_CHANNEL_PATH | chpid.id << 8; +} + +static inline sclp_cmdw_t get_deconfigure_cmdw(struct chp_id chpid) +{ + return SCLP_CMDW_DECONFIGURE_CHANNEL_PATH | chpid.id << 8; +} + +static void chp_callback(struct sclp_req *req, void *data) +{ + struct completion *completion = data; + + complete(completion); +} + +struct chp_cfg_sccb { + struct sccb_header header; + u8 ccm; + u8 reserved[6]; + u8 cssid; +} __attribute__((packed)); + +struct chp_cfg_data { + struct chp_cfg_sccb sccb; + struct sclp_req req; + struct completion completion; +} __attribute__((packed)); + +static int do_configure(sclp_cmdw_t cmd) +{ + struct chp_cfg_data *data; + int rc; + + /* Prepare sccb. */ + data = (struct chp_cfg_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!data) + return -ENOMEM; + data->sccb.header.length = sizeof(struct chp_cfg_sccb); + data->req.command = cmd; + data->req.sccb = &(data->sccb); + data->req.status = SCLP_REQ_FILLED; + data->req.callback = chp_callback; + data->req.callback_data = &(data->completion); + init_completion(&data->completion); + + /* Perform sclp request. */ + rc = sclp_add_request(&(data->req)); + if (rc) + goto out; + wait_for_completion(&data->completion); + + /* Check response .*/ + if (data->req.status != SCLP_REQ_DONE) { + printk(KERN_WARNING TAG "configure channel-path request failed " + "(status=0x%02x)\n", data->req.status); + rc = -EIO; + goto out; + } + switch (data->sccb.header.response_code) { + case 0x0020: + case 0x0120: + case 0x0440: + case 0x0450: + break; + default: + printk(KERN_WARNING TAG "configure channel-path failed " + "(cmd=0x%08x, response=0x%04x)\n", cmd, + data->sccb.header.response_code); + rc = -EIO; + break; + } +out: + free_page((unsigned long) data); + + return rc; +} + +/** + * sclp_chp_configure - perform configure channel-path sclp command + * @chpid: channel-path ID + * + * Perform configure channel-path command sclp command for specified chpid. + * Return 0 after command successfully finished, non-zero otherwise. + */ +int sclp_chp_configure(struct chp_id chpid) +{ + return do_configure(get_configure_cmdw(chpid)); +} + +/** + * sclp_chp_deconfigure - perform deconfigure channel-path sclp command + * @chpid: channel-path ID + * + * Perform deconfigure channel-path command sclp command for specified chpid + * and wait for completion. On success return 0. Return non-zero otherwise. + */ +int sclp_chp_deconfigure(struct chp_id chpid) +{ + return do_configure(get_deconfigure_cmdw(chpid)); +} + +struct chp_info_sccb { + struct sccb_header header; + u8 recognized[SCLP_CHP_INFO_MASK_SIZE]; + u8 standby[SCLP_CHP_INFO_MASK_SIZE]; + u8 configured[SCLP_CHP_INFO_MASK_SIZE]; + u8 ccm; + u8 reserved[6]; + u8 cssid; +} __attribute__((packed)); + +struct chp_info_data { + struct chp_info_sccb sccb; + struct sclp_req req; + struct completion completion; +} __attribute__((packed)); + +/** + * sclp_chp_read_info - perform read channel-path information sclp command + * @info: resulting channel-path information data + * + * Perform read channel-path information sclp command and wait for completion. + * On success, store channel-path information in @info and return 0. Return + * non-zero otherwise. + */ +int sclp_chp_read_info(struct sclp_chp_info *info) +{ + struct chp_info_data *data; + int rc; + + /* Prepare sccb. */ + data = (struct chp_info_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!data) + return -ENOMEM; + data->sccb.header.length = sizeof(struct chp_info_sccb); + data->req.command = SCLP_CMDW_READ_CHANNEL_PATH_INFORMATION; + data->req.sccb = &(data->sccb); + data->req.status = SCLP_REQ_FILLED; + data->req.callback = chp_callback; + data->req.callback_data = &(data->completion); + init_completion(&data->completion); + + /* Perform sclp request. */ + rc = sclp_add_request(&(data->req)); + if (rc) + goto out; + wait_for_completion(&data->completion); + + /* Check response .*/ + if (data->req.status != SCLP_REQ_DONE) { + printk(KERN_WARNING TAG "read channel-path info request failed " + "(status=0x%02x)\n", data->req.status); + rc = -EIO; + goto out; + } + if (data->sccb.header.response_code != 0x0010) { + printk(KERN_WARNING TAG "read channel-path info failed " + "(response=0x%04x)\n", data->sccb.header.response_code); + rc = -EIO; + goto out; + } + memcpy(info->recognized, data->sccb.recognized, + SCLP_CHP_INFO_MASK_SIZE); + memcpy(info->standby, data->sccb.standby, + SCLP_CHP_INFO_MASK_SIZE); + memcpy(info->configured, data->sccb.configured, + SCLP_CHP_INFO_MASK_SIZE); +out: + free_page((unsigned long) data); + + return rc; +} diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index 8a5839147f8a..0e92c8c89860 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -10,9 +10,14 @@ #include #include #include +#include +#include +#include +#include #include +#include +#include -#include "chpid.h" #include "cio.h" #include "css.h" #include "ioasm.h" @@ -20,6 +25,32 @@ #include "chp.h" #define to_channelpath(device) container_of(device, struct channel_path, dev) +#define CHP_INFO_UPDATE_INTERVAL 1*HZ + +enum cfg_task_t { + cfg_none, + cfg_configure, + cfg_deconfigure +}; + +/* Map for pending configure tasks. */ +static enum cfg_task_t chp_cfg_task[__MAX_CSSID + 1][__MAX_CHPID + 1]; +static DEFINE_MUTEX(cfg_lock); +static int cfg_busy; + +/* Map for channel-path status. */ +static struct sclp_chp_info chp_info; +static DEFINE_MUTEX(info_lock); + +/* Time after which channel-path status may be outdated. */ +static unsigned long chp_info_expires; + +/* Workqueue to perform pending configure tasks. */ +static struct workqueue_struct *chp_wq; +static struct work_struct cfg_work; + +/* Wait queue for configure completion events. */ +static wait_queue_head_t cfg_wait_queue; /* Return channel_path struct for given chpid. */ static inline struct channel_path *chpid_to_chp(struct chp_id chpid) @@ -251,6 +282,43 @@ static ssize_t chp_status_write(struct device *dev, static DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write); +static ssize_t chp_configure_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct channel_path *cp; + int status; + + cp = container_of(dev, struct channel_path, dev); + status = chp_info_get_status(cp->chpid); + if (status < 0) + return status; + + return snprintf(buf, PAGE_SIZE, "%d\n", status); +} + +static int cfg_wait_idle(void); + +static ssize_t chp_configure_write(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct channel_path *cp; + int val; + char delim; + + if (sscanf(buf, "%d %c", &val, &delim) != 1) + return -EINVAL; + if (val != 0 && val != 1) + return -EINVAL; + cp = container_of(dev, struct channel_path, dev); + chp_cfg_schedule(cp->chpid, val); + cfg_wait_idle(); + + return count; +} + +static DEVICE_ATTR(configure, 0644, chp_configure_show, chp_configure_write); + static ssize_t chp_type_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -293,6 +361,7 @@ static DEVICE_ATTR(shared, 0444, chp_shared_show, NULL); static struct attribute * chp_attrs[] = { &dev_attr_status.attr, + &dev_attr_configure.attr, &dev_attr_type.attr, &dev_attr_cmg.attr, &dev_attr_shared.attr, @@ -323,6 +392,8 @@ int chp_new(struct chp_id chpid) struct channel_path *chp; int ret; + if (chp_is_registered(chpid)) + return 0; chp = kzalloc(sizeof(struct channel_path), GFP_KERNEL); if (!chp) return -ENOMEM; @@ -435,3 +506,180 @@ int chp_process_crw(int id, int status) return 0; } } + +static inline int info_bit_num(struct chp_id id) +{ + return id.id + id.cssid * (__MAX_CHPID + 1); +} + +/* Force chp_info refresh on next call to info_validate(). */ +static void info_expire(void) +{ + mutex_lock(&info_lock); + chp_info_expires = jiffies - 1; + mutex_unlock(&info_lock); +} + +/* Ensure that chp_info is up-to-date. */ +static int info_update(void) +{ + int rc; + + mutex_lock(&info_lock); + rc = 0; + if (time_after(jiffies, chp_info_expires)) { + /* Data is too old, update. */ + rc = sclp_chp_read_info(&chp_info); + chp_info_expires = jiffies + CHP_INFO_UPDATE_INTERVAL ; + } + mutex_unlock(&info_lock); + + return rc; +} + +/** + * chp_info_get_status - retrieve configure status of a channel-path + * @chpid: channel-path ID + * + * On success, return 0 for standby, 1 for configured, 2 for reserved, + * 3 for not recognized. Return negative error code on error. + */ +int chp_info_get_status(struct chp_id chpid) +{ + int rc; + int bit; + + rc = info_update(); + if (rc) + return rc; + + bit = info_bit_num(chpid); + mutex_lock(&info_lock); + if (!chp_test_bit(chp_info.recognized, bit)) + rc = CHP_STATUS_NOT_RECOGNIZED; + else if (chp_test_bit(chp_info.configured, bit)) + rc = CHP_STATUS_CONFIGURED; + else if (chp_test_bit(chp_info.standby, bit)) + rc = CHP_STATUS_STANDBY; + else + rc = CHP_STATUS_RESERVED; + mutex_unlock(&info_lock); + + return rc; +} + +/* Return configure task for chpid. */ +static enum cfg_task_t cfg_get_task(struct chp_id chpid) +{ + return chp_cfg_task[chpid.cssid][chpid.id]; +} + +/* Set configure task for chpid. */ +static void cfg_set_task(struct chp_id chpid, enum cfg_task_t cfg) +{ + chp_cfg_task[chpid.cssid][chpid.id] = cfg; +} + +/* Perform one configure/deconfigure request. Reschedule work function until + * last request. */ +static void cfg_func(struct work_struct *work) +{ + struct chp_id chpid; + enum cfg_task_t t; + + mutex_lock(&cfg_lock); + t = cfg_none; + chp_id_for_each(&chpid) { + t = cfg_get_task(chpid); + if (t != cfg_none) { + cfg_set_task(chpid, cfg_none); + break; + } + } + mutex_unlock(&cfg_lock); + + switch (t) { + case cfg_configure: + sclp_chp_configure(chpid); + info_expire(); + chsc_chp_online(chpid); + break; + case cfg_deconfigure: + sclp_chp_deconfigure(chpid); + info_expire(); + chsc_chp_offline(chpid); + break; + case cfg_none: + /* Get updated information after last change. */ + info_update(); + mutex_lock(&cfg_lock); + cfg_busy = 0; + mutex_unlock(&cfg_lock); + wake_up_interruptible(&cfg_wait_queue); + return; + } + queue_work(chp_wq, &cfg_work); +} + +/** + * chp_cfg_schedule - schedule chpid configuration request + * @chpid - channel-path ID + * @configure - Non-zero for configure, zero for deconfigure + * + * Schedule a channel-path configuration/deconfiguration request. + */ +void chp_cfg_schedule(struct chp_id chpid, int configure) +{ + CIO_MSG_EVENT(2, "chp_cfg_sched%x.%02x=%d\n", chpid.cssid, chpid.id, + configure); + mutex_lock(&cfg_lock); + cfg_set_task(chpid, configure ? cfg_configure : cfg_deconfigure); + cfg_busy = 1; + mutex_unlock(&cfg_lock); + queue_work(chp_wq, &cfg_work); +} + +/** + * chp_cfg_cancel_deconfigure - cancel chpid deconfiguration request + * @chpid - channel-path ID + * + * Cancel an active channel-path deconfiguration request if it has not yet + * been performed. + */ +void chp_cfg_cancel_deconfigure(struct chp_id chpid) +{ + CIO_MSG_EVENT(2, "chp_cfg_cancel:%x.%02x\n", chpid.cssid, chpid.id); + mutex_lock(&cfg_lock); + if (cfg_get_task(chpid) == cfg_deconfigure) + cfg_set_task(chpid, cfg_none); + mutex_unlock(&cfg_lock); +} + +static int cfg_wait_idle(void) +{ + if (wait_event_interruptible(cfg_wait_queue, !cfg_busy)) + return -ERESTARTSYS; + return 0; +} + +static int __init chp_init(void) +{ + struct chp_id chpid; + + chp_wq = create_singlethread_workqueue("cio_chp"); + if (!chp_wq) + return -ENOMEM; + INIT_WORK(&cfg_work, cfg_func); + init_waitqueue_head(&cfg_wait_queue); + if (info_update()) + return 0; + /* Register available channel-paths. */ + chp_id_for_each(&chpid) { + if (chp_info_get_status(chpid) != CHP_STATUS_NOT_RECOGNIZED) + chp_new(chpid); + } + + return 0; +} + +subsys_initcall(chp_init); diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h index ac2b1a9c3bc2..862af69d9707 100644 --- a/drivers/s390/cio/chp.h +++ b/drivers/s390/cio/chp.h @@ -10,10 +10,23 @@ #include #include - -#include "chpid.h" +#include #include "chsc.h" +#define CHP_STATUS_STANDBY 0 +#define CHP_STATUS_CONFIGURED 1 +#define CHP_STATUS_RESERVED 2 +#define CHP_STATUS_NOT_RECOGNIZED 3 + +static inline int chp_test_bit(u8 *bitmap, int num) +{ + int byte = num >> 3; + int mask = 128 >> (num & 7); + + return (bitmap[byte] & mask) ? 1 : 0; +} + + struct channel_path { struct chp_id chpid; int state; @@ -33,5 +46,8 @@ int chp_process_crw(int id, int available); void chp_remove_cmg_attr(struct channel_path *chp); int chp_add_cmg_attr(struct channel_path *chp); int chp_new(struct chp_id chpid); +void chp_cfg_schedule(struct chp_id chpid, int configure); +void chp_cfg_cancel_deconfigure(struct chp_id chpid); +int chp_info_get_status(struct chp_id chpid); #endif /* S390_CHP_H */ diff --git a/drivers/s390/cio/chpid.h b/drivers/s390/cio/chpid.h deleted file mode 100644 index 44cc00ed9b59..000000000000 --- a/drivers/s390/cio/chpid.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * drivers/s390/cio/chpid.h - * - * Copyright IBM Corp. 2007 - * Author(s): Peter Oberparleiter - */ - -#ifndef S390_CHP_ID_H -#define S390_CHP_ID_H S390_CHP_ID_H - -#include -#include -#include "css.h" - -struct chp_id { - u8 reserved1; - u8 cssid; - u8 reserved2; - u8 id; -} __attribute__((packed)); - -static inline void chp_id_init(struct chp_id *chpid) -{ - memset(chpid, 0, sizeof(struct chp_id)); -} - -static inline int chp_id_is_equal(struct chp_id *a, struct chp_id *b) -{ - return (a->id == b->id) && (a->cssid == b->cssid); -} - -static inline void chp_id_next(struct chp_id *chpid) -{ - if (chpid->id < __MAX_CHPID) - chpid->id++; - else { - chpid->id = 0; - chpid->cssid++; - } -} - -static inline int chp_id_is_valid(struct chp_id *chpid) -{ - return (chpid->cssid <= __MAX_CSSID); -} - - -#define chp_id_for_each(c) \ - for (chp_id_init(c); chp_id_is_valid(c); chp_id_next(c)) - -#endif /* S390_CHP_ID_H */ diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index d99f525eac08..3dec460bba27 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -15,12 +15,12 @@ #include #include +#include #include "css.h" #include "cio.h" #include "cio_debug.h" #include "ioasm.h" -#include "chpid.h" #include "chp.h" #include "chsc.h" @@ -498,6 +498,45 @@ static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) return rc; } +struct chp_config_data { + u8 map[32]; + u8 op; + u8 pc; +}; + +static int chsc_process_sei_chp_config(struct chsc_sei_area *sei_area) +{ + struct chp_config_data *data; + struct chp_id chpid; + int num; + + CIO_CRW_EVENT(4, "chsc: channel-path-configuration notification\n"); + if (sei_area->rs != 0) + return 0; + data = (struct chp_config_data *) &(sei_area->ccdf); + chp_id_init(&chpid); + for (num = 0; num <= __MAX_CHPID; num++) { + if (!chp_test_bit(data->map, num)) + continue; + chpid.id = num; + printk(KERN_WARNING "cio: processing configure event %d for " + "chpid %x.%02x\n", data->op, chpid.cssid, chpid.id); + switch (data->op) { + case 0: + chp_cfg_schedule(chpid, 1); + break; + case 1: + chp_cfg_schedule(chpid, 0); + break; + case 2: + chp_cfg_cancel_deconfigure(chpid); + break; + } + } + + return 0; +} + static int chsc_process_sei(struct chsc_sei_area *sei_area) { int rc; @@ -514,6 +553,9 @@ static int chsc_process_sei(struct chsc_sei_area *sei_area) case 2: /* i/o resource accessibiliy */ rc = chsc_process_sei_res_acc(sei_area); break; + case 8: /* channel-path-configuration notification */ + rc = chsc_process_sei_chp_config(sei_area); + break; default: /* other stuff */ CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n", sei_area->cc); diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 0e40defc6087..322586f27cc0 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -3,7 +3,7 @@ #include #include -#include "chpid.h" +#include #define CHSC_SDA_OC_MSS 0x2 diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 7dd0649c95d1..ea1defba5693 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "airq.h" #include "cio.h" #include "css.h" diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index 35154a210357..e62ab5c52863 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h @@ -3,6 +3,7 @@ #include "schid.h" #include +#include /* * where we put the ssd info diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 4319e1ced791..b2b1a265c602 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -5,8 +5,10 @@ #include #include #include +#include #include +#include #include "schid.h" @@ -149,8 +151,6 @@ extern void css_reiterate_subchannels(void); #define __MAX_SUBCHANNEL 65535 #define __MAX_SSID 3 -#define __MAX_CHPID 255 -#define __MAX_CSSID 0 struct channel_subsystem { u8 cssid; diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index db3d1b990f58..d6226881d0df 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -15,6 +15,7 @@ #include #include +#include #include "cio.h" #include "cio_debug.h" @@ -22,7 +23,6 @@ #include "device.h" #include "chsc.h" #include "ioasm.h" -#include "chpid.h" #include "chp.h" int diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index d819ae2ee9ae..16f59fcb66b1 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -16,13 +16,13 @@ #include #include +#include #include "cio.h" #include "cio_debug.h" #include "css.h" #include "chsc.h" #include "device.h" -#include "chpid.h" #include "chp.h" int ccw_device_set_options_mask(struct ccw_device *cdev, unsigned long flags) diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h index 78c7db6daa84..7153dd959082 100644 --- a/drivers/s390/cio/ioasm.h +++ b/drivers/s390/cio/ioasm.h @@ -1,8 +1,8 @@ #ifndef S390_CIO_IOASM_H #define S390_CIO_IOASM_H +#include #include "schid.h" -#include "chpid.h" /* * TPI info structure diff --git a/include/asm-s390/chpid.h b/include/asm-s390/chpid.h new file mode 100644 index 000000000000..b203336fd892 --- /dev/null +++ b/include/asm-s390/chpid.h @@ -0,0 +1,53 @@ +/* + * drivers/s390/cio/chpid.h + * + * Copyright IBM Corp. 2007 + * Author(s): Peter Oberparleiter + */ + +#ifndef _ASM_S390_CHPID_H +#define _ASM_S390_CHPID_H _ASM_S390_CHPID_H + +#include +#include +#include + +#define __MAX_CHPID 255 + +struct chp_id { + u8 reserved1; + u8 cssid; + u8 reserved2; + u8 id; +} __attribute__((packed)); + +static inline void chp_id_init(struct chp_id *chpid) +{ + memset(chpid, 0, sizeof(struct chp_id)); +} + +static inline int chp_id_is_equal(struct chp_id *a, struct chp_id *b) +{ + return (a->id == b->id) && (a->cssid == b->cssid); +} + +static inline void chp_id_next(struct chp_id *chpid) +{ + if (chpid->id < __MAX_CHPID) + chpid->id++; + else { + chpid->id = 0; + chpid->cssid++; + } +} + +static inline int chp_id_is_valid(struct chp_id *chpid) +{ + return (chpid->cssid <= __MAX_CSSID); +} + + +#define chp_id_for_each(c) \ + for (chp_id_init(c); chp_id_is_valid(c); chp_id_next(c)) + +#endif /* _ASM_S390_CHPID_H */ diff --git a/include/asm-s390/cio.h b/include/asm-s390/cio.h index 0db017bc7d09..f738d2827582 100644 --- a/include/asm-s390/cio.h +++ b/include/asm-s390/cio.h @@ -13,6 +13,7 @@ #ifdef __KERNEL__ #define LPM_ANYPATH 0xff +#define __MAX_CSSID 0 /* * subchannel status word diff --git a/include/asm-s390/sclp.h b/include/asm-s390/sclp.h index 468b97018405..3996daaa8f54 100644 --- a/include/asm-s390/sclp.h +++ b/include/asm-s390/sclp.h @@ -9,6 +9,7 @@ #define _ASM_S390_SCLP_H #include +#include struct sccb_header { u16 length; @@ -33,7 +34,18 @@ struct sclp_readinfo_sccb { u8 _reserved3[4096 - 112]; /* 112-4095 */ } __attribute__((packed, aligned(4096))); +#define SCLP_CHP_INFO_MASK_SIZE 32 + +struct sclp_chp_info { + u8 recognized[SCLP_CHP_INFO_MASK_SIZE]; + u8 standby[SCLP_CHP_INFO_MASK_SIZE]; + u8 configured[SCLP_CHP_INFO_MASK_SIZE]; +}; + extern struct sclp_readinfo_sccb s390_readinfo_sccb; extern void sclp_readinfo_early(void); +extern int sclp_chp_configure(struct chp_id chpid); +extern int sclp_chp_deconfigure(struct chp_id chpid); +extern int sclp_chp_read_info(struct sclp_chp_info *info); #endif /* _ASM_S390_SCLP_H */ -- cgit v1.2.3 From 83b3370c79b91b9be3f6540c3c914e689134b45f Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Fri, 27 Apr 2007 16:01:34 +0200 Subject: [S390] cio: replace subchannel evaluation queue with bitmap Use a bitmap for indicating which subchannels require evaluation instead of allocating memory for each evaluation request. This approach reduces memory consumption during recovery in case of massive evaluation request occurrence and removes the need for memory allocation failure handling. Cc: Heiko Carstens Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/Makefile | 2 +- drivers/s390/cio/chp.c | 8 +-- drivers/s390/cio/chp.h | 2 +- drivers/s390/cio/chsc.c | 130 ++++++++++--------------------------- drivers/s390/cio/chsc.h | 4 +- drivers/s390/cio/css.c | 148 ++++++++++++++---------------------------- drivers/s390/cio/css.h | 10 +-- drivers/s390/cio/device_fsm.c | 6 +- drivers/s390/cio/idset.c | 112 ++++++++++++++++++++++++++++++++ drivers/s390/cio/idset.h | 25 +++++++ drivers/s390/s390mach.c | 24 ++----- 11 files changed, 235 insertions(+), 236 deletions(-) create mode 100644 drivers/s390/cio/idset.c create mode 100644 drivers/s390/cio/idset.h (limited to 'drivers/s390/cio/chp.h') diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile index fe7b3ffa1eaa..cfaf77b320f5 100644 --- a/drivers/s390/cio/Makefile +++ b/drivers/s390/cio/Makefile @@ -2,7 +2,7 @@ # Makefile for the S/390 common i/o drivers # -obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o +obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o ccw_device-objs += device.o device_fsm.o device_ops.o ccw_device-objs += device_id.o device_pgid.o device_status.o obj-y += ccw_device.o cmf.o diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index 0e92c8c89860..ac289e6eadfe 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -491,7 +491,7 @@ void *chp_get_chp_desc(struct chp_id chpid) * Handle channel-report-words indicating that the status of a channel-path * has changed. */ -int chp_process_crw(int id, int status) +void chp_process_crw(int id, int status) { struct chp_id chpid; @@ -500,11 +500,9 @@ int chp_process_crw(int id, int status) if (status) { if (!chp_is_registered(chpid)) chp_new(chpid); - return chsc_chp_online(chpid); - } else { + chsc_chp_online(chpid); + } else chsc_chp_offline(chpid); - return 0; - } } static inline int info_bit_num(struct chp_id id) diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h index 862af69d9707..65286563c592 100644 --- a/drivers/s390/cio/chp.h +++ b/drivers/s390/cio/chp.h @@ -42,7 +42,7 @@ int chp_get_status(struct chp_id chpid); u8 chp_get_sch_opm(struct subchannel *sch); int chp_is_registered(struct chp_id chpid); void *chp_get_chp_desc(struct chp_id chpid); -int chp_process_crw(int id, int available); +void chp_process_crw(int id, int available); void chp_remove_cmg_attr(struct channel_path *chp); int chp_add_cmg_attr(struct channel_path *chp); int chp_new(struct chp_id chpid); diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 02615eb43984..89a130a62654 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -195,12 +195,8 @@ static void terminate_internal_io(struct subchannel *sch) if (cio_clear(sch)) { /* Recheck device in case clear failed. */ sch->lpm = 0; - if (device_trigger_verify(sch) != 0) { - if(css_enqueue_subchannel_slow(sch->schid)) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - } - } + if (device_trigger_verify(sch) != 0) + css_schedule_eval(sch->schid); return; } /* Request retry of internal operation. */ @@ -262,11 +258,8 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) out_unreg: sch->lpm = 0; - if (css_enqueue_subchannel_slow(sch->schid)) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - } spin_unlock_irq(sch->lock); + css_schedule_eval(sch->schid); return 0; } @@ -281,9 +274,6 @@ void chsc_chp_offline(struct chp_id chpid) return; bus_for_each_dev(&css_bus_type, NULL, &chpid, s390_subchannel_remove_chpid); - - if (need_rescan || css_slow_subchannels_exist()) - queue_work(slow_path_wq, &slow_path_work); } struct res_acc_data { @@ -331,7 +321,6 @@ static int s390_process_res_acc_new_sch(struct subchannel_id schid) { struct schib schib; - int ret; /* * We don't know the device yet, but since a path * may be available now to the device we'll have @@ -342,15 +331,10 @@ s390_process_res_acc_new_sch(struct subchannel_id schid) */ if (stsch_err(schid, &schib)) /* We're through */ - return need_rescan ? -EAGAIN : -ENXIO; + return -ENXIO; /* Put it on the slow path. */ - ret = css_enqueue_subchannel_slow(schid); - if (ret) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - return -EAGAIN; - } + css_schedule_eval(schid); return 0; } @@ -392,10 +376,8 @@ __s390_process_res_acc(struct subchannel_id schid, void *data) } -static int -s390_process_res_acc (struct res_acc_data *res_data) +static void s390_process_res_acc (struct res_acc_data *res_data) { - int rc; char dbf_txt[15]; sprintf(dbf_txt, "accpr%x.%02x", res_data->chpid.cssid, @@ -413,12 +395,7 @@ s390_process_res_acc (struct res_acc_data *res_data) * The more information we have (info), the less scanning * will we have to do. */ - rc = for_each_subchannel(__s390_process_res_acc, res_data); - if (css_slow_subchannels_exist()) - rc = -EAGAIN; - else if (rc != -EAGAIN) - rc = 0; - return rc; + for_each_subchannel(__s390_process_res_acc, res_data); } static int @@ -470,7 +447,7 @@ struct chsc_sei_area { /* ccdf has to be big enough for a link-incident record */ } __attribute__ ((packed)); -static int chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) +static void chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) { struct chp_id chpid; int id; @@ -478,7 +455,7 @@ static int chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x)\n", sei_area->rs, sei_area->rsid); if (sei_area->rs != 4) - return 0; + return; id = __get_chpid_from_lir(sei_area->ccdf); if (id < 0) CIO_CRW_EVENT(4, "chsc: link incident - invalid LIR\n"); @@ -487,21 +464,18 @@ static int chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) chpid.id = id; chsc_chp_offline(chpid); } - - return 0; } -static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) +static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) { struct res_acc_data res_data; struct chp_id chpid; int status; - int rc; CIO_CRW_EVENT(4, "chsc: resource accessibility event (rs=%02x, " "rs_id=%04x)\n", sei_area->rs, sei_area->rsid); if (sei_area->rs != 4) - return 0; + return; chp_id_init(&chpid); chpid.id = sei_area->rsid; /* allocate a new channel path structure, if needed */ @@ -509,7 +483,7 @@ static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) if (status < 0) chp_new(chpid); else if (!status) - return 0; + return; memset(&res_data, 0, sizeof(struct res_acc_data)); res_data.chpid = chpid; if ((sei_area->vf & 0xc0) != 0) { @@ -521,9 +495,7 @@ static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) /* link address */ res_data.fla_mask = 0xff00; } - rc = s390_process_res_acc(&res_data); - - return rc; + s390_process_res_acc(&res_data); } struct chp_config_data { @@ -532,7 +504,7 @@ struct chp_config_data { u8 pc; }; -static int chsc_process_sei_chp_config(struct chsc_sei_area *sei_area) +static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area) { struct chp_config_data *data; struct chp_id chpid; @@ -540,7 +512,7 @@ static int chsc_process_sei_chp_config(struct chsc_sei_area *sei_area) CIO_CRW_EVENT(4, "chsc: channel-path-configuration notification\n"); if (sei_area->rs != 0) - return 0; + return; data = (struct chp_config_data *) &(sei_area->ccdf); chp_id_init(&chpid); for (num = 0; num <= __MAX_CHPID; num++) { @@ -561,52 +533,44 @@ static int chsc_process_sei_chp_config(struct chsc_sei_area *sei_area) break; } } - - return 0; } -static int chsc_process_sei(struct chsc_sei_area *sei_area) +static void chsc_process_sei(struct chsc_sei_area *sei_area) { - int rc; - /* Check if we might have lost some information. */ - if (sei_area->flags & 0x40) + if (sei_area->flags & 0x40) { CIO_CRW_EVENT(2, "chsc: event overflow\n"); + css_schedule_eval_all(); + } /* which kind of information was stored? */ - rc = 0; switch (sei_area->cc) { case 1: /* link incident*/ - rc = chsc_process_sei_link_incident(sei_area); + chsc_process_sei_link_incident(sei_area); break; case 2: /* i/o resource accessibiliy */ - rc = chsc_process_sei_res_acc(sei_area); + chsc_process_sei_res_acc(sei_area); break; case 8: /* channel-path-configuration notification */ - rc = chsc_process_sei_chp_config(sei_area); + chsc_process_sei_chp_config(sei_area); break; default: /* other stuff */ CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n", sei_area->cc); break; } - - return rc; } -int chsc_process_crw(void) +void chsc_process_crw(void) { struct chsc_sei_area *sei_area; - int ret; - int rc; if (!sei_page) - return 0; + return; /* Access to sei_page is serialized through machine check handler * thread, so no need for locking. */ sei_area = sei_page; CIO_TRACE_EVENT( 2, "prcss"); - ret = 0; do { memset(sei_area, 0, sizeof(*sei_area)); sei_area->request.length = 0x0010; @@ -616,37 +580,26 @@ int chsc_process_crw(void) if (sei_area->response.code == 0x0001) { CIO_CRW_EVENT(4, "chsc: sei successful\n"); - rc = chsc_process_sei(sei_area); - if (rc) - ret = rc; + chsc_process_sei(sei_area); } else { CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n", sei_area->response.code); - ret = 0; break; } } while (sei_area->flags & 0x80); - - return ret; } static int __chp_add_new_sch(struct subchannel_id schid) { struct schib schib; - int ret; if (stsch_err(schid, &schib)) /* We're through */ - return need_rescan ? -EAGAIN : -ENXIO; + return -ENXIO; /* Put it on the slow path. */ - ret = css_enqueue_subchannel_slow(schid); - if (ret) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - return -EAGAIN; - } + css_schedule_eval(schid); return 0; } @@ -693,22 +646,15 @@ __chp_add(struct subchannel_id schid, void *data) return 0; } -int chsc_chp_online(struct chp_id chpid) +void chsc_chp_online(struct chp_id chpid) { - int rc; char dbf_txt[15]; sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id); CIO_TRACE_EVENT(2, dbf_txt); - if (chp_get_status(chpid) == 0) - return 0; - rc = for_each_subchannel(__chp_add, &chpid); - if (css_slow_subchannels_exist()) - rc = -EAGAIN; - if (rc != -EAGAIN) - rc = 0; - return rc; + if (chp_get_status(chpid) != 0) + for_each_subchannel(__chp_add, &chpid); } static void __s390_subchannel_vary_chpid(struct subchannel *sch, @@ -749,12 +695,8 @@ static void __s390_subchannel_vary_chpid(struct subchannel *sch, sch->driver->verify(&sch->dev); } } else if (!sch->lpm) { - if (device_trigger_verify(sch) != 0) { - if (css_enqueue_subchannel_slow(sch->schid)) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - } - } + if (device_trigger_verify(sch) != 0) + css_schedule_eval(sch->schid); } else if (sch->driver && sch->driver->verify) sch->driver->verify(&sch->dev); break; @@ -801,11 +743,7 @@ __s390_vary_chpid_on(struct subchannel_id schid, void *data) /* We're through */ return -ENXIO; /* Put it on the slow path. */ - if (css_enqueue_subchannel_slow(schid)) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - return -EAGAIN; - } + css_schedule_eval(schid); return 0; } @@ -826,8 +764,6 @@ int chsc_chp_vary(struct chp_id chpid, int on) if (on) /* Scan for new devices on varied on path. */ for_each_subchannel(__s390_vary_chpid_on, NULL); - if (need_rescan || css_slow_subchannels_exist()) - queue_work(slow_path_wq, &slow_path_work); return 0; } diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 322586f27cc0..742ef57d2c58 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -36,7 +36,7 @@ struct channel_path_desc { struct channel_path; extern int css_get_ssd_info(struct subchannel *); -extern int chsc_process_crw(void); +extern void chsc_process_crw(void); struct css_general_char { u64 : 41; @@ -79,7 +79,7 @@ extern int chsc_secm(struct channel_subsystem *, int); int chsc_chp_vary(struct chp_id chpid, int on); int chsc_determine_channel_path_description(struct chp_id chpid, struct channel_path_desc *desc); -int chsc_chp_online(struct chp_id chpid); +void chsc_chp_online(struct chp_id chpid); void chsc_chp_offline(struct chp_id chpid); int chsc_get_channel_measurement_chars(struct channel_path *chp); diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index fe0ace7aece8..fcc641e578f4 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -20,8 +20,8 @@ #include "ioasm.h" #include "chsc.h" #include "device.h" +#include "idset.h" -int need_rescan = 0; int css_init_done = 0; static int need_reprobe = 0; static int max_ssid = 0; @@ -306,7 +306,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,53 +317,66 @@ 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) { + printk(KERN_WARNING "cio: could not allocate slow subchannel " + "set\n"); + return -ENOMEM; + } + return 0; } -struct slow_subchannel { - struct list_head slow_list; - struct subchannel_id schid; -}; - -static LIST_HEAD(slow_subchannels_head); -static DEFINE_SPINLOCK(slow_subchannel_lock); +subsys_initcall(slow_subchannel_init); -static void -css_trigger_slow_path(struct work_struct *unused) +static void css_slow_path_func(struct work_struct *unused) { - CIO_TRACE_EVENT(4, "slowpath"); - - if (need_rescan) { - need_rescan = 0; - for_each_subchannel(css_rescan_devices, NULL); - return; - } + struct subchannel_id schid; + CIO_TRACE_EVENT(4, "slowpath"); 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) { @@ -425,34 +438,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 +457,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 @@ -745,47 +732,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); diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index b2b1a265c602..4b3133a7bae1 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -146,7 +146,7 @@ extern void css_sch_device_unregister(struct subchannel *); extern struct subchannel * get_subchannel_by_schid(struct subchannel_id); extern int css_init_done; extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *); -extern int css_process_crw(int, int); +extern void css_process_crw(int, int); extern void css_reiterate_subchannels(void); #define __MAX_SUBCHANNEL 65535 @@ -186,16 +186,12 @@ int device_trigger_verify(struct subchannel *sch); void device_kill_pending_timer(struct subchannel *); /* Helper functions to build lists for the slow path. */ -extern int css_enqueue_subchannel_slow(struct subchannel_id schid); -void css_walk_subchannel_slow_list(void (*fn)(unsigned long)); -void css_clear_subchannel_slow_list(void); -int css_slow_subchannels_exist(void); -extern int need_rescan; +void css_schedule_eval(struct subchannel_id schid); +void css_schedule_eval_all(void); int sch_is_pseudo_sch(struct subchannel *); extern struct workqueue_struct *slow_path_wq; -extern struct work_struct slow_path_work; int subchannel_add_files (struct device *); extern struct attribute_group *subch_attr_groups[]; diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index d6226881d0df..898ec3b2bebb 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -222,10 +222,8 @@ __recover_lost_chpids(struct subchannel *sch, int old_lpm) if (old_lpm & mask) continue; chpid.id = sch->schib.pmcw.chpid[i]; - if (!chp_is_registered(chpid)) { - need_rescan = 1; - queue_work(slow_path_wq, &slow_path_work); - } + if (!chp_is_registered(chpid)) + css_schedule_eval_all(); } } diff --git a/drivers/s390/cio/idset.c b/drivers/s390/cio/idset.c new file mode 100644 index 000000000000..16ea828e99f7 --- /dev/null +++ b/drivers/s390/cio/idset.c @@ -0,0 +1,112 @@ +/* + * drivers/s390/cio/idset.c + * + * Copyright IBM Corp. 2007 + * Author(s): Peter Oberparleiter + */ + +#include +#include +#include "idset.h" +#include "css.h" + +struct idset { + int num_ssid; + int num_id; + unsigned long bitmap[0]; +}; + +static inline unsigned long bitmap_size(int num_ssid, int num_id) +{ + return __BITOPS_WORDS(num_ssid * num_id) * sizeof(unsigned long); +} + +static struct idset *idset_new(int num_ssid, int num_id) +{ + struct idset *set; + + set = kzalloc(sizeof(struct idset) + bitmap_size(num_ssid, num_id), + GFP_KERNEL); + if (set) { + set->num_ssid = num_ssid; + set->num_id = num_id; + } + return set; +} + +void idset_free(struct idset *set) +{ + kfree(set); +} + +void idset_clear(struct idset *set) +{ + memset(set->bitmap, 0, bitmap_size(set->num_ssid, set->num_id)); +} + +void idset_fill(struct idset *set) +{ + memset(set->bitmap, 0xff, bitmap_size(set->num_ssid, set->num_id)); +} + +static inline void idset_add(struct idset *set, int ssid, int id) +{ + set_bit(ssid * set->num_id + id, set->bitmap); +} + +static inline void idset_del(struct idset *set, int ssid, int id) +{ + clear_bit(ssid * set->num_id + id, set->bitmap); +} + +static inline int idset_contains(struct idset *set, int ssid, int id) +{ + return test_bit(ssid * set->num_id + id, set->bitmap); +} + +static inline int idset_get_first(struct idset *set, int *ssid, int *id) +{ + int bitnum; + + bitnum = find_first_bit(set->bitmap, set->num_ssid * set->num_id); + if (bitnum >= set->num_ssid * set->num_id) + return 0; + *ssid = bitnum / set->num_id; + *id = bitnum % set->num_id; + return 1; +} + +struct idset *idset_sch_new(void) +{ + return idset_new(__MAX_SSID + 1, __MAX_SUBCHANNEL + 1); +} + +void idset_sch_add(struct idset *set, struct subchannel_id schid) +{ + idset_add(set, schid.ssid, schid.sch_no); +} + +void idset_sch_del(struct idset *set, struct subchannel_id schid) +{ + idset_del(set, schid.ssid, schid.sch_no); +} + +int idset_sch_contains(struct idset *set, struct subchannel_id schid) +{ + return idset_contains(set, schid.ssid, schid.sch_no); +} + +int idset_sch_get_first(struct idset *set, struct subchannel_id *schid) +{ + int ssid = 0; + int id = 0; + int rc; + + rc = idset_get_first(set, &ssid, &id); + if (rc) { + init_subchannel_id(schid); + schid->ssid = ssid; + schid->sch_no = id; + } + return rc; +} diff --git a/drivers/s390/cio/idset.h b/drivers/s390/cio/idset.h new file mode 100644 index 000000000000..144466ab8c15 --- /dev/null +++ b/drivers/s390/cio/idset.h @@ -0,0 +1,25 @@ +/* + * drivers/s390/cio/idset.h + * + * Copyright IBM Corp. 2007 + * Author(s): Peter Oberparleiter + */ + +#ifndef S390_IDSET_H +#define S390_IDSET_H S390_IDSET_H + +#include "schid.h" + +struct idset; + +void idset_free(struct idset *set); +void idset_clear(struct idset *set); +void idset_fill(struct idset *set); + +struct idset *idset_sch_new(void); +void idset_sch_add(struct idset *set, struct subchannel_id id); +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); + +#endif /* S390_IDSET_H */ diff --git a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c index afd8a3c0f8d6..644a06eba828 100644 --- a/drivers/s390/s390mach.c +++ b/drivers/s390/s390mach.c @@ -45,14 +45,13 @@ static int s390_collect_crw_info(void *param) { struct crw crw[2]; - int ccode, ret, slow; + int ccode; struct semaphore *sem; unsigned int chain; sem = (struct semaphore *)param; repeat: down_interruptible(sem); - slow = 0; chain = 0; while (1) { if (unlikely(chain > 1)) { @@ -85,9 +84,8 @@ repeat: /* Check for overflows. */ if (crw[chain].oflw) { pr_debug("%s: crw overflow detected!\n", __FUNCTION__); - css_reiterate_subchannels(); + css_schedule_eval_all(); chain = 0; - slow = 1; continue; } switch (crw[chain].rsc) { @@ -95,10 +93,7 @@ repeat: if (crw[0].chn && !chain) break; pr_debug("source is subchannel %04X\n", crw[0].rsid); - ret = css_process_crw (crw[0].rsid, - chain ? crw[1].rsid : 0); - if (ret == -EAGAIN) - slow = 1; + css_process_crw(crw[0].rsid, chain ? crw[1].rsid : 0); break; case CRW_RSC_MONITOR: pr_debug("source is monitoring facility\n"); @@ -117,28 +112,23 @@ repeat: } switch (crw[0].erc) { case CRW_ERC_IPARM: /* Path has come. */ - ret = chp_process_crw(crw[0].rsid, 1); + chp_process_crw(crw[0].rsid, 1); break; case CRW_ERC_PERRI: /* Path has gone. */ case CRW_ERC_PERRN: - ret = chp_process_crw(crw[0].rsid, 0); + chp_process_crw(crw[0].rsid, 0); break; default: pr_debug("Don't know how to handle erc=%x\n", crw[0].erc); - ret = 0; } - if (ret == -EAGAIN) - slow = 1; break; case CRW_RSC_CONFIG: pr_debug("source is configuration-alert facility\n"); break; case CRW_RSC_CSS: pr_debug("source is channel subsystem\n"); - ret = chsc_process_crw(); - if (ret == -EAGAIN) - slow = 1; + chsc_process_crw(); break; default: pr_debug("unknown source\n"); @@ -147,8 +137,6 @@ repeat: /* chain is always 0 or 1 here. */ chain = crw[chain].chn ? chain + 1 : 0; } - if (slow) - queue_work(slow_path_wq, &slow_path_work); goto repeat; return 0; } -- cgit v1.2.3