summaryrefslogtreecommitdiff
path: root/drivers/s390/cio
diff options
context:
space:
mode:
authorStefan Richter <stefanr@s5r6.in-berlin.de>2006-09-17 18:17:19 +0200
committerStefan Richter <stefanr@s5r6.in-berlin.de>2006-09-17 18:19:31 +0200
commit9b4f2e9576658c4e52d95dc8d309f51b2e2db096 (patch)
tree7b1902b0f931783fccc6fee45c6f9c16b4fde5ce /drivers/s390/cio
parent3c6c65f5ed5a6d307bd607aecd06d658c0934d88 (diff)
parent803db244b9f71102e366fd689000c1417b9a7508 (diff)
ieee1394: merge from Linus
Conflicts: drivers/ieee1394/hosts.c Patch "lockdep: annotate ieee1394 skb-queue-head locking" was meddling with patch "ieee1394: fix kerneldoc of hpsb_alloc_host". Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers/s390/cio')
-rw-r--r--drivers/s390/cio/ccwgroup.c26
-rw-r--r--drivers/s390/cio/chsc.c44
-rw-r--r--drivers/s390/cio/cio.c6
-rw-r--r--drivers/s390/cio/cio.h3
-rw-r--r--drivers/s390/cio/cmf.c5
-rw-r--r--drivers/s390/cio/css.c36
-rw-r--r--drivers/s390/cio/css.h4
-rw-r--r--drivers/s390/cio/device.c33
-rw-r--r--drivers/s390/cio/device_fsm.c109
-rw-r--r--drivers/s390/cio/device_ops.c3
-rw-r--r--drivers/s390/cio/device_pgid.c149
-rw-r--r--drivers/s390/cio/device_status.c7
-rw-r--r--drivers/s390/cio/qdio.c2
13 files changed, 309 insertions, 118 deletions
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c
index c7319a07ba35..38954f5cd14c 100644
--- a/drivers/s390/cio/ccwgroup.c
+++ b/drivers/s390/cio/ccwgroup.c
@@ -152,7 +152,6 @@ ccwgroup_create(struct device *root,
struct ccwgroup_device *gdev;
int i;
int rc;
- int del_drvdata;
if (argc > 256) /* disallow dumb users */
return -EINVAL;
@@ -163,7 +162,6 @@ ccwgroup_create(struct device *root,
atomic_set(&gdev->onoff, 0);
- del_drvdata = 0;
for (i = 0; i < argc; i++) {
gdev->cdev[i] = get_ccwdev_by_busid(cdrv, argv[i]);
@@ -180,18 +178,14 @@ ccwgroup_create(struct device *root,
rc = -EINVAL;
goto free_dev;
}
- }
- for (i = 0; i < argc; i++)
gdev->cdev[i]->dev.driver_data = gdev;
- del_drvdata = 1;
+ }
gdev->creator_id = creator_id;
gdev->count = argc;
- gdev->dev = (struct device ) {
- .bus = &ccwgroup_bus_type,
- .parent = root,
- .release = ccwgroup_release,
- };
+ gdev->dev.bus = &ccwgroup_bus_type;
+ gdev->dev.parent = root;
+ gdev->dev.release = ccwgroup_release;
snprintf (gdev->dev.bus_id, BUS_ID_SIZE, "%s",
gdev->cdev[0]->dev.bus_id);
@@ -226,9 +220,9 @@ error:
free_dev:
for (i = 0; i < argc; i++)
if (gdev->cdev[i]) {
- put_device(&gdev->cdev[i]->dev);
- if (del_drvdata)
+ if (gdev->cdev[i]->dev.driver_data == gdev)
gdev->cdev[i]->dev.driver_data = NULL;
+ put_device(&gdev->cdev[i]->dev);
}
kfree(gdev);
return rc;
@@ -319,7 +313,7 @@ ccwgroup_online_store (struct device *dev, struct device_attribute *attr, const
if (!try_module_get(gdrv->owner))
return -EINVAL;
- value = simple_strtoul(buf, 0, 0);
+ value = simple_strtoul(buf, NULL, 0);
ret = count;
if (value == 1)
ccwgroup_set_online(gdev);
@@ -395,10 +389,8 @@ int
ccwgroup_driver_register (struct ccwgroup_driver *cdriver)
{
/* register our new driver with the core */
- cdriver->driver = (struct device_driver) {
- .bus = &ccwgroup_bus_type,
- .name = cdriver->name,
- };
+ cdriver->driver.bus = &ccwgroup_bus_type;
+ cdriver->driver.name = cdriver->name;
return driver_register(&cdriver->driver);
}
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index a01f3bba4a7b..c28444af0919 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -238,8 +238,6 @@ s390_subchannel_remove_chpid(struct device *dev, void *data)
/* Check for single path devices. */
if (sch->schib.pmcw.pim == 0x80)
goto out_unreg;
- if (sch->vpm == mask)
- goto out_unreg;
if ((sch->schib.scsw.actl & SCSW_ACTL_DEVACT) &&
(sch->schib.scsw.actl & SCSW_ACTL_SCHACT) &&
@@ -258,6 +256,8 @@ s390_subchannel_remove_chpid(struct device *dev, void *data)
/* trigger path verification. */
if (sch->driver && sch->driver->verify)
sch->driver->verify(&sch->dev);
+ else if (sch->vpm == mask)
+ goto out_unreg;
out_unlock:
spin_unlock_irq(&sch->lock);
return 0;
@@ -1391,10 +1391,8 @@ new_channel_path(int chpid)
/* fill in status, etc. */
chp->id = chpid;
chp->state = 1;
- chp->dev = (struct device) {
- .parent = &css[0]->device,
- .release = chp_release,
- };
+ chp->dev.parent = &css[0]->device;
+ chp->dev.release = chp_release;
snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp0.%x", chpid);
/* Obtain channel path description and fill it in. */
@@ -1464,6 +1462,40 @@ chsc_get_chp_desc(struct subchannel *sch, int chp_no)
return desc;
}
+static int reset_channel_path(struct channel_path *chp)
+{
+ int cc;
+
+ cc = rchp(chp->id);
+ switch (cc) {
+ case 0:
+ return 0;
+ case 2:
+ return -EBUSY;
+ default:
+ return -ENODEV;
+ }
+}
+
+static void reset_channel_paths_css(struct channel_subsystem *css)
+{
+ int i;
+
+ for (i = 0; i <= __MAX_CHPID; i++) {
+ if (css->chps[i])
+ reset_channel_path(css->chps[i]);
+ }
+}
+
+void cio_reset_channel_paths(void)
+{
+ int i;
+
+ for (i = 0; i <= __MAX_CSSID; i++) {
+ if (css[i] && css[i]->valid)
+ reset_channel_paths_css(css[i]);
+ }
+}
static int __init
chsc_alloc_sei_area(void)
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index a3423267467f..89320c1ad825 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -147,7 +147,7 @@ cio_tpi(void)
sch->driver->irq(&sch->dev);
spin_unlock(&sch->lock);
irq_exit ();
- __local_bh_enable();
+ _local_bh_enable();
return 1;
}
@@ -519,6 +519,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
memset(sch, 0, sizeof(struct subchannel));
spin_lock_init(&sch->lock);
+ mutex_init(&sch->reg_mutex);
/* Set a name for the subchannel */
snprintf (sch->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x", schid.ssid,
@@ -797,7 +798,7 @@ struct subchannel *
cio_get_console_subchannel(void)
{
if (!console_subchannel_in_use)
- return 0;
+ return NULL;
return &console_subchannel;
}
@@ -875,5 +876,6 @@ void
reipl(unsigned long devno)
{
clear_all_subchannels();
+ cio_reset_channel_paths();
do_reipl(devno);
}
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index 0ca987344e07..4541c1af4b66 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -2,6 +2,7 @@
#define S390_CIO_H
#include "schid.h"
+#include <linux/mutex.h>
/*
* where we put the ssd info
@@ -87,7 +88,7 @@ struct orb {
struct subchannel {
struct subchannel_id schid;
spinlock_t lock; /* subchannel lock */
-
+ struct mutex reg_mutex;
enum {
SUBCHANNEL_TYPE_IO = 0,
SUBCHANNEL_TYPE_CHSC = 1,
diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c
index 1c3e8e9012b0..828b2d334f0a 100644
--- a/drivers/s390/cio/cmf.c
+++ b/drivers/s390/cio/cmf.c
@@ -1068,6 +1068,7 @@ cmb_show_avg_sample_interval(struct device *dev, struct device_attribute *attr,
if (count) {
interval = cmb_data->last_update -
cdev->private->cmb_start_time;
+ interval = (interval * 1000) >> 12;
interval /= count;
} else
interval = -1;
@@ -1140,7 +1141,7 @@ static struct attribute *cmf_attributes[] = {
&dev_attr_avg_device_disconnect_time.attr,
&dev_attr_avg_control_unit_queuing_time.attr,
&dev_attr_avg_device_active_only_time.attr,
- 0,
+ NULL,
};
static struct attribute_group cmf_attr_group = {
@@ -1160,7 +1161,7 @@ static struct attribute *cmf_attributes_ext[] = {
&dev_attr_avg_device_active_only_time.attr,
&dev_attr_avg_device_busy_time.attr,
&dev_attr_avg_initial_command_response_time.attr,
- 0,
+ NULL,
};
static struct attribute_group cmf_attr_group_ext = {
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 1d3be80797f8..13eeea3d547f 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -108,6 +108,24 @@ css_subchannel_release(struct device *dev)
extern int css_get_ssd_info(struct subchannel *sch);
+
+int css_sch_device_register(struct subchannel *sch)
+{
+ int ret;
+
+ mutex_lock(&sch->reg_mutex);
+ ret = device_register(&sch->dev);
+ mutex_unlock(&sch->reg_mutex);
+ return ret;
+}
+
+void css_sch_device_unregister(struct subchannel *sch)
+{
+ mutex_lock(&sch->reg_mutex);
+ device_unregister(&sch->dev);
+ mutex_unlock(&sch->reg_mutex);
+}
+
static int
css_register_subchannel(struct subchannel *sch)
{
@@ -119,7 +137,7 @@ css_register_subchannel(struct subchannel *sch)
sch->dev.release = &css_subchannel_release;
/* make it known to the system */
- ret = device_register(&sch->dev);
+ ret = css_sch_device_register(sch);
if (ret)
printk (KERN_WARNING "%s: could not register %s\n",
__func__, sch->dev.bus_id);
@@ -250,7 +268,7 @@ css_evaluate_subchannel(struct subchannel_id schid, int slow)
* The device will be killed automatically.
*/
cio_disable_subchannel(sch);
- device_unregister(&sch->dev);
+ css_sch_device_unregister(sch);
/* Reset intparm to zeroes. */
sch->schib.pmcw.intparm = 0;
cio_modify(sch);
@@ -264,7 +282,7 @@ css_evaluate_subchannel(struct subchannel_id schid, int slow)
* away in any case.
*/
if (!disc) {
- device_unregister(&sch->dev);
+ css_sch_device_unregister(sch);
/* Reset intparm to zeroes. */
sch->schib.pmcw.intparm = 0;
cio_modify(sch);
@@ -605,9 +623,13 @@ init_channel_subsystem (void)
ret = device_register(&css[i]->device);
if (ret)
goto out_free;
- if (css_characteristics_avail && css_chsc_characteristics.secm)
- device_create_file(&css[i]->device,
- &dev_attr_cm_enable);
+ if (css_characteristics_avail &&
+ css_chsc_characteristics.secm) {
+ ret = device_create_file(&css[i]->device,
+ &dev_attr_cm_enable);
+ if (ret)
+ goto out_device;
+ }
}
css_init_done = 1;
@@ -615,6 +637,8 @@ init_channel_subsystem (void)
for_each_subchannel(__init_channel_subsystem, NULL);
return 0;
+out_device:
+ device_unregister(&css[i]->device);
out_free:
kfree(css[i]);
out_unregister:
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h
index e210f89a2449..8aabb4adeb5f 100644
--- a/drivers/s390/cio/css.h
+++ b/drivers/s390/cio/css.h
@@ -100,7 +100,7 @@ struct ccw_device_private {
struct qdio_irq *qdio_data;
struct irb irb; /* device status */
struct senseid senseid; /* SenseID info */
- struct pgid pgid; /* path group ID */
+ struct pgid pgid[8]; /* path group IDs per chpid*/
struct ccw1 iccws[2]; /* ccws for SNID/SID/SPGID commands */
struct work_struct kick_work;
wait_queue_head_t wait_q;
@@ -136,6 +136,8 @@ extern struct bus_type css_bus_type;
extern struct css_driver io_subchannel_driver;
extern int css_probe_device(struct subchannel_id);
+extern int css_sch_device_register(struct subchannel *);
+extern void css_sch_device_unregister(struct subchannel *);
extern struct subchannel * get_subchannel_by_schid(struct subchannel_id);
extern int css_init_done;
extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *);
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index 67f0de6aed33..646da5640401 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -100,7 +100,7 @@ ccw_uevent (struct device *dev, char **envp, int num_envp,
if ((buffer_size - length <= 0) || (i >= num_envp))
return -ENOMEM;
- envp[i] = 0;
+ envp[i] = NULL;
return 0;
}
@@ -280,7 +280,7 @@ ccw_device_remove_disconnected(struct ccw_device *cdev)
* 'throw away device'.
*/
sch = to_subchannel(cdev->dev.parent);
- device_unregister(&sch->dev);
+ css_sch_device_unregister(sch);
/* Reset intparm to zeroes. */
sch->schib.pmcw.intparm = 0;
cio_modify(sch);
@@ -556,12 +556,11 @@ get_disc_ccwdev_by_devno(unsigned int devno, unsigned int ssid,
struct ccw_device *sibling)
{
struct device *dev;
- struct match_data data = {
- .devno = devno,
- .ssid = ssid,
- .sibling = sibling,
- };
+ struct match_data data;
+ data.devno = devno;
+ data.ssid = ssid;
+ data.sibling = sibling;
dev = bus_find_device(&ccw_bus_type, NULL, &data, match_devno);
return dev ? to_ccwdev(dev) : NULL;
@@ -625,7 +624,7 @@ ccw_device_do_unreg_rereg(void *data)
other_sch->schib.pmcw.intparm = 0;
cio_modify(other_sch);
}
- device_unregister(&other_sch->dev);
+ css_sch_device_unregister(other_sch);
}
}
/* Update ssd info here. */
@@ -709,7 +708,7 @@ ccw_device_call_sch_unregister(void *data)
struct subchannel *sch;
sch = to_subchannel(cdev->dev.parent);
- device_unregister(&sch->dev);
+ css_sch_device_unregister(sch);
/* Reset intparm to zeroes. */
sch->schib.pmcw.intparm = 0;
cio_modify(sch);
@@ -835,10 +834,8 @@ io_subchannel_probe (struct subchannel *sch)
return -ENOMEM;
}
atomic_set(&cdev->private->onoff, 0);
- cdev->dev = (struct device) {
- .parent = &sch->dev,
- .release = ccw_device_release,
- };
+ cdev->dev.parent = &sch->dev;
+ cdev->dev.release = ccw_device_release;
INIT_LIST_HEAD(&cdev->private->kick_work.entry);
/* Do first half of device_register. */
device_initialize(&cdev->dev);
@@ -977,9 +974,7 @@ ccw_device_console_enable (struct ccw_device *cdev, struct subchannel *sch)
int rc;
/* Initialize the ccw_device structure. */
- cdev->dev = (struct device) {
- .parent = &sch->dev,
- };
+ cdev->dev.parent= &sch->dev;
rc = io_subchannel_recog(cdev, sch);
if (rc)
return rc;
@@ -1057,7 +1052,7 @@ get_ccwdev_by_busid(struct ccw_driver *cdrv, const char *bus_id)
__ccwdev_check_busid);
put_driver(drv);
- return dev ? to_ccwdev(dev) : 0;
+ return dev ? to_ccwdev(dev) : NULL;
}
/************************** device driver handling ************************/
@@ -1082,7 +1077,7 @@ ccw_device_probe (struct device *dev)
ret = cdrv->probe ? cdrv->probe(cdev) : -ENODEV;
if (ret) {
- cdev->drv = 0;
+ cdev->drv = NULL;
return ret;
}
@@ -1113,7 +1108,7 @@ ccw_device_remove (struct device *dev)
ret, cdev->dev.bus_id);
}
ccw_device_set_timeout(cdev, 0);
- cdev->drv = 0;
+ cdev->drv = NULL;
return 0;
}
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index cb1af0b6f033..35e162ba6d54 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -152,7 +152,8 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev)
if (cdev->private->iretry) {
cdev->private->iretry--;
ret = cio_halt(sch);
- return (ret == 0) ? -EBUSY : ret;
+ if (ret != -EBUSY)
+ return (ret == 0) ? -EBUSY : ret;
}
/* halt io unsuccessful. */
cdev->private->iretry = 255; /* 255 clear retries. */
@@ -266,12 +267,10 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
notify = 1;
}
/* fill out sense information */
- cdev->id = (struct ccw_device_id) {
- .cu_type = cdev->private->senseid.cu_type,
- .cu_model = cdev->private->senseid.cu_model,
- .dev_type = cdev->private->senseid.dev_type,
- .dev_model = cdev->private->senseid.dev_model,
- };
+ cdev->id.cu_type = cdev->private->senseid.cu_type;
+ cdev->id.cu_model = cdev->private->senseid.cu_model;
+ cdev->id.dev_type = cdev->private->senseid.dev_type;
+ cdev->id.dev_model = cdev->private->senseid.dev_model;
if (notify) {
cdev->private->state = DEV_STATE_OFFLINE;
if (same_dev) {
@@ -378,6 +377,56 @@ ccw_device_done(struct ccw_device *cdev, int state)
put_device (&cdev->dev);
}
+static inline int cmp_pgid(struct pgid *p1, struct pgid *p2)
+{
+ char *c1;
+ char *c2;
+
+ c1 = (char *)p1;
+ c2 = (char *)p2;
+
+ return memcmp(c1 + 1, c2 + 1, sizeof(struct pgid) - 1);
+}
+
+static void __ccw_device_get_common_pgid(struct ccw_device *cdev)
+{
+ int i;
+ int last;
+
+ last = 0;
+ for (i = 0; i < 8; i++) {
+ if (cdev->private->pgid[i].inf.ps.state1 == SNID_STATE1_RESET)
+ /* No PGID yet */
+ continue;
+ if (cdev->private->pgid[last].inf.ps.state1 ==
+ SNID_STATE1_RESET) {
+ /* First non-zero PGID */
+ last = i;
+ continue;
+ }
+ if (cmp_pgid(&cdev->private->pgid[i],
+ &cdev->private->pgid[last]) == 0)
+ /* Non-conflicting PGIDs */
+ continue;
+
+ /* PGID mismatch, can't pathgroup. */
+ CIO_MSG_EVENT(0, "SNID - pgid mismatch for device "
+ "0.%x.%04x, can't pathgroup\n",
+ cdev->private->ssid, cdev->private->devno);
+ cdev->private->options.pgroup = 0;
+ return;
+ }
+ if (cdev->private->pgid[last].inf.ps.state1 ==
+ SNID_STATE1_RESET)
+ /* No previous pgid found */
+ memcpy(&cdev->private->pgid[0], &css[0]->global_pgid,
+ sizeof(struct pgid));
+ else
+ /* Use existing pgid */
+ memcpy(&cdev->private->pgid[0], &cdev->private->pgid[last],
+ sizeof(struct pgid));
+}
+
/*
* Function called from device_pgid.c after sense path ground has completed.
*/
@@ -388,24 +437,26 @@ ccw_device_sense_pgid_done(struct ccw_device *cdev, int err)
sch = to_subchannel(cdev->dev.parent);
switch (err) {
- case 0:
- /* Start Path Group verification. */
- sch->vpm = 0; /* Start with no path groups set. */
- cdev->private->state = DEV_STATE_VERIFY;
- ccw_device_verify_start(cdev);
+ case -EOPNOTSUPP: /* path grouping not supported, use nop instead. */
+ cdev->private->options.pgroup = 0;
+ break;
+ case 0: /* success */
+ case -EACCES: /* partial success, some paths not operational */
+ /* Check if all pgids are equal or 0. */
+ __ccw_device_get_common_pgid(cdev);
break;
case -ETIME: /* Sense path group id stopped by timeout. */
case -EUSERS: /* device is reserved for someone else. */
ccw_device_done(cdev, DEV_STATE_BOXED);
- break;
- case -EOPNOTSUPP: /* path grouping not supported, just set online. */
- cdev->private->options.pgroup = 0;
- ccw_device_done(cdev, DEV_STATE_ONLINE);
- break;
+ return;
default:
ccw_device_done(cdev, DEV_STATE_NOT_OPER);
- break;
+ return;
}
+ /* Start Path Group verification. */
+ sch->vpm = 0; /* Start with no path groups set. */
+ cdev->private->state = DEV_STATE_VERIFY;
+ ccw_device_verify_start(cdev);
}
/*
@@ -513,12 +564,10 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
/* 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 = (struct scsw) {
- .cc = 1,
- .fctl = SCSW_FCTL_START_FUNC,
- .actl = SCSW_ACTL_START_PEND,
- .stctl = SCSW_STCTL_STATUS_PEND,
- };
+ cdev->private->irb.scsw.cc = 1;
+ cdev->private->irb.scsw.fctl = SCSW_FCTL_START_FUNC;
+ cdev->private->irb.scsw.actl = SCSW_ACTL_START_PEND;
+ cdev->private->irb.scsw.stctl = SCSW_STCTL_STATUS_PEND;
cdev->private->flags.fake_irb = 0;
if (cdev->handler)
cdev->handler(cdev, cdev->private->intparm,
@@ -562,8 +611,9 @@ ccw_device_online(struct ccw_device *cdev)
}
/* Do we want to do path grouping? */
if (!cdev->private->options.pgroup) {
- /* No, set state online immediately. */
- ccw_device_done(cdev, DEV_STATE_ONLINE);
+ /* Start initial path verification. */
+ cdev->private->state = DEV_STATE_VERIFY;
+ ccw_device_verify_start(cdev);
return 0;
}
/* Do a SensePGID first. */
@@ -609,6 +659,7 @@ ccw_device_offline(struct ccw_device *cdev)
/* Are we doing path grouping? */
if (!cdev->private->options.pgroup) {
/* No, set state offline immediately. */
+ sch->vpm = 0;
ccw_device_done(cdev, DEV_STATE_OFFLINE);
return 0;
}
@@ -705,8 +756,6 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)
{
struct subchannel *sch;
- if (!cdev->private->options.pgroup)
- return;
if (cdev->private->state == DEV_STATE_W4SENSE) {
cdev->private->flags.doverify = 1;
return;
@@ -719,6 +768,7 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)
stsch(sch->schid, &sch->schib);
if (sch->schib.scsw.actl != 0 ||
+ (sch->schib.scsw.stctl & SCSW_STCTL_STATUS_PEND) ||
(cdev->private->irb.scsw.stctl & SCSW_STCTL_STATUS_PEND)) {
/*
* No final status yet or final status not yet delivered
@@ -995,8 +1045,7 @@ static void
ccw_device_wait4io_verify(struct ccw_device *cdev, enum dev_event dev_event)
{
/* When the I/O has terminated, we have to start verification. */
- if (cdev->private->options.pgroup)
- cdev->private->flags.doverify = 1;
+ cdev->private->flags.doverify = 1;
}
static void
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index a60124264bee..9e3de0bd59b5 100644
--- a/drivers/s390/cio/device_ops.c
+++ b/drivers/s390/cio/device_ops.c
@@ -263,6 +263,9 @@ ccw_device_wake_up(struct ccw_device *cdev, unsigned long ip, struct irb *irb)
/* Abuse intparm for error reporting. */
if (IS_ERR(irb))
cdev->private->intparm = -EIO;
+ else if (irb->scsw.cc == 1)
+ /* Retry for deferred condition code. */
+ cdev->private->intparm = -EAGAIN;
else if ((irb->scsw.dstat !=
(DEV_STAT_CHN_END|DEV_STAT_DEV_END)) ||
(irb->scsw.cstat != 0)) {
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c
index 54cb64ed0786..1693a102dcfe 100644
--- a/drivers/s390/cio/device_pgid.c
+++ b/drivers/s390/cio/device_pgid.c
@@ -24,6 +24,21 @@
#include "ioasm.h"
/*
+ * Helper function called from interrupt context to decide whether an
+ * operation should be tried again.
+ */
+static int __ccw_device_should_retry(struct scsw *scsw)
+{
+ /* CC is only valid if start function bit is set. */
+ if ((scsw->fctl & SCSW_FCTL_START_FUNC) && scsw->cc == 1)
+ return 1;
+ /* No more activity. For sense and set PGID we stubbornly try again. */
+ if (!scsw->actl)
+ return 1;
+ return 0;
+}
+
+/*
* Start Sense Path Group ID helper function. Used in ccw_device_recog
* and ccw_device_sense_pgid.
*/
@@ -33,12 +48,17 @@ __ccw_device_sense_pgid_start(struct ccw_device *cdev)
struct subchannel *sch;
struct ccw1 *ccw;
int ret;
+ int i;
sch = to_subchannel(cdev->dev.parent);
+ /* Return if we already checked on all paths. */
+ if (cdev->private->imask == 0)
+ return (sch->lpm == 0) ? -ENODEV : -EACCES;
+ i = 8 - ffs(cdev->private->imask);
+
/* Setup sense path group id channel program. */
ccw = cdev->private->iccws;
ccw->cmd_code = CCW_CMD_SENSE_PGID;
- ccw->cda = (__u32) __pa (&cdev->private->pgid);
ccw->count = sizeof (struct pgid);
ccw->flags = CCW_FLAG_SLI;
@@ -48,6 +68,7 @@ __ccw_device_sense_pgid_start(struct ccw_device *cdev)
ret = -ENODEV;
while (cdev->private->imask != 0) {
/* Try every path multiple times. */
+ ccw->cda = (__u32) __pa (&cdev->private->pgid[i]);
if (cdev->private->iretry > 0) {
cdev->private->iretry--;
ret = cio_start (sch, cdev->private->iccws,
@@ -64,7 +85,9 @@ __ccw_device_sense_pgid_start(struct ccw_device *cdev)
}
cdev->private->imask >>= 1;
cdev->private->iretry = 5;
+ i++;
}
+
return ret;
}
@@ -76,7 +99,7 @@ ccw_device_sense_pgid_start(struct ccw_device *cdev)
cdev->private->state = DEV_STATE_SENSE_PGID;
cdev->private->imask = 0x80;
cdev->private->iretry = 5;
- memset (&cdev->private->pgid, 0, sizeof (struct pgid));
+ memset (&cdev->private->pgid, 0, sizeof (cdev->private->pgid));
ret = __ccw_device_sense_pgid_start(cdev);
if (ret && ret != -EBUSY)
ccw_device_sense_pgid_done(cdev, ret);
@@ -91,6 +114,7 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev)
{
struct subchannel *sch;
struct irb *irb;
+ int i;
sch = to_subchannel(cdev->dev.parent);
irb = &cdev->private->irb;
@@ -124,7 +148,8 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev)
sch->schid.sch_no, sch->orb.lpm);
return -EACCES;
}
- if (cdev->private->pgid.inf.ps.state2 == SNID_STATE2_RESVD_ELSE) {
+ i = 8 - ffs(cdev->private->imask);
+ if (cdev->private->pgid[i].inf.ps.state2 == SNID_STATE2_RESVD_ELSE) {
CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x "
"is reserved by someone else\n",
cdev->private->devno, sch->schid.ssid,
@@ -145,10 +170,10 @@ ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event)
int ret;
irb = (struct irb *) __LC_IRB;
- /* Retry sense pgid for cc=1. */
+
if (irb->scsw.stctl ==
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
- if (irb->scsw.cc == 1) {
+ if (__ccw_device_should_retry(&irb->scsw)) {
ret = __ccw_device_sense_pgid_start(cdev);
if (ret && ret != -EBUSY)
ccw_device_sense_pgid_done(cdev, ret);
@@ -162,12 +187,6 @@ ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event)
memset(&cdev->private->irb, 0, sizeof(struct irb));
switch (ret) {
/* 0, -ETIME, -EOPNOTSUPP, -EAGAIN, -EACCES or -EUSERS */
- case 0: /* Sense Path Group ID successful. */
- if (cdev->private->pgid.inf.ps.state1 == SNID_STATE1_RESET)
- memcpy(&cdev->private->pgid, &css[0]->global_pgid,
- sizeof(struct pgid));
- ccw_device_sense_pgid_done(cdev, 0);
- break;
case -EOPNOTSUPP: /* Sense Path Group ID not supported */
ccw_device_sense_pgid_done(cdev, -EOPNOTSUPP);
break;
@@ -176,13 +195,15 @@ ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event)
break;
case -EACCES: /* channel is not operational. */
sch->lpm &= ~cdev->private->imask;
+ /* Fall through. */
+ case 0: /* Sense Path Group ID successful. */
cdev->private->imask >>= 1;
cdev->private->iretry = 5;
/* Fall through. */
case -EAGAIN: /* Try again. */
ret = __ccw_device_sense_pgid_start(cdev);
if (ret != 0 && ret != -EBUSY)
- ccw_device_sense_pgid_done(cdev, -ENODEV);
+ ccw_device_sense_pgid_done(cdev, ret);
break;
case -EUSERS: /* device is reserved for someone else. */
ccw_device_sense_pgid_done(cdev, -EUSERS);
@@ -203,20 +224,20 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
sch = to_subchannel(cdev->dev.parent);
/* Setup sense path group id channel program. */
- cdev->private->pgid.inf.fc = func;
+ cdev->private->pgid[0].inf.fc = func;
ccw = cdev->private->iccws;
if (!cdev->private->flags.pgid_single) {
- cdev->private->pgid.inf.fc |= SPID_FUNC_MULTI_PATH;
+ cdev->private->pgid[0].inf.fc |= SPID_FUNC_MULTI_PATH;
ccw->cmd_code = CCW_CMD_SUSPEND_RECONN;
ccw->cda = 0;
ccw->count = 0;
ccw->flags = CCW_FLAG_SLI | CCW_FLAG_CC;
ccw++;
} else
- cdev->private->pgid.inf.fc |= SPID_FUNC_SINGLE_PATH;
+ cdev->private->pgid[0].inf.fc |= SPID_FUNC_SINGLE_PATH;
ccw->cmd_code = CCW_CMD_SET_PGID;
- ccw->cda = (__u32) __pa (&cdev->private->pgid);
+ ccw->cda = (__u32) __pa (&cdev->private->pgid[0]);
ccw->count = sizeof (struct pgid);
ccw->flags = CCW_FLAG_SLI;
@@ -244,6 +265,48 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
}
/*
+ * Helper function to send a nop ccw down a path.
+ */
+static int __ccw_device_do_nop(struct ccw_device *cdev)
+{
+ struct subchannel *sch;
+ struct ccw1 *ccw;
+ int ret;
+
+ sch = to_subchannel(cdev->dev.parent);
+
+ /* Setup nop channel program. */
+ ccw = cdev->private->iccws;
+ ccw->cmd_code = CCW_CMD_NOOP;
+ ccw->cda = 0;
+ ccw->count = 0;
+ ccw->flags = CCW_FLAG_SLI;
+
+ /* Reset device status. */
+ memset(&cdev->private->irb, 0, sizeof(struct irb));
+
+ /* Try multiple times. */
+ ret = -ENODEV;
+ if (cdev->private->iretry > 0) {
+ cdev->private->iretry--;
+ ret = cio_start (sch, cdev->private->iccws,
+ cdev->private->imask);
+ /* ret is 0, -EBUSY, -EACCES or -ENODEV */
+ if ((ret != -EACCES) && (ret != -ENODEV))
+ return ret;
+ }
+ /* nop command failed on this path. Switch it off. */
+ sch->lpm &= ~cdev->private->imask;
+ sch->vpm &= ~cdev->private->imask;
+ CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel "
+ "0.%x.%04x, lpm %02X, became 'not operational'\n",
+ cdev->private->devno, sch->schid.ssid,
+ sch->schid.sch_no, cdev->private->imask);
+ return ret;
+}
+
+
+/*
* Called from interrupt context to check if a valid answer
* to Set Path Group ID was received.
*/
@@ -282,6 +345,29 @@ __ccw_device_check_pgid(struct ccw_device *cdev)
return 0;
}
+/*
+ * Called from interrupt context to check the path status after a nop has
+ * been send.
+ */
+static int __ccw_device_check_nop(struct ccw_device *cdev)
+{
+ struct subchannel *sch;
+ struct irb *irb;
+
+ sch = to_subchannel(cdev->dev.parent);
+ irb = &cdev->private->irb;
+ if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))
+ return -ETIME;
+ if (irb->scsw.cc == 3) {
+ CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel 0.%x.%04x,"
+ " lpm %02X, became 'not operational'\n",
+ cdev->private->devno, sch->schid.ssid,
+ sch->schid.sch_no, cdev->private->imask);
+ return -EACCES;
+ }
+ return 0;
+}
+
static void
__ccw_device_verify_start(struct ccw_device *cdev)
{
@@ -296,9 +382,12 @@ __ccw_device_verify_start(struct ccw_device *cdev)
if ((sch->vpm & imask) != (sch->lpm & imask))
break;
cdev->private->imask = imask;
- func = (sch->vpm & imask) ?
- SPID_FUNC_RESIGN : SPID_FUNC_ESTABLISH;
- ret = __ccw_device_do_pgid(cdev, func);
+ if (cdev->private->options.pgroup) {
+ func = (sch->vpm & imask) ?
+ SPID_FUNC_RESIGN : SPID_FUNC_ESTABLISH;
+ ret = __ccw_device_do_pgid(cdev, func);
+ } else
+ ret = __ccw_device_do_nop(cdev);
if (ret == 0 || ret == -EBUSY)
return;
cdev->private->iretry = 5;
@@ -317,17 +406,20 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
int ret;
irb = (struct irb *) __LC_IRB;
- /* Retry set pgid for cc=1. */
+
if (irb->scsw.stctl ==
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
- if (irb->scsw.cc == 1)
+ if (__ccw_device_should_retry(&irb->scsw))
__ccw_device_verify_start(cdev);
return;
}
if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
return;
sch = to_subchannel(cdev->dev.parent);
- ret = __ccw_device_check_pgid(cdev);
+ if (cdev->private->options.pgroup)
+ ret = __ccw_device_check_pgid(cdev);
+ else
+ ret = __ccw_device_check_nop(cdev);
memset(&cdev->private->irb, 0, sizeof(struct irb));
switch (ret) {
/* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
@@ -345,11 +437,10 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
* One of those strange devices which claim to be able
* to do multipathing but not for Set Path Group ID.
*/
- if (cdev->private->flags.pgid_single) {
- ccw_device_verify_done(cdev, -EOPNOTSUPP);
- break;
- }
- cdev->private->flags.pgid_single = 1;
+ if (cdev->private->flags.pgid_single)
+ cdev->private->options.pgroup = 0;
+ else
+ cdev->private->flags.pgid_single = 1;
/* fall through. */
case -EAGAIN: /* Try again. */
__ccw_device_verify_start(cdev);
@@ -418,10 +509,10 @@ ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event)
int ret;
irb = (struct irb *) __LC_IRB;
- /* Retry set pgid for cc=1. */
+
if (irb->scsw.stctl ==
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
- if (irb->scsw.cc == 1)
+ if (__ccw_device_should_retry(&irb->scsw))
__ccw_device_disband_start(cdev);
return;
}
diff --git a/drivers/s390/cio/device_status.c b/drivers/s390/cio/device_status.c
index 14bef2c179bf..caf148d5caad 100644
--- a/drivers/s390/cio/device_status.c
+++ b/drivers/s390/cio/device_status.c
@@ -67,8 +67,7 @@ ccw_device_path_notoper(struct ccw_device *cdev)
sch->schib.pmcw.pnom);
sch->lpm &= ~sch->schib.pmcw.pnom;
- if (cdev->private->options.pgroup)
- cdev->private->flags.doverify = 1;
+ cdev->private->flags.doverify = 1;
}
/*
@@ -180,7 +179,7 @@ ccw_device_accumulate_esw(struct ccw_device *cdev, struct irb *irb)
cdev_irb->esw.esw0.erw.auth = irb->esw.esw0.erw.auth;
/* Copy path verification required flag. */
cdev_irb->esw.esw0.erw.pvrf = irb->esw.esw0.erw.pvrf;
- if (irb->esw.esw0.erw.pvrf && cdev->private->options.pgroup)
+ if (irb->esw.esw0.erw.pvrf)
cdev->private->flags.doverify = 1;
/* Copy concurrent sense bit. */
cdev_irb->esw.esw0.erw.cons = irb->esw.esw0.erw.cons;
@@ -354,7 +353,7 @@ ccw_device_accumulate_basic_sense(struct ccw_device *cdev, struct irb *irb)
}
/* Check if path verification is required. */
if (ccw_device_accumulate_esw_valid(irb) &&
- irb->esw.esw0.erw.pvrf && cdev->private->options.pgroup)
+ irb->esw.esw0.erw.pvrf)
cdev->private->flags.doverify = 1;
}
diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c
index b70039af70d6..7c93a8798d23 100644
--- a/drivers/s390/cio/qdio.c
+++ b/drivers/s390/cio/qdio.c
@@ -2735,7 +2735,7 @@ qdio_free(struct ccw_device *cdev)
QDIO_DBF_TEXT1(0,trace,dbf_text);
QDIO_DBF_TEXT0(0,setup,dbf_text);
- cdev->private->qdio_data = 0;
+ cdev->private->qdio_data = NULL;
up(&irq_ptr->setting_up_sema);