diff options
Diffstat (limited to 'drivers/s390')
26 files changed, 666 insertions, 329 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 65894f05a801..eef27a197c00 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -17,7 +17,6 @@ #include <linux/ctype.h> #include <linux/major.h> #include <linux/slab.h> -#include <linux/buffer_head.h> #include <linux/hdreg.h> #include <linux/async.h> #include <linux/mutex.h> @@ -1073,7 +1072,7 @@ static const struct file_operations dasd_stats_global_fops = { static void dasd_profile_init(struct dasd_profile *profile, struct dentry *base_dentry) { - mode_t mode; + umode_t mode; struct dentry *pde; if (!base_dentry) @@ -1112,7 +1111,7 @@ static void dasd_statistics_removeroot(void) static void dasd_statistics_createroot(void) { - mode_t mode; + umode_t mode; struct dentry *pde; dasd_debugfs_root_entry = NULL; diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index 87a0cf160fe5..0326571e7ffa 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -1718,7 +1718,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) erp->startdev = device; erp->memdev = device; erp->magic = default_erp->magic; - erp->expires = 0; + erp->expires = default_erp->expires; erp->retries = 256; erp->buildclk = get_clock(); erp->status = DASD_CQR_FILLED; @@ -2363,7 +2363,7 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr) erp->memdev = device; erp->block = cqr->block; erp->magic = cqr->magic; - erp->expires = 0; + erp->expires = cqr->expires; erp->retries = 256; erp->buildclk = get_clock(); erp->status = DASD_CQR_FILLED; diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c index c388eda1e2b1..553b3c5abb0a 100644 --- a/drivers/s390/block/dasd_alias.c +++ b/drivers/s390/block/dasd_alias.c @@ -705,6 +705,16 @@ struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *base_device) if (lcu->pav == NO_PAV || lcu->flags & (NEED_UAC_UPDATE | UPDATE_PENDING)) return NULL; + if (unlikely(!(private->features.feature[8] & 0x01))) { + /* + * PAV enabled but prefix not, very unlikely + * seems to be a lost pathgroup + * use base device to do IO + */ + DBF_DEV_EVENT(DBF_ERR, base_device, "%s", + "Prefix not enabled with PAV enabled\n"); + return NULL; + } spin_lock_irqsave(&lcu->lock, flags); alias_device = group->next; diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 6ab29680586a..bbcd5e9206ee 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -752,24 +752,13 @@ dasd_eckd_cdl_reclen(int recid) return sizes_trk0[recid]; return LABEL_SIZE; } - -/* - * Generate device unique id that specifies the physical device. - */ -static int dasd_eckd_generate_uid(struct dasd_device *device) +/* create unique id from private structure. */ +static void create_uid(struct dasd_eckd_private *private) { - struct dasd_eckd_private *private; - struct dasd_uid *uid; int count; - unsigned long flags; + struct dasd_uid *uid; - private = (struct dasd_eckd_private *) device->private; - if (!private) - return -ENODEV; - if (!private->ned || !private->gneq) - return -ENODEV; uid = &private->uid; - spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); memset(uid, 0, sizeof(struct dasd_uid)); memcpy(uid->vendor, private->ned->HDA_manufacturer, sizeof(uid->vendor) - 1); @@ -792,6 +781,23 @@ static int dasd_eckd_generate_uid(struct dasd_device *device) private->vdsneq->uit[count]); } } +} + +/* + * Generate device unique id that specifies the physical device. + */ +static int dasd_eckd_generate_uid(struct dasd_device *device) +{ + struct dasd_eckd_private *private; + unsigned long flags; + + private = (struct dasd_eckd_private *) device->private; + if (!private) + return -ENODEV; + if (!private->ned || !private->gneq) + return -ENODEV; + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + create_uid(private); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); return 0; } @@ -811,6 +817,21 @@ static int dasd_eckd_get_uid(struct dasd_device *device, struct dasd_uid *uid) return -EINVAL; } +/* + * compare device UID with data of a given dasd_eckd_private structure + * return 0 for match + */ +static int dasd_eckd_compare_path_uid(struct dasd_device *device, + struct dasd_eckd_private *private) +{ + struct dasd_uid device_uid; + + create_uid(private); + dasd_eckd_get_uid(device, &device_uid); + + return memcmp(&device_uid, &private->uid, sizeof(struct dasd_uid)); +} + static void dasd_eckd_fill_rcd_cqr(struct dasd_device *device, struct dasd_ccw_req *cqr, __u8 *rcd_buffer, @@ -1005,59 +1026,120 @@ static int dasd_eckd_read_conf(struct dasd_device *device) int conf_len, conf_data_saved; int rc; __u8 lpm, opm; - struct dasd_eckd_private *private; + struct dasd_eckd_private *private, path_private; struct dasd_path *path_data; + struct dasd_uid *uid; + char print_path_uid[60], print_device_uid[60]; private = (struct dasd_eckd_private *) device->private; path_data = &device->path_data; opm = ccw_device_get_path_mask(device->cdev); - lpm = 0x80; conf_data_saved = 0; /* get configuration data per operational path */ for (lpm = 0x80; lpm; lpm>>= 1) { - if (lpm & opm) { - rc = dasd_eckd_read_conf_lpm(device, &conf_data, - &conf_len, lpm); - if (rc && rc != -EOPNOTSUPP) { /* -EOPNOTSUPP is ok */ - DBF_EVENT_DEVID(DBF_WARNING, device->cdev, - "Read configuration data returned " - "error %d", rc); - return rc; - } - if (conf_data == NULL) { - DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", - "No configuration data " - "retrieved"); - /* no further analysis possible */ - path_data->opm |= lpm; - continue; /* no error */ + if (!(lpm & opm)) + continue; + rc = dasd_eckd_read_conf_lpm(device, &conf_data, + &conf_len, lpm); + if (rc && rc != -EOPNOTSUPP) { /* -EOPNOTSUPP is ok */ + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, + "Read configuration data returned " + "error %d", rc); + return rc; + } + if (conf_data == NULL) { + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", + "No configuration data " + "retrieved"); + /* no further analysis possible */ + path_data->opm |= lpm; + continue; /* no error */ + } + /* save first valid configuration data */ + if (!conf_data_saved) { + kfree(private->conf_data); + private->conf_data = conf_data; + private->conf_len = conf_len; + if (dasd_eckd_identify_conf_parts(private)) { + private->conf_data = NULL; + private->conf_len = 0; + kfree(conf_data); + continue; } - /* save first valid configuration data */ - if (!conf_data_saved) { - kfree(private->conf_data); - private->conf_data = conf_data; - private->conf_len = conf_len; - if (dasd_eckd_identify_conf_parts(private)) { - private->conf_data = NULL; - private->conf_len = 0; - kfree(conf_data); - continue; - } - conf_data_saved++; + /* + * build device UID that other path data + * can be compared to it + */ + dasd_eckd_generate_uid(device); + conf_data_saved++; + } else { + path_private.conf_data = conf_data; + path_private.conf_len = DASD_ECKD_RCD_DATA_SIZE; + if (dasd_eckd_identify_conf_parts( + &path_private)) { + path_private.conf_data = NULL; + path_private.conf_len = 0; + kfree(conf_data); + continue; } - switch (dasd_eckd_path_access(conf_data, conf_len)) { - case 0x02: - path_data->npm |= lpm; - break; - case 0x03: - path_data->ppm |= lpm; - break; + + if (dasd_eckd_compare_path_uid( + device, &path_private)) { + uid = &path_private.uid; + if (strlen(uid->vduit) > 0) + snprintf(print_path_uid, + sizeof(print_path_uid), + "%s.%s.%04x.%02x.%s", + uid->vendor, uid->serial, + uid->ssid, uid->real_unit_addr, + uid->vduit); + else + snprintf(print_path_uid, + sizeof(print_path_uid), + "%s.%s.%04x.%02x", + uid->vendor, uid->serial, + uid->ssid, + uid->real_unit_addr); + uid = &private->uid; + if (strlen(uid->vduit) > 0) + snprintf(print_device_uid, + sizeof(print_device_uid), + "%s.%s.%04x.%02x.%s", + uid->vendor, uid->serial, + uid->ssid, uid->real_unit_addr, + uid->vduit); + else + snprintf(print_device_uid, + sizeof(print_device_uid), + "%s.%s.%04x.%02x", + uid->vendor, uid->serial, + uid->ssid, + uid->real_unit_addr); + dev_err(&device->cdev->dev, + "Not all channel paths lead to " + "the same device, path %02X leads to " + "device %s instead of %s\n", lpm, + print_path_uid, print_device_uid); + return -EINVAL; } - path_data->opm |= lpm; - if (conf_data != private->conf_data) - kfree(conf_data); + + path_private.conf_data = NULL; + path_private.conf_len = 0; } + switch (dasd_eckd_path_access(conf_data, conf_len)) { + case 0x02: + path_data->npm |= lpm; + break; + case 0x03: + path_data->ppm |= lpm; + break; + } + path_data->opm |= lpm; + + if (conf_data != private->conf_data) + kfree(conf_data); } + return 0; } @@ -1090,12 +1172,61 @@ static int verify_fcx_max_data(struct dasd_device *device, __u8 lpm) return 0; } +static int rebuild_device_uid(struct dasd_device *device, + struct path_verification_work_data *data) +{ + struct dasd_eckd_private *private; + struct dasd_path *path_data; + __u8 lpm, opm; + int rc; + + rc = -ENODEV; + private = (struct dasd_eckd_private *) device->private; + path_data = &device->path_data; + opm = device->path_data.opm; + + for (lpm = 0x80; lpm; lpm >>= 1) { + if (!(lpm & opm)) + continue; + memset(&data->rcd_buffer, 0, sizeof(data->rcd_buffer)); + memset(&data->cqr, 0, sizeof(data->cqr)); + data->cqr.cpaddr = &data->ccw; + rc = dasd_eckd_read_conf_immediately(device, &data->cqr, + data->rcd_buffer, + lpm); + + if (rc) { + if (rc == -EOPNOTSUPP) /* -EOPNOTSUPP is ok */ + continue; + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, + "Read configuration data " + "returned error %d", rc); + break; + } + memcpy(private->conf_data, data->rcd_buffer, + DASD_ECKD_RCD_DATA_SIZE); + if (dasd_eckd_identify_conf_parts(private)) { + rc = -ENODEV; + } else /* first valid path is enough */ + break; + } + + if (!rc) + rc = dasd_eckd_generate_uid(device); + + return rc; +} + static void do_path_verification_work(struct work_struct *work) { struct path_verification_work_data *data; struct dasd_device *device; + struct dasd_eckd_private path_private; + struct dasd_uid *uid; + __u8 path_rcd_buf[DASD_ECKD_RCD_DATA_SIZE]; __u8 lpm, opm, npm, ppm, epm; unsigned long flags; + char print_uid[60]; int rc; data = container_of(work, struct path_verification_work_data, worker); @@ -1112,64 +1243,129 @@ static void do_path_verification_work(struct work_struct *work) ppm = 0; epm = 0; for (lpm = 0x80; lpm; lpm >>= 1) { - if (lpm & data->tbvpm) { - memset(data->rcd_buffer, 0, sizeof(data->rcd_buffer)); - memset(&data->cqr, 0, sizeof(data->cqr)); - data->cqr.cpaddr = &data->ccw; - rc = dasd_eckd_read_conf_immediately(device, &data->cqr, - data->rcd_buffer, - lpm); - if (!rc) { - switch (dasd_eckd_path_access(data->rcd_buffer, - DASD_ECKD_RCD_DATA_SIZE)) { - case 0x02: - npm |= lpm; - break; - case 0x03: - ppm |= lpm; - break; - } - opm |= lpm; - } else if (rc == -EOPNOTSUPP) { - DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", - "path verification: No configuration " - "data retrieved"); - opm |= lpm; - } else if (rc == -EAGAIN) { - DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", + if (!(lpm & data->tbvpm)) + continue; + memset(&data->rcd_buffer, 0, sizeof(data->rcd_buffer)); + memset(&data->cqr, 0, sizeof(data->cqr)); + data->cqr.cpaddr = &data->ccw; + rc = dasd_eckd_read_conf_immediately(device, &data->cqr, + data->rcd_buffer, + lpm); + if (!rc) { + switch (dasd_eckd_path_access(data->rcd_buffer, + DASD_ECKD_RCD_DATA_SIZE) + ) { + case 0x02: + npm |= lpm; + break; + case 0x03: + ppm |= lpm; + break; + } + opm |= lpm; + } else if (rc == -EOPNOTSUPP) { + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", + "path verification: No configuration " + "data retrieved"); + opm |= lpm; + } else if (rc == -EAGAIN) { + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", "path verification: device is stopped," " try again later"); - epm |= lpm; - } else { - dev_warn(&device->cdev->dev, - "Reading device feature codes failed " - "(rc=%d) for new path %x\n", rc, lpm); - continue; - } - if (verify_fcx_max_data(device, lpm)) { + epm |= lpm; + } else { + dev_warn(&device->cdev->dev, + "Reading device feature codes failed " + "(rc=%d) for new path %x\n", rc, lpm); + continue; + } + if (verify_fcx_max_data(device, lpm)) { + opm &= ~lpm; + npm &= ~lpm; + ppm &= ~lpm; + continue; + } + + /* + * save conf_data for comparison after + * rebuild_device_uid may have changed + * the original data + */ + memcpy(&path_rcd_buf, data->rcd_buffer, + DASD_ECKD_RCD_DATA_SIZE); + path_private.conf_data = (void *) &path_rcd_buf; + path_private.conf_len = DASD_ECKD_RCD_DATA_SIZE; + if (dasd_eckd_identify_conf_parts(&path_private)) { + path_private.conf_data = NULL; + path_private.conf_len = 0; + continue; + } + + /* + * compare path UID with device UID only if at least + * one valid path is left + * in other case the device UID may have changed and + * the first working path UID will be used as device UID + */ + if (device->path_data.opm && + dasd_eckd_compare_path_uid(device, &path_private)) { + /* + * the comparison was not successful + * rebuild the device UID with at least one + * known path in case a z/VM hyperswap command + * has changed the device + * + * after this compare again + * + * if either the rebuild or the recompare fails + * the path can not be used + */ + if (rebuild_device_uid(device, data) || + dasd_eckd_compare_path_uid( + device, &path_private)) { + uid = &path_private.uid; + if (strlen(uid->vduit) > 0) + snprintf(print_uid, sizeof(print_uid), + "%s.%s.%04x.%02x.%s", + uid->vendor, uid->serial, + uid->ssid, uid->real_unit_addr, + uid->vduit); + else + snprintf(print_uid, sizeof(print_uid), + "%s.%s.%04x.%02x", + uid->vendor, uid->serial, + uid->ssid, + uid->real_unit_addr); + dev_err(&device->cdev->dev, + "The newly added channel path %02X " + "will not be used because it leads " + "to a different device %s\n", + lpm, print_uid); opm &= ~lpm; npm &= ~lpm; ppm &= ~lpm; + continue; } } + + /* + * There is a small chance that a path is lost again between + * above path verification and the following modification of + * the device opm mask. We could avoid that race here by using + * yet another path mask, but we rather deal with this unlikely + * situation in dasd_start_IO. + */ + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + if (!device->path_data.opm && opm) { + device->path_data.opm = opm; + dasd_generic_path_operational(device); + } else + device->path_data.opm |= opm; + device->path_data.npm |= npm; + device->path_data.ppm |= ppm; + device->path_data.tbvpm |= epm; + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); } - /* - * There is a small chance that a path is lost again between - * above path verification and the following modification of - * the device opm mask. We could avoid that race here by using - * yet another path mask, but we rather deal with this unlikely - * situation in dasd_start_IO. - */ - spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - if (!device->path_data.opm && opm) { - device->path_data.opm = opm; - dasd_generic_path_operational(device); - } else - device->path_data.opm |= opm; - device->path_data.npm |= npm; - device->path_data.ppm |= ppm; - device->path_data.tbvpm |= epm; - spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); dasd_put_device(device); if (data->isglobal) @@ -1441,11 +1637,6 @@ dasd_eckd_check_characteristics(struct dasd_device *device) device->default_expires = value; } - /* Generate device unique id */ - rc = dasd_eckd_generate_uid(device); - if (rc) - goto out_err1; - dasd_eckd_get_uid(device, &temp_uid); if (temp_uid.type == UA_BASE_DEVICE) { block = dasd_alloc_block(); @@ -2206,7 +2397,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( sizeof(struct PFX_eckd_data)); } else { if (define_extent(ccw++, cqr->data, first_trk, - last_trk, cmd, startdev) == -EAGAIN) { + last_trk, cmd, basedev) == -EAGAIN) { /* Clock not in sync and XRC is enabled. * Try again later. */ diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c index 98f3e4ade924..690c3338a8ae 100644 --- a/drivers/s390/block/xpram.c +++ b/drivers/s390/block/xpram.c @@ -36,7 +36,7 @@ #include <linux/blkdev.h> #include <linux/blkpg.h> #include <linux/hdreg.h> /* HDIO_GETGEO */ -#include <linux/sysdev.h> +#include <linux/device.h> #include <linux/bio.h> #include <linux/suspend.h> #include <linux/platform_device.h> diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index e5cb9248a442..f3b8bb84faf2 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -75,7 +75,7 @@ static LIST_HEAD(raw3270_devices); static int raw3270_registered; /* Module parameters */ -static int tubxcorrect = 0; +static bool tubxcorrect = 0; module_param(tubxcorrect, bool, 0); /* diff --git a/drivers/s390/char/sclp_config.c b/drivers/s390/char/sclp_config.c index 95b909ac2b73..3c03c1060be6 100644 --- a/drivers/s390/char/sclp_config.c +++ b/drivers/s390/char/sclp_config.c @@ -11,7 +11,7 @@ #include <linux/init.h> #include <linux/errno.h> #include <linux/cpu.h> -#include <linux/sysdev.h> +#include <linux/device.h> #include <linux/workqueue.h> #include <asm/smp.h> @@ -31,14 +31,14 @@ static struct work_struct sclp_cpu_change_work; static void sclp_cpu_capability_notify(struct work_struct *work) { int cpu; - struct sys_device *sysdev; + struct device *dev; s390_adjust_jiffies(); pr_warning("cpu capability changed.\n"); get_online_cpus(); for_each_online_cpu(cpu) { - sysdev = get_cpu_sysdev(cpu); - kobject_uevent(&sysdev->kobj, KOBJ_CHANGE); + dev = get_cpu_device(cpu); + kobject_uevent(&dev->kobj, KOBJ_CHANGE); } put_online_cpus(); } diff --git a/drivers/s390/char/tape_class.h b/drivers/s390/char/tape_class.h index 9e32780c317f..ba2092f741d5 100644 --- a/drivers/s390/char/tape_class.h +++ b/drivers/s390/char/tape_class.h @@ -14,7 +14,6 @@ #include <linux/module.h> #include <linux/fs.h> #include <linux/major.h> -#include <linux/kobj_map.h> #include <linux/cdev.h> #include <linux/device.h> diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c index 11312f401c70..2211277a1079 100644 --- a/drivers/s390/char/vmwatchdog.c +++ b/drivers/s390/char/vmwatchdog.c @@ -28,9 +28,9 @@ #define MAX_CMDLEN 240 #define MIN_INTERVAL 15 static char vmwdt_cmd[MAX_CMDLEN] = "IPL"; -static int vmwdt_conceal; +static bool vmwdt_conceal; -static int vmwdt_nowayout = WATCHDOG_NOWAYOUT; +static bool vmwdt_nowayout = WATCHDOG_NOWAYOUT; MODULE_LICENSE("GPL"); MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>"); diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 75c3f1f8fd43..a84631a7391d 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -529,10 +529,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)); - link.chpid = chpid; /* Wait until previous actions have settled. */ css_wait_for_slow_path(); /* @@ -542,10 +539,10 @@ int chsc_chp_vary(struct chp_id chpid, int 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); + __s390_vary_chpid_on, &chpid); } else for_each_subchannel_staged(s390_subchannel_vary_chpid_off, - NULL, &link); + NULL, &chpid); return 0; } diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index 155a82bcb9e5..4a1ff5c2eb88 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h @@ -68,8 +68,13 @@ struct schib { __u8 mda[4]; /* model dependent area */ } __attribute__ ((packed,aligned(4))); +/* + * When rescheduled, todo's with higher values will overwrite those + * with lower values. + */ enum sch_todo { SCH_TODO_NOTHING, + SCH_TODO_EVAL, SCH_TODO_UNREG, }; diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index 2985eb439485..204ca728e7fd 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -98,7 +98,7 @@ enum cmb_format { * enum cmb_format. */ static int format = CMF_AUTODETECT; -module_param(format, bool, 0444); +module_param(format, bint, 0444); /** * struct cmb_operations - functions to use depending on cmb_format diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 92d7324acb1c..21908e67bf67 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -195,51 +195,6 @@ void css_sch_device_unregister(struct subchannel *sch) } EXPORT_SYMBOL_GPL(css_sch_device_unregister); -static void css_sch_todo(struct work_struct *work) -{ - struct subchannel *sch; - enum sch_todo todo; - - sch = container_of(work, struct subchannel, todo_work); - /* Find out todo. */ - spin_lock_irq(sch->lock); - todo = sch->todo; - CIO_MSG_EVENT(4, "sch_todo: sch=0.%x.%04x, todo=%d\n", sch->schid.ssid, - sch->schid.sch_no, todo); - sch->todo = SCH_TODO_NOTHING; - spin_unlock_irq(sch->lock); - /* Perform todo. */ - if (todo == SCH_TODO_UNREG) - css_sch_device_unregister(sch); - /* Release workqueue ref. */ - put_device(&sch->dev); -} - -/** - * css_sched_sch_todo - schedule a subchannel operation - * @sch: subchannel - * @todo: todo - * - * Schedule the operation identified by @todo to be performed on the slow path - * workqueue. Do nothing if another operation with higher priority is already - * scheduled. Needs to be called with subchannel lock held. - */ -void css_sched_sch_todo(struct subchannel *sch, enum sch_todo todo) -{ - CIO_MSG_EVENT(4, "sch_todo: sched sch=0.%x.%04x todo=%d\n", - sch->schid.ssid, sch->schid.sch_no, todo); - if (sch->todo >= todo) - return; - /* Get workqueue ref. */ - if (!get_device(&sch->dev)) - return; - sch->todo = todo; - if (!queue_work(cio_work_q, &sch->todo_work)) { - /* Already queued, release workqueue ref. */ - put_device(&sch->dev); - } -} - static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw) { int i; @@ -466,6 +421,65 @@ static void css_evaluate_subchannel(struct subchannel_id schid, int slow) css_schedule_eval(schid); } +/** + * css_sched_sch_todo - schedule a subchannel operation + * @sch: subchannel + * @todo: todo + * + * Schedule the operation identified by @todo to be performed on the slow path + * workqueue. Do nothing if another operation with higher priority is already + * scheduled. Needs to be called with subchannel lock held. + */ +void css_sched_sch_todo(struct subchannel *sch, enum sch_todo todo) +{ + CIO_MSG_EVENT(4, "sch_todo: sched sch=0.%x.%04x todo=%d\n", + sch->schid.ssid, sch->schid.sch_no, todo); + if (sch->todo >= todo) + return; + /* Get workqueue ref. */ + if (!get_device(&sch->dev)) + return; + sch->todo = todo; + if (!queue_work(cio_work_q, &sch->todo_work)) { + /* Already queued, release workqueue ref. */ + put_device(&sch->dev); + } +} + +static void css_sch_todo(struct work_struct *work) +{ + struct subchannel *sch; + enum sch_todo todo; + int ret; + + sch = container_of(work, struct subchannel, todo_work); + /* Find out todo. */ + spin_lock_irq(sch->lock); + todo = sch->todo; + CIO_MSG_EVENT(4, "sch_todo: sch=0.%x.%04x, todo=%d\n", sch->schid.ssid, + sch->schid.sch_no, todo); + sch->todo = SCH_TODO_NOTHING; + spin_unlock_irq(sch->lock); + /* Perform todo. */ + switch (todo) { + case SCH_TODO_NOTHING: + break; + case SCH_TODO_EVAL: + ret = css_evaluate_known_subchannel(sch, 1); + if (ret == -EAGAIN) { + spin_lock_irq(sch->lock); + css_sched_sch_todo(sch, todo); + spin_unlock_irq(sch->lock); + } + break; + case SCH_TODO_UNREG: + css_sch_device_unregister(sch); + break; + } + /* Release workqueue ref. */ + put_device(&sch->dev); +} + static struct idset *slow_subchannel_set; static spinlock_t slow_subchannel_lock; static wait_queue_head_t css_eval_wq; diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index d734f4a0ecac..47269858ecb6 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1868,9 +1868,9 @@ static void __ccw_device_pm_restore(struct ccw_device *cdev) */ cdev->private->flags.resuming = 1; cdev->private->path_new_mask = LPM_ANYPATH; - css_schedule_eval(sch->schid); + css_sched_sch_todo(sch, SCH_TODO_EVAL); spin_unlock_irq(sch->lock); - css_complete_work(); + css_wait_for_slow_path(); /* cdev may have been moved to a different subchannel. */ sch = to_subchannel(cdev->dev.parent); diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 52c233fa2b12..1b853513c891 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -496,8 +496,26 @@ static void ccw_device_reset_path_events(struct ccw_device *cdev) cdev->private->pgid_reset_mask = 0; } -void -ccw_device_verify_done(struct ccw_device *cdev, int err) +static void create_fake_irb(struct irb *irb, int type) +{ + memset(irb, 0, sizeof(*irb)); + if (type == FAKE_CMD_IRB) { + struct cmd_scsw *scsw = &irb->scsw.cmd; + scsw->cc = 1; + scsw->fctl = SCSW_FCTL_START_FUNC; + scsw->actl = SCSW_ACTL_START_PEND; + scsw->stctl = SCSW_STCTL_STATUS_PEND; + } else if (type == FAKE_TM_IRB) { + struct tm_scsw *scsw = &irb->scsw.tm; + scsw->x = 1; + scsw->cc = 1; + scsw->fctl = SCSW_FCTL_START_FUNC; + scsw->actl = SCSW_ACTL_START_PEND; + scsw->stctl = SCSW_STCTL_STATUS_PEND; + } +} + +void ccw_device_verify_done(struct ccw_device *cdev, int err) { struct subchannel *sch; @@ -520,12 +538,8 @@ callback: ccw_device_done(cdev, DEV_STATE_ONLINE); /* Deliver fake irb to device driver, if needed. */ if (cdev->private->flags.fake_irb) { - memset(&cdev->private->irb, 0, sizeof(struct irb)); - cdev->private->irb.scsw.cmd.cc = 1; - cdev->private->irb.scsw.cmd.fctl = SCSW_FCTL_START_FUNC; - cdev->private->irb.scsw.cmd.actl = SCSW_ACTL_START_PEND; - cdev->private->irb.scsw.cmd.stctl = - SCSW_STCTL_STATUS_PEND; + create_fake_irb(&cdev->private->irb, + cdev->private->flags.fake_irb); cdev->private->flags.fake_irb = 0; if (cdev->handler) cdev->handler(cdev, cdev->private->intparm, diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index f98698d5735e..ec7fb6d3b479 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -198,7 +198,7 @@ int ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa, if (cdev->private->state == DEV_STATE_VERIFY) { /* Remember to fake irb when finished. */ if (!cdev->private->flags.fake_irb) { - cdev->private->flags.fake_irb = 1; + cdev->private->flags.fake_irb = FAKE_CMD_IRB; cdev->private->intparm = intparm; return 0; } else @@ -213,9 +213,9 @@ int ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa, ret = cio_set_options (sch, flags); if (ret) return ret; - /* Adjust requested path mask to excluded varied off paths. */ + /* Adjust requested path mask to exclude unusable paths. */ if (lpm) { - lpm &= sch->opm; + lpm &= sch->lpm; if (lpm == 0) return -EACCES; } @@ -605,11 +605,21 @@ int ccw_device_tm_start_key(struct ccw_device *cdev, struct tcw *tcw, sch = to_subchannel(cdev->dev.parent); if (!sch->schib.pmcw.ena) return -EINVAL; + if (cdev->private->state == DEV_STATE_VERIFY) { + /* Remember to fake irb when finished. */ + if (!cdev->private->flags.fake_irb) { + cdev->private->flags.fake_irb = FAKE_TM_IRB; + cdev->private->intparm = intparm; + return 0; + } else + /* There's already a fake I/O around. */ + return -EBUSY; + } if (cdev->private->state != DEV_STATE_ONLINE) return -EIO; - /* Adjust requested path mask to excluded varied off paths. */ + /* Adjust requested path mask to exclude unusable paths. */ if (lpm) { - lpm &= sch->opm; + lpm &= sch->lpm; if (lpm == 0) return -EACCES; } diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index 2ebb492a5c17..76253dfcc1be 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h @@ -111,6 +111,9 @@ enum cdev_todo { CDEV_TODO_UNREG_EVAL, }; +#define FAKE_CMD_IRB 1 +#define FAKE_TM_IRB 2 + struct ccw_device_private { struct ccw_device *cdev; struct subchannel *sch; @@ -138,7 +141,7 @@ struct ccw_device_private { unsigned int doverify:1; /* delayed path verification */ unsigned int donotify:1; /* call notify function */ unsigned int recog_done:1; /* dev. recog. complete */ - unsigned int fake_irb:1; /* deliver faked irb */ + unsigned int fake_irb:2; /* deliver faked irb */ unsigned int resuming:1; /* recognition while resume */ unsigned int pgroup:1; /* pathgroup is set up */ unsigned int mpath:1; /* multipathing is set up */ diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c index 2acc01f90a6a..452989a7ec13 100644 --- a/drivers/s390/cio/qdio_setup.c +++ b/drivers/s390/cio/qdio_setup.c @@ -22,12 +22,9 @@ static struct kmem_cache *qdio_q_cache; static struct kmem_cache *qdio_aob_cache; -struct qaob *qdio_allocate_aob() +struct qaob *qdio_allocate_aob(void) { - struct qaob *aob; - - aob = kmem_cache_zalloc(qdio_aob_cache, GFP_ATOMIC); - return aob; + return kmem_cache_zalloc(qdio_aob_cache, GFP_ATOMIC); } EXPORT_SYMBOL_GPL(qdio_allocate_aob); @@ -180,7 +177,8 @@ static void setup_queues(struct qdio_irq *irq_ptr, setup_queues_misc(q, irq_ptr, qdio_init->input_handler, i); q->is_input_q = 1; - q->u.in.queue_start_poll = qdio_init->queue_start_poll[i]; + q->u.in.queue_start_poll = qdio_init->queue_start_poll_array ? + qdio_init->queue_start_poll_array[i] : NULL; setup_storage_lists(q, irq_ptr, input_sbal_array, i); input_sbal_array += QDIO_MAX_BUFFERS_PER_Q; diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index ec94f049e995..96bbe9d12a79 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -1552,6 +1552,8 @@ static void ap_reset(struct ap_device *ap_dev) rc = ap_init_queue(ap_dev->qid); if (rc == -ENODEV) ap_dev->unregistered = 1; + else + __ap_schedule_poll_timer(); } static int __ap_poll_device(struct ap_device *ap_dev, unsigned long *flags) diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c index dd4737808e06..077b7d109fde 100644 --- a/drivers/s390/crypto/zcrypt_pcixcc.c +++ b/drivers/s390/crypto/zcrypt_pcixcc.c @@ -56,11 +56,6 @@ #define PCIXCC_MAX_ICA_RESPONSE_SIZE 0x77c /* max size type86 v2 reply */ #define PCIXCC_MAX_XCRB_MESSAGE_SIZE (12*1024) -#define PCIXCC_MAX_XCRB_RESPONSE_SIZE PCIXCC_MAX_XCRB_MESSAGE_SIZE -#define PCIXCC_MAX_XCRB_DATA_SIZE (11*1024) -#define PCIXCC_MAX_XCRB_REPLY_SIZE (5*1024) - -#define PCIXCC_MAX_RESPONSE_SIZE PCIXCC_MAX_XCRB_RESPONSE_SIZE #define PCIXCC_CLEANUP_TIME (15*HZ) @@ -265,7 +260,7 @@ static int ICACRT_msg_to_type6CRT_msgX(struct zcrypt_device *zdev, * @ap_msg: pointer to AP message * @xcRB: pointer to user input data * - * Returns 0 on success or -EFAULT. + * Returns 0 on success or -EFAULT, -EINVAL. */ struct type86_fmt2_msg { struct type86_hdr hdr; @@ -295,19 +290,12 @@ static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev, CEIL4(xcRB->request_control_blk_length) + xcRB->request_data_length; if (ap_msg->length > PCIXCC_MAX_XCRB_MESSAGE_SIZE) - return -EFAULT; - if (CEIL4(xcRB->reply_control_blk_length) > PCIXCC_MAX_XCRB_REPLY_SIZE) - return -EFAULT; - if (CEIL4(xcRB->reply_data_length) > PCIXCC_MAX_XCRB_DATA_SIZE) - return -EFAULT; - replylen = CEIL4(xcRB->reply_control_blk_length) + - CEIL4(xcRB->reply_data_length) + - sizeof(struct type86_fmt2_msg); - if (replylen > PCIXCC_MAX_XCRB_RESPONSE_SIZE) { - xcRB->reply_control_blk_length = PCIXCC_MAX_XCRB_RESPONSE_SIZE - - (sizeof(struct type86_fmt2_msg) + - CEIL4(xcRB->reply_data_length)); - } + return -EINVAL; + replylen = sizeof(struct type86_fmt2_msg) + + CEIL4(xcRB->reply_control_blk_length) + + xcRB->reply_data_length; + if (replylen > PCIXCC_MAX_XCRB_MESSAGE_SIZE) + return -EINVAL; /* prepare type6 header */ msg->hdr = static_type6_hdrX; @@ -326,7 +314,7 @@ static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev, return -EFAULT; if (msg->cprbx.cprb_len + sizeof(msg->hdr.function_code) > xcRB->request_control_blk_length) - return -EFAULT; + return -EINVAL; function_code = ((unsigned char *)&msg->cprbx) + msg->cprbx.cprb_len; memcpy(msg->hdr.function_code, function_code, sizeof(msg->hdr.function_code)); @@ -678,7 +666,7 @@ static void zcrypt_pcixcc_receive(struct ap_device *ap_dev, break; case PCIXCC_RESPONSE_TYPE_XCRB: length = t86r->fmt2.offset2 + t86r->fmt2.count2; - length = min(PCIXCC_MAX_XCRB_RESPONSE_SIZE, length); + length = min(PCIXCC_MAX_XCRB_MESSAGE_SIZE, length); memcpy(msg->message, reply->message, length); break; default: @@ -1043,7 +1031,7 @@ static int zcrypt_pcixcc_probe(struct ap_device *ap_dev) struct zcrypt_device *zdev; int rc = 0; - zdev = zcrypt_device_alloc(PCIXCC_MAX_RESPONSE_SIZE); + zdev = zcrypt_device_alloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE); if (!zdev) return -ENOMEM; zdev->ap_dev = ap_dev; diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c index 94f49ffa70ba..7bc1955337ea 100644 --- a/drivers/s390/kvm/kvm_virtio.c +++ b/drivers/s390/kvm/kvm_virtio.c @@ -198,7 +198,7 @@ static struct virtqueue *kvm_find_vq(struct virtio_device *vdev, goto out; vq = vring_new_virtqueue(config->num, KVM_S390_VIRTIO_RING_ALIGN, - vdev, (void *) config->address, + vdev, true, (void *) config->address, kvm_notify, callback, name); if (!vq) { err = -ENOMEM; @@ -263,6 +263,11 @@ error: return PTR_ERR(vqs[i]); } +static const char *kvm_bus_name(struct virtio_device *vdev) +{ + return ""; +} + /* * The config ops structure as defined by virtio config */ @@ -276,6 +281,7 @@ static struct virtio_config_ops kvm_vq_configspace_ops = { .reset = kvm_reset, .find_vqs = kvm_find_vqs, .del_vqs = kvm_del_vqs, + .bus_name = kvm_bus_name, }; /* diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index b6a6356d09b3..8160591913f9 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -63,6 +63,7 @@ #include <asm/io.h> #include <asm/uaccess.h> +#include <asm/ebcdic.h> #include <net/iucv/iucv.h> #include "fsm.h" @@ -75,7 +76,7 @@ MODULE_DESCRIPTION ("Linux for S/390 IUCV network driver"); * Debug Facility stuff */ #define IUCV_DBF_SETUP_NAME "iucv_setup" -#define IUCV_DBF_SETUP_LEN 32 +#define IUCV_DBF_SETUP_LEN 64 #define IUCV_DBF_SETUP_PAGES 2 #define IUCV_DBF_SETUP_NR_AREAS 1 #define IUCV_DBF_SETUP_LEVEL 3 @@ -226,6 +227,7 @@ struct iucv_connection { struct net_device *netdev; struct connection_profile prof; char userid[9]; + char userdata[17]; }; /** @@ -263,7 +265,7 @@ struct ll_header { }; #define NETIUCV_HDRLEN (sizeof(struct ll_header)) -#define NETIUCV_BUFSIZE_MAX 32768 +#define NETIUCV_BUFSIZE_MAX 65537 #define NETIUCV_BUFSIZE_DEFAULT NETIUCV_BUFSIZE_MAX #define NETIUCV_MTU_MAX (NETIUCV_BUFSIZE_MAX - NETIUCV_HDRLEN) #define NETIUCV_MTU_DEFAULT 9216 @@ -288,7 +290,12 @@ static inline int netiucv_test_and_set_busy(struct net_device *dev) return test_and_set_bit(0, &priv->tbusy); } -static u8 iucvMagic[16] = { +static u8 iucvMagic_ascii[16] = { + 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 +}; + +static u8 iucvMagic_ebcdic[16] = { 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 }; @@ -301,18 +308,38 @@ static u8 iucvMagic[16] = { * * @returns The printable string (static data!!) */ -static char *netiucv_printname(char *name) +static char *netiucv_printname(char *name, int len) { - static char tmp[9]; + static char tmp[17]; char *p = tmp; - memcpy(tmp, name, 8); - tmp[8] = '\0'; - while (*p && (!isspace(*p))) + memcpy(tmp, name, len); + tmp[len] = '\0'; + while (*p && ((p - tmp) < len) && (!isspace(*p))) p++; *p = '\0'; return tmp; } +static char *netiucv_printuser(struct iucv_connection *conn) +{ + static char tmp_uid[9]; + static char tmp_udat[17]; + static char buf[100]; + + if (memcmp(conn->userdata, iucvMagic_ebcdic, 16)) { + tmp_uid[8] = '\0'; + tmp_udat[16] = '\0'; + memcpy(tmp_uid, conn->userid, 8); + memcpy(tmp_uid, netiucv_printname(tmp_uid, 8), 8); + memcpy(tmp_udat, conn->userdata, 16); + EBCASC(tmp_udat, 16); + memcpy(tmp_udat, netiucv_printname(tmp_udat, 16), 16); + sprintf(buf, "%s.%s", tmp_uid, tmp_udat); + return buf; + } else + return netiucv_printname(conn->userid, 8); +} + /** * States of the interface statemachine. */ @@ -563,15 +590,18 @@ static int netiucv_callback_connreq(struct iucv_path *path, { struct iucv_connection *conn = path->private; struct iucv_event ev; + static char tmp_user[9]; + static char tmp_udat[17]; int rc; - if (memcmp(iucvMagic, ipuser, 16)) - /* ipuser must match iucvMagic. */ - return -EINVAL; rc = -EINVAL; + memcpy(tmp_user, netiucv_printname(ipvmid, 8), 8); + memcpy(tmp_udat, ipuser, 16); + EBCASC(tmp_udat, 16); read_lock_bh(&iucv_connection_rwlock); list_for_each_entry(conn, &iucv_connection_list, list) { - if (strncmp(ipvmid, conn->userid, 8)) + if (strncmp(ipvmid, conn->userid, 8) || + strncmp(ipuser, conn->userdata, 16)) continue; /* Found a matching connection for this path. */ conn->path = path; @@ -580,6 +610,8 @@ static int netiucv_callback_connreq(struct iucv_path *path, fsm_event(conn->fsm, CONN_EVENT_CONN_REQ, &ev); rc = 0; } + IUCV_DBF_TEXT_(setup, 2, "Connection requested for %s.%s\n", + tmp_user, netiucv_printname(tmp_udat, 16)); read_unlock_bh(&iucv_connection_rwlock); return rc; } @@ -816,7 +848,7 @@ static void conn_action_connaccept(fsm_instance *fi, int event, void *arg) conn->path = path; path->msglim = NETIUCV_QUEUELEN_DEFAULT; path->flags = 0; - rc = iucv_path_accept(path, &netiucv_handler, NULL, conn); + rc = iucv_path_accept(path, &netiucv_handler, conn->userdata , conn); if (rc) { IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc); return; @@ -854,7 +886,7 @@ static void conn_action_conntimsev(fsm_instance *fi, int event, void *arg) IUCV_DBF_TEXT(trace, 3, __func__); fsm_deltimer(&conn->timer); - iucv_path_sever(conn->path, NULL); + iucv_path_sever(conn->path, conn->userdata); fsm_newstate(fi, CONN_STATE_STARTWAIT); } @@ -867,9 +899,9 @@ static void conn_action_connsever(fsm_instance *fi, int event, void *arg) IUCV_DBF_TEXT(trace, 3, __func__); fsm_deltimer(&conn->timer); - iucv_path_sever(conn->path, NULL); - dev_info(privptr->dev, "The peer interface of the IUCV device" - " has closed the connection\n"); + iucv_path_sever(conn->path, conn->userdata); + dev_info(privptr->dev, "The peer z/VM guest %s has closed the " + "connection\n", netiucv_printuser(conn)); IUCV_DBF_TEXT(data, 2, "conn_action_connsever: Remote dropped connection\n"); fsm_newstate(fi, CONN_STATE_STARTWAIT); @@ -886,8 +918,6 @@ static void conn_action_start(fsm_instance *fi, int event, void *arg) IUCV_DBF_TEXT(trace, 3, __func__); fsm_newstate(fi, CONN_STATE_STARTWAIT); - IUCV_DBF_TEXT_(setup, 2, "%s('%s'): connecting ...\n", - netdev->name, conn->userid); /* * We must set the state before calling iucv_connect because the @@ -897,8 +927,11 @@ static void conn_action_start(fsm_instance *fi, int event, void *arg) fsm_newstate(fi, CONN_STATE_SETUPWAIT); conn->path = iucv_path_alloc(NETIUCV_QUEUELEN_DEFAULT, 0, GFP_KERNEL); + IUCV_DBF_TEXT_(setup, 2, "%s: connecting to %s ...\n", + netdev->name, netiucv_printuser(conn)); + rc = iucv_path_connect(conn->path, &netiucv_handler, conn->userid, - NULL, iucvMagic, conn); + NULL, conn->userdata, conn); switch (rc) { case 0: netdev->tx_queue_len = conn->path->msglim; @@ -908,13 +941,13 @@ static void conn_action_start(fsm_instance *fi, int event, void *arg) case 11: dev_warn(privptr->dev, "The IUCV device failed to connect to z/VM guest %s\n", - netiucv_printname(conn->userid)); + netiucv_printname(conn->userid, 8)); fsm_newstate(fi, CONN_STATE_STARTWAIT); break; case 12: dev_warn(privptr->dev, "The IUCV device failed to connect to the peer on z/VM" - " guest %s\n", netiucv_printname(conn->userid)); + " guest %s\n", netiucv_printname(conn->userid, 8)); fsm_newstate(fi, CONN_STATE_STARTWAIT); break; case 13: @@ -927,7 +960,7 @@ static void conn_action_start(fsm_instance *fi, int event, void *arg) dev_err(privptr->dev, "z/VM guest %s has too many IUCV connections" " to connect with the IUCV device\n", - netiucv_printname(conn->userid)); + netiucv_printname(conn->userid, 8)); fsm_newstate(fi, CONN_STATE_CONNERR); break; case 15: @@ -972,7 +1005,7 @@ static void conn_action_stop(fsm_instance *fi, int event, void *arg) netiucv_purge_skb_queue(&conn->collect_queue); if (conn->path) { IUCV_DBF_TEXT(trace, 5, "calling iucv_path_sever\n"); - iucv_path_sever(conn->path, iucvMagic); + iucv_path_sever(conn->path, conn->userdata); kfree(conn->path); conn->path = NULL; } @@ -1090,7 +1123,8 @@ dev_action_connup(fsm_instance *fi, int event, void *arg) fsm_newstate(fi, DEV_STATE_RUNNING); dev_info(privptr->dev, "The IUCV device has been connected" - " successfully to %s\n", privptr->conn->userid); + " successfully to %s\n", + netiucv_printuser(privptr->conn)); IUCV_DBF_TEXT(setup, 3, "connection is up and running\n"); break; @@ -1452,45 +1486,72 @@ static ssize_t user_show(struct device *dev, struct device_attribute *attr, struct netiucv_priv *priv = dev_get_drvdata(dev); IUCV_DBF_TEXT(trace, 5, __func__); - return sprintf(buf, "%s\n", netiucv_printname(priv->conn->userid)); + return sprintf(buf, "%s\n", netiucv_printuser(priv->conn)); } -static ssize_t user_write(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static int netiucv_check_user(const char *buf, size_t count, char *username, + char *userdata) { - struct netiucv_priv *priv = dev_get_drvdata(dev); - struct net_device *ndev = priv->conn->netdev; - char *p; - char *tmp; - char username[9]; - int i; - struct iucv_connection *cp; + const char *p; + int i; - IUCV_DBF_TEXT(trace, 3, __func__); - if (count > 9) { - IUCV_DBF_TEXT_(setup, 2, - "%d is length of username\n", (int) count); + p = strchr(buf, '.'); + if ((p && ((count > 26) || + ((p - buf) > 8) || + (buf + count - p > 18))) || + (!p && (count > 9))) { + IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n"); return -EINVAL; } - tmp = strsep((char **) &buf, "\n"); - for (i = 0, p = tmp; i < 8 && *p; i++, p++) { - if (isalnum(*p) || (*p == '$')) { - username[i]= toupper(*p); + for (i = 0, p = buf; i < 8 && *p && *p != '.'; i++, p++) { + if (isalnum(*p) || *p == '$') { + username[i] = toupper(*p); continue; } - if (*p == '\n') { + if (*p == '\n') /* trailing lf, grr */ break; - } IUCV_DBF_TEXT_(setup, 2, - "username: invalid character %c\n", *p); + "conn_write: invalid character %02x\n", *p); return -EINVAL; } while (i < 8) username[i++] = ' '; username[8] = '\0'; + if (*p == '.') { + p++; + for (i = 0; i < 16 && *p; i++, p++) { + if (*p == '\n') + break; + userdata[i] = toupper(*p); + } + while (i > 0 && i < 16) + userdata[i++] = ' '; + } else + memcpy(userdata, iucvMagic_ascii, 16); + userdata[16] = '\0'; + ASCEBC(userdata, 16); + + return 0; +} + +static ssize_t user_write(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct netiucv_priv *priv = dev_get_drvdata(dev); + struct net_device *ndev = priv->conn->netdev; + char username[9]; + char userdata[17]; + int rc; + struct iucv_connection *cp; + + IUCV_DBF_TEXT(trace, 3, __func__); + rc = netiucv_check_user(buf, count, username, userdata); + if (rc) + return rc; + if (memcmp(username, priv->conn->userid, 9) && (ndev->flags & (IFF_UP | IFF_RUNNING))) { /* username changed while the interface is active. */ @@ -1499,15 +1560,17 @@ static ssize_t user_write(struct device *dev, struct device_attribute *attr, } read_lock_bh(&iucv_connection_rwlock); list_for_each_entry(cp, &iucv_connection_list, list) { - if (!strncmp(username, cp->userid, 9) && cp->netdev != ndev) { + if (!strncmp(username, cp->userid, 9) && + !strncmp(userdata, cp->userdata, 17) && cp->netdev != ndev) { read_unlock_bh(&iucv_connection_rwlock); - IUCV_DBF_TEXT_(setup, 2, "user_write: Connection " - "to %s already exists\n", username); + IUCV_DBF_TEXT_(setup, 2, "user_write: Connection to %s " + "already exists\n", netiucv_printuser(cp)); return -EEXIST; } } read_unlock_bh(&iucv_connection_rwlock); memcpy(priv->conn->userid, username, 9); + memcpy(priv->conn->userdata, userdata, 17); return count; } @@ -1537,7 +1600,8 @@ static ssize_t buffer_write (struct device *dev, struct device_attribute *attr, bs1 = simple_strtoul(buf, &e, 0); if (e && (!isspace(*e))) { - IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %c\n", *e); + IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %02x\n", + *e); return -EINVAL; } if (bs1 > NETIUCV_BUFSIZE_MAX) { @@ -1864,7 +1928,8 @@ static void netiucv_unregister_device(struct device *dev) * Add it to the list of netiucv connections; */ static struct iucv_connection *netiucv_new_connection(struct net_device *dev, - char *username) + char *username, + char *userdata) { struct iucv_connection *conn; @@ -1893,6 +1958,8 @@ static struct iucv_connection *netiucv_new_connection(struct net_device *dev, fsm_settimer(conn->fsm, &conn->timer); fsm_newstate(conn->fsm, CONN_STATE_INVALID); + if (userdata) + memcpy(conn->userdata, userdata, 17); if (username) { memcpy(conn->userid, username, 9); fsm_newstate(conn->fsm, CONN_STATE_STOPPED); @@ -1919,6 +1986,7 @@ out: */ static void netiucv_remove_connection(struct iucv_connection *conn) { + IUCV_DBF_TEXT(trace, 3, __func__); write_lock_bh(&iucv_connection_rwlock); list_del_init(&conn->list); @@ -1926,7 +1994,7 @@ static void netiucv_remove_connection(struct iucv_connection *conn) fsm_deltimer(&conn->timer); netiucv_purge_skb_queue(&conn->collect_queue); if (conn->path) { - iucv_path_sever(conn->path, iucvMagic); + iucv_path_sever(conn->path, conn->userdata); kfree(conn->path); conn->path = NULL; } @@ -1985,7 +2053,7 @@ static void netiucv_setup_netdevice(struct net_device *dev) /** * Allocate and initialize everything of a net device. */ -static struct net_device *netiucv_init_netdevice(char *username) +static struct net_device *netiucv_init_netdevice(char *username, char *userdata) { struct netiucv_priv *privptr; struct net_device *dev; @@ -2004,7 +2072,7 @@ static struct net_device *netiucv_init_netdevice(char *username) if (!privptr->fsm) goto out_netdev; - privptr->conn = netiucv_new_connection(dev, username); + privptr->conn = netiucv_new_connection(dev, username, userdata); if (!privptr->conn) { IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n"); goto out_fsm; @@ -2022,47 +2090,31 @@ out_netdev: static ssize_t conn_write(struct device_driver *drv, const char *buf, size_t count) { - const char *p; char username[9]; - int i, rc; + char userdata[17]; + int rc; struct net_device *dev; struct netiucv_priv *priv; struct iucv_connection *cp; IUCV_DBF_TEXT(trace, 3, __func__); - if (count>9) { - IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n"); - return -EINVAL; - } - - for (i = 0, p = buf; i < 8 && *p; i++, p++) { - if (isalnum(*p) || *p == '$') { - username[i] = toupper(*p); - continue; - } - if (*p == '\n') - /* trailing lf, grr */ - break; - IUCV_DBF_TEXT_(setup, 2, - "conn_write: invalid character %c\n", *p); - return -EINVAL; - } - while (i < 8) - username[i++] = ' '; - username[8] = '\0'; + rc = netiucv_check_user(buf, count, username, userdata); + if (rc) + return rc; read_lock_bh(&iucv_connection_rwlock); list_for_each_entry(cp, &iucv_connection_list, list) { - if (!strncmp(username, cp->userid, 9)) { + if (!strncmp(username, cp->userid, 9) && + !strncmp(userdata, cp->userdata, 17)) { read_unlock_bh(&iucv_connection_rwlock); - IUCV_DBF_TEXT_(setup, 2, "conn_write: Connection " - "to %s already exists\n", username); + IUCV_DBF_TEXT_(setup, 2, "conn_write: Connection to %s " + "already exists\n", netiucv_printuser(cp)); return -EEXIST; } } read_unlock_bh(&iucv_connection_rwlock); - dev = netiucv_init_netdevice(username); + dev = netiucv_init_netdevice(username, userdata); if (!dev) { IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n"); return -ENODEV; @@ -2083,8 +2135,9 @@ static ssize_t conn_write(struct device_driver *drv, if (rc) goto out_unreg; - dev_info(priv->dev, "The IUCV interface to %s has been" - " established successfully\n", netiucv_printname(username)); + dev_info(priv->dev, "The IUCV interface to %s has been established " + "successfully\n", + netiucv_printuser(priv->conn)); return count; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index fff57de78943..9c3f38da4c01 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -66,7 +66,7 @@ static void qeth_release_skbs(struct qeth_qdio_out_buffer *buf); static void qeth_clear_output_buffer(struct qeth_qdio_out_q *queue, struct qeth_qdio_out_buffer *buf, enum qeth_qdio_buffer_states newbufstate); - +static int qeth_init_qdio_out_buf(struct qeth_qdio_out_q *, int); static inline const char *qeth_get_cardname(struct qeth_card *card) { @@ -363,6 +363,9 @@ static inline enum iucv_tx_notify qeth_compute_cq_notification(int sbalf15, static inline void qeth_cleanup_handled_pending(struct qeth_qdio_out_q *q, int bidx, int forced_cleanup) { + if (q->card->options.cq != QETH_CQ_ENABLED) + return; + if (q->bufs[bidx]->next_pending != NULL) { struct qeth_qdio_out_buffer *head = q->bufs[bidx]; struct qeth_qdio_out_buffer *c = q->bufs[bidx]->next_pending; @@ -390,6 +393,13 @@ static inline void qeth_cleanup_handled_pending(struct qeth_qdio_out_q *q, } } + if (forced_cleanup && (atomic_read(&(q->bufs[bidx]->state)) == + QETH_QDIO_BUF_HANDLED_DELAYED)) { + /* for recovery situations */ + q->bufs[bidx]->aob = q->bufstates[bidx].aob; + qeth_init_qdio_out_buf(q, bidx); + QETH_CARD_TEXT(q->card, 2, "clprecov"); + } } @@ -412,7 +422,6 @@ static inline void qeth_qdio_handle_aob(struct qeth_card *card, notification = TX_NOTIFY_OK; } else { BUG_ON(atomic_read(&buffer->state) != QETH_QDIO_BUF_PENDING); - atomic_set(&buffer->state, QETH_QDIO_BUF_IN_CQ); notification = TX_NOTIFY_DELAYED_OK; } @@ -425,7 +434,8 @@ static inline void qeth_qdio_handle_aob(struct qeth_card *card, buffer->aob = NULL; qeth_clear_output_buffer(buffer->q, buffer, - QETH_QDIO_BUF_HANDLED_DELAYED); + QETH_QDIO_BUF_HANDLED_DELAYED); + /* from here on: do not touch buffer anymore */ qdio_release_aob(aob); } @@ -1113,11 +1123,25 @@ out: static void qeth_release_skbs(struct qeth_qdio_out_buffer *buf) { struct sk_buff *skb; + struct iucv_sock *iucv; + int notify_general_error = 0; + + if (atomic_read(&buf->state) == QETH_QDIO_BUF_PENDING) + notify_general_error = 1; + + /* release may never happen from within CQ tasklet scope */ + BUG_ON(atomic_read(&buf->state) == QETH_QDIO_BUF_IN_CQ); skb = skb_dequeue(&buf->skb_list); while (skb) { QETH_CARD_TEXT(buf->q->card, 5, "skbr"); QETH_CARD_TEXT_(buf->q->card, 5, "%lx", (long) skb); + if (notify_general_error && skb->protocol == ETH_P_AF_IUCV) { + if (skb->sk) { + iucv = iucv_sk(skb->sk); + iucv->sk_txnotify(skb, TX_NOTIFY_GENERALERROR); + } + } atomic_dec(&skb->users); dev_kfree_skb_any(skb); skb = skb_dequeue(&buf->skb_list); @@ -1160,7 +1184,7 @@ static void qeth_clear_outq_buffers(struct qeth_qdio_out_q *q, int free) for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) { if (!q->bufs[j]) continue; - qeth_cleanup_handled_pending(q, j, free); + qeth_cleanup_handled_pending(q, j, 1); qeth_clear_output_buffer(q, q->bufs[j], QETH_QDIO_BUF_EMPTY); if (free) { kmem_cache_free(qeth_qdio_outbuf_cache, q->bufs[j]); @@ -1207,7 +1231,7 @@ static void qeth_free_qdio_buffers(struct qeth_card *card) qeth_free_cq(card); cancel_delayed_work_sync(&card->buffer_reclaim_work); for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j) - kfree_skb(card->qdio.in_q->bufs[j].rx_skb); + dev_kfree_skb_any(card->qdio.in_q->bufs[j].rx_skb); kfree(card->qdio.in_q); card->qdio.in_q = NULL; /* inbound buffer pool */ @@ -1329,6 +1353,7 @@ static int qeth_do_start_thread(struct qeth_card *card, unsigned long thread) static void qeth_start_kernel_thread(struct work_struct *work) { + struct task_struct *ts; struct qeth_card *card = container_of(work, struct qeth_card, kernel_thread_starter); QETH_CARD_TEXT(card , 2, "strthrd"); @@ -1336,9 +1361,15 @@ static void qeth_start_kernel_thread(struct work_struct *work) if (card->read.state != CH_STATE_UP && card->write.state != CH_STATE_UP) return; - if (qeth_do_start_thread(card, QETH_RECOVER_THREAD)) - kthread_run(card->discipline.recover, (void *) card, + if (qeth_do_start_thread(card, QETH_RECOVER_THREAD)) { + ts = kthread_run(card->discipline.recover, (void *)card, "qeth_recover"); + if (IS_ERR(ts)) { + qeth_clear_thread_start_bit(card, QETH_RECOVER_THREAD); + qeth_clear_thread_running_bit(card, + QETH_RECOVER_THREAD); + } + } } static int qeth_setup_card(struct qeth_card *card) @@ -4521,7 +4552,7 @@ static int qeth_qdio_establish(struct qeth_card *card) init_data.no_output_qs = card->qdio.no_out_queues; init_data.input_handler = card->discipline.input_handler; init_data.output_handler = card->discipline.output_handler; - init_data.queue_start_poll = queue_start_poll; + init_data.queue_start_poll_array = queue_start_poll; init_data.int_parm = (unsigned long) card; init_data.input_sbal_addr_array = (void **) in_sbal_ptrs; init_data.output_sbal_addr_array = (void **) out_sbal_ptrs; diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index a21ae3d549db..c12967133114 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -301,21 +301,21 @@ static void qeth_l2_process_vlans(struct qeth_card *card) spin_unlock_bh(&card->vlanlock); } -static void qeth_l2_vlan_rx_add_vid(struct net_device *dev, unsigned short vid) +static int qeth_l2_vlan_rx_add_vid(struct net_device *dev, unsigned short vid) { struct qeth_card *card = dev->ml_priv; struct qeth_vlan_vid *id; QETH_CARD_TEXT_(card, 4, "aid:%d", vid); if (!vid) - return; + return 0; if (card->info.type == QETH_CARD_TYPE_OSM) { QETH_CARD_TEXT(card, 3, "aidOSM"); - return; + return 0; } if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) { QETH_CARD_TEXT(card, 3, "aidREC"); - return; + return 0; } id = kmalloc(sizeof(struct qeth_vlan_vid), GFP_ATOMIC); if (id) { @@ -324,10 +324,13 @@ static void qeth_l2_vlan_rx_add_vid(struct net_device *dev, unsigned short vid) spin_lock_bh(&card->vlanlock); list_add_tail(&id->list, &card->vid_list); spin_unlock_bh(&card->vlanlock); + } else { + return -ENOMEM; } + return 0; } -static void qeth_l2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) +static int qeth_l2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) { struct qeth_vlan_vid *id, *tmpid = NULL; struct qeth_card *card = dev->ml_priv; @@ -335,11 +338,11 @@ static void qeth_l2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) QETH_CARD_TEXT_(card, 4, "kid:%d", vid); if (card->info.type == QETH_CARD_TYPE_OSM) { QETH_CARD_TEXT(card, 3, "kidOSM"); - return; + return 0; } if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) { QETH_CARD_TEXT(card, 3, "kidREC"); - return; + return 0; } spin_lock_bh(&card->vlanlock); list_for_each_entry(id, &card->vid_list, list) { @@ -355,6 +358,7 @@ static void qeth_l2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) kfree(tmpid); } qeth_l2_set_multicast_list(card->dev); + return 0; } static int qeth_l2_stop_card(struct qeth_card *card, int recovery_mode) @@ -1169,6 +1173,7 @@ static void __exit qeth_l2_exit(void) static void qeth_l2_shutdown(struct ccwgroup_device *gdev) { struct qeth_card *card = dev_get_drvdata(&gdev->dev); + qeth_set_allowed_threads(card, 0, 1); if ((gdev->state == CCWGROUP_ONLINE) && card->info.hwtrap) qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM); qeth_qdio_clear_card(card, 0); diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 4d5307ddbe55..9648e4e68337 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1869,15 +1869,15 @@ static void qeth_l3_free_vlan_addresses(struct qeth_card *card, qeth_l3_free_vlan_addresses6(card, vid); } -static void qeth_l3_vlan_rx_add_vid(struct net_device *dev, unsigned short vid) +static int qeth_l3_vlan_rx_add_vid(struct net_device *dev, unsigned short vid) { struct qeth_card *card = dev->ml_priv; set_bit(vid, card->active_vlans); - return; + return 0; } -static void qeth_l3_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) +static int qeth_l3_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) { struct qeth_card *card = dev->ml_priv; unsigned long flags; @@ -1885,7 +1885,7 @@ static void qeth_l3_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) QETH_CARD_TEXT_(card, 4, "kid:%d", vid); if (qeth_wait_for_threads(card, QETH_RECOVER_THREAD)) { QETH_CARD_TEXT(card, 3, "kidREC"); - return; + return 0; } spin_lock_irqsave(&card->vlanlock, flags); /* unregister IP addresses of vlan device */ @@ -1893,6 +1893,7 @@ static void qeth_l3_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) clear_bit(vid, card->active_vlans); spin_unlock_irqrestore(&card->vlanlock, flags); qeth_l3_set_multicast_list(card->dev); + return 0; } static inline int qeth_l3_rebuild_skb(struct qeth_card *card, @@ -2759,7 +2760,7 @@ int inline qeth_l3_get_cast_type(struct qeth_card *card, struct sk_buff *skb) rcu_read_lock(); dst = skb_dst(skb); if (dst) - n = dst_get_neighbour(dst); + n = dst_get_neighbour_noref(dst); if (n) { cast_type = n->type; rcu_read_unlock(); @@ -2855,7 +2856,7 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, rcu_read_lock(); dst = skb_dst(skb); if (dst) - n = dst_get_neighbour(dst); + n = dst_get_neighbour_noref(dst); if (ipv == 4) { /* IPv4 */ hdr->hdr.l3.flags = qeth_l3_get_qeth_hdr_flags4(cast_type); @@ -3209,7 +3210,8 @@ static int qeth_l3_stop(struct net_device *dev) return 0; } -static u32 qeth_l3_fix_features(struct net_device *dev, u32 features) +static netdev_features_t qeth_l3_fix_features(struct net_device *dev, + netdev_features_t features) { struct qeth_card *card = dev->ml_priv; @@ -3223,7 +3225,8 @@ static u32 qeth_l3_fix_features(struct net_device *dev, u32 features) return features; } -static int qeth_l3_set_features(struct net_device *dev, u32 features) +static int qeth_l3_set_features(struct net_device *dev, + netdev_features_t features) { struct qeth_card *card = dev->ml_priv; u32 changed = dev->features ^ features; @@ -3489,14 +3492,13 @@ contin: else netif_carrier_off(card->dev); if (recover_flag == CARD_STATE_RECOVER) { + rtnl_lock(); if (recovery_mode) __qeth_l3_open(card->dev); - else { - rtnl_lock(); + else dev_open(card->dev); - rtnl_unlock(); - } qeth_l3_set_multicast_list(card->dev); + rtnl_unlock(); } /* let user_space know that device is online */ kobject_uevent(&gdev->dev.kobj, KOBJ_CHANGE); @@ -3542,6 +3544,11 @@ static int __qeth_l3_set_offline(struct ccwgroup_device *cgdev, card->info.hwtrap = 1; } qeth_l3_stop_card(card, recovery_mode); + if ((card->options.cq == QETH_CQ_ENABLED) && card->dev) { + rtnl_lock(); + call_netdevice_notifiers(NETDEV_REBOOT, card->dev); + rtnl_unlock(); + } rc = ccw_device_set_offline(CARD_DDEV(card)); rc2 = ccw_device_set_offline(CARD_WDEV(card)); rc3 = ccw_device_set_offline(CARD_RDEV(card)); @@ -3596,6 +3603,7 @@ static int qeth_l3_recover(void *ptr) static void qeth_l3_shutdown(struct ccwgroup_device *gdev) { struct qeth_card *card = dev_get_drvdata(&gdev->dev); + qeth_set_allowed_threads(card, 0, 1); if ((gdev->state == CCWGROUP_ONLINE) && card->info.hwtrap) qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM); qeth_qdio_clear_card(card, 0); diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 11f07f888223..b79576b64f45 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -55,6 +55,10 @@ static void zfcp_scsi_slave_destroy(struct scsi_device *sdev) { struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); + /* if previous slave_alloc returned early, there is nothing to do */ + if (!zfcp_sdev->port) + return; + zfcp_erp_lun_shutdown_wait(sdev, "scssd_1"); put_device(&zfcp_sdev->port->dev); } |