diff options
Diffstat (limited to 'drivers/s390')
28 files changed, 697 insertions, 534 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 0e3fdfdbd098..6fb3fd5efc11 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1712,8 +1712,11 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, /* check for for attention message */ if (scsw_dstat(&irb->scsw) & DEV_STAT_ATTENTION) { device = dasd_device_from_cdev_locked(cdev); - device->discipline->check_attention(device, irb->esw.esw1.lpum); - dasd_put_device(device); + if (!IS_ERR(device)) { + device->discipline->check_attention(device, + irb->esw.esw1.lpum); + dasd_put_device(device); + } } if (!cqr) @@ -3598,10 +3601,11 @@ int dasd_generic_set_offline(struct ccw_device *cdev) * empty */ /* sync blockdev and partitions */ - rc = fsync_bdev(device->block->bdev); - if (rc != 0) - goto interrupted; - + if (device->block) { + rc = fsync_bdev(device->block->bdev); + if (rc != 0) + goto interrupted; + } /* schedule device tasklet and wait for completion */ dasd_schedule_device_bh(device); rc = wait_event_interruptible(shutdown_waitq, diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index dd46e96a3034..1164b51d09f3 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -26,6 +26,7 @@ /* This is ugly... */ #define PRINTK_HEADER "dasd_devmap:" #define DASD_BUS_ID_SIZE 20 +#define DASD_MAX_PARAMS 256 #include "dasd_int.h" @@ -76,7 +77,7 @@ EXPORT_SYMBOL_GPL(dasd_nofcx); * it is named 'dasd' to directly be filled by insmod with the comma separated * strings when running as a module. */ -static char *dasd[256]; +static char *dasd[DASD_MAX_PARAMS]; module_param_array(dasd, charp, NULL, S_IRUGO); /* @@ -104,18 +105,19 @@ dasd_hash_busid(const char *bus_id) } #ifndef MODULE -/* - * The parameter parsing functions for builtin-drivers are called - * before kmalloc works. Store the pointers to the parameters strings - * into dasd[] for later processing. - */ -static int __init -dasd_call_setup(char *str) +static int __init dasd_call_setup(char *opt) { - static int count = 0; + static int i __initdata; + char *tmp; + + while (i < DASD_MAX_PARAMS) { + tmp = strsep(&opt, ","); + if (!tmp) + break; + + dasd[i++] = tmp; + } - if (count < 256) - dasd[count++] = str; return 1; } @@ -127,14 +129,13 @@ __setup ("dasd=", dasd_call_setup); /* * Read a device busid/devno from a string. */ -static int - -dasd_busid(char **str, int *id0, int *id1, int *devno) +static int __init dasd_busid(char *str, int *id0, int *id1, int *devno) { - int val, old_style; + unsigned int val; + char *tok; /* Interpret ipldev busid */ - if (strncmp(DASD_IPLDEV, *str, strlen(DASD_IPLDEV)) == 0) { + if (strncmp(DASD_IPLDEV, str, strlen(DASD_IPLDEV)) == 0) { if (ipl_info.type != IPL_TYPE_CCW) { pr_err("The IPL device is not a CCW device\n"); return -EINVAL; @@ -142,63 +143,50 @@ dasd_busid(char **str, int *id0, int *id1, int *devno) *id0 = 0; *id1 = ipl_info.data.ccw.dev_id.ssid; *devno = ipl_info.data.ccw.dev_id.devno; - *str += strlen(DASD_IPLDEV); return 0; } - /* check for leading '0x' */ - old_style = 0; - if ((*str)[0] == '0' && (*str)[1] == 'x') { - *str += 2; - old_style = 1; - } - if (!isxdigit((*str)[0])) /* We require at least one hex digit */ - return -EINVAL; - val = simple_strtoul(*str, str, 16); - if (old_style || (*str)[0] != '.') { + + /* Old style 0xXXXX or XXXX */ + if (!kstrtouint(str, 16, &val)) { *id0 = *id1 = 0; if (val < 0 || val > 0xffff) return -EINVAL; *devno = val; return 0; } + /* New style x.y.z busid */ - if (val < 0 || val > 0xff) + tok = strsep(&str, "."); + if (kstrtouint(tok, 16, &val) || val > 0xff) return -EINVAL; *id0 = val; - (*str)++; - if (!isxdigit((*str)[0])) /* We require at least one hex digit */ - return -EINVAL; - val = simple_strtoul(*str, str, 16); - if (val < 0 || val > 0xff || (*str)++[0] != '.') + + tok = strsep(&str, "."); + if (kstrtouint(tok, 16, &val) || val > 0xff) return -EINVAL; *id1 = val; - if (!isxdigit((*str)[0])) /* We require at least one hex digit */ - return -EINVAL; - val = simple_strtoul(*str, str, 16); - if (val < 0 || val > 0xffff) + + tok = strsep(&str, "."); + if (kstrtouint(tok, 16, &val) || val > 0xffff) return -EINVAL; *devno = val; + return 0; } /* - * Read colon separated list of dasd features. Currently there is - * only one: "ro" for read-only devices. The default feature set - * is empty (value 0). + * Read colon separated list of dasd features. */ -static int -dasd_feature_list(char *str, char **endp) +static int __init dasd_feature_list(char *str) { int features, len, rc; + features = 0; rc = 0; - if (*str != '(') { - *endp = str; + + if (!str) return DASD_FEATURE_DEFAULT; - } - str++; - features = 0; while (1) { for (len = 0; @@ -223,15 +211,8 @@ dasd_feature_list(char *str, char **endp) break; str++; } - if (*str != ')') { - pr_warn("A closing parenthesis ')' is missing in the dasd= parameter\n"); - rc = -EINVAL; - } else - str++; - *endp = str; - if (rc != 0) - return rc; - return features; + + return rc ? : features; } /* @@ -240,48 +221,38 @@ dasd_feature_list(char *str, char **endp) * action and return a pointer to the residual string. If the first element * could not be matched to any keyword then return an error code. */ -static char * -dasd_parse_keyword( char *parsestring ) { - - char *nextcomma, *residual_str; - int length; +static int __init dasd_parse_keyword(char *keyword) +{ + int length = strlen(keyword); - nextcomma = strchr(parsestring,','); - if (nextcomma) { - length = nextcomma - parsestring; - residual_str = nextcomma + 1; - } else { - length = strlen(parsestring); - residual_str = parsestring + length; - } - if (strncmp("autodetect", parsestring, length) == 0) { + if (strncmp("autodetect", keyword, length) == 0) { dasd_autodetect = 1; pr_info("The autodetection mode has been activated\n"); - return residual_str; + return 0; } - if (strncmp("probeonly", parsestring, length) == 0) { + if (strncmp("probeonly", keyword, length) == 0) { dasd_probeonly = 1; pr_info("The probeonly mode has been activated\n"); - return residual_str; + return 0; } - if (strncmp("nopav", parsestring, length) == 0) { + if (strncmp("nopav", keyword, length) == 0) { if (MACHINE_IS_VM) pr_info("'nopav' is not supported on z/VM\n"); else { dasd_nopav = 1; pr_info("PAV support has be deactivated\n"); } - return residual_str; + return 0; } - if (strncmp("nofcx", parsestring, length) == 0) { + if (strncmp("nofcx", keyword, length) == 0) { dasd_nofcx = 1; pr_info("High Performance FICON support has been " "deactivated\n"); - return residual_str; + return 0; } - if (strncmp("fixedbuffers", parsestring, length) == 0) { + if (strncmp("fixedbuffers", keyword, length) == 0) { if (dasd_page_cache) - return residual_str; + return 0; dasd_page_cache = kmem_cache_create("dasd_page_cache", PAGE_SIZE, PAGE_SIZE, SLAB_CACHE_DMA, @@ -292,107 +263,126 @@ dasd_parse_keyword( char *parsestring ) { else DBF_EVENT(DBF_INFO, "%s", "turning on fixed buffer mode"); - return residual_str; - } - return ERR_PTR(-EINVAL); + return 0; + } + + return -EINVAL; } /* - * Try to interprete the first element on the comma separated parse string - * as a device number or a range of devices. If the interpretation is - * successful, create the matching dasd_devmap entries and return a pointer - * to the residual string. - * If interpretation fails or in case of an error, return an error code. + * Split a string of a device range into its pieces and return the from, to, and + * feature parts separately. + * e.g.: + * 0.0.1234-0.0.5678(ro:erplog) -> from: 0.0.1234 to: 0.0.5678 features: ro:erplog + * 0.0.8765(raw) -> from: 0.0.8765 to: null features: raw + * 0x4321 -> from: 0x4321 to: null features: null */ -static char * -dasd_parse_range( char *parsestring ) { +static int __init dasd_evaluate_range_param(char *range, char **from_str, + char **to_str, char **features_str) +{ + int rc = 0; + + /* Do we have a range or a single device? */ + if (strchr(range, '-')) { + *from_str = strsep(&range, "-"); + *to_str = strsep(&range, "("); + *features_str = strsep(&range, ")"); + } else { + *from_str = strsep(&range, "("); + *features_str = strsep(&range, ")"); + } + if (*features_str && !range) { + pr_warn("A closing parenthesis ')' is missing in the dasd= parameter\n"); + rc = -EINVAL; + } + + return rc; +} + +/* + * Try to interprete the range string as a device number or a range of devices. + * If the interpretation is successful, create the matching dasd_devmap entries. + * If interpretation fails or in case of an error, return an error code. + */ +static int __init dasd_parse_range(const char *range) +{ struct dasd_devmap *devmap; int from, from_id0, from_id1; int to, to_id0, to_id1; - int features, rc; - char bus_id[DASD_BUS_ID_SIZE+1], *str; - - str = parsestring; - rc = dasd_busid(&str, &from_id0, &from_id1, &from); - if (rc == 0) { - to = from; - to_id0 = from_id0; - to_id1 = from_id1; - if (*str == '-') { - str++; - rc = dasd_busid(&str, &to_id0, &to_id1, &to); + int features; + char bus_id[DASD_BUS_ID_SIZE + 1]; + char *features_str = NULL; + char *from_str = NULL; + char *to_str = NULL; + size_t len = strlen(range) + 1; + char tmp[len]; + + strlcpy(tmp, range, len); + + if (dasd_evaluate_range_param(tmp, &from_str, &to_str, &features_str)) + goto out_err; + + if (dasd_busid(from_str, &from_id0, &from_id1, &from)) + goto out_err; + + to = from; + to_id0 = from_id0; + to_id1 = from_id1; + if (to_str) { + if (dasd_busid(to_str, &to_id0, &to_id1, &to)) + goto out_err; + if (from_id0 != to_id0 || from_id1 != to_id1 || from > to) { + pr_err("%s is not a valid device range\n", range); + goto out_err; } } - if (rc == 0 && - (from_id0 != to_id0 || from_id1 != to_id1 || from > to)) - rc = -EINVAL; - if (rc) { - pr_err("%s is not a valid device range\n", parsestring); - return ERR_PTR(rc); - } - features = dasd_feature_list(str, &str); + + features = dasd_feature_list(features_str); if (features < 0) - return ERR_PTR(-EINVAL); + goto out_err; /* each device in dasd= parameter should be set initially online */ features |= DASD_FEATURE_INITIAL_ONLINE; while (from <= to) { - sprintf(bus_id, "%01x.%01x.%04x", - from_id0, from_id1, from++); + sprintf(bus_id, "%01x.%01x.%04x", from_id0, from_id1, from++); devmap = dasd_add_busid(bus_id, features); if (IS_ERR(devmap)) - return (char *)devmap; + return PTR_ERR(devmap); } - if (*str == ',') - return str + 1; - if (*str == '\0') - return str; - pr_warn("The dasd= parameter value %s has an invalid ending\n", str); - return ERR_PTR(-EINVAL); -} -static char * -dasd_parse_next_element( char *parsestring ) { - char * residual_str; - residual_str = dasd_parse_keyword(parsestring); - if (!IS_ERR(residual_str)) - return residual_str; - residual_str = dasd_parse_range(parsestring); - return residual_str; + return 0; + +out_err: + return -EINVAL; } /* * Parse parameters stored in dasd[] * The 'dasd=...' parameter allows to specify a comma separated list of - * keywords and device ranges. When the dasd driver is build into the kernel, - * the complete list will be stored as one element of the dasd[] array. - * When the dasd driver is build as a module, then the list is broken into - * it's elements and each dasd[] entry contains one element. + * keywords and device ranges. The parameters in that list will be stored as + * separate elementes in dasd[]. */ -int -dasd_parse(void) +int __init dasd_parse(void) { int rc, i; - char *parsestring; + char *cur; rc = 0; - for (i = 0; i < 256; i++) { - if (dasd[i] == NULL) + for (i = 0; i < DASD_MAX_PARAMS; i++) { + cur = dasd[i]; + if (!cur) break; - parsestring = dasd[i]; - /* loop over the comma separated list in the parsestring */ - while (*parsestring) { - parsestring = dasd_parse_next_element(parsestring); - if(IS_ERR(parsestring)) { - rc = PTR_ERR(parsestring); - break; - } - } - if (rc) { - DBF_EVENT(DBF_ALERT, "%s", "invalid range found"); + if (*cur == '\0') + continue; + + rc = dasd_parse_keyword(cur); + if (rc) + rc = dasd_parse_range(cur); + + if (rc) break; - } } + return rc; } @@ -1528,14 +1518,12 @@ dasd_path_threshold_store(struct device *dev, struct device_attribute *attr, if (IS_ERR(device)) return -ENODEV; - if ((kstrtoul(buf, 10, &val) != 0) || - (val > DASD_THRHLD_MAX) || val == 0) { + if (kstrtoul(buf, 10, &val) != 0 || val > DASD_THRHLD_MAX) { dasd_put_device(device); return -EINVAL; } spin_lock_irqsave(get_ccwdev_lock(to_ccwdev(dev)), flags); - if (val) - device->path_thrhld = val; + device->path_thrhld = val; spin_unlock_irqrestore(get_ccwdev_lock(to_ccwdev(dev)), flags); dasd_put_device(device); return count; diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index ade04216c970..0f1713727d4c 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -2543,8 +2543,8 @@ dasd_eckd_build_format(struct dasd_device *base, DASD_ECKD_CCW_WRITE_CKD_MT; ccw->flags = CCW_FLAG_SLI; ccw->count = 8; - ccw->cda = (__u32)(addr_t) ect; - ccw++; + ccw->cda = (__u32)(addr_t) ect; + ccw++; } } } diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 24be210c10e5..518dba2732d5 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -805,7 +805,7 @@ struct dasd_device *dasd_device_from_devindex(int); void dasd_add_link_to_gendisk(struct gendisk *, struct dasd_device *); struct dasd_device *dasd_device_from_gendisk(struct gendisk *); -int dasd_parse(void); +int dasd_parse(void) __init; int dasd_busid_known(const char *); /* externals in dasd_gendisk.c */ diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 9d66b4fb174b..415d10a67b7a 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -892,7 +892,7 @@ dcssblk_direct_access (struct block_device *bdev, sector_t secnum, dev_info = bdev->bd_disk->private_data; if (!dev_info) return -ENODEV; - dev_sz = dev_info->end - dev_info->start; + dev_sz = dev_info->end - dev_info->start + 1; offset = secnum * 512; *kaddr = (void *) dev_info->start + offset; *pfn = __pfn_to_pfn_t(PFN_DOWN(dev_info->start + offset), PFN_DEV); diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index 41e28b23b26a..0c443e26835d 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -2,9 +2,23 @@ # S/390 character devices # +ifdef CONFIG_FUNCTION_TRACER +# Do not trace early setup code +CFLAGS_REMOVE_sclp_early_core.o = $(CC_FLAGS_FTRACE) +endif + +GCOV_PROFILE_sclp_early_core.o := n +KCOV_INSTRUMENT_sclp_early_core.o := n +UBSAN_SANITIZE_sclp_early_core.o := n + +ifneq ($(CC_FLAGS_MARCH),-march=z900) +CFLAGS_REMOVE_sclp_early_core.o += $(CC_FLAGS_MARCH) +CFLAGS_sclp_early_core.o += -march=z900 +endif + obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \ sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o sclp_ctl.o \ - sclp_early.o + sclp_early.o sclp_early_core.o obj-$(CONFIG_TN3270) += raw3270.o obj-$(CONFIG_TN3270_CONSOLE) += con3270.o diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 285b4006f44b..8522cfce5b4e 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -31,7 +31,7 @@ static struct raw3270_fn con3270_fn; -static bool auto_update = 1; +static bool auto_update = true; module_param(auto_update, bool, 0); /* diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index a2da898ce90f..710f2292911d 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -82,7 +82,7 @@ static LIST_HEAD(raw3270_devices); static int raw3270_registered; /* Module parameters */ -static bool tubxcorrect = 0; +static bool tubxcorrect; module_param(tubxcorrect, bool, 0); /* diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index 272898225dbb..9c471ea1b99c 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -94,13 +94,6 @@ static struct timer_list sclp_request_timer; /* Timer for queued requests. */ static struct timer_list sclp_queue_timer; -/* Internal state: is the driver initialized? */ -static volatile enum sclp_init_state_t { - sclp_init_state_uninitialized, - sclp_init_state_initializing, - sclp_init_state_initialized -} sclp_init_state = sclp_init_state_uninitialized; - /* Internal state: is a request active at the sclp? */ static volatile enum sclp_running_state_t { sclp_running_state_idle, @@ -147,31 +140,6 @@ static void __sclp_make_read_req(void); static int sclp_init_mask(int calculate); static int sclp_init(void); -/* Perform service call. Return 0 on success, non-zero otherwise. */ -int -sclp_service_call(sclp_cmdw_t command, void *sccb) -{ - int cc = 4; /* Initialize for program check handling */ - - asm volatile( - "0: .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */ - "1: ipm %0\n" - " srl %0,28\n" - "2:\n" - EX_TABLE(0b, 2b) - EX_TABLE(1b, 2b) - : "+&d" (cc) : "d" (command), "a" (__pa(sccb)) - : "cc", "memory"); - if (cc == 4) - return -EINVAL; - if (cc == 3) - return -EIO; - if (cc == 2) - return -EBUSY; - return 0; -} - - static void __sclp_queue_read_req(void) { diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h index e1fc7eb043d6..53b5d1b9761a 100644 --- a/drivers/s390/char/sclp.h +++ b/drivers/s390/char/sclp.h @@ -204,19 +204,57 @@ void sclp_unregister(struct sclp_register *reg); int sclp_remove_processed(struct sccb_header *sccb); int sclp_deactivate(void); int sclp_reactivate(void); -int sclp_service_call(sclp_cmdw_t command, void *sccb); int sclp_sync_request(sclp_cmdw_t command, void *sccb); int sclp_sync_request_timeout(sclp_cmdw_t command, void *sccb, int timeout); int sclp_sdias_init(void); void sclp_sdias_exit(void); +enum { + sclp_init_state_uninitialized, + sclp_init_state_initializing, + sclp_init_state_initialized +}; + +extern int sclp_init_state; extern int sclp_console_pages; extern int sclp_console_drop; extern unsigned long sclp_console_full; +extern char sclp_early_sccb[PAGE_SIZE]; + +void sclp_early_wait_irq(void); +int sclp_early_cmd(sclp_cmdw_t cmd, void *sccb); +unsigned int sclp_early_con_check_linemode(struct init_sccb *sccb); +int sclp_early_set_event_mask(struct init_sccb *sccb, + unsigned long receive_mask, + unsigned long send_mask); + /* useful inlines */ +/* Perform service call. Return 0 on success, non-zero otherwise. */ +static inline int sclp_service_call(sclp_cmdw_t command, void *sccb) +{ + int cc = 4; /* Initialize for program check handling */ + + asm volatile( + "0: .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */ + "1: ipm %0\n" + " srl %0,28\n" + "2:\n" + EX_TABLE(0b, 2b) + EX_TABLE(1b, 2b) + : "+&d" (cc) : "d" (command), "a" ((unsigned long)sccb) + : "cc", "memory"); + if (cc == 4) + return -EINVAL; + if (cc == 3) + return -EIO; + if (cc == 2) + return -EBUSY; + return 0; +} + /* VM uses EBCDIC 037, LPAR+native(SE+HMC) use EBCDIC 500 */ /* translate single character from ASCII to EBCDIC */ static inline unsigned char diff --git a/drivers/s390/char/sclp_early.c b/drivers/s390/char/sclp_early.c index f8e46c22e641..519ec1787117 100644 --- a/drivers/s390/char/sclp_early.c +++ b/drivers/s390/char/sclp_early.c @@ -55,46 +55,23 @@ struct read_info_sccb { u8 _pad_128[4096 - 128]; /* 128-4095 */ } __packed __aligned(PAGE_SIZE); -static char sccb_early[PAGE_SIZE] __aligned(PAGE_SIZE) __initdata; static struct sclp_ipl_info sclp_ipl_info; struct sclp_info sclp; EXPORT_SYMBOL(sclp); -static int __init sclp_cmd_sync_early(sclp_cmdw_t cmd, void *sccb) +static int __init sclp_early_read_info(struct read_info_sccb *sccb) { - int rc; - - __ctl_set_bit(0, 9); - rc = sclp_service_call(cmd, sccb); - if (rc) - goto out; - __load_psw_mask(PSW_DEFAULT_KEY | PSW_MASK_BASE | PSW_MASK_EA | - PSW_MASK_BA | PSW_MASK_EXT | PSW_MASK_WAIT); - local_irq_disable(); -out: - /* Contents of the sccb might have changed. */ - barrier(); - __ctl_clear_bit(0, 9); - return rc; -} - -static int __init sclp_read_info_early(struct read_info_sccb *sccb) -{ - int rc, i; + int i; sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED, SCLP_CMDW_READ_SCP_INFO}; for (i = 0; i < ARRAY_SIZE(commands); i++) { - do { - memset(sccb, 0, sizeof(*sccb)); - sccb->header.length = sizeof(*sccb); - sccb->header.function_code = 0x80; - sccb->header.control_mask[2] = 0x80; - rc = sclp_cmd_sync_early(commands[i], sccb); - } while (rc == -EBUSY); - - if (rc) + memset(sccb, 0, sizeof(*sccb)); + sccb->header.length = sizeof(*sccb); + sccb->header.function_code = 0x80; + sccb->header.control_mask[2] = 0x80; + if (sclp_early_cmd(commands[i], sccb)) break; if (sccb->header.response_code == 0x10) return 0; @@ -104,12 +81,12 @@ static int __init sclp_read_info_early(struct read_info_sccb *sccb) return -EIO; } -static void __init sclp_facilities_detect(struct read_info_sccb *sccb) +static void __init sclp_early_facilities_detect(struct read_info_sccb *sccb) { struct sclp_core_entry *cpue; u16 boot_cpu_address, cpu; - if (sclp_read_info_early(sccb)) + if (sclp_early_read_info(sccb)) return; sclp.facilities = sccb->facilities; @@ -172,141 +149,96 @@ static void __init sclp_facilities_detect(struct read_info_sccb *sccb) } /* - * This function will be called after sclp_facilities_detect(), which gets - * called from early.c code. The sclp_facilities_detect() function retrieves + * This function will be called after sclp_early_facilities_detect(), which gets + * called from early.c code. The sclp_early_facilities_detect() function retrieves * and saves the IPL information. */ -void __init sclp_get_ipl_info(struct sclp_ipl_info *info) +void __init sclp_early_get_ipl_info(struct sclp_ipl_info *info) { *info = sclp_ipl_info; } -static int __init sclp_cmd_early(sclp_cmdw_t cmd, void *sccb) -{ - int rc; - - do { - rc = sclp_cmd_sync_early(cmd, sccb); - } while (rc == -EBUSY); - - if (rc) - return -EIO; - if (((struct sccb_header *) sccb)->response_code != 0x0020) - return -EIO; - return 0; -} - -static void __init sccb_init_eq_size(struct sdias_sccb *sccb) -{ - memset(sccb, 0, sizeof(*sccb)); - - sccb->hdr.length = sizeof(*sccb); - sccb->evbuf.hdr.length = sizeof(struct sdias_evbuf); - sccb->evbuf.hdr.type = EVTYP_SDIAS; - sccb->evbuf.event_qual = SDIAS_EQ_SIZE; - sccb->evbuf.data_id = SDIAS_DI_FCP_DUMP; - sccb->evbuf.event_id = 4712; - sccb->evbuf.dbs = 1; -} +static struct sclp_core_info sclp_early_core_info __initdata; +static int sclp_early_core_info_valid __initdata; -static int __init sclp_set_event_mask(struct init_sccb *sccb, - unsigned long receive_mask, - unsigned long send_mask) +static void __init sclp_early_init_core_info(struct read_cpu_info_sccb *sccb) { - memset(sccb, 0, sizeof(*sccb)); - sccb->header.length = sizeof(*sccb); - sccb->mask_length = sizeof(sccb_mask_t); - sccb->receive_mask = receive_mask; - sccb->send_mask = send_mask; - return sclp_cmd_early(SCLP_CMDW_WRITE_EVENT_MASK, sccb); -} - -static struct sclp_core_info sclp_core_info_early __initdata; -static int sclp_core_info_early_valid __initdata; - -static void __init sclp_init_core_info_early(struct read_cpu_info_sccb *sccb) -{ - int rc; - if (!SCLP_HAS_CPU_INFO) return; memset(sccb, 0, sizeof(*sccb)); sccb->header.length = sizeof(*sccb); - do { - rc = sclp_cmd_sync_early(SCLP_CMDW_READ_CPU_INFO, sccb); - } while (rc == -EBUSY); - if (rc) + if (sclp_early_cmd(SCLP_CMDW_READ_CPU_INFO, sccb)) return; if (sccb->header.response_code != 0x0010) return; - sclp_fill_core_info(&sclp_core_info_early, sccb); - sclp_core_info_early_valid = 1; + sclp_fill_core_info(&sclp_early_core_info, sccb); + sclp_early_core_info_valid = 1; } -int __init _sclp_get_core_info_early(struct sclp_core_info *info) +int __init sclp_early_get_core_info(struct sclp_core_info *info) { - if (!sclp_core_info_early_valid) + if (!sclp_early_core_info_valid) return -EIO; - *info = sclp_core_info_early; + *info = sclp_early_core_info; return 0; } -static long __init sclp_hsa_size_init(struct sdias_sccb *sccb) +static long __init sclp_early_hsa_size_init(struct sdias_sccb *sccb) { - sccb_init_eq_size(sccb); - if (sclp_cmd_early(SCLP_CMDW_WRITE_EVENT_DATA, sccb)) + memset(sccb, 0, sizeof(*sccb)); + sccb->hdr.length = sizeof(*sccb); + sccb->evbuf.hdr.length = sizeof(struct sdias_evbuf); + sccb->evbuf.hdr.type = EVTYP_SDIAS; + sccb->evbuf.event_qual = SDIAS_EQ_SIZE; + sccb->evbuf.data_id = SDIAS_DI_FCP_DUMP; + sccb->evbuf.event_id = 4712; + sccb->evbuf.dbs = 1; + if (sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb)) + return -EIO; + if (sccb->hdr.response_code != 0x20) return -EIO; if (sccb->evbuf.blk_cnt == 0) return 0; return (sccb->evbuf.blk_cnt - 1) * PAGE_SIZE; } -static long __init sclp_hsa_copy_wait(struct sccb_header *sccb) +static long __init sclp_early_hsa_copy_wait(struct sdias_sccb *sccb) { memset(sccb, 0, PAGE_SIZE); - sccb->length = PAGE_SIZE; - if (sclp_cmd_early(SCLP_CMDW_READ_EVENT_DATA, sccb)) + sccb->hdr.length = PAGE_SIZE; + if (sclp_early_cmd(SCLP_CMDW_READ_EVENT_DATA, sccb)) return -EIO; - if (((struct sdias_sccb *) sccb)->evbuf.blk_cnt == 0) + if ((sccb->hdr.response_code != 0x20) && (sccb->hdr.response_code != 0x220)) + return -EIO; + if (sccb->evbuf.blk_cnt == 0) return 0; - return (((struct sdias_sccb *) sccb)->evbuf.blk_cnt - 1) * PAGE_SIZE; + return (sccb->evbuf.blk_cnt - 1) * PAGE_SIZE; } -static void __init sclp_hsa_size_detect(void *sccb) +static void __init sclp_early_hsa_size_detect(void *sccb) { - long size; + unsigned long flags; + long size = -EIO; - /* First try synchronous interface (LPAR) */ - if (sclp_set_event_mask(sccb, 0, 0x40000010)) - return; - size = sclp_hsa_size_init(sccb); - if (size < 0) - return; - if (size != 0) + raw_local_irq_save(flags); + if (sclp_early_set_event_mask(sccb, EVTYP_SDIAS_MASK, EVTYP_SDIAS_MASK)) goto out; - /* Then try asynchronous interface (z/VM) */ - if (sclp_set_event_mask(sccb, 0x00000010, 0x40000010)) - return; - size = sclp_hsa_size_init(sccb); - if (size < 0) - return; - size = sclp_hsa_copy_wait(sccb); - if (size < 0) - return; + size = sclp_early_hsa_size_init(sccb); + /* First check for synchronous response (LPAR) */ + if (size) + goto out_mask; + if (!(S390_lowcore.ext_params & 1)) + sclp_early_wait_irq(); + size = sclp_early_hsa_copy_wait(sccb); +out_mask: + sclp_early_set_event_mask(sccb, 0, 0); out: - sclp.hsa_size = size; -} - -static unsigned int __init sclp_con_check_linemode(struct init_sccb *sccb) -{ - if (!(sccb->sclp_send_mask & EVTYP_OPCMD_MASK)) - return 0; - if (!(sccb->sclp_receive_mask & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK))) - return 0; - return 1; + raw_local_irq_restore(flags); + if (size > 0) + sclp.hsa_size = size; } -static void __init sclp_console_detect(struct init_sccb *sccb) +static void __init sclp_early_console_detect(struct init_sccb *sccb) { if (sccb->header.response_code != 0x20) return; @@ -314,21 +246,22 @@ static void __init sclp_console_detect(struct init_sccb *sccb) if (sccb->sclp_send_mask & EVTYP_VT220MSG_MASK) sclp.has_vt220 = 1; - if (sclp_con_check_linemode(sccb)) + if (sclp_early_con_check_linemode(sccb)) sclp.has_linemode = 1; } void __init sclp_early_detect(void) { - void *sccb = &sccb_early; + void *sccb = &sclp_early_sccb; - sclp_facilities_detect(sccb); - sclp_init_core_info_early(sccb); - sclp_hsa_size_detect(sccb); + sclp_early_facilities_detect(sccb); + sclp_early_init_core_info(sccb); + sclp_early_hsa_size_detect(sccb); - /* Turn off SCLP event notifications. Also save remote masks in the + /* + * Turn off SCLP event notifications. Also save remote masks in the * sccb. These are sufficient to detect sclp console capabilities. */ - sclp_set_event_mask(sccb, 0, 0); - sclp_console_detect(sccb); + sclp_early_set_event_mask(sccb, 0, 0); + sclp_early_console_detect(sccb); } diff --git a/drivers/s390/char/sclp_early_core.c b/drivers/s390/char/sclp_early_core.c new file mode 100644 index 000000000000..5029cc87e80f --- /dev/null +++ b/drivers/s390/char/sclp_early_core.c @@ -0,0 +1,208 @@ +/* + * Copyright IBM Corp. 2015 + * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com> + */ + +#include <linux/kernel.h> +#include <asm/processor.h> +#include <asm/lowcore.h> +#include <asm/ebcdic.h> +#include <asm/irq.h> +#include "sclp.h" +#include "sclp_rw.h" + +char sclp_early_sccb[PAGE_SIZE] __aligned(PAGE_SIZE) __section(data); +int sclp_init_state __section(data) = sclp_init_state_uninitialized; + +void sclp_early_wait_irq(void) +{ + unsigned long psw_mask, addr; + psw_t psw_ext_save, psw_wait; + union ctlreg0 cr0, cr0_new; + + __ctl_store(cr0.val, 0, 0); + cr0_new.val = cr0.val & ~CR0_IRQ_SUBCLASS_MASK; + cr0_new.lap = 0; + cr0_new.sssm = 1; + __ctl_load(cr0_new.val, 0, 0); + + psw_ext_save = S390_lowcore.external_new_psw; + psw_mask = __extract_psw(); + S390_lowcore.external_new_psw.mask = psw_mask; + psw_wait.mask = psw_mask | PSW_MASK_EXT | PSW_MASK_WAIT; + S390_lowcore.ext_int_code = 0; + + do { + asm volatile( + " larl %[addr],0f\n" + " stg %[addr],%[psw_wait_addr]\n" + " stg %[addr],%[psw_ext_addr]\n" + " lpswe %[psw_wait]\n" + "0:\n" + : [addr] "=&d" (addr), + [psw_wait_addr] "=Q" (psw_wait.addr), + [psw_ext_addr] "=Q" (S390_lowcore.external_new_psw.addr) + : [psw_wait] "Q" (psw_wait) + : "cc", "memory"); + } while (S390_lowcore.ext_int_code != EXT_IRQ_SERVICE_SIG); + + S390_lowcore.external_new_psw = psw_ext_save; + __ctl_load(cr0.val, 0, 0); +} + +int sclp_early_cmd(sclp_cmdw_t cmd, void *sccb) +{ + unsigned long flags; + int rc; + + raw_local_irq_save(flags); + rc = sclp_service_call(cmd, sccb); + if (rc) + goto out; + sclp_early_wait_irq(); +out: + raw_local_irq_restore(flags); + return rc; +} + +struct write_sccb { + struct sccb_header header; + struct msg_buf msg; +} __packed; + +/* Output multi-line text using SCLP Message interface. */ +static void sclp_early_print_lm(const char *str, unsigned int len) +{ + unsigned char *ptr, *end, ch; + unsigned int count, offset; + struct write_sccb *sccb; + struct msg_buf *msg; + struct mdb *mdb; + struct mto *mto; + struct go *go; + + sccb = (struct write_sccb *) &sclp_early_sccb; + end = (unsigned char *) sccb + sizeof(sclp_early_sccb) - 1; + memset(sccb, 0, sizeof(*sccb)); + ptr = (unsigned char *) &sccb->msg.mdb.mto; + offset = 0; + do { + for (count = sizeof(*mto); offset < len; count++) { + ch = str[offset++]; + if ((ch == 0x0a) || (ptr + count > end)) + break; + ptr[count] = _ascebc[ch]; + } + mto = (struct mto *) ptr; + memset(mto, 0, sizeof(*mto)); + mto->length = count; + mto->type = 4; + mto->line_type_flags = LNTPFLGS_ENDTEXT; + ptr += count; + } while ((offset < len) && (ptr + sizeof(*mto) <= end)); + len = ptr - (unsigned char *) sccb; + sccb->header.length = len - offsetof(struct write_sccb, header); + msg = &sccb->msg; + msg->header.type = EVTYP_MSG; + msg->header.length = len - offsetof(struct write_sccb, msg.header); + mdb = &msg->mdb; + mdb->header.type = 1; + mdb->header.tag = 0xD4C4C240; + mdb->header.revision_code = 1; + mdb->header.length = len - offsetof(struct write_sccb, msg.mdb.header); + go = &mdb->go; + go->length = sizeof(*go); + go->type = 1; + sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb); +} + +struct vt220_sccb { + struct sccb_header header; + struct { + struct evbuf_header header; + char data[]; + } msg; +} __packed; + +/* Output multi-line text using SCLP VT220 interface. */ +static void sclp_early_print_vt220(const char *str, unsigned int len) +{ + struct vt220_sccb *sccb; + + sccb = (struct vt220_sccb *) &sclp_early_sccb; + if (sizeof(*sccb) + len >= sizeof(sclp_early_sccb)) + len = sizeof(sclp_early_sccb) - sizeof(*sccb); + memset(sccb, 0, sizeof(*sccb)); + memcpy(&sccb->msg.data, str, len); + sccb->header.length = sizeof(*sccb) + len; + sccb->msg.header.length = sizeof(sccb->msg) + len; + sccb->msg.header.type = EVTYP_VT220MSG; + sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb); +} + +int sclp_early_set_event_mask(struct init_sccb *sccb, + unsigned long receive_mask, + unsigned long send_mask) +{ + memset(sccb, 0, sizeof(*sccb)); + sccb->header.length = sizeof(*sccb); + sccb->mask_length = sizeof(sccb_mask_t); + sccb->receive_mask = receive_mask; + sccb->send_mask = send_mask; + if (sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_MASK, sccb)) + return -EIO; + if (sccb->header.response_code != 0x20) + return -EIO; + return 0; +} + +unsigned int sclp_early_con_check_linemode(struct init_sccb *sccb) +{ + if (!(sccb->sclp_send_mask & EVTYP_OPCMD_MASK)) + return 0; + if (!(sccb->sclp_receive_mask & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK))) + return 0; + return 1; +} + +static int sclp_early_setup(int disable, int *have_linemode, int *have_vt220) +{ + unsigned long receive_mask, send_mask; + struct init_sccb *sccb; + int rc; + + *have_linemode = *have_vt220 = 0; + sccb = (struct init_sccb *) &sclp_early_sccb; + receive_mask = disable ? 0 : EVTYP_OPCMD_MASK; + send_mask = disable ? 0 : EVTYP_VT220MSG_MASK | EVTYP_MSG_MASK; + rc = sclp_early_set_event_mask(sccb, receive_mask, send_mask); + if (rc) + return rc; + *have_linemode = sclp_early_con_check_linemode(sccb); + *have_vt220 = sccb->send_mask & EVTYP_VT220MSG_MASK; + return rc; +} + +/* + * Output one or more lines of text on the SCLP console (VT220 and / + * or line-mode). + */ +void __sclp_early_printk(const char *str, unsigned int len) +{ + int have_linemode, have_vt220; + + if (sclp_init_state != sclp_init_state_uninitialized) + return; + if (sclp_early_setup(0, &have_linemode, &have_vt220) != 0) + return; + if (have_linemode) + sclp_early_print_lm(str, len); + if (have_vt220) + sclp_early_print_vt220(str, len); + sclp_early_setup(1, &have_linemode, &have_vt220); +} + +void sclp_early_printk(const char *str) +{ + __sclp_early_printk(str, strlen(str)); +} diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index d3b51edb056e..aaed778f67c4 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -15,7 +15,6 @@ #include <linux/init.h> #include <linux/slab.h> -#include <linux/miscdevice.h> #include <linux/debugfs.h> #include <linux/memblock.h> @@ -273,7 +272,7 @@ static int __init zcore_reipl_init(void) rc = memcpy_hsa_kernel(ipl_block, ipib_info.ipib, PAGE_SIZE); else rc = memcpy_real(ipl_block, (void *) ipib_info.ipib, PAGE_SIZE); - if (rc || csum_partial(ipl_block, ipl_block->hdr.len, 0) != + if (rc || (__force u32)csum_partial(ipl_block, ipl_block->hdr.len, 0) != ipib_info.checksum) { TRACE("Checksum does not match\n"); free_page((unsigned long) ipl_block); diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index 876c7e6e3a99..7e0d4f724dda 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -444,6 +444,7 @@ int chp_update_desc(struct channel_path *chp) */ int chp_new(struct chp_id chpid) { + struct channel_subsystem *css = css_by_id(chpid.cssid); struct channel_path *chp; int ret; @@ -456,7 +457,7 @@ int chp_new(struct chp_id chpid) /* fill in status, etc. */ chp->chpid = chpid; chp->state = 1; - chp->dev.parent = &channel_subsystems[chpid.cssid]->device; + chp->dev.parent = &css->device; chp->dev.groups = chp_attr_groups; chp->dev.release = chp_release; mutex_init(&chp->lock); @@ -479,17 +480,17 @@ int chp_new(struct chp_id chpid) put_device(&chp->dev); goto out; } - mutex_lock(&channel_subsystems[chpid.cssid]->mutex); - if (channel_subsystems[chpid.cssid]->cm_enabled) { + mutex_lock(&css->mutex); + if (css->cm_enabled) { ret = chp_add_cmg_attr(chp); if (ret) { device_unregister(&chp->dev); - mutex_unlock(&channel_subsystems[chpid.cssid]->mutex); + mutex_unlock(&css->mutex); goto out; } } - channel_subsystems[chpid.cssid]->chps[chpid.id] = chp; - mutex_unlock(&channel_subsystems[chpid.cssid]->mutex); + css->chps[chpid.id] = chp; + mutex_unlock(&css->mutex); goto out; out_free: kfree(chp); diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h index bb5a68226cda..0d8437b7ea72 100644 --- a/drivers/s390/cio/chp.h +++ b/drivers/s390/cio/chp.h @@ -54,7 +54,7 @@ struct channel_path { /* 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]; + return css_by_id(chpid.cssid)->chps[chpid.id]; } int chp_get_status(struct chp_id chpid); diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 11674698b36d..7b0b295b2313 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -1131,6 +1131,52 @@ int chsc_enable_facility(int operation_code) return ret; } +int __init chsc_get_cssid(int idx) +{ + struct { + struct chsc_header request; + u8 atype; + u32 : 24; + u32 reserved1[6]; + struct chsc_header response; + u32 reserved2[3]; + struct { + u8 cssid; + u32 : 24; + } list[0]; + } __packed *sdcal_area; + int ret; + + spin_lock_irq(&chsc_page_lock); + memset(chsc_page, 0, PAGE_SIZE); + sdcal_area = chsc_page; + sdcal_area->request.length = 0x0020; + sdcal_area->request.code = 0x0034; + sdcal_area->atype = 4; + + ret = chsc(sdcal_area); + if (ret) { + ret = (ret == 3) ? -ENODEV : -EBUSY; + goto exit; + } + + ret = chsc_error_from_response(sdcal_area->response.code); + if (ret) { + CIO_CRW_EVENT(2, "chsc: sdcal failed (rc=%04x)\n", + sdcal_area->response.code); + goto exit; + } + + if ((addr_t) &sdcal_area->list[idx] < + (addr_t) &sdcal_area->response + sdcal_area->response.length) + ret = sdcal_area->list[idx].cssid; + else + ret = -ENODEV; +exit: + spin_unlock_irq(&chsc_page_lock); + return ret; +} + struct css_general_char css_general_characteristics; struct css_chsc_char css_chsc_characteristics; @@ -1216,7 +1262,7 @@ int chsc_sstpi(void *page, void *result, size_t size) struct chsc_header request; unsigned int rsvd0[3]; struct chsc_header response; - char data[size]; + char data[]; } __attribute__ ((packed)) *rr; int rc; diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 67c87b6e63ec..321a3f765810 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -242,6 +242,8 @@ int chsc_pnso_brinfo(struct subchannel_id schid, struct chsc_brinfo_resume_token resume_token, int cnc); +int __init chsc_get_cssid(int idx); + #ifdef CONFIG_SCM_BUS int scm_update_information(void); int scm_process_availability_information(void); diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index 6b6386e9a500..220491d27ef4 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -1085,15 +1085,9 @@ static ssize_t cmb_show_avg_utilization(struct device *dev, data.function_pending_time + data.device_disconnect_time; - /* shift to avoid long long division */ - while (-1ul < (data.elapsed_time | utilization)) { - utilization >>= 8; - data.elapsed_time >>= 8; - } - /* calculate value in 0.1 percent units */ - t = (unsigned long) data.elapsed_time / 1000; - u = (unsigned long) utilization / t; + t = data.elapsed_time / 1000; + u = utilization / t; return sprintf(buf, "%02ld.%01ld%%\n", u/ 10, u - (u/ 10) * 10); } diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index bc099b61394d..e2aa944eb566 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -36,7 +36,8 @@ int css_init_done = 0; int max_ssid; -struct channel_subsystem *channel_subsystems[__MAX_CSSID + 1]; +#define MAX_CSS_IDX 0 +struct channel_subsystem *channel_subsystems[MAX_CSS_IDX + 1]; static struct bus_type css_bus_type; int @@ -702,7 +703,8 @@ css_generate_pgid(struct channel_subsystem *css, u32 tod_high) if (css_general_characteristics.mcss) { css->global_pgid.pgid_high.ext_cssid.version = 0x80; - css->global_pgid.pgid_high.ext_cssid.cssid = css->cssid; + css->global_pgid.pgid_high.ext_cssid.cssid = + (css->cssid < 0) ? 0 : css->cssid; } else { css->global_pgid.pgid_high.cpu_addr = stap(); } @@ -712,43 +714,44 @@ css_generate_pgid(struct channel_subsystem *css, u32 tod_high) css->global_pgid.tod_high = tod_high; } -static void -channel_subsystem_release(struct device *dev) +static void channel_subsystem_release(struct device *dev) { - struct channel_subsystem *css; + struct channel_subsystem *css = to_css(dev); - css = to_css(dev); mutex_destroy(&css->mutex); - if (css->pseudo_subchannel) { - /* Implies that it has been generated but never registered. */ - css_subchannel_release(&css->pseudo_subchannel->dev); - css->pseudo_subchannel = NULL; - } kfree(css); } -static ssize_t -css_cm_enable_show(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t real_cssid_show(struct device *dev, struct device_attribute *a, + char *buf) +{ + struct channel_subsystem *css = to_css(dev); + + if (css->cssid < 0) + return -EINVAL; + + return sprintf(buf, "%x\n", css->cssid); +} +static DEVICE_ATTR_RO(real_cssid); + +static ssize_t cm_enable_show(struct device *dev, struct device_attribute *a, + char *buf) { struct channel_subsystem *css = to_css(dev); int ret; - if (!css) - return 0; mutex_lock(&css->mutex); ret = sprintf(buf, "%x\n", css->cm_enabled); mutex_unlock(&css->mutex); return ret; } -static ssize_t -css_cm_enable_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t cm_enable_store(struct device *dev, struct device_attribute *a, + const char *buf, size_t count) { struct channel_subsystem *css = to_css(dev); - int ret; unsigned long val; + int ret; ret = kstrtoul(buf, 16, &val); if (ret) @@ -767,51 +770,104 @@ css_cm_enable_store(struct device *dev, struct device_attribute *attr, mutex_unlock(&css->mutex); return ret < 0 ? ret : count; } +static DEVICE_ATTR_RW(cm_enable); + +static umode_t cm_enable_mode(struct kobject *kobj, struct attribute *attr, + int index) +{ + return css_chsc_characteristics.secm ? attr->mode : 0; +} + +static struct attribute *cssdev_attrs[] = { + &dev_attr_real_cssid.attr, + NULL, +}; + +static struct attribute_group cssdev_attr_group = { + .attrs = cssdev_attrs, +}; + +static struct attribute *cssdev_cm_attrs[] = { + &dev_attr_cm_enable.attr, + NULL, +}; + +static struct attribute_group cssdev_cm_attr_group = { + .attrs = cssdev_cm_attrs, + .is_visible = cm_enable_mode, +}; -static DEVICE_ATTR(cm_enable, 0644, css_cm_enable_show, css_cm_enable_store); +static const struct attribute_group *cssdev_attr_groups[] = { + &cssdev_attr_group, + &cssdev_cm_attr_group, + NULL, +}; static int __init setup_css(int nr) { - u32 tod_high; - int ret; struct channel_subsystem *css; + int ret; - css = channel_subsystems[nr]; - memset(css, 0, sizeof(struct channel_subsystem)); - css->pseudo_subchannel = - kzalloc(sizeof(*css->pseudo_subchannel), GFP_KERNEL); - if (!css->pseudo_subchannel) + css = kzalloc(sizeof(*css), GFP_KERNEL); + if (!css) return -ENOMEM; + + channel_subsystems[nr] = css; + dev_set_name(&css->device, "css%x", nr); + css->device.groups = cssdev_attr_groups; + css->device.release = channel_subsystem_release; + + mutex_init(&css->mutex); + css->cssid = chsc_get_cssid(nr); + css_generate_pgid(css, (u32) (get_tod_clock() >> 32)); + + ret = device_register(&css->device); + if (ret) { + put_device(&css->device); + goto out_err; + } + + css->pseudo_subchannel = kzalloc(sizeof(*css->pseudo_subchannel), + GFP_KERNEL); + if (!css->pseudo_subchannel) { + device_unregister(&css->device); + ret = -ENOMEM; + goto out_err; + } + css->pseudo_subchannel->dev.parent = &css->device; css->pseudo_subchannel->dev.release = css_subchannel_release; - dev_set_name(&css->pseudo_subchannel->dev, "defunct"); mutex_init(&css->pseudo_subchannel->reg_mutex); ret = css_sch_create_locks(css->pseudo_subchannel); if (ret) { kfree(css->pseudo_subchannel); - return ret; + device_unregister(&css->device); + goto out_err; } - mutex_init(&css->mutex); - css->valid = 1; - css->cssid = nr; - dev_set_name(&css->device, "css%x", nr); - css->device.release = channel_subsystem_release; - tod_high = (u32) (get_tod_clock() >> 32); - css_generate_pgid(css, tod_high); - return 0; + + dev_set_name(&css->pseudo_subchannel->dev, "defunct"); + ret = device_register(&css->pseudo_subchannel->dev); + if (ret) { + put_device(&css->pseudo_subchannel->dev); + device_unregister(&css->device); + goto out_err; + } + + return ret; +out_err: + channel_subsystems[nr] = NULL; + return ret; } static int css_reboot_event(struct notifier_block *this, unsigned long event, void *ptr) { - int ret, i; + struct channel_subsystem *css; + int ret; ret = NOTIFY_DONE; - for (i = 0; i <= __MAX_CSSID; i++) { - struct channel_subsystem *css; - - css = channel_subsystems[i]; + for_each_css(css) { mutex_lock(&css->mutex); if (css->cm_enabled) if (chsc_secm(css, 0)) @@ -835,16 +891,14 @@ static struct notifier_block css_reboot_notifier = { static int css_power_event(struct notifier_block *this, unsigned long event, void *ptr) { - int ret, i; + struct channel_subsystem *css; + int ret; switch (event) { case PM_HIBERNATION_PREPARE: case PM_SUSPEND_PREPARE: ret = NOTIFY_DONE; - for (i = 0; i <= __MAX_CSSID; i++) { - struct channel_subsystem *css; - - css = channel_subsystems[i]; + for_each_css(css) { mutex_lock(&css->mutex); if (!css->cm_enabled) { mutex_unlock(&css->mutex); @@ -858,10 +912,7 @@ static int css_power_event(struct notifier_block *this, unsigned long event, case PM_POST_HIBERNATION: case PM_POST_SUSPEND: ret = NOTIFY_DONE; - for (i = 0; i <= __MAX_CSSID; i++) { - struct channel_subsystem *css; - - css = channel_subsystems[i]; + for_each_css(css) { mutex_lock(&css->mutex); if (!css->cm_enabled) { mutex_unlock(&css->mutex); @@ -916,36 +967,10 @@ static int __init css_bus_init(void) goto out; /* Setup css structure. */ - for (i = 0; i <= __MAX_CSSID; i++) { - struct channel_subsystem *css; - - css = kmalloc(sizeof(struct channel_subsystem), GFP_KERNEL); - if (!css) { - ret = -ENOMEM; - goto out_unregister; - } - channel_subsystems[i] = css; + for (i = 0; i <= MAX_CSS_IDX; i++) { ret = setup_css(i); - if (ret) { - kfree(channel_subsystems[i]); - goto out_unregister; - } - ret = device_register(&css->device); - if (ret) { - put_device(&css->device); + if (ret) goto out_unregister; - } - if (css_chsc_characteristics.secm) { - ret = device_create_file(&css->device, - &dev_attr_cm_enable); - if (ret) - goto out_device; - } - ret = device_register(&css->pseudo_subchannel->dev); - if (ret) { - put_device(&css->pseudo_subchannel->dev); - goto out_file; - } } ret = register_reboot_notifier(&css_reboot_notifier); if (ret) @@ -961,23 +986,10 @@ static int __init css_bus_init(void) isc_register(IO_SCH_ISC); return 0; -out_file: - if (css_chsc_characteristics.secm) - device_remove_file(&channel_subsystems[i]->device, - &dev_attr_cm_enable); -out_device: - device_unregister(&channel_subsystems[i]->device); out_unregister: - while (i > 0) { - struct channel_subsystem *css; - - i--; - css = channel_subsystems[i]; + while (i-- > 0) { + struct channel_subsystem *css = channel_subsystems[i]; device_unregister(&css->pseudo_subchannel->dev); - css->pseudo_subchannel = NULL; - if (css_chsc_characteristics.secm) - device_remove_file(&css->device, - &dev_attr_cm_enable); device_unregister(&css->device); } bus_unregister(&css_bus_type); @@ -993,14 +1005,9 @@ out: static void __init css_bus_cleanup(void) { struct channel_subsystem *css; - int i; - for (i = 0; i <= __MAX_CSSID; i++) { - css = channel_subsystems[i]; + for_each_css(css) { device_unregister(&css->pseudo_subchannel->dev); - css->pseudo_subchannel = NULL; - if (css_chsc_characteristics.secm) - device_remove_file(&css->device, &dev_attr_cm_enable); device_unregister(&css->device); } bus_unregister(&css_bus_type); diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index 2c9107e20251..c9f3fb39ebeb 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -113,8 +113,7 @@ extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *); void css_update_ssd_info(struct subchannel *sch); struct channel_subsystem { - u8 cssid; - int valid; + int cssid; struct channel_path *chps[__MAX_CHPID + 1]; struct device device; struct pgid global_pgid; @@ -130,6 +129,16 @@ struct channel_subsystem { extern struct channel_subsystem *channel_subsystems[]; +/* Dummy helper which needs to change once we support more than one css. */ +static inline struct channel_subsystem *css_by_id(u8 cssid) +{ + return channel_subsystems[0]; +} + +/* Dummy iterator which needs to change once we support more than one css. */ +#define for_each_css(css) \ + for ((css) = channel_subsystems[0]; (css); (css) = NULL) + /* Helper functions to build lists for the slow path. */ void css_schedule_eval(struct subchannel_id schid); void css_schedule_eval_all(void); diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 71bf9bded485..a4ad39ba3873 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -457,7 +457,7 @@ static inline void inbound_primed(struct qdio_q *q, int count) { int new; - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in prim: %02x", count); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in prim:%1d %02x", q->nr, count); /* for QEBSM the ACK was already set by EQBS */ if (is_qebsm(q)) { @@ -544,7 +544,8 @@ static int get_inbound_buffer_frontier(struct qdio_q *q) case SLSB_P_INPUT_ACK: if (q->irq_ptr->perf_stat_enabled) q->q_stats.nr_sbal_nop++; - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in nop"); + DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in nop:%1d %#02x", + q->nr, q->first_to_check); break; default: WARN_ON_ONCE(1); diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index 5d06253c2a7a..8ad98a902a91 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c @@ -147,11 +147,11 @@ static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq) struct qdio_q *q; int i; - for_each_input_queue(irq, q, i) { - if (!references_shared_dsci(irq) && - has_multiple_inq_on_dsci(irq)) - xchg(q->irq_ptr->dsci, 0); + if (!references_shared_dsci(irq) && + has_multiple_inq_on_dsci(irq)) + xchg(irq->dsci, 0); + for_each_input_queue(irq, q, i) { if (q->u.in.queue_start_poll) { /* skip if polling is enabled or already in work */ if (test_and_set_bit(QDIO_QUEUE_IRQS_DISABLED, @@ -161,11 +161,11 @@ static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq) } /* avoid dsci clear here, done after processing */ - q->u.in.queue_start_poll(q->irq_ptr->cdev, q->nr, - q->irq_ptr->int_parm); + q->u.in.queue_start_poll(irq->cdev, q->nr, + irq->int_parm); } else { - if (!shared_ind(q->irq_ptr)) - xchg(q->irq_ptr->dsci, 0); + if (!shared_ind(irq)) + xchg(irq->dsci, 0); /* * Call inbound processing but not directly @@ -178,8 +178,7 @@ static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq) /** * tiqdio_thinint_handler - thin interrupt handler for qdio - * @alsi: pointer to adapter local summary indicator - * @data: NULL + * @airq: pointer to adapter interrupt descriptor */ static void tiqdio_thinint_handler(struct airq_struct *airq) { diff --git a/drivers/s390/crypto/ap_asm.h b/drivers/s390/crypto/ap_asm.h index 7a630047c372..287b4ad0999e 100644 --- a/drivers/s390/crypto/ap_asm.h +++ b/drivers/s390/crypto/ap_asm.h @@ -129,7 +129,6 @@ static inline struct ap_queue_status ap_nqap(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length) { - struct msgblock { char _[length]; }; register unsigned long reg0 asm ("0") = qid | 0x40000000UL; register struct ap_queue_status reg1 asm ("1"); register unsigned long reg2 asm ("2") = (unsigned long) msg; @@ -141,8 +140,8 @@ static inline struct ap_queue_status ap_nqap(ap_qid_t qid, "0: .long 0xb2ad0042\n" /* NQAP */ " brc 2,0b" : "+d" (reg0), "=d" (reg1), "+d" (reg2), "+d" (reg3) - : "d" (reg4), "d" (reg5), "m" (*(struct msgblock *) msg) - : "cc"); + : "d" (reg4), "d" (reg5) + : "cc", "memory"); return reg1; } @@ -168,7 +167,6 @@ static inline struct ap_queue_status ap_dqap(ap_qid_t qid, unsigned long long *psmid, void *msg, size_t length) { - struct msgblock { char _[length]; }; register unsigned long reg0 asm("0") = qid | 0x80000000UL; register struct ap_queue_status reg1 asm ("1"); register unsigned long reg2 asm("2") = 0UL; @@ -182,8 +180,8 @@ static inline struct ap_queue_status ap_dqap(ap_qid_t qid, "0: .long 0xb2ae0064\n" /* DQAP */ " brc 6,0b\n" : "+d" (reg0), "=d" (reg1), "+d" (reg2), - "+d" (reg4), "+d" (reg5), "+d" (reg6), "+d" (reg7), - "=m" (*(struct msgblock *) msg) : : "cc"); + "+d" (reg4), "+d" (reg5), "+d" (reg6), "+d" (reg7) + : : "cc", "memory"); *psmid = (((unsigned long long) reg6) << 32) + reg7; return reg1; } diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 5fa699192864..56db76c05775 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -27,7 +27,7 @@ #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt #include <linux/kernel_stat.h> -#include <linux/module.h> +#include <linux/moduleparam.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/err.h> @@ -54,16 +54,7 @@ #include "ap_debug.h" /* - * Module description. - */ -MODULE_AUTHOR("IBM Corporation"); -MODULE_DESCRIPTION("Adjunct Processor Bus driver, " \ - "Copyright IBM Corp. 2006, 2012"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_CRYPTO("z90crypt"); - -/* - * Module parameter + * Module parameters; note though this file itself isn't modular. */ int ap_domain_index = -1; /* Adjunct Processor Domain Index */ static DEFINE_SPINLOCK(ap_domain_lock); @@ -86,7 +77,6 @@ static bool initialised; /* * AP bus related debug feature things. */ -static struct dentry *ap_dbf_root; debug_info_t *ap_dbf_info; /* @@ -1148,7 +1138,6 @@ static struct reset_call ap_reset_call = { int __init ap_debug_init(void) { - ap_dbf_root = debugfs_create_dir("ap", NULL); ap_dbf_info = debug_register("ap", 1, 1, DBF_MAX_SPRINTF_ARGS * sizeof(long)); debug_register_view(ap_dbf_info, &debug_sprintf_view); @@ -1159,7 +1148,6 @@ int __init ap_debug_init(void) void ap_debug_exit(void) { - debugfs_remove(ap_dbf_root); debug_unregister(ap_dbf_info); } @@ -1270,43 +1258,4 @@ out_free: kfree(ap_configuration); return rc; } - -/** - * ap_modules_exit(): The module termination code - * - * Terminates the module. - */ -void ap_module_exit(void) -{ - int i; - - initialised = false; - ap_reset_domain(); - ap_poll_thread_stop(); - del_timer_sync(&ap_config_timer); - hrtimer_cancel(&ap_poll_timer); - tasklet_kill(&ap_tasklet); - - /* first remove queue devices */ - bus_for_each_dev(&ap_bus_type, NULL, NULL, - __ap_queue_devices_unregister); - /* now remove the card devices */ - bus_for_each_dev(&ap_bus_type, NULL, NULL, - __ap_card_devices_unregister); - - /* remove bus attributes */ - for (i = 0; ap_bus_attrs[i]; i++) - bus_remove_file(&ap_bus_type, ap_bus_attrs[i]); - unregister_pm_notifier(&ap_power_notifier); - root_device_unregister(ap_root_device); - bus_unregister(&ap_bus_type); - kfree(ap_configuration); - unregister_reset_call(&ap_reset_call); - if (ap_using_interrupts()) - unregister_adapter_interrupt(&ap_airq); - - ap_debug_exit(); -} - -module_init(ap_module_init); -module_exit(ap_module_exit); +device_initcall(ap_module_init); diff --git a/drivers/s390/crypto/ap_card.c b/drivers/s390/crypto/ap_card.c index 0110d44172a3..1cd9128593e4 100644 --- a/drivers/s390/crypto/ap_card.c +++ b/drivers/s390/crypto/ap_card.c @@ -137,7 +137,7 @@ static const struct attribute_group *ap_card_dev_attr_groups[] = { NULL }; -struct device_type ap_card_type = { +static struct device_type ap_card_type = { .name = "ap_card", .groups = ap_card_dev_attr_groups, }; diff --git a/drivers/s390/crypto/ap_queue.c b/drivers/s390/crypto/ap_queue.c index b58a917dc510..7be67fa9f224 100644 --- a/drivers/s390/crypto/ap_queue.c +++ b/drivers/s390/crypto/ap_queue.c @@ -564,7 +564,7 @@ static const struct attribute_group *ap_queue_dev_attr_groups[] = { NULL }; -struct device_type ap_queue_type = { +static struct device_type ap_queue_type = { .name = "ap_queue", .groups = ap_queue_dev_attr_groups, }; diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 51eece9af577..144a17941e6f 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -81,7 +81,6 @@ EXPORT_SYMBOL(zcrypt_rescan_req); static LIST_HEAD(zcrypt_ops_list); /* Zcrypt related debug feature stuff. */ -static struct dentry *zcrypt_dbf_root; debug_info_t *zcrypt_dbf_info; /** @@ -201,7 +200,7 @@ static inline bool zcrypt_card_compare(struct zcrypt_card *zc, unsigned weight, unsigned pref_weight) { if (!pref_zc) - return 0; + return false; weight += atomic_read(&zc->load); pref_weight += atomic_read(&pref_zc->load); if (weight == pref_weight) @@ -215,7 +214,7 @@ static inline bool zcrypt_queue_compare(struct zcrypt_queue *zq, unsigned weight, unsigned pref_weight) { if (!pref_zq) - return 0; + return false; weight += atomic_read(&zq->load); pref_weight += atomic_read(&pref_zq->load); if (weight == pref_weight) @@ -668,6 +667,7 @@ static void zcrypt_qdepth_mask(char qdepth[AP_DEVICES]) memset(qdepth, 0, sizeof(char) * AP_DEVICES); spin_lock(&zcrypt_list_lock); + local_bh_disable(); for_each_zcrypt_card(zc) { for_each_zcrypt_queue(zq, zc) { if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index) @@ -679,6 +679,7 @@ static void zcrypt_qdepth_mask(char qdepth[AP_DEVICES]) spin_unlock(&zq->queue->lock); } } + local_bh_enable(); spin_unlock(&zcrypt_list_lock); } @@ -689,6 +690,7 @@ static void zcrypt_perdev_reqcnt(int reqcnt[AP_DEVICES]) memset(reqcnt, 0, sizeof(int) * AP_DEVICES); spin_lock(&zcrypt_list_lock); + local_bh_disable(); for_each_zcrypt_card(zc) { for_each_zcrypt_queue(zq, zc) { if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index) @@ -699,6 +701,7 @@ static void zcrypt_perdev_reqcnt(int reqcnt[AP_DEVICES]) spin_unlock(&zq->queue->lock); } } + local_bh_enable(); spin_unlock(&zcrypt_list_lock); } @@ -710,6 +713,7 @@ static int zcrypt_pendingq_count(void) pendingq_count = 0; spin_lock(&zcrypt_list_lock); + local_bh_disable(); for_each_zcrypt_card(zc) { for_each_zcrypt_queue(zq, zc) { if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index) @@ -719,6 +723,7 @@ static int zcrypt_pendingq_count(void) spin_unlock(&zq->queue->lock); } } + local_bh_enable(); spin_unlock(&zcrypt_list_lock); return pendingq_count; } @@ -731,6 +736,7 @@ static int zcrypt_requestq_count(void) requestq_count = 0; spin_lock(&zcrypt_list_lock); + local_bh_disable(); for_each_zcrypt_card(zc) { for_each_zcrypt_queue(zq, zc) { if (AP_QID_QUEUE(zq->queue->qid) != ap_domain_index) @@ -740,6 +746,7 @@ static int zcrypt_requestq_count(void) spin_unlock(&zq->queue->lock); } } + local_bh_enable(); spin_unlock(&zcrypt_list_lock); return requestq_count; } @@ -1419,7 +1426,6 @@ void zcrypt_rng_device_remove(void) int __init zcrypt_debug_init(void) { - zcrypt_dbf_root = debugfs_create_dir("zcrypt", NULL); zcrypt_dbf_info = debug_register("zcrypt", 1, 1, DBF_MAX_SPRINTF_ARGS * sizeof(long)); debug_register_view(zcrypt_dbf_info, &debug_sprintf_view); @@ -1430,7 +1436,6 @@ int __init zcrypt_debug_init(void) void zcrypt_debug_exit(void) { - debugfs_remove(zcrypt_dbf_root); debug_unregister(zcrypt_dbf_info); } diff --git a/drivers/s390/virtio/virtio_ccw.c b/drivers/s390/virtio/virtio_ccw.c index 070c4da95f48..648373cde4a1 100644 --- a/drivers/s390/virtio/virtio_ccw.c +++ b/drivers/s390/virtio/virtio_ccw.c @@ -661,7 +661,7 @@ static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs, ret = virtio_ccw_register_adapter_ind(vcdev, vqs, nvqs, ccw); if (ret) /* no error, just fall back to legacy interrupts */ - vcdev->is_thinint = 0; + vcdev->is_thinint = false; } if (!vcdev->is_thinint) { /* Register queue indicators with host. */ |