aboutsummaryrefslogtreecommitdiff
path: root/drivers/s390
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/block/dasd.c5
-rw-r--r--drivers/s390/block/dasd_3990_erp.c4
-rw-r--r--drivers/s390/block/dasd_alias.c10
-rw-r--r--drivers/s390/block/dasd_eckd.c411
-rw-r--r--drivers/s390/block/xpram.c2
-rw-r--r--drivers/s390/char/sclp_config.c8
-rw-r--r--drivers/s390/char/zcore.c2
-rw-r--r--drivers/s390/cio/chsc.c7
-rw-r--r--drivers/s390/cio/cio.h5
-rw-r--r--drivers/s390/cio/css.c104
-rw-r--r--drivers/s390/cio/device.c4
-rw-r--r--drivers/s390/cio/device_fsm.c30
-rw-r--r--drivers/s390/cio/device_ops.c20
-rw-r--r--drivers/s390/cio/io_sch.h5
-rw-r--r--drivers/s390/cio/qdio_setup.c10
-rw-r--r--drivers/s390/crypto/ap_bus.c25
-rw-r--r--drivers/s390/crypto/zcrypt_pcixcc.c32
-rw-r--r--drivers/s390/kvm/kvm_virtio.c6
-rw-r--r--drivers/s390/net/Kconfig2
-rw-r--r--drivers/s390/net/lcs.c6
-rw-r--r--drivers/s390/net/netiucv.c219
-rw-r--r--drivers/s390/net/qeth_core.h3
-rw-r--r--drivers/s390/net/qeth_core_main.c48
-rw-r--r--drivers/s390/net/qeth_l2_main.c19
-rw-r--r--drivers/s390/net/qeth_l3_main.c39
-rw-r--r--drivers/s390/net/qeth_l3_sys.c4
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c4
27 files changed, 696 insertions, 338 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 65894f05a80..eef27a197c0 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 87a0cf160fe..0326571e7ff 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 c388eda1e2b..553b3c5abb0 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 6ab29680586..bbcd5e9206e 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 98f3e4ade92..690c3338a8a 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/sclp_config.c b/drivers/s390/char/sclp_config.c
index 95b909ac2b7..3c03c1060be 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/zcore.c b/drivers/s390/char/zcore.c
index 43068fbd0ba..1b6d9247fdc 100644
--- a/drivers/s390/char/zcore.c
+++ b/drivers/s390/char/zcore.c
@@ -641,6 +641,8 @@ static int __init zcore_init(void)
if (ipl_info.type != IPL_TYPE_FCP_DUMP)
return -ENODATA;
+ if (OLDMEM_BASE)
+ return -ENODATA;
zcore_dbf = debug_register("zcore", 4, 1, 4 * sizeof(long));
debug_register_view(zcore_dbf, &debug_sprintf_view);
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 75c3f1f8fd4..a84631a7391 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 155a82bcb9e..4a1ff5c2eb8 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/css.c b/drivers/s390/cio/css.c
index 92d7324acb1..21908e67bf6 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 d734f4a0eca..47269858ecb 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 52c233fa2b1..1b853513c89 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 *