From 7ea8d32199a3751527bf06b91c03de92d5ad5b3e Mon Sep 17 00:00:00 2001 From: Stefan Weinhuber Date: Mon, 25 Oct 2010 16:10:08 +0200 Subject: [S390] dasd: let recovery cqr inherit flags from failed cqr The usual way to recover a failed DASD ECKD request (cqr) is to create a new request with an appropriate recovery CCW program. Certain features, e.g. failfast, can be enabled per request and are stored in the requests flags. These flags have to be copied from the failed to the recovery request, to let the recovery request use the same features as the original one. Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_3990_erp.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index e82d427ff5eb..968c76cf7127 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -221,6 +221,7 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) ccw->cmd_code = CCW_CMD_DCTL; ccw->count = 4; ccw->cda = (__u32)(addr_t) DCTL_data; + dctl_cqr->flags = erp->flags; dctl_cqr->function = dasd_3990_erp_DCTL; dctl_cqr->refers = erp; dctl_cqr->startdev = device; @@ -1710,6 +1711,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) ccw->cda = cpa; /* fill erp related fields */ + erp->flags = default_erp->flags; erp->function = dasd_3990_erp_action_1B_32; erp->refers = default_erp->refers; erp->startdev = device; @@ -2354,6 +2356,7 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr) ccw->cda = (long)(cqr->cpaddr); } + erp->flags = cqr->flags; erp->function = dasd_3990_erp_add_erp; erp->refers = cqr; erp->startdev = device; -- cgit v1.2.3 From bf2106ae114807772506e62cbf8a7d8e2c5403a1 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 25 Oct 2010 16:10:20 +0200 Subject: [S390] drivers/s390/char: Use static const char arrays Signed-off-by: Joe Perches Signed-off-by: Martin Schwidefsky --- drivers/s390/char/vmlogrdr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index 0d6dc4b92cc2..9f661426e4a1 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -215,7 +215,7 @@ static void vmlogrdr_iucv_message_pending(struct iucv_path *path, static int vmlogrdr_get_recording_class_AB(void) { - char cp_command[]="QUERY COMMAND RECORDING "; + static const char cp_command[] = "QUERY COMMAND RECORDING "; char cp_response[80]; char *tail; int len,i; @@ -638,7 +638,7 @@ static ssize_t vmlogrdr_recording_status_show(struct device_driver *driver, char *buf) { - char cp_command[] = "QUERY RECORDING "; + static const char cp_command[] = "QUERY RECORDING "; int len; cpcmd(cp_command, buf, 4096, NULL); -- cgit v1.2.3 From c77f7cf74b9bc0e56c8698097228c1784e9f03c9 Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Mon, 25 Oct 2010 16:10:22 +0200 Subject: [S390] hvc_iucv: do not call iucv_unregister if iucv_register failed If the iucv_register() functions fails, the error recovery calls iucv_unregister() which might cause the following stack backtrace: (<0000000000100ab2> show_trace+0xee/0x144) <00000000004f1842> panic+0xb6/0x248 <00000000001010a6> die+0x15a/0x16c <000000000011d936> do_no_context+0xa6/0xe4 <00000000004f84dc> do_protection_exception+0x2e8/0x3a4 <0000000000113afc> pgm_exit+0x0/0x14 <00000000004e786e> iucv_unregister+0x5a/0x17c (<00000000004e785e> iucv_unregister+0x4a/0x17c) <000000000076de74> hvc_iucv_init+0x228/0x5dc <00000000001000c2> do_one_initcall+0x3e/0x19c <00000000007524a2> kernel_init+0x28e/0x404 <0000000000105dd6> kernel_thread_starter+0x6/0xc <0000000000105dd0> kernel_thread_starter+0x0/0xc Remove the call to iucv_unregister() and remove the goto label as unregistering is the last step in the hvc_iucv initialization. If iucv_register() fails, simply clean up hvc terminals and free resources. Signed-off-by: Hendrik Brueckner Signed-off-by: Martin Schwidefsky --- drivers/char/hvc_iucv.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c index 7b01bc609de3..c3425bb3a1f6 100644 --- a/drivers/char/hvc_iucv.c +++ b/drivers/char/hvc_iucv.c @@ -1303,13 +1303,11 @@ static int __init hvc_iucv_init(void) if (rc) { pr_err("Registering IUCV handlers failed with error code=%d\n", rc); - goto out_error_iucv; + goto out_error_hvc; } return 0; -out_error_iucv: - iucv_unregister(&hvc_iucv_handler, 0); out_error_hvc: for (i = 0; i < hvc_iucv_devices; i++) if (hvc_iucv_table[i]) -- cgit v1.2.3 From a8481c2afeed297426f67ce3fd4ad7eea6b7ddf5 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 25 Oct 2010 16:10:23 +0200 Subject: [S390] css: fix sparse warning fix this sparse warning: drivers/s390/cio/css.c:580:6: warning: symbol 'css_schedule_eval_all_unreg' was not declared. Should it be static? Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/css.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index ca8e1c240c3c..064d3003ed20 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -577,7 +577,7 @@ static int __unset_registered(struct device *dev, void *data) return 0; } -void css_schedule_eval_all_unreg(void) +static void css_schedule_eval_all_unreg(void) { unsigned long flags; struct idset *unreg_set; -- cgit v1.2.3 From ed3640b285d831065eb0507cdca7d125f6f78e8d Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Mon, 25 Oct 2010 16:10:24 +0200 Subject: [S390] dasd fix dump_sense_dbf The dasd_eckd_dump_sense_dbf function uses a macro for s390 debug feature that can handle up to 8 parameters (for the DASD device driver). Fix the function to use only the maximum number of parameters. Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_eckd.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 59b4ecfb967b..ea0e565ebc9d 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -3093,16 +3093,20 @@ dasd_eckd_dump_sense_dbf(struct dasd_device *device, struct irb *irb, char *reason) { u64 *sense; + u32 stat; sense = (u64 *) dasd_get_sense(irb); + stat = scsw_cstat(&irb->scsw); + stat <<= 8; + stat |= scsw_dstat(&irb->scsw); + stat <<= 8; + stat |= scsw_cc(&irb->scsw); + if (sense) { DBF_DEV_EVENT(DBF_EMERG, device, - "%s: %s %02x%02x%02x %016llx %016llx %016llx " - "%016llx", reason, - scsw_is_tm(&irb->scsw) ? "t" : "c", - scsw_cc(&irb->scsw), scsw_cstat(&irb->scsw), - scsw_dstat(&irb->scsw), sense[0], sense[1], - sense[2], sense[3]); + "%s: %s %06x %016llx %016llx %016llx %016llx", + reason, scsw_is_tm(&irb->scsw) ? "t" : "c", stat, + sense[0], sense[1], sense[2], sense[3]); } else { DBF_DEV_EVENT(DBF_EMERG, device, "%s", "SORRY - NO VALID SENSE AVAILABLE\n"); -- cgit v1.2.3 From f2777077aa1f6f8a7c76b83f240975289a9fb894 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 25 Oct 2010 16:10:25 +0200 Subject: [S390] cio: remove custom implementation of hex_to_bin() Signed-off-by: Andy Shevchenko Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/blacklist.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c index 13cb60162e42..76058a5166ed 100644 --- a/drivers/s390/cio/blacklist.c +++ b/drivers/s390/cio/blacklist.c @@ -79,17 +79,15 @@ static int pure_hex(char **cp, unsigned int *val, int min_digit, int max_digit, int max_val) { int diff; - unsigned int value; diff = 0; *val = 0; - while (isxdigit(**cp) && (diff <= max_digit)) { + while (diff <= max_digit) { + int value = hex_to_bin(**cp); - if (isdigit(**cp)) - value = **cp - '0'; - else - value = tolower(**cp) - 'a' + 10; + if (value < 0) + break; *val = *val * 16 + value; (*cp)++; diff++; -- cgit v1.2.3 From 74b6127e6c35abf06364468636dd261850639f8b Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 25 Oct 2010 16:10:26 +0200 Subject: [S390] cio: fix memleak in resume path If a ccwdevice is lost during hibernation and a different ccwdevice is attached to the same subchannel, we will deregister the old ccw device and register the new one. Since deregistration is not allowed in this context, we handle this action later. However, some parts of the registration process for the new device were started anyway, so that the old device structure is no longer accessible. Fix this by deferring both actions to the afterwards scheduled subchannel event. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 51bd3687d163..07b1a074beaf 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1468,9 +1468,13 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process) goto out; break; case IO_SCH_UNREG_ATTACH: + if (cdev->private->flags.resuming) { + /* Device will be handled later. */ + rc = 0; + goto out; + } /* Unregister ccw device. */ - if (!cdev->private->flags.resuming) - ccw_device_unregister(cdev); + ccw_device_unregister(cdev); break; default: break; -- cgit v1.2.3 From b730f3a933958362ee1080c257f2cc158149310a Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 25 Oct 2010 16:10:27 +0200 Subject: [S390] cio: add lock to struct channel_path Serialize access to members of struct channel_path with a mutex. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/chp.c | 35 +++++++++++++++++++++++------------ drivers/s390/cio/chp.h | 6 ++++-- 2 files changed, 27 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index 6c9fa15aac7b..b1addd78689d 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -1,7 +1,7 @@ /* * drivers/s390/cio/chp.c * - * Copyright IBM Corp. 1999,2007 + * Copyright IBM Corp. 1999,2010 * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com) * Arnd Bergmann (arndb@de.ibm.com) * Peter Oberparleiter @@ -241,11 +241,13 @@ static ssize_t chp_status_show(struct device *dev, struct device_attribute *attr, char *buf) { struct channel_path *chp = to_channelpath(dev); + int status; - if (!chp) - return 0; - return (chp_get_status(chp->chpid) ? sprintf(buf, "online\n") : - sprintf(buf, "offline\n")); + mutex_lock(&chp->lock); + status = chp->state; + mutex_unlock(&chp->lock); + + return status ? sprintf(buf, "online\n") : sprintf(buf, "offline\n"); } static ssize_t chp_status_write(struct device *dev, @@ -261,15 +263,18 @@ static ssize_t chp_status_write(struct device *dev, if (!num_args) return count; - if (!strnicmp(cmd, "on", 2) || !strcmp(cmd, "1")) + if (!strnicmp(cmd, "on", 2) || !strcmp(cmd, "1")) { + mutex_lock(&cp->lock); error = s390_vary_chpid(cp->chpid, 1); - else if (!strnicmp(cmd, "off", 3) || !strcmp(cmd, "0")) + mutex_unlock(&cp->lock); + } else if (!strnicmp(cmd, "off", 3) || !strcmp(cmd, "0")) { + mutex_lock(&cp->lock); error = s390_vary_chpid(cp->chpid, 0); - else + mutex_unlock(&cp->lock); + } else error = -EINVAL; return error < 0 ? error : count; - } static DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write); @@ -315,10 +320,12 @@ static ssize_t chp_type_show(struct device *dev, struct device_attribute *attr, char *buf) { struct channel_path *chp = to_channelpath(dev); + u8 type; - if (!chp) - return 0; - return sprintf(buf, "%x\n", chp->desc.desc); + mutex_lock(&chp->lock); + type = chp->desc.desc; + mutex_unlock(&chp->lock); + return sprintf(buf, "%x\n", type); } static DEVICE_ATTR(type, 0444, chp_type_show, NULL); @@ -395,6 +402,7 @@ int chp_new(struct chp_id chpid) chp->state = 1; chp->dev.parent = &channel_subsystems[chpid.cssid]->device; chp->dev.release = chp_release; + mutex_init(&chp->lock); /* Obtain channel path description and fill it in. */ ret = chsc_determine_base_channel_path_desc(chpid, &chp->desc); @@ -464,7 +472,10 @@ void *chp_get_chp_desc(struct chp_id chpid) desc = kmalloc(sizeof(struct channel_path_desc), GFP_KERNEL); if (!desc) return NULL; + + mutex_lock(&chp->lock); memcpy(desc, &chp->desc, sizeof(struct channel_path_desc)); + mutex_unlock(&chp->lock); return desc; } diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h index 26c3d2246176..695ec20f1c59 100644 --- a/drivers/s390/cio/chp.h +++ b/drivers/s390/cio/chp.h @@ -1,7 +1,7 @@ /* * drivers/s390/cio/chp.h * - * Copyright IBM Corp. 2007 + * Copyright IBM Corp. 2007,2010 * Author(s): Peter Oberparleiter */ @@ -10,6 +10,7 @@ #include #include +#include #include #include "chsc.h" #include "css.h" @@ -40,14 +41,15 @@ static inline int chp_test_bit(u8 *bitmap, int num) struct channel_path { + struct device dev; struct chp_id chpid; + struct mutex lock; /* Serialize access to below members. */ 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); -- cgit v1.2.3 From 34aec07c170b972a29c954b37047184bd0f9f294 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 25 Oct 2010 16:10:28 +0200 Subject: [S390] chsc: initialization fixes This patch fixes: * kfree vs. free_page usage * structure definition for determine_css_characteristics * naming convention for the chsc init function * deregistration of crw handlers in the cleanup path Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/chsc.c | 12 ++++++------ drivers/s390/cio/chsc.h | 4 ++-- drivers/s390/cio/css.c | 18 +++++++++--------- 3 files changed, 17 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 4cbb1a6ca33c..f26cc3e16181 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -2,7 +2,7 @@ * drivers/s390/cio/chsc.c * S/390 common I/O routines -- channel subsystem call * - * Copyright IBM Corp. 1999,2008 + * Copyright IBM Corp. 1999,2010 * Author(s): Ingo Adlung (adlung@de.ibm.com) * Cornelia Huck (cornelia.huck@de.ibm.com) * Arnd Bergmann (arndb@de.ibm.com) @@ -813,7 +813,7 @@ out: return ret; } -int __init chsc_alloc_sei_area(void) +int __init chsc_init(void) { int ret; @@ -825,14 +825,14 @@ int __init chsc_alloc_sei_area(void) } ret = crw_register_handler(CRW_RSC_CSS, chsc_process_crw); if (ret) - kfree(sei_page); + free_page((unsigned long)sei_page); return ret; } -void __init chsc_free_sei_area(void) +void __init chsc_init_cleanup(void) { crw_unregister_handler(CRW_RSC_CSS); - kfree(sei_page); + free_page((unsigned long)sei_page); } int chsc_enable_facility(int operation_code) @@ -895,7 +895,7 @@ chsc_determine_css_characteristics(void) struct chsc_header response; u32 reserved4; u32 general_char[510]; - u32 chsc_char[518]; + u32 chsc_char[508]; } __attribute__ ((packed)) *scsc_area; scsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 5453013f094b..6d669dd0dd84 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -60,8 +60,8 @@ struct chsc_ssd_info { extern int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd); extern int chsc_determine_css_characteristics(void); -extern int chsc_alloc_sei_area(void); -extern void chsc_free_sei_area(void); +extern int chsc_init(void); +extern void chsc_init_cleanup(void); extern int chsc_enable_facility(int); struct channel_subsystem; diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 064d3003ed20..fa1ad3aab66f 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -1,7 +1,7 @@ /* * driver for channel subsystem * - * Copyright IBM Corp. 2002, 2009 + * Copyright IBM Corp. 2002, 2010 * * Author(s): Arnd Bergmann (arndb@de.ibm.com) * Cornelia Huck (cornelia.huck@de.ibm.com) @@ -863,14 +863,14 @@ static int __init css_bus_init(void) { int ret, i; + ret = chsc_init(); + if (ret) + return ret; + ret = chsc_determine_css_characteristics(); if (ret == -ENOMEM) goto out; - ret = chsc_alloc_sei_area(); - if (ret) - goto out; - /* Try to enable MSS. */ ret = chsc_enable_facility(CHSC_SDA_OC_MSS); if (ret) @@ -956,9 +956,9 @@ out_unregister: } bus_unregister(&css_bus_type); out: - crw_unregister_handler(CRW_RSC_CSS); - chsc_free_sei_area(); + crw_unregister_handler(CRW_RSC_SCH); idset_free(slow_subchannel_set); + chsc_init_cleanup(); pr_alert("The CSS device driver initialization failed with " "errno=%d\n", ret); return ret; @@ -978,9 +978,9 @@ static void __init css_bus_cleanup(void) device_unregister(&css->device); } bus_unregister(&css_bus_type); - crw_unregister_handler(CRW_RSC_CSS); - chsc_free_sei_area(); + crw_unregister_handler(CRW_RSC_SCH); idset_free(slow_subchannel_set); + chsc_init_cleanup(); isc_unregister(IO_SCH_ISC); } -- cgit v1.2.3 From 34196f82b16749e119db5572271944c4add0a9aa Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 25 Oct 2010 16:10:29 +0200 Subject: [S390] chsc: consolidate memory allocations Most wrappers around the channel subsystem call have their own logic to allocate memory (with proper alignment) or use preallocated or static memory. This patch converts most users of the channel subsystem call to use the same preallocated page (proteced by a spinlock). Note: The sei_page which is used in our crw handler to call "store event information" has to coexist, since a) in crw context, while accessing the sei_page, sleeping is allowed (which will conflict with the spinlock protection of the chsc_page) b) in crw context, while accessing the sei_page, channel subsystem calls are allowed (which itself would require the page). Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/chsc.c | 226 ++++++++++++++++++++++++------------------------ drivers/s390/cio/chsc.h | 2 +- drivers/s390/cio/css.c | 24 +---- 3 files changed, 116 insertions(+), 136 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index f26cc3e16181..d12c152cb691 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -29,8 +29,8 @@ #include "chsc.h" static void *sei_page; -static DEFINE_SPINLOCK(siosl_lock); -static DEFINE_SPINLOCK(sda_lock); +static void *chsc_page; +static DEFINE_SPINLOCK(chsc_page_lock); /** * chsc_error_from_response() - convert a chsc response to an error @@ -85,17 +85,15 @@ struct chsc_ssd_area { int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd) { - unsigned long page; struct chsc_ssd_area *ssd_area; int ccode; int ret; int i; int mask; - page = get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!page) - return -ENOMEM; - ssd_area = (struct chsc_ssd_area *) page; + spin_lock_irq(&chsc_page_lock); + memset(chsc_page, 0, PAGE_SIZE); + ssd_area = chsc_page; ssd_area->request.length = 0x0010; ssd_area->request.code = 0x0004; ssd_area->ssid = schid.ssid; @@ -106,25 +104,25 @@ int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd) /* Check response. */ if (ccode > 0) { ret = (ccode == 3) ? -ENODEV : -EBUSY; - goto out_free; + goto out; } ret = chsc_error_from_response(ssd_area->response.code); if (ret != 0) { CIO_MSG_EVENT(2, "chsc: ssd failed for 0.%x.%04x (rc=%04x)\n", schid.ssid, schid.sch_no, ssd_area->response.code); - goto out_free; + goto out; } if (!ssd_area->sch_valid) { ret = -ENODEV; - goto out_free; + goto out; } /* Copy data */ ret = 0; memset(ssd, 0, sizeof(struct chsc_ssd_info)); if ((ssd_area->st != SUBCHANNEL_TYPE_IO) && (ssd_area->st != SUBCHANNEL_TYPE_MSG)) - goto out_free; + goto out; ssd->path_mask = ssd_area->path_mask; ssd->fla_valid_mask = ssd_area->fla_valid_mask; for (i = 0; i < 8; i++) { @@ -136,8 +134,8 @@ int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd) if (ssd_area->fla_valid_mask & mask) ssd->fla[i] = ssd_area->fla[i]; } -out_free: - free_page(page); +out: + spin_unlock_irq(&chsc_page_lock); return ret; } @@ -552,7 +550,7 @@ cleanup: return ret; } -int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) +int __chsc_do_secm(struct channel_subsystem *css, int enable) { struct { struct chsc_header request; @@ -573,7 +571,9 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) } __attribute__ ((packed)) *secm_area; int ret, ccode; - secm_area = page; + spin_lock_irq(&chsc_page_lock); + memset(chsc_page, 0, PAGE_SIZE); + secm_area = chsc_page; secm_area->request.length = 0x0050; secm_area->request.code = 0x0016; @@ -584,8 +584,10 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) secm_area->operation_code = enable ? 0 : 1; ccode = chsc(secm_area); - if (ccode > 0) - return (ccode == 3) ? -ENODEV : -EBUSY; + if (ccode > 0) { + ret = (ccode == 3) ? -ENODEV : -EBUSY; + goto out; + } switch (secm_area->response.code) { case 0x0102: @@ -598,37 +600,32 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) if (ret != 0) CIO_CRW_EVENT(2, "chsc: secm failed (rc=%04x)\n", secm_area->response.code); +out: + spin_unlock_irq(&chsc_page_lock); return ret; } int chsc_secm(struct channel_subsystem *css, int enable) { - void *secm_area; int ret; - secm_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!secm_area) - return -ENOMEM; - if (enable && !css->cm_enabled) { css->cub_addr1 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); css->cub_addr2 = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!css->cub_addr1 || !css->cub_addr2) { free_page((unsigned long)css->cub_addr1); free_page((unsigned long)css->cub_addr2); - free_page((unsigned long)secm_area); return -ENOMEM; } } - ret = __chsc_do_secm(css, enable, secm_area); + ret = __chsc_do_secm(css, enable); if (!ret) { css->cm_enabled = enable; if (css->cm_enabled) { ret = chsc_add_cmg_attr(css); if (ret) { - memset(secm_area, 0, PAGE_SIZE); - __chsc_do_secm(css, 0, secm_area); + __chsc_do_secm(css, 0); css->cm_enabled = 0; } } else @@ -638,7 +635,6 @@ chsc_secm(struct channel_subsystem *css, int enable) free_page((unsigned long)css->cub_addr1); free_page((unsigned long)css->cub_addr2); } - free_page((unsigned long)secm_area); return ret; } @@ -669,13 +665,12 @@ int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, return -EINVAL; if ((rfmt == 2) && !css_general_characteristics.cib) return -EINVAL; - scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!scpd_area) - return -ENOMEM; + spin_lock_irq(&chsc_page_lock); + memset(chsc_page, 0, PAGE_SIZE); + scpd_area = chsc_page; scpd_area->request.length = 0x0010; scpd_area->request.code = 0x0002; - scpd_area->cssid = chpid.cssid; scpd_area->first_chpid = chpid.id; scpd_area->last_chpid = chpid.id; @@ -698,7 +693,7 @@ int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, CIO_CRW_EVENT(2, "chsc: scpd failed (rc=%04x)\n", scpd_area->response.code); out: - free_page((unsigned long)scpd_area); + spin_unlock_irq(&chsc_page_lock); return ret; } EXPORT_SYMBOL_GPL(chsc_determine_channel_path_desc); @@ -725,33 +720,22 @@ static void chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv, struct cmg_chars *chars) { - switch (chp->cmg) { - case 2: - case 3: - chp->cmg_chars = kmalloc(sizeof(struct cmg_chars), - GFP_KERNEL); - if (chp->cmg_chars) { - int i, mask; - struct cmg_chars *cmg_chars; - - cmg_chars = chp->cmg_chars; - for (i = 0; i < NR_MEASUREMENT_CHARS; i++) { - mask = 0x80 >> (i + 3); - if (cmcv & mask) - cmg_chars->values[i] = chars->values[i]; - else - cmg_chars->values[i] = 0; - } - } - break; - default: - /* No cmg-dependent data. */ - break; + struct cmg_chars *cmg_chars; + int i, mask; + + cmg_chars = chp->cmg_chars; + for (i = 0; i < NR_MEASUREMENT_CHARS; i++) { + mask = 0x80 >> (i + 3); + if (cmcv & mask) + cmg_chars->values[i] = chars->values[i]; + else + cmg_chars->values[i] = 0; } } int chsc_get_channel_measurement_chars(struct channel_path *chp) { + struct cmg_chars *cmg_chars; int ccode, ret; struct { @@ -775,13 +759,16 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp) u32 data[NR_MEASUREMENT_CHARS]; } __attribute__ ((packed)) *scmc_area; - scmc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!scmc_area) + chp->cmg_chars = NULL; + cmg_chars = kmalloc(sizeof(*cmg_chars), GFP_KERNEL); + if (!cmg_chars) return -ENOMEM; + spin_lock_irq(&chsc_page_lock); + memset(chsc_page, 0, PAGE_SIZE); + scmc_area = chsc_page; scmc_area->request.length = 0x0010; scmc_area->request.code = 0x0022; - scmc_area->first_chpid = chp->chpid.id; scmc_area->last_chpid = chp->chpid.id; @@ -792,24 +779,30 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp) } ret = chsc_error_from_response(scmc_area->response.code); - if (ret == 0) { - /* Success. */ - if (!scmc_area->not_valid) { - chp->cmg = scmc_area->cmg; - chp->shared = scmc_area->shared; - chsc_initialize_cmg_chars(chp, scmc_area->cmcv, - (struct cmg_chars *) - &scmc_area->data); - } else { - chp->cmg = -1; - chp->shared = -1; - } - } else { + if (ret) { CIO_CRW_EVENT(2, "chsc: scmc failed (rc=%04x)\n", scmc_area->response.code); + goto out; + } + if (scmc_area->not_valid) { + chp->cmg = -1; + chp->shared = -1; + goto out; + } + chp->cmg = scmc_area->cmg; + chp->shared = scmc_area->shared; + if (chp->cmg != 2 && chp->cmg != 3) { + /* No cmg-dependent data. */ + goto out; } + chp->cmg_chars = cmg_chars; + chsc_initialize_cmg_chars(chp, scmc_area->cmcv, + (struct cmg_chars *) &scmc_area->data); out: - free_page((unsigned long)scmc_area); + spin_unlock_irq(&chsc_page_lock); + if (!chp->cmg_chars) + kfree(cmg_chars); + return ret; } @@ -818,27 +811,33 @@ int __init chsc_init(void) int ret; sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!sei_page) { - CIO_MSG_EVENT(0, "Can't allocate page for processing of " - "chsc machine checks!\n"); - return -ENOMEM; + chsc_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sei_page || !chsc_page) { + ret = -ENOMEM; + goto out_err; } ret = crw_register_handler(CRW_RSC_CSS, chsc_process_crw); if (ret) - free_page((unsigned long)sei_page); + goto out_err; + return ret; +out_err: + free_page((unsigned long)chsc_page); + free_page((unsigned long)sei_page); return ret; } void __init chsc_init_cleanup(void) { crw_unregister_handler(CRW_RSC_CSS); + free_page((unsigned long)chsc_page); free_page((unsigned long)sei_page); } int chsc_enable_facility(int operation_code) { + unsigned long flags; int ret; - static struct { + struct { struct chsc_header request; u8 reserved1:4; u8 format:4; @@ -851,32 +850,33 @@ int chsc_enable_facility(int operation_code) u32 reserved5:4; u32 format2:4; u32 reserved6:24; - } __attribute__ ((packed, aligned(4096))) sda_area; + } __attribute__ ((packed)) *sda_area; - spin_lock(&sda_lock); - memset(&sda_area, 0, sizeof(sda_area)); - sda_area.request.length = 0x0400; - sda_area.request.code = 0x0031; - sda_area.operation_code = operation_code; + spin_lock_irqsave(&chsc_page_lock, flags); + memset(chsc_page, 0, PAGE_SIZE); + sda_area = chsc_page; + sda_area->request.length = 0x0400; + sda_area->request.code = 0x0031; + sda_area->operation_code = operation_code; - ret = chsc(&sda_area); + ret = chsc(sda_area); if (ret > 0) { ret = (ret == 3) ? -ENODEV : -EBUSY; goto out; } - switch (sda_area.response.code) { + switch (sda_area->response.code) { case 0x0101: ret = -EOPNOTSUPP; break; default: - ret = chsc_error_from_response(sda_area.response.code); + ret = chsc_error_from_response(sda_area->response.code); } if (ret != 0) CIO_CRW_EVENT(2, "chsc: sda (oc=%x) failed (rc=%04x)\n", - operation_code, sda_area.response.code); - out: - spin_unlock(&sda_lock); + operation_code, sda_area->response.code); +out: + spin_unlock_irqrestore(&chsc_page_lock, flags); return ret; } @@ -898,10 +898,9 @@ chsc_determine_css_characteristics(void) u32 chsc_char[508]; } __attribute__ ((packed)) *scsc_area; - scsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!scsc_area) - return -ENOMEM; - + spin_lock_irq(&chsc_page_lock); + memset(chsc_page, 0, PAGE_SIZE); + scsc_area = chsc_page; scsc_area->request.length = 0x0010; scsc_area->request.code = 0x0010; @@ -921,7 +920,7 @@ chsc_determine_css_characteristics(void) CIO_CRW_EVENT(2, "chsc: scsc failed (rc=%04x)\n", scsc_area->response.code); exit: - free_page ((unsigned long) scsc_area); + spin_unlock_irq(&chsc_page_lock); return result; } @@ -976,29 +975,29 @@ int chsc_sstpi(void *page, void *result, size_t size) return (rr->response.code == 0x0001) ? 0 : -EIO; } -static struct { - struct chsc_header request; - u32 word1; - struct subchannel_id sid; - u32 word3; - struct chsc_header response; - u32 word[11]; -} __attribute__ ((packed)) siosl_area __attribute__ ((__aligned__(PAGE_SIZE))); - int chsc_siosl(struct subchannel_id schid) { + struct { + struct chsc_header request; + u32 word1; + struct subchannel_id sid; + u32 word3; + struct chsc_header response; + u32 word[11]; + } __attribute__ ((packed)) *siosl_area; unsigned long flags; int ccode; int rc; - spin_lock_irqsave(&siosl_lock, flags); - memset(&siosl_area, 0, sizeof(siosl_area)); - siosl_area.request.length = 0x0010; - siosl_area.request.code = 0x0046; - siosl_area.word1 = 0x80000000; - siosl_area.sid = schid; + spin_lock_irqsave(&chsc_page_lock, flags); + memset(chsc_page, 0, PAGE_SIZE); + siosl_area = chsc_page; + siosl_area->request.length = 0x0010; + siosl_area->request.code = 0x0046; + siosl_area->word1 = 0x80000000; + siosl_area->sid = schid; - ccode = chsc(&siosl_area); + ccode = chsc(siosl_area); if (ccode > 0) { if (ccode == 3) rc = -ENODEV; @@ -1008,17 +1007,16 @@ int chsc_siosl(struct subchannel_id schid) schid.ssid, schid.sch_no, ccode); goto out; } - rc = chsc_error_from_response(siosl_area.response.code); + rc = chsc_error_from_response(siosl_area->response.code); if (rc) CIO_MSG_EVENT(2, "chsc: siosl failed for 0.%x.%04x (rc=%04x)\n", schid.ssid, schid.sch_no, - siosl_area.response.code); + siosl_area->response.code); else CIO_MSG_EVENT(4, "chsc: siosl succeeded for 0.%x.%04x\n", schid.ssid, schid.sch_no); out: - spin_unlock_irqrestore(&siosl_lock, flags); - + spin_unlock_irqrestore(&chsc_page_lock, flags); return rc; } EXPORT_SYMBOL_GPL(chsc_siosl); diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 6d669dd0dd84..852b61fc56ea 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -66,7 +66,7 @@ extern void chsc_init_cleanup(void); extern int chsc_enable_facility(int); struct channel_subsystem; extern int chsc_secm(struct channel_subsystem *, int); -int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page); +int __chsc_do_secm(struct channel_subsystem *css, int enable); int chsc_chp_vary(struct chp_id chpid, int on); int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index fa1ad3aab66f..5e1235c6aba0 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -790,7 +790,6 @@ static struct notifier_block css_reboot_notifier = { static int css_power_event(struct notifier_block *this, unsigned long event, void *ptr) { - void *secm_area; int ret, i; switch (event) { @@ -806,15 +805,8 @@ static int css_power_event(struct notifier_block *this, unsigned long event, mutex_unlock(&css->mutex); continue; } - secm_area = (void *)get_zeroed_page(GFP_KERNEL | - GFP_DMA); - if (secm_area) { - if (__chsc_do_secm(css, 0, secm_area)) - ret = NOTIFY_BAD; - free_page((unsigned long)secm_area); - } else + if (__chsc_do_secm(css, 0)) ret = NOTIFY_BAD; - mutex_unlock(&css->mutex); } break; @@ -830,15 +822,8 @@ static int css_power_event(struct notifier_block *this, unsigned long event, mutex_unlock(&css->mutex); continue; } - secm_area = (void *)get_zeroed_page(GFP_KERNEL | - GFP_DMA); - if (secm_area) { - if (__chsc_do_secm(css, 1, secm_area)) - ret = NOTIFY_BAD; - free_page((unsigned long)secm_area); - } else + if (__chsc_do_secm(css, 1)) ret = NOTIFY_BAD; - mutex_unlock(&css->mutex); } /* search for subchannels, which appeared during hibernation */ @@ -867,10 +852,7 @@ static int __init css_bus_init(void) if (ret) return ret; - ret = chsc_determine_css_characteristics(); - if (ret == -ENOMEM) - goto out; - + chsc_determine_css_characteristics(); /* Try to enable MSS. */ ret = chsc_enable_facility(CHSC_SDA_OC_MSS); if (ret) -- cgit v1.2.3 From 906c9768c7750ce76b85597174b9c3599a6ca9f6 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 25 Oct 2010 16:10:30 +0200 Subject: [S390] chsc: use the global page to determine the chp desriptor chsc_determine_channel_path_desc is called by a wrapper who allocates a response struct. The response data is then memcpy'ed to this response struct by chsc_determine_channel_path_desc. Change chsc_determine_base_channel_path_desc to use the global chsc_page and deliver it to the function doing the actual chsc call. The channel path desriptor is then directly read from the response data. As a result we get rid of the additional allocation for the response struct. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/chsc.c | 54 +++++++++++++-------------------------------- drivers/s390/cio/chsc.h | 22 ++++++++++++++++-- drivers/s390/cio/chsc_sch.c | 12 +++++++--- 3 files changed, 44 insertions(+), 44 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index d12c152cb691..27aca3906a53 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -639,36 +639,18 @@ chsc_secm(struct channel_subsystem *css, int enable) } int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, - int c, int m, - struct chsc_response_struct *resp) + int c, int m, void *page) { + struct chsc_scpd *scpd_area; int ccode, ret; - struct { - struct chsc_header request; - u32 : 2; - u32 m : 1; - u32 c : 1; - u32 fmt : 4; - u32 cssid : 8; - u32 : 4; - u32 rfmt : 4; - u32 first_chpid : 8; - u32 : 24; - u32 last_chpid : 8; - u32 zeroes1; - struct chsc_header response; - u8 data[PAGE_SIZE - 20]; - } __attribute__ ((packed)) *scpd_area; - if ((rfmt == 1) && !css_general_characteristics.fcs) return -EINVAL; if ((rfmt == 2) && !css_general_characteristics.cib) return -EINVAL; - spin_lock_irq(&chsc_page_lock); - memset(chsc_page, 0, PAGE_SIZE); - scpd_area = chsc_page; + memset(page, 0, PAGE_SIZE); + scpd_area = page; scpd_area->request.length = 0x0010; scpd_area->request.code = 0x0002; scpd_area->cssid = chpid.cssid; @@ -680,20 +662,13 @@ int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, scpd_area->rfmt = rfmt; ccode = chsc(scpd_area); - if (ccode > 0) { - ret = (ccode == 3) ? -ENODEV : -EBUSY; - goto out; - } + if (ccode > 0) + return (ccode == 3) ? -ENODEV : -EBUSY; ret = chsc_error_from_response(scpd_area->response.code); - if (ret == 0) - /* Success. */ - memcpy(resp, &scpd_area->response, scpd_area->response.length); - else + if (ret) CIO_CRW_EVENT(2, "chsc: scpd failed (rc=%04x)\n", scpd_area->response.code); -out: - spin_unlock_irq(&chsc_page_lock); return ret; } EXPORT_SYMBOL_GPL(chsc_determine_channel_path_desc); @@ -702,17 +677,18 @@ int chsc_determine_base_channel_path_desc(struct chp_id chpid, struct channel_path_desc *desc) { struct chsc_response_struct *chsc_resp; + struct chsc_scpd *scpd_area; int ret; - chsc_resp = kzalloc(sizeof(*chsc_resp), GFP_KERNEL); - if (!chsc_resp) - return -ENOMEM; - ret = chsc_determine_channel_path_desc(chpid, 0, 0, 0, 0, chsc_resp); + spin_lock_irq(&chsc_page_lock); + scpd_area = chsc_page; + ret = chsc_determine_channel_path_desc(chpid, 0, 0, 0, 0, scpd_area); if (ret) - goto out_free; + goto out; + chsc_resp = (void *)&scpd_area->response; memcpy(desc, &chsc_resp->data, sizeof(*desc)); -out_free: - kfree(chsc_resp); +out: + spin_unlock_irq(&chsc_page_lock); return ret; } diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 852b61fc56ea..6693f5e3176f 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -57,6 +57,25 @@ struct chsc_ssd_info { struct chp_id chpid[8]; u16 fla[8]; }; + +struct chsc_scpd { + struct chsc_header request; + u32:2; + u32 m:1; + u32 c:1; + u32 fmt:4; + u32 cssid:8; + u32:4; + u32 rfmt:4; + u32 first_chpid:8; + u32:24; + u32 last_chpid:8; + u32 zeroes1; + struct chsc_header response; + u8 data[PAGE_SIZE - 20]; +} __attribute__ ((packed)); + + extern int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd); extern int chsc_determine_css_characteristics(void); @@ -70,8 +89,7 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable); int chsc_chp_vary(struct chp_id chpid, int on); int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, - int c, int m, - struct chsc_response_struct *resp); + int c, int m, void *page); int chsc_determine_base_channel_path_desc(struct chp_id chpid, struct channel_path_desc *desc); void chsc_chp_online(struct chp_id chpid); diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c index f2b77e7bfc6f..3c3f3ffe2179 100644 --- a/drivers/s390/cio/chsc_sch.c +++ b/drivers/s390/cio/chsc_sch.c @@ -688,25 +688,31 @@ out_free: static int chsc_ioctl_chpd(void __user *user_chpd) { + struct chsc_scpd *scpd_area; struct chsc_cpd_info *chpd; int ret; chpd = kzalloc(sizeof(*chpd), GFP_KERNEL); - if (!chpd) - return -ENOMEM; + scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!scpd_area || !chpd) { + ret = -ENOMEM; + goto out_free; + } if (copy_from_user(chpd, user_chpd, sizeof(*chpd))) { ret = -EFAULT; goto out_free; } ret = chsc_determine_channel_path_desc(chpd->chpid, chpd->fmt, chpd->rfmt, chpd->c, chpd->m, - &chpd->chpdb); + scpd_area); if (ret) goto out_free; + memcpy(&chpd->chpdb, &scpd_area->response, scpd_area->response.length); if (copy_to_user(user_chpd, chpd, sizeof(*chpd))) ret = -EFAULT; out_free: kfree(chpd); + free_page((unsigned long)scpd_area); return ret; } -- cgit v1.2.3 From c38a90a34cfc880eb2f7234c511cdb7d8bdebc45 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 25 Oct 2010 16:10:31 +0200 Subject: [S390] cio: update descriptor in chsc_chp_vary Update the channel path descriptor at the beginning of to the vary_on operation. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/chp.c | 6 ------ drivers/s390/cio/chp.h | 6 ++++++ drivers/s390/cio/chsc.c | 8 +++++--- 3 files changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index b1addd78689d..2d32233943a9 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -54,12 +54,6 @@ 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) -{ - return channel_subsystems[chpid.cssid]->chps[chpid.id]; -} - /* Set vary state for given chpid. */ static void set_chp_logically_online(struct chp_id chpid, int onoff) { diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h index 695ec20f1c59..12b4903d6fe3 100644 --- a/drivers/s390/cio/chp.h +++ b/drivers/s390/cio/chp.h @@ -52,6 +52,12 @@ struct channel_path { void *cmg_chars; }; +/* Return channel_path struct for given chpid. */ +static inline struct channel_path *chpid_to_chp(struct chp_id chpid) +{ + return channel_subsystems[chpid.cssid]->chps[chpid.id]; +} + int chp_get_status(struct chp_id chpid); u8 chp_get_sch_opm(struct subchannel *sch); int chp_is_registered(struct chp_id chpid); diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 27aca3906a53..44d7cc2f9738 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -495,6 +495,7 @@ __s390_vary_chpid_on(struct subchannel_id schid, void *data) */ int chsc_chp_vary(struct chp_id chpid, int on) { + struct channel_path *chp = chpid_to_chp(chpid); struct chp_link link; memset(&link, 0, sizeof(struct chp_link)); @@ -504,11 +505,12 @@ int chsc_chp_vary(struct chp_id chpid, int on) /* * Redo PathVerification on the devices the chpid connects to */ - - if (on) + if (on) { + /* Try to update the channel path descritor. */ + chsc_determine_base_channel_path_desc(chpid, &chp->desc); for_each_subchannel_staged(s390_subchannel_vary_chpid_on, __s390_vary_chpid_on, &link); - else + } else for_each_subchannel_staged(s390_subchannel_vary_chpid_off, NULL, &link); -- cgit v1.2.3 From 62da177ac2bc1f6f3707230070af268b1e689651 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 25 Oct 2010 16:10:32 +0200 Subject: [S390] css: update descriptor after hibernate Update the channel path descriptors after hibernation. This is done unlocked, since we are the only active task at this time. Note: chsc_determine_base_channel_path_desc is changed to use spin_lock_irqsave, since it's called with interrupts disabled in this case. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/chsc.c | 5 +++-- drivers/s390/cio/css.c | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 44d7cc2f9738..1aaddea673e0 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -680,9 +680,10 @@ int chsc_determine_base_channel_path_desc(struct chp_id chpid, { struct chsc_response_struct *chsc_resp; struct chsc_scpd *scpd_area; + unsigned long flags; int ret; - spin_lock_irq(&chsc_page_lock); + spin_lock_irqsave(&chsc_page_lock, flags); scpd_area = chsc_page; ret = chsc_determine_channel_path_desc(chpid, 0, 0, 0, 0, scpd_area); if (ret) @@ -690,7 +691,7 @@ int chsc_determine_base_channel_path_desc(struct chp_id chpid, chsc_resp = (void *)&scpd_area->response; memcpy(desc, &chsc_resp->data, sizeof(*desc)); out: - spin_unlock_irq(&chsc_page_lock); + spin_unlock_irqrestore(&chsc_page_lock, flags); return ret; } diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 5e1235c6aba0..771576bb2928 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -1030,7 +1030,16 @@ subsys_initcall_sync(channel_subsystem_init_sync); void channel_subsystem_reinit(void) { + struct channel_path *chp; + struct chp_id chpid; + chsc_enable_facility(CHSC_SDA_OC_MSS); + chp_id_for_each(&chpid) { + chp = chpid_to_chp(chpid); + if (!chp) + continue; + chsc_determine_base_channel_path_desc(chpid, &chp->desc); + } } #ifdef CONFIG_PROC_FS -- cgit v1.2.3 From eb4f5d93d70458ea644e0f46737f0bcf87280e83 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 25 Oct 2010 16:10:33 +0200 Subject: [S390] css: update subchannel description after hibernate Update the subchannel descriptor while resuming from hibernate in order to obtain current link addresses. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/css.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 771576bb2928..a5050e217150 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -1191,6 +1191,7 @@ static int css_pm_restore(struct device *dev) struct subchannel *sch = to_subchannel(dev); struct css_driver *drv; + css_update_ssd_info(sch); if (!sch->dev.driver) return 0; drv = to_cssdriver(sch->dev.driver); -- cgit v1.2.3 From 585b954e1f2fa325d425b0786e4525ac7c9ae575 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 25 Oct 2010 16:10:34 +0200 Subject: [S390] cio: notify drivers of channel path events This patch adds a notification mechanism to inform ccw drivers about changes to channel paths, which occured while the device is online. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/ccwdev.h | 12 ++++++++++++ drivers/s390/cio/device.c | 9 +++++++++ drivers/s390/cio/device_fsm.c | 32 ++++++++++++++++++++++++++++++++ drivers/s390/cio/device_pgid.c | 23 +++++++++++++++++------ drivers/s390/cio/io_sch.h | 7 +++++-- 5 files changed, 75 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/arch/s390/include/asm/ccwdev.h b/arch/s390/include/asm/ccwdev.h index f3ba0fa98de6..e8501115eca8 100644 --- a/arch/s390/include/asm/ccwdev.h +++ b/arch/s390/include/asm/ccwdev.h @@ -91,6 +91,16 @@ struct ccw_device { void (*handler) (struct ccw_device *, unsigned long, struct irb *); }; +/* + * Possible events used by the path_event notifier. + */ +#define PE_NONE 0x0 +#define PE_PATH_GONE 0x1 /* A path is no longer available. */ +#define PE_PATH_AVAILABLE 0x2 /* A path has become available and + was successfully verified. */ +#define PE_PATHGROUP_ESTABLISHED 0x4 /* A pathgroup was reset and had + to be established again. */ + /* * Possible CIO actions triggered by the unit check handler. */ @@ -109,6 +119,7 @@ enum uc_todo { * @set_online: called when setting device online * @set_offline: called when setting device offline * @notify: notify driver of device state changes + * @path_event: notify driver of channel path events * @shutdown: called at device shutdown * @prepare: prepare for pm state transition * @complete: undo work done in @prepare @@ -127,6 +138,7 @@ struct ccw_driver { int (*set_online) (struct ccw_device *); int (*set_offline) (struct ccw_device *); int (*notify) (struct ccw_device *, int); + void (*path_event) (struct ccw_device *, int *); void (*shutdown) (struct ccw_device *); int (*prepare) (struct ccw_device *); void (*complete) (struct ccw_device *); diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 07b1a074beaf..881bdfd99140 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1147,6 +1147,7 @@ err: static int io_subchannel_chp_event(struct subchannel *sch, struct chp_link *link, int event) { + struct ccw_device *cdev = sch_get_cdev(sch); int mask; mask = chp_ssd_get_mask(&sch->ssd_info, link); @@ -1156,22 +1157,30 @@ static int io_subchannel_chp_event(struct subchannel *sch, case CHP_VARY_OFF: sch->opm &= ~mask; sch->lpm &= ~mask; + if (cdev) + cdev->private->path_gone_mask |= mask; io_subchannel_terminate_path(sch, mask); break; case CHP_VARY_ON: sch->opm |= mask; sch->lpm |= mask; + if (cdev) + cdev->private->path_new_mask |= mask; io_subchannel_verify(sch); break; case CHP_OFFLINE: if (cio_update_schib(sch)) return -ENODEV; + if (cdev) + cdev->private->path_gone_mask |= mask; io_subchannel_terminate_path(sch, mask); break; case CHP_ONLINE: if (cio_update_schib(sch)) return -ENODEV; sch->lpm |= mask & sch->opm; + if (cdev) + cdev->private->path_new_mask |= mask; io_subchannel_verify(sch); break; } diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index c9b852647f01..4395c01a9dac 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -349,9 +349,13 @@ out: static void ccw_device_oper_notify(struct ccw_device *cdev) { + struct subchannel *sch = to_subchannel(cdev->dev.parent); + if (ccw_device_notify(cdev, CIO_OPER) == NOTIFY_OK) { /* Reenable channel measurements, if needed. */ ccw_device_sched_todo(cdev, CDEV_TODO_ENABLE_CMF); + /* Save indication for new paths. */ + cdev->private->path_new_mask = sch->vpm; return; } /* Driver doesn't want device back. */ @@ -462,6 +466,32 @@ static void ccw_device_request_event(struct ccw_device *cdev, enum dev_event e) } } +static void ccw_device_report_path_events(struct ccw_device *cdev) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + int path_event[8]; + int chp, mask; + + for (chp = 0, mask = 0x80; chp < 8; chp++, mask >>= 1) { + path_event[chp] = PE_NONE; + if (mask & cdev->private->path_gone_mask & ~(sch->vpm)) + path_event[chp] |= PE_PATH_GONE; + if (mask & cdev->private->path_new_mask & sch->vpm) + path_event[chp] |= PE_PATH_AVAILABLE; + if (mask & cdev->private->pgid_reset_mask & sch->vpm) + path_event[chp] |= PE_PATHGROUP_ESTABLISHED; + } + if (cdev->online && cdev->drv->path_event) + cdev->drv->path_event(cdev, path_event); +} + +static void ccw_device_reset_path_events(struct ccw_device *cdev) +{ + cdev->private->path_gone_mask = 0; + cdev->private->path_new_mask = 0; + cdev->private->pgid_reset_mask = 0; +} + void ccw_device_verify_done(struct ccw_device *cdev, int err) { @@ -498,6 +528,7 @@ callback: &cdev->private->irb); memset(&cdev->private->irb, 0, sizeof(struct irb)); } + ccw_device_report_path_events(cdev); break; case -ETIME: case -EUSERS: @@ -516,6 +547,7 @@ callback: ccw_device_done(cdev, DEV_STATE_NOT_OPER); break; } + ccw_device_reset_path_events(cdev); } /* diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index 82a5ad0d63f6..07a4fd29f096 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -213,6 +213,17 @@ static void spid_start(struct ccw_device *cdev) spid_do(cdev); } +static int pgid_is_reset(struct pgid *p) +{ + char *c; + + for (c = (char *)p + 1; c < (char *)(p + 1); c++) { + if (*c != 0) + return 0; + } + return 1; +} + static int pgid_cmp(struct pgid *p1, struct pgid *p2) { return memcmp((char *) p1 + 1, (char *) p2 + 1, @@ -223,7 +234,7 @@ static int pgid_cmp(struct pgid *p1, struct pgid *p2) * Determine pathgroup state from PGID data. */ static void pgid_analyze(struct ccw_device *cdev, struct pgid **p, - int *mismatch, int *reserved, int *reset) + int *mismatch, int *reserved, u8 *reset) { struct pgid *pgid = &cdev->private->pgid[0]; struct pgid *first = NULL; @@ -238,9 +249,8 @@ static void pgid_analyze(struct ccw_device *cdev, struct pgid **p, continue; if (pgid->inf.ps.state2 == SNID_STATE2_RESVD_ELSE) *reserved = 1; - if (pgid->inf.ps.state1 == SNID_STATE1_RESET) { - /* A PGID was reset. */ - *reset = 1; + if (pgid_is_reset(pgid)) { + *reset |= lpm; continue; } if (!first) { @@ -307,7 +317,7 @@ static void snid_done(struct ccw_device *cdev, int rc) struct pgid *pgid; int mismatch = 0; int reserved = 0; - int reset = 0; + u8 reset = 0; u8 donepm; if (rc) @@ -321,11 +331,12 @@ static void snid_done(struct ccw_device *cdev, int rc) donepm = pgid_to_donepm(cdev); sch->vpm = donepm & sch->opm; cdev->private->pgid_todo_mask &= ~donepm; + cdev->private->pgid_reset_mask |= reset; pgid_fill(cdev, pgid); } out: CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x vpm=%02x " - "todo=%02x mism=%d rsvd=%d reset=%d\n", id->ssid, + "todo=%02x mism=%d rsvd=%d reset=%02x\n", id->ssid, id->devno, rc, cdev->private->pgid_valid_mask, sch->vpm, cdev->private->pgid_todo_mask, mismatch, reserved, reset); switch (rc) { diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index 469ef93f2302..d024d2c21897 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h @@ -151,8 +151,11 @@ struct ccw_device_private { struct subchannel_id schid; /* subchannel number */ struct ccw_request req; /* internal I/O request */ int iretry; - u8 pgid_valid_mask; /* mask of valid PGIDs */ - u8 pgid_todo_mask; /* mask of PGIDs to be adjusted */ + u8 pgid_valid_mask; /* mask of valid PGIDs */ + u8 pgid_todo_mask; /* mask of PGIDs to be adjusted */ + u8 pgid_reset_mask; /* mask of PGIDs which were reset */ + u8 path_gone_mask; /* mask of paths, that became unavailable */ + u8 path_new_mask; /* mask of paths, that became available */ struct { unsigned int fast:1; /* post with "channel end" */ unsigned int repall:1; /* report every interrupt status */ -- cgit v1.2.3 From f6649a7e5a9ee99e9623878f4a5579cc2f6cdd51 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Mon, 25 Oct 2010 16:10:38 +0200 Subject: [S390] cleanup lowcore access from external interrupts Read external interrupts parameters from the lowcore in the first level interrupt handler in entry[64].S. Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/s390_ext.h | 2 +- arch/s390/kernel/asm-offsets.c | 2 -- arch/s390/kernel/entry.S | 3 ++- arch/s390/kernel/entry.h | 2 +- arch/s390/kernel/entry64.S | 5 ++++- arch/s390/kernel/s390_ext.c | 9 ++++++--- arch/s390/kernel/smp.c | 3 ++- arch/s390/kernel/time.c | 17 +++++++++-------- arch/s390/kernel/vtime.c | 3 ++- arch/s390/mm/fault.c | 11 ++++++++--- drivers/s390/block/dasd_diag.c | 19 ++++++++----------- drivers/s390/block/dasd_diag.h | 4 ---- drivers/s390/char/sclp.c | 14 +++++++------- drivers/s390/kvm/kvm_virtio.c | 9 +++++---- net/iucv/iucv.c | 3 ++- 15 files changed, 57 insertions(+), 49 deletions(-) (limited to 'drivers') diff --git a/arch/s390/include/asm/s390_ext.h b/arch/s390/include/asm/s390_ext.h index 2afc060266a2..1a9307e70842 100644 --- a/arch/s390/include/asm/s390_ext.h +++ b/arch/s390/include/asm/s390_ext.h @@ -12,7 +12,7 @@ #include -typedef void (*ext_int_handler_t)(__u16 code); +typedef void (*ext_int_handler_t)(unsigned int, unsigned int, unsigned long); typedef struct ext_int_info_t { struct ext_int_info_t *next; diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c index 76328deb31f7..f3c1b823c9a8 100644 --- a/arch/s390/kernel/asm-offsets.c +++ b/arch/s390/kernel/asm-offsets.c @@ -143,10 +143,8 @@ int main(void) DEFINE(__LC_GPREGS_SAVE_AREA, offsetof(struct _lowcore, gpregs_save_area)); DEFINE(__LC_CREGS_SAVE_AREA, offsetof(struct _lowcore, cregs_save_area)); #ifdef CONFIG_32BIT - DEFINE(__LC_PFAULT_INTPARM, offsetof(struct _lowcore, ext_params)); DEFINE(SAVE_AREA_BASE, offsetof(struct _lowcore, extended_save_area_addr)); #else /* CONFIG_32BIT */ - DEFINE(__LC_PFAULT_INTPARM, offsetof(struct _lowcore, ext_params2)); DEFINE(__LC_EXT_PARAMS2, offsetof(struct _lowcore, ext_params2)); DEFINE(SAVE_AREA_BASE, offsetof(struct _lowcore, floating_pt_save_area)); DEFINE(__LC_PASTE, offsetof(struct _lowcore, paste)); diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index adf25246f72e..736d7010629e 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -722,7 +722,8 @@ ext_no_vtime: l %r9,__LC_THREAD_INFO # load pointer to thread_info struct TRACE_IRQS_OFF la %r2,SP_PTREGS(%r15) # address of register-save area - lh %r3,__LC_EXT_INT_CODE # get interruption code + l %r3,__LC_CPU_ADDRESS # get cpu address + interruption code + l %r4,__LC_EXT_PARAMS # get external parameters l %r1,BASED(.Ldo_extint) basr %r14,%r1 b BASED(io_return) diff --git a/arch/s390/kernel/entry.h b/arch/s390/kernel/entry.h index 714ff2e57c6b..95c1dfc4ef31 100644 --- a/arch/s390/kernel/entry.h +++ b/arch/s390/kernel/entry.h @@ -19,7 +19,7 @@ void do_signal(struct pt_regs *regs); int handle_signal32(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *oldset, struct pt_regs *regs); -void do_extint(struct pt_regs *regs, unsigned short code); +void do_extint(struct pt_regs *regs, unsigned int, unsigned int, unsigned long); int __cpuinit start_secondary(void *cpuvoid); void __init startup_init(void); void die(const char * str, struct pt_regs * regs, long err); diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S index 2d205e4e7bb6..e4038ea4dc57 100644 --- a/arch/s390/kernel/entry64.S +++ b/arch/s390/kernel/entry64.S @@ -727,8 +727,11 @@ ext_int_handler: ext_no_vtime: HANDLE_SIE_INTERCEPT TRACE_IRQS_OFF + lghi %r1,4096 la %r2,SP_PTREGS(%r15) # address of register-save area - llgh %r3,__LC_EXT_INT_CODE # get interruption code + llgf %r3,__LC_CPU_ADDRESS # get cpu address + interruption code + llgf %r4,__LC_EXT_PARAMS # get external parameter + lg %r5,__LC_EXT_PARAMS2-4096(%r1) # get 64 bit external parameter brasl %r14,do_extint j io_return diff --git a/arch/s390/kernel/s390_ext.c b/arch/s390/kernel/s390_ext.c index 9ce641b5291f..bd1db508e8af 100644 --- a/arch/s390/kernel/s390_ext.c +++ b/arch/s390/kernel/s390_ext.c @@ -113,12 +113,15 @@ int unregister_early_external_interrupt(__u16 code, ext_int_handler_t handler, return 0; } -void __irq_entry do_extint(struct pt_regs *regs, unsigned short code) +void __irq_entry do_extint(struct pt_regs *regs, unsigned int ext_int_code, + unsigned int param32, unsigned long param64) { + struct pt_regs *old_regs; + unsigned short code; ext_int_info_t *p; int index; - struct pt_regs *old_regs; + code = (unsigned short) ext_int_code; old_regs = set_irq_regs(regs); s390_idle_check(regs, S390_lowcore.int_clock, S390_lowcore.async_enter_timer); @@ -132,7 +135,7 @@ void __irq_entry do_extint(struct pt_regs *regs, unsigned short code) index = ext_hash(code); for (p = ext_int_hash[index]; p; p = p->next) { if (likely(p->code == code)) - p->handler(code); + p->handler(ext_int_code, param32, param64); } irq_exit(); set_irq_regs(old_regs); diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 8127ebd59c4d..354589d096b1 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -156,7 +156,8 @@ void smp_send_stop(void) * cpus are handled. */ -static void do_ext_call_interrupt(__u16 code) +static void do_ext_call_interrupt(unsigned int ext_int_code, + unsigned int param32, unsigned long param64) { unsigned long bits; diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index 2896cac9c14a..f754a6dc4f94 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -155,7 +155,9 @@ void init_cpu_timer(void) __ctl_set_bit(0, 4); } -static void clock_comparator_interrupt(__u16 code) +static void clock_comparator_interrupt(unsigned int ext_int_code, + unsigned int param32, + unsigned long param64) { if (S390_lowcore.clock_comparator == -1ULL) set_clock_comparator(S390_lowcore.clock_comparator); @@ -164,14 +166,13 @@ static void clock_comparator_interrupt(__u16 code) static void etr_timing_alert(struct etr_irq_parm *); static void stp_timing_alert(struct stp_irq_parm *); -static void timing_alert_interrupt(__u16 code) +static void timing_alert_interrupt(unsigned int ext_int_code, + unsigned int param32, unsigned long param64) { - if (S390_lowcore.ext_params & 0x00c40000) - etr_timing_alert((struct etr_irq_parm *) - &S390_lowcore.ext_params); - if (S390_lowcore.ext_params & 0x00038000) - stp_timing_alert((struct stp_irq_parm *) - &S390_lowcore.ext_params); + if (param32 & 0x00c40000) + etr_timing_alert((struct etr_irq_parm *) ¶m32); + if (param32 & 0x00038000) + stp_timing_alert((struct stp_irq_parm *) ¶m32); } static void etr_reset(void); diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c index 3479f1b0d4e0..56c8687b29b3 100644 --- a/arch/s390/kernel/vtime.c +++ b/arch/s390/kernel/vtime.c @@ -314,7 +314,8 @@ static void do_callbacks(struct list_head *cb_list) /* * Handler for the virtual CPU timer. */ -static void do_cpu_timer_interrupt(__u16 error_code) +static void do_cpu_timer_interrupt(unsigned int ext_int_code, + unsigned int param32, unsigned long param64) { struct vtimer_queue *vq; struct vtimer_list *event, *tmp; diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index b6570069b127..b4aad0c1f562 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -542,7 +542,8 @@ void pfault_fini(void) : : "a" (&refbk), "m" (refbk) : "cc"); } -static void pfault_interrupt(__u16 int_code) +static void pfault_interrupt(unsigned int ext_int_code, + unsigned int param32, unsigned long param64) { struct task_struct *tsk; __u16 subcode; @@ -553,14 +554,18 @@ static void pfault_interrupt(__u16 int_code) * in the 'cpu address' field associated with the * external interrupt. */ - subcode = S390_lowcore.cpu_addr; + subcode = ext_int_code >> 16; if ((subcode & 0xff00) != __SUBCODE_MASK) return; /* * Get the token (= address of the task structure of the affected task). */ - tsk = *(struct task_struct **) __LC_PFAULT_INTPARM; +#ifdef CONFIG_64BIT + tsk = *(struct task_struct **) param64; +#else + tsk = *(struct task_struct **) param32; +#endif if (subcode & 0x0080) { /* signal bit is set -> a page has been swapped in by VM */ diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index 2b3bc3ec0541..266b34b55403 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -228,25 +228,22 @@ dasd_diag_term_IO(struct dasd_ccw_req * cqr) } /* Handle external interruption. */ -static void -dasd_ext_handler(__u16 code) +static void dasd_ext_handler(unsigned int ext_int_code, + unsigned int param32, unsigned long param64) { struct dasd_ccw_req *cqr, *next; struct dasd_device *device; unsigned long long expires; unsigned long flags; - u8 int_code, status; addr_t ip; int rc; - int_code = *((u8 *) DASD_DIAG_LC_INT_CODE); - status = *((u8 *) DASD_DIAG_LC_INT_STATUS); - switch (int_code) { + switch (ext_int_code >> 24) { case DASD_DIAG_CODE_31BIT: - ip = (addr_t) *((u32 *) DASD_DIAG_LC_INT_PARM_31BIT); + ip = (addr_t) param32; break; case DASD_DIAG_CODE_64BIT: - ip = (addr_t) *((u64 *) DASD_DIAG_LC_INT_PARM_64BIT); + ip = (addr_t) param64; break; default: return; @@ -281,7 +278,7 @@ dasd_ext_handler(__u16 code) cqr->stopclk = get_clock(); expires = 0; - if (status == 0) { + if ((ext_int_code & 0xff0000) == 0) { cqr->status = DASD_CQR_SUCCESS; /* Start first request on queue if possible -> fast_io. */ if (!list_empty(&device->ccw_queue)) { @@ -296,8 +293,8 @@ dasd_ext_handler(__u16 code) } else { cqr->status = DASD_CQR_QUEUED; DBF_DEV_EVENT(DBF_DEBUG, device, "interrupt status for " - "request %p was %d (%d retries left)", cqr, status, - cqr->retries); + "request %p was %d (%d retries left)", cqr, + (ext_int_code >> 16) & 0xff, cqr->retries); dasd_diag_erp(device); } diff --git a/drivers/s390/block/dasd_diag.h b/drivers/s390/block/dasd_diag.h index b8c78267ff3e..4f71fbe60c82 100644 --- a/drivers/s390/block/dasd_diag.h +++ b/drivers/s390/block/dasd_diag.h @@ -18,10 +18,6 @@ #define DEV_CLASS_FBA 0x01 #define DEV_CLASS_ECKD 0x04 -#define DASD_DIAG_LC_INT_CODE 132 -#define DASD_DIAG_LC_INT_STATUS 133 -#define DASD_DIAG_LC_INT_PARM_31BIT 128 -#define DASD_DIAG_LC_INT_PARM_64BIT 4536 #define DASD_DIAG_CODE_31BIT 0x03 #define DASD_DIAG_CODE_64BIT 0x07 diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index 5707a80b96b6..35cc4686b99b 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -395,16 +395,16 @@ __sclp_find_req(u32 sccb) /* Handler for external interruption. Perform request post-processing. * Prepare read event data request if necessary. Start processing of next * request on queue. */ -static void -sclp_interrupt_handler(__u16 code) +static void sclp_interrupt_handler(unsigned int ext_int_code, + unsigned int param32, unsigned long param64) { struct sclp_req *req; u32 finished_sccb; u32 evbuf_pending; spin_lock(&sclp_lock); - finished_sccb = S390_lowcore.ext_params & 0xfffffff8; - evbuf_pending = S390_lowcore.ext_params & 0x3; + finished_sccb = param32 & 0xfffffff8; + evbuf_pending = param32 & 0x3; if (finished_sccb) { del_timer(&sclp_request_timer); sclp_running_state = sclp_running_state_reset_pending; @@ -819,12 +819,12 @@ EXPORT_SYMBOL(sclp_reactivate); /* Handler for external interruption used during initialization. Modify * request state to done. */ -static void -sclp_check_handler(__u16 code) +static void sclp_check_handler(unsigned int ext_int_code, + unsigned int param32, unsigned long param64) { u32 finished_sccb; - finished_sccb = S390_lowcore.ext_params & 0xfffffff8; + finished_sccb = param32 & 0xfffffff8; /* Is this the interrupt we are waiting for? */ if (finished_sccb == 0) return; diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c index 5a46b8c5d68a..375aeeaf9ea5 100644 --- a/drivers/s390/kvm/kvm_virtio.c +++ b/drivers/s390/kvm/kvm_virtio.c @@ -372,21 +372,22 @@ static void hotplug_devices(struct work_struct *dummy) /* * we emulate the request_irq behaviour on top of s390 extints */ -static void kvm_extint_handler(u16 code) +static void kvm_extint_handler(unsigned int ext_int_code, + unsigned int param32, unsigned long param64) { struct virtqueue *vq; u16 subcode; u32 param; - subcode = S390_lowcore.cpu_addr; + subcode = ext_int_code >> 16; if ((subcode & 0xff00) != VIRTIO_SUBCODE_64) return; /* The LSB might be overloaded, we have to mask it */ - vq = (struct virtqueue *)(S390_lowcore.ext_params2 & ~1UL); + vq = (struct virtqueue *)(param64 & ~1UL); /* We use ext_params to decide what this interrupt means */ - param = S390_lowcore.ext_params & VIRTIO_PARAM_MASK; + param = param32 & VIRTIO_PARAM_MASK; switch (param) { case VIRTIO_PARAM_CONFIG_CHANGED: diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c index 499c045d6910..f7db676de77d 100644 --- a/net/iucv/iucv.c +++ b/net/iucv/iucv.c @@ -1798,7 +1798,8 @@ static void iucv_work_fn(struct work_struct *work) * Handles external interrupts coming in from CP. * Places the interrupt buffer on a queue and schedules iucv_tasklet_fn(). */ -static void iucv_external_interrupt(u16 code) +static void iucv_external_interrupt(unsigned int ext_int_code, + unsigned int param32, unsigned long param64) { struct iucv_irq_data *p; struct iucv_irq_list *work; -- cgit v1.2.3 From 376ae4752e3a387d41a2ba9c9ea45c2df625e6e4 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 25 Oct 2010 16:10:44 +0200 Subject: [S390] cio: fix I/O cancel function Function ccw_device_cancel_halt_clear may cause an unexpected kernel panic if a clear function is currently active at the subchannel for which it is called. In that case, the iretry counter used to determine the number of retries is never initialized, leading to an immediate failure of the function which results in a kernel panic. Fix this by initializing the iretry counter when the function is first called. Also replace the kernel panic with a return code: a single malfunctioning I/O device should not automatically cause a system-wide kernel panic. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 1 + drivers/s390/cio/device_fsm.c | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 881bdfd99140..2ff8a22d4257 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1205,6 +1205,7 @@ static void io_subchannel_quiesce(struct subchannel *sch) cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO)); while (ret == -EBUSY) { cdev->private->state = DEV_STATE_QUIESCE; + cdev->private->iretry = 255; ret = ccw_device_cancel_halt_clear(cdev); if (ret == -EBUSY) { ccw_device_set_timeout(cdev, HZ/10); diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 4395c01a9dac..a845695ac314 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -174,7 +174,10 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev) ret = cio_clear (sch); return (ret == 0) ? -EBUSY : ret; } - panic("Can't stop i/o on subchannel.\n"); + /* Function was unsuccessful */ + CIO_MSG_EVENT(0, "0.%x.%04x: could not stop I/O\n", + cdev->private->dev_id.ssid, cdev->private->dev_id.devno); + return -EIO; } void ccw_device_update_sense_data(struct ccw_device *cdev) @@ -766,13 +769,14 @@ ccw_device_online_timeout(struct ccw_device *cdev, enum dev_event dev_event) int ret; ccw_device_set_timeout(cdev, 0); + cdev->private->iretry = 255; ret = ccw_device_cancel_halt_clear(cdev); if (ret == -EBUSY) { ccw_device_set_timeout(cdev, 3*HZ); cdev->private->state = DEV_STATE_TIMEOUT_KILL; return; } - if (ret == -ENODEV) + if (ret) dev_fsm_event(cdev, DEV_EVENT_NOTOPER); else if (cdev->handler) cdev->handler(cdev, cdev->private->intparm, @@ -869,6 +873,7 @@ void ccw_device_kill_io(struct ccw_device *cdev) { int ret; + cdev->private->iretry = 255; ret = ccw_device_cancel_halt_clear(cdev); if (ret == -EBUSY) { ccw_device_set_timeout(cdev, 3*HZ); -- cgit v1.2.3 From 26cffecf84c8cb33787dd13a72bd2124d107d413 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 25 Oct 2010 16:10:46 +0200 Subject: [S390] dasd: fix use after free in dbf Writing to /proc/dasd/statistics while the debug level of the generic dasd debug entry is set to DBF_DEBUG will lead to an use after free when accessing the debug entry later. Since for the format string "%s" in the s390 dbf only a pointer to the string is stored in the debug feature and the buffer used here is freed afterwards. To fix this just remove the debug message. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_proc.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c index 2eb025592809..c4a6a31bd9cd 100644 --- a/drivers/s390/block/dasd_proc.c +++ b/drivers/s390/block/dasd_proc.c @@ -251,7 +251,6 @@ static ssize_t dasd_stats_proc_write(struct file *file, buffer = dasd_get_user_string(user_buf, user_len); if (IS_ERR(buffer)) return PTR_ERR(buffer); - DBF_EVENT(DBF_DEBUG, "/proc/dasd/statictics: '%s'\n", buffer); /* check for valid verbs */ str = skip_spaces(buffer); -- cgit v1.2.3 From a5a0061fb3a22bbd9b108af8382142fd0f41ebee Mon Sep 17 00:00:00 2001 From: Stefan Weinhuber Date: Mon, 25 Oct 2010 16:10:47 +0200 Subject: [S390] dasd: fix unsolicited interrupt recognition The dasd interrupt handler needs to distinguish solicited from unsolicited interrupts, as unsolicited interrupts may require special handling (e.g. summary unit checks) and solicited interrupts require proper error recovery for the failed I/O request. The interrupt handler needs to check several bit fields in the interrupt response block (irb) to make this distinction. So far our check of the status control bits has not been specific enough, which may lead to a failed request getting just retried instead of the necessary error recovery. Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 11 ++++++- drivers/s390/block/dasd_eckd.c | 70 ++++++++++++++++++------------------------ 2 files changed, 40 insertions(+), 41 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index aa95f1001761..80e45de096a9 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1099,11 +1099,20 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, cqr = (struct dasd_ccw_req *) intparm; if (!cqr || ((scsw_cc(&irb->scsw) == 1) && (scsw_fctl(&irb->scsw) & SCSW_FCTL_START_FUNC) && - (scsw_stctl(&irb->scsw) & SCSW_STCTL_STATUS_PEND))) { + ((scsw_stctl(&irb->scsw) == SCSW_STCTL_STATUS_PEND) || + (scsw_stctl(&irb->scsw) == (SCSW_STCTL_STATUS_PEND | + SCSW_STCTL_ALERT_STATUS))))) { if (cqr && cqr->status == DASD_CQR_IN_IO) cqr->status = DASD_CQR_QUEUED; + if (cqr) + memcpy(&cqr->irb, irb, sizeof(*irb)); device = dasd_device_from_cdev_locked(cdev); if (!IS_ERR(device)) { + device->discipline->dump_sense_dbf(device, irb, + "unsolicited"); + if ((device->features & DASD_FEATURE_ERPLOG)) + device->discipline->dump_sense(device, cqr, + irb); dasd_device_clear_timer(device); device->discipline->handle_unsolicited_interrupt(device, irb); diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index ea0e565ebc9d..50cf96389d2c 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1776,13 +1776,13 @@ static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device, } /* summary unit check */ - if ((scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK) && - (irb->ecw[7] == 0x0D)) { + sense = dasd_get_sense(irb); + if (sense && (sense[7] == 0x0D) && + (scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK)) { dasd_alias_handle_summary_unit_check(device, irb); return; } - sense = dasd_get_sense(irb); /* service information message SIM */ if (sense && !(sense[27] & DASD_SENSE_BIT_0) && ((sense[6] & DASD_SIM_SENSE) == DASD_SIM_SENSE)) { @@ -1791,26 +1791,15 @@ static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device, return; } - if ((scsw_cc(&irb->scsw) == 1) && - (scsw_fctl(&irb->scsw) & SCSW_FCTL_START_FUNC) && - (scsw_actl(&irb->scsw) & SCSW_ACTL_START_PEND) && - (scsw_stctl(&irb->scsw) & SCSW_STCTL_STATUS_PEND)) { + if ((scsw_cc(&irb->scsw) == 1) && !sense && + (scsw_fctl(&irb->scsw) == SCSW_FCTL_START_FUNC) && + (scsw_actl(&irb->scsw) == SCSW_ACTL_START_PEND) && + (scsw_stctl(&irb->scsw) == SCSW_STCTL_STATUS_PEND)) { /* fake irb do nothing, they are handled elsewhere */ dasd_schedule_device_bh(device); return; } - if (!sense) { - /* just report other unsolicited interrupts */ - DBF_DEV_EVENT(DBF_ERR, device, "%s", - "unsolicited interrupt received"); - } else { - DBF_DEV_EVENT(DBF_ERR, device, "%s", - "unsolicited interrupt received " - "(sense available)"); - device->discipline->dump_sense_dbf(device, irb, "unsolicited"); - } - dasd_schedule_device_bh(device); return; }; @@ -3093,23 +3082,19 @@ dasd_eckd_dump_sense_dbf(struct dasd_device *device, struct irb *irb, char *reason) { u64 *sense; - u32 stat; + u64 *stat; sense = (u64 *) dasd_get_sense(irb); - stat = scsw_cstat(&irb->scsw); - stat <<= 8; - stat |= scsw_dstat(&irb->scsw); - stat <<= 8; - stat |= scsw_cc(&irb->scsw); - + stat = (u64 *) &irb->scsw; if (sense) { - DBF_DEV_EVENT(DBF_EMERG, device, - "%s: %s %06x %016llx %016llx %016llx %016llx", - reason, scsw_is_tm(&irb->scsw) ? "t" : "c", stat, + DBF_DEV_EVENT(DBF_EMERG, device, "%s: %016llx %08x : " + "%016llx %016llx %016llx %016llx", + reason, *stat, *((u32 *) (stat + 1)), sense[0], sense[1], sense[2], sense[3]); } else { - DBF_DEV_EVENT(DBF_EMERG, device, "%s", - "SORRY - NO VALID SENSE AVAILABLE\n"); + DBF_DEV_EVENT(DBF_EMERG, device, "%s: %016llx %08x : %s", + reason, *stat, *((u32 *) (stat + 1)), + "NO VALID SENSE"); } } @@ -3135,9 +3120,12 @@ static void dasd_eckd_dump_sense_ccw(struct dasd_device *device, " I/O status report for device %s:\n", dev_name(&device->cdev->dev)); len += sprintf(page + len, KERN_ERR PRINTK_HEADER - " in req: %p CS: 0x%02X DS: 0x%02X CC: 0x%02X RC: %d\n", - req, scsw_cstat(&irb->scsw), scsw_dstat(&irb->scsw), - scsw_cc(&irb->scsw), req ? req->intrc : 0); + " in req: %p CC:%02X FC:%02X AC:%02X SC:%02X DS:%02X " + "CS:%02X RC:%d\n", + req, scsw_cc(&irb->scsw), scsw_fctl(&irb->scsw), + scsw_actl(&irb->scsw), scsw_stctl(&irb->scsw), + scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw), + req ? req->intrc : 0); len += sprintf(page + len, KERN_ERR PRINTK_HEADER " device %s: Failing CCW: %p\n", dev_name(&device->cdev->dev), @@ -3238,11 +3226,13 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device, " I/O status report for device %s:\n", dev_name(&device->cdev->dev)); len += sprintf(page + len, KERN_ERR PRINTK_HEADER - " in req: %p CS: 0x%02X DS: 0x%02X CC: 0x%02X RC: %d " - "fcxs: 0x%02X schxs: 0x%02X\n", req, - scsw_cstat(&irb->scsw), scsw_dstat(&irb->scsw), - scsw_cc(&irb->scsw), req->intrc, - irb->scsw.tm.fcxs, irb->scsw.tm.schxs); + " in req: %p CC:%02X FC:%02X AC:%02X SC:%02X DS:%02X " + "CS:%02X fcxs:%02X schxs:%02X RC:%d\n", + req, scsw_cc(&irb->scsw), scsw_fctl(&irb->scsw), + scsw_actl(&irb->scsw), scsw_stctl(&irb->scsw), + scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw), + irb->scsw.tm.fcxs, irb->scsw.tm.schxs, + req ? req->intrc : 0); len += sprintf(page + len, KERN_ERR PRINTK_HEADER " device %s: Failing TCW: %p\n", dev_name(&device->cdev->dev), @@ -3250,7 +3240,7 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device, tsb = NULL; sense = NULL; - if (irb->scsw.tm.tcw && (irb->scsw.tm.fcxs == 0x01)) + if (irb->scsw.tm.tcw && (irb->scsw.tm.fcxs & 0x01)) tsb = tcw_get_tsb( (struct tcw *)(unsigned long)irb->scsw.tm.tcw); @@ -3348,7 +3338,7 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device, static void dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req *req, struct irb *irb) { - if (req && scsw_is_tm(&req->irb.scsw)) + if (scsw_is_tm(&irb->scsw)) dasd_eckd_dump_sense_tcw(device, req, irb); else dasd_eckd_dump_sense_ccw(device, req, irb); -- cgit v1.2.3 From 56b86b615b807e043339979878a2de88f900ee4f Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Mon, 25 Oct 2010 16:10:49 +0200 Subject: [S390] dasd: ignore unsolicited interrupts for DIAG For the DASD DIAG discipline IO is started through special diagnose calls. Unsolicited interrupts may contain information about the device itself. But this information is not needed because the device is not used directly. Fix the case that an unimplemented dicipline function may be called by ignoring unsolicited interrupts for the DIAG disciplin. Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 80e45de096a9..fb613d70c2cb 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1107,17 +1107,22 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, if (cqr) memcpy(&cqr->irb, irb, sizeof(*irb)); device = dasd_device_from_cdev_locked(cdev); - if (!IS_ERR(device)) { - device->discipline->dump_sense_dbf(device, irb, - "unsolicited"); - if ((device->features & DASD_FEATURE_ERPLOG)) - device->discipline->dump_sense(device, cqr, - irb); - dasd_device_clear_timer(device); - device->discipline->handle_unsolicited_interrupt(device, - irb); + if (IS_ERR(device)) + return; + /* ignore unsolicited interrupts for DIAG discipline */ + if (device->discipline == dasd_diag_discipline_pointer) { dasd_put_device(device); + return; } + device->discipline->dump_sense_dbf(device, irb, + "unsolicited"); + if ((device->features & DASD_FEATURE_ERPLOG)) + device->discipline->dump_sense(device, cqr, + irb); + dasd_device_clear_timer(device); + device->discipline->handle_unsolicited_interrupt(device, + irb); + dasd_put_device(device); return; } -- cgit v1.2.3 From 14375bc4eb8dd0fb0e765390650564c35bb31068 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Mon, 25 Oct 2010 16:10:51 +0200 Subject: [S390] cleanup facility list handling Store the facility list once at system startup with stfl/stfle and reuse the result for all facility tests. Signed-off-by: Martin Schwidefsky --- arch/s390/crypto/crypt_s390.h | 2 +- arch/s390/include/asm/lowcore.h | 11 ++++++++--- arch/s390/include/asm/system.h | 33 ++++++++++++--------------------- arch/s390/kernel/early.c | 40 +++++++++++++++++++++++++++++----------- arch/s390/kernel/setup.c | 19 +++++++------------ arch/s390/kernel/smp.c | 2 ++ arch/s390/kernel/topology.c | 5 +---- arch/s390/kernel/vdso.c | 6 +----- arch/s390/kvm/kvm-s390.c | 2 +- arch/s390/kvm/priv.c | 4 ++-- arch/s390/mm/fault.c | 7 +------ drivers/s390/crypto/ap_bus.c | 9 +-------- 12 files changed, 66 insertions(+), 74 deletions(-) (limited to 'drivers') diff --git a/arch/s390/crypto/crypt_s390.h b/arch/s390/crypto/crypt_s390.h index 0ef9829f2ad6..7ee9a1b4ad9f 100644 --- a/arch/s390/crypto/crypt_s390.h +++ b/arch/s390/crypto/crypt_s390.h @@ -297,7 +297,7 @@ static inline int crypt_s390_func_available(int func) int ret; /* check if CPACF facility (bit 17) is available */ - if (!(stfl() & 1ULL << (31 - 17))) + if (!test_facility(17)) return 0; switch (func & CRYPT_S390_OP_MASK) { diff --git a/arch/s390/include/asm/lowcore.h b/arch/s390/include/asm/lowcore.h index 0f97ef2d92ac..65e172f8209d 100644 --- a/arch/s390/include/asm/lowcore.h +++ b/arch/s390/include/asm/lowcore.h @@ -150,9 +150,10 @@ struct _lowcore { */ __u32 ipib; /* 0x0e00 */ __u32 ipib_checksum; /* 0x0e04 */ + __u8 pad_0x0e08[0x0f00-0x0e08]; /* 0x0e08 */ - /* Align to the top 1k of prefix area */ - __u8 pad_0x0e08[0x1000-0x0e08]; /* 0x0e08 */ + /* Extended facility list */ + __u64 stfle_fac_list[32]; /* 0x0f00 */ } __packed; #else /* CONFIG_32BIT */ @@ -285,7 +286,11 @@ struct _lowcore { */ __u64 ipib; /* 0x0e00 */ __u32 ipib_checksum; /* 0x0e08 */ - __u8 pad_0x0e0c[0x11b8-0x0e0c]; /* 0x0e0c */ + __u8 pad_0x0e0c[0x0f00-0x0e0c]; /* 0x0e0c */ + + /* Extended facility list */ + __u64 stfle_fac_list[32]; /* 0x0f00 */ + __u8 pad_0x1000[0x11b8-0x1000]; /* 0x1000 */ /* 64 bit extparam used for pfault/diag 250: defined by architecture */ __u64 ext_params2; /* 0x11B8 */ diff --git a/arch/s390/include/asm/system.h b/arch/s390/include/asm/system.h index 3c079dd5ee79..3ad16dbf622e 100644 --- a/arch/s390/include/asm/system.h +++ b/arch/s390/include/asm/system.h @@ -420,30 +420,21 @@ extern void smp_ctl_clear_bit(int cr, int bit); #endif /* CONFIG_SMP */ -static inline unsigned int stfl(void) -{ - asm volatile( - " .insn s,0xb2b10000,0(0)\n" /* stfl */ - "0:\n" - EX_TABLE(0b,0b)); - return S390_lowcore.stfl_fac_list; -} +#define MAX_FACILITY_BIT (256*8) /* stfle_fac_list has 256 bytes */ -static inline int __stfle(unsigned long long *list, int doublewords) +/* + * The test_facility function uses the bit odering where the MSB is bit 0. + * That makes it easier to query facility bits with the bit number as + * documented in the Principles of Operation. + */ +static inline int test_facility(unsigned long nr) { - typedef struct { unsigned long long _[doublewords]; } addrtype; - register unsigned long __nr asm("0") = doublewords - 1; - - asm volatile(".insn s,0xb2b00000,%0" /* stfle */ - : "=m" (*(addrtype *) list), "+d" (__nr) : : "cc"); - return __nr + 1; -} + unsigned char *ptr; -static inline int stfle(unsigned long long *list, int doublewords) -{ - if (!(stfl() & (1UL << 24))) - return -EOPNOTSUPP; - return __stfle(list, doublewords); + if (nr >= MAX_FACILITY_BIT) + return 0; + ptr = (unsigned char *) &S390_lowcore.stfle_fac_list + (nr >> 3); + return (*ptr & (0x80 >> (nr & 7))) != 0; } static inline unsigned short stap(void) diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index 0badc6344eb4..d2455d44d99a 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c @@ -256,13 +256,35 @@ static noinline __init void setup_lowcore_early(void) s390_base_pgm_handler_fn = early_pgm_check_handler; } +static noinline __init void setup_facility_list(void) +{ + unsigned long nr; + + S390_lowcore.stfl_fac_list = 0; + asm volatile( + " .insn s,0xb2b10000,0(0)\n" /* stfl */ + "0:\n" + EX_TABLE(0b,0b) : "=m" (S390_lowcore.stfl_fac_list)); + memcpy(&S390_lowcore.stfle_fac_list, &S390_lowcore.stfl_fac_list, 4); + nr = 4; /* # bytes stored by stfl */ + if (test_facility(7)) { + /* More facility bits available with stfle */ + register unsigned long reg0 asm("0") = MAX_FACILITY_BIT/64 - 1; + asm volatile(".insn s,0xb2b00000,%0" /* stfle */ + : "=m" (S390_lowcore.stfle_fac_list), "+d" (reg0) + : : "cc"); + nr = (reg0 + 1) * 8; /* # bytes stored by stfle */ + } + memset((char *) S390_lowcore.stfle_fac_list + nr, 0, + MAX_FACILITY_BIT/8 - nr); +} + static noinline __init void setup_hpage(void) { #ifndef CONFIG_DEBUG_PAGEALLOC unsigned int facilities; - facilities = stfl(); - if (!(facilities & (1UL << 23)) || !(facilities & (1UL << 29))) + if (!test_facility(2) || !test_facility(8)) return; S390_lowcore.machine_flags |= MACHINE_FLAG_HPAGE; __ctl_set_bit(0, 23); @@ -356,18 +378,13 @@ static __init void detect_diag44(void) static __init void detect_machine_facilities(void) { #ifdef CONFIG_64BIT - unsigned int facilities; - unsigned long long facility_bits; - - facilities = stfl(); - if (facilities & (1 << 28)) + if (test_facility(3)) S390_lowcore.machine_flags |= MACHINE_FLAG_IDTE; - if (facilities & (1 << 23)) + if (test_facility(8)) S390_lowcore.machine_flags |= MACHINE_FLAG_PFMF; - if (facilities & (1 << 4)) + if (test_facility(27)) S390_lowcore.machine_flags |= MACHINE_FLAG_MVCOS; - if ((stfle(&facility_bits, 1) > 0) && - (facility_bits & (1ULL << (63 - 40)))) + if (test_facility(40)) S390_lowcore.machine_flags |= MACHINE_FLAG_SPP; #endif } @@ -448,6 +465,7 @@ void __init startup_init(void) lockdep_off(); sort_main_extable(); setup_lowcore_early(); + setup_facility_list(); detect_machine_type(); ipl_update_parameters(); setup_boot_command_line(); diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 9071e984dcf1..e3ceb911dc75 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -409,6 +409,9 @@ setup_lowcore(void) lc->current_task = (unsigned long) init_thread_union.thread_info.task; lc->thread_info = (unsigned long) &init_thread_union; lc->machine_flags = S390_lowcore.machine_flags; + lc->stfl_fac_list = S390_lowcore.stfl_fac_list; + memcpy(lc->stfle_fac_list, S390_lowcore.stfle_fac_list, + MAX_FACILITY_BIT/8); #ifndef CONFIG_64BIT if (MACHINE_HAS_IEEE) { lc->extended_save_area_addr = (__u32) @@ -675,12 +678,9 @@ setup_memory(void) static void __init setup_hwcaps(void) { static const int stfl_bits[6] = { 0, 2, 7, 17, 19, 21 }; - unsigned long long facility_list_extended; - unsigned int facility_list; struct cpuid cpu_id; int i; - facility_list = stfl(); /* * The store facility list bits numbers as found in the principles * of operation are numbered with bit 1UL<<31 as number 0 to @@ -700,11 +700,10 @@ static void __init setup_hwcaps(void) * HWCAP_S390_ETF3EH bit 8 (22 && 30). */ for (i = 0; i < 6; i++) - if (facility_list & (1UL << (31 - stfl_bits[i]))) + if (test_facility(stfl_bits[i])) elf_hwcap |= 1UL << i; - if ((facility_list & (1UL << (31 - 22))) - && (facility_list & (1UL << (31 - 30)))) + if (test_facility(22) && test_facility(30)) elf_hwcap |= HWCAP_S390_ETF3EH; /* @@ -720,12 +719,8 @@ static void __init setup_hwcaps(void) * translated to: * HWCAP_S390_DFP bit 6 (42 && 44). */ - if ((elf_hwcap & (1UL << 2)) && - __stfle(&facility_list_extended, 1) > 0) { - if ((facility_list_extended & (1ULL << (63 - 42))) - && (facility_list_extended & (1ULL << (63 - 44)))) - elf_hwcap |= HWCAP_S390_DFP; - } + if ((elf_hwcap & (1UL << 2)) && test_facility(42) && test_facility(44)) + elf_hwcap |= HWCAP_S390_DFP; /* * Huge page support HWCAP_S390_HPAGE is bit 7. diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 354589d096b1..94cf510b8fe1 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -594,6 +594,8 @@ int __cpuinit __cpu_up(unsigned int cpu) cpu_lowcore->kernel_asce = S390_lowcore.kernel_asce; cpu_lowcore->machine_flags = S390_lowcore.machine_flags; cpu_lowcore->ftrace_func = S390_lowcore.ftrace_func; + memcpy(cpu_lowcore->stfle_fac_list, S390_lowcore.stfle_fac_list, + MAX_FACILITY_BIT/8); eieio(); while (sigp(cpu, sigp_restart) == sigp_busy) diff --git a/arch/s390/kernel/topology.c b/arch/s390/kernel/topology.c index eb0bc4752ae8..91fb66baa50b 100644 --- a/arch/s390/kernel/topology.c +++ b/arch/s390/kernel/topology.c @@ -351,13 +351,10 @@ static void alloc_masks(struct tl_info *info, struct mask_info *mask, int offset void __init s390_init_cpu_topology(void) { - unsigned long long facility_bits; struct tl_info *info; int i; - if (stfle(&facility_bits, 1) <= 0) - return; - if (!(facility_bits & (1ULL << 52)) || !(facility_bits & (1ULL << 61))) + if (!test_facility(2) || !test_facility(11)) return; machine_has_topology = 1; diff --git a/arch/s390/kernel/vdso.c b/arch/s390/kernel/vdso.c index 6b83870507d5..e3150dd2fe74 100644 --- a/arch/s390/kernel/vdso.c +++ b/arch/s390/kernel/vdso.c @@ -84,11 +84,7 @@ struct vdso_data *vdso_data = &vdso_data_store.data; */ static void vdso_init_data(struct vdso_data *vd) { - unsigned int facility_list; - - facility_list = stfl(); - vd->ectg_available = - user_mode != HOME_SPACE_MODE && (facility_list & 1); + vd->ectg_available = user_mode != HOME_SPACE_MODE && test_facility(31); } #ifdef CONFIG_64BIT diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 76482b65ba3e..985d825494f1 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -740,7 +740,7 @@ static int __init kvm_s390_init(void) kvm_exit(); return -ENOMEM; } - stfle(facilities, 1); + memcpy(facilities, S390_lowcore.stfle_fac_list, 16); facilities[0] &= 0xff00fff3f47c0000ULL; return 0; } diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c index 44205507717c..9194a4b52b22 100644 --- a/arch/s390/kvm/priv.c +++ b/arch/s390/kvm/priv.c @@ -154,12 +154,12 @@ static int handle_chsc(struct kvm_vcpu *vcpu) static int handle_stfl(struct kvm_vcpu *vcpu) { - unsigned int facility_list = stfl(); + unsigned int facility_list; int rc; vcpu->stat.instruction_stfl++; /* only pass the facility bits, which we can handle */ - facility_list &= 0xff00fff3; + facility_list = S390_lowcore.stfl_fac_list & 0xff00fff3; rc = copy_to_guest(vcpu, offsetof(struct _lowcore, stfl_fac_list), &facility_list, sizeof(facility_list)); diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index b4aad0c1f562..fe5701e9efbf 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -56,12 +56,7 @@ static unsigned long store_indication; void fault_init(void) { - unsigned long long facility_list[2]; - - if (stfle(facility_list, 2) < 2) - return; - if ((facility_list[0] & (1ULL << 61)) && - (facility_list[1] & (1ULL << 52))) + if (test_facility(2) && test_facility(75)) store_indication = 0xc00; } diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 91c6028d7b74..8fd8c62455e9 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -154,14 +154,7 @@ static inline int ap_instructions_available(void) */ static int ap_interrupts_available(void) { - unsigned long long facility_bits[2]; - - if (stfle(facility_bits, 2) <= 1) - return 0; - if (!(facility_bits[0] & (1ULL << 61)) || - !(facility_bits[1] & (1ULL << 62))) - return 0; - return 1; + return test_facility(1) && test_facility(2); } /** -- cgit v1.2.3