Commit 6019886d authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] s390: common i/o layer

From: Martin Schwidefsky <schwidefsky@de.ibm.com>

Common i/o layer changes:
 - Delay unregister/register of ccw devices reappering on a different
   subchannel. Search for the old ccw_device & subchannel for the
   reattached device and deregister it too to avoid inconsistencies.
 - Fix path grouping for devices that present command reject for
   SetPGID but not for SensePGID.
parent 9cb51c16
/* /*
* drivers/s390/cio/css.c * drivers/s390/cio/css.c
* driver for channel subsystem * driver for channel subsystem
* $Revision: 1.73 $ * $Revision: 1.74 $
* *
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation * IBM Corporation
...@@ -218,12 +218,21 @@ css_evaluate_subchannel(int irq, int slow) ...@@ -218,12 +218,21 @@ css_evaluate_subchannel(int irq, int slow)
* We don't notify the driver since we have to throw the device * We don't notify the driver since we have to throw the device
* away in any case. * away in any case.
*/ */
if (!disc) {
device_unregister(&sch->dev); device_unregister(&sch->dev);
/* Reset intparm to zeroes. */ /* Reset intparm to zeroes. */
sch->schib.pmcw.intparm = 0; sch->schib.pmcw.intparm = 0;
cio_modify(sch); cio_modify(sch);
put_device(&sch->dev); put_device(&sch->dev);
ret = css_probe_device(irq); ret = css_probe_device(irq);
} else {
/*
* We can't immediately deregister the disconnected
* device since it might block.
*/
device_trigger_reprobe(sch);
ret = 0;
}
break; break;
case CIO_OPER: case CIO_OPER:
if (disc) if (disc)
......
/* /*
* drivers/s390/cio/device.c * drivers/s390/cio/device.c
* bus driver for ccw devices * bus driver for ccw devices
* $Revision: 1.115 $ * $Revision: 1.117 $
* *
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
* IBM Corporation * IBM Corporation
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "cio.h" #include "cio.h"
#include "css.h" #include "css.h"
#include "device.h" #include "device.h"
#include "ioasm.h"
/******************* bus type handling ***********************/ /******************* bus type handling ***********************/
...@@ -499,20 +500,93 @@ ccw_device_register(struct ccw_device *cdev) ...@@ -499,20 +500,93 @@ ccw_device_register(struct ccw_device *cdev)
return ret; return ret;
} }
void static struct ccw_device *
ccw_device_do_unreg_rereg(void *data) get_disc_ccwdev_by_devno(unsigned int devno, struct ccw_device *sibling)
{ {
struct ccw_device *cdev;
struct list_head *entry;
struct device *dev; struct device *dev;
dev = (struct device *)data; if (!get_bus(&ccw_bus_type))
device_remove_files(dev); return NULL;
device_del(dev); down_read(&ccw_bus_type.subsys.rwsem);
if (device_add(dev)) { cdev = NULL;
list_for_each(entry, &ccw_bus_type.devices.list) {
dev = get_device(container_of(entry,
struct device, bus_list));
if (!dev)
continue;
cdev = to_ccwdev(dev);
if ((cdev->private->state == DEV_STATE_DISCONNECTED) &&
(cdev->private->devno == devno) &&
(!strncmp(cdev->dev.bus_id, sibling->dev.bus_id, 4))) {
cdev->private->state = DEV_STATE_NOT_OPER;
break;
}
put_device(dev); put_device(dev);
cdev = NULL;
}
up_read(&ccw_bus_type.subsys.rwsem);
put_bus(&ccw_bus_type);
return cdev;
}
void
ccw_device_do_unreg_rereg(void *data)
{
struct ccw_device *cdev;
struct subchannel *sch;
int need_rename;
cdev = (struct ccw_device *)data;
sch = to_subchannel(cdev->dev.parent);
if (cdev->private->devno != sch->schib.pmcw.dev) {
/*
* The device number has changed. This is usually only when
* a device has been detached under VM and then re-appeared
* on another subchannel because of a different attachment
* order than before. Ideally, we should should just switch
* subchannels, but unfortunately, this is not possible with
* the current implementation.
* Instead, we search for the old subchannel for this device
* number and deregister so there are no collisions with the
* newly registered ccw_device.
* FIXME: Find another solution so the block layer doesn't
* get possibly sick...
*/
struct ccw_device *other_cdev;
need_rename = 1;
other_cdev = get_disc_ccwdev_by_devno(sch->schib.pmcw.dev,
cdev);
if (other_cdev) {
struct subchannel *other_sch;
other_sch = to_subchannel(other_cdev->dev.parent);
if (get_device(&other_sch->dev)) {
stsch(other_sch->irq, &other_sch->schib);
if (other_sch->schib.pmcw.dnv) {
other_sch->schib.pmcw.intparm = 0;
cio_modify(other_sch);
}
device_unregister(&other_sch->dev);
}
}
cdev->private->devno = sch->schib.pmcw.dev;
} else
need_rename = 0;
device_remove_files(&cdev->dev);
device_del(&cdev->dev);
if (need_rename)
snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.0.%04x",
sch->schib.pmcw.dev);
if (device_add(&cdev->dev)) {
put_device(&cdev->dev);
return; return;
} }
if (device_add_files(dev)) if (device_add_files(&cdev->dev))
device_unregister(dev); device_unregister(&cdev->dev);
} }
static void static void
......
...@@ -152,14 +152,15 @@ ccw_device_handle_oper(struct ccw_device *cdev) ...@@ -152,14 +152,15 @@ ccw_device_handle_oper(struct ccw_device *cdev)
/* /*
* Check if cu type and device type still match. If * Check if cu type and device type still match. If
* not, it is certainly another device and we have to * not, it is certainly another device and we have to
* de- and re-register. * de- and re-register. Also check here for non-matching devno.
*/ */
if (cdev->id.cu_type != cdev->private->senseid.cu_type || if (cdev->id.cu_type != cdev->private->senseid.cu_type ||
cdev->id.cu_model != cdev->private->senseid.cu_model || cdev->id.cu_model != cdev->private->senseid.cu_model ||
cdev->id.dev_type != cdev->private->senseid.dev_type || cdev->id.dev_type != cdev->private->senseid.dev_type ||
cdev->id.dev_model != cdev->private->senseid.dev_model) { cdev->id.dev_model != cdev->private->senseid.dev_model ||
cdev->private->devno != sch->schib.pmcw.dev) {
PREPARE_WORK(&cdev->private->kick_work, PREPARE_WORK(&cdev->private->kick_work,
ccw_device_do_unreg_rereg, (void *)&cdev->dev); ccw_device_do_unreg_rereg, (void *)cdev);
queue_work(ccw_device_work, &cdev->private->kick_work); queue_work(ccw_device_work, &cdev->private->kick_work);
return; return;
} }
...@@ -295,7 +296,7 @@ ccw_device_oper_notify(void *data) ...@@ -295,7 +296,7 @@ ccw_device_oper_notify(void *data)
sch->driver->notify(&sch->dev, CIO_OPER) : 0; sch->driver->notify(&sch->dev, CIO_OPER) : 0;
if (!ret) if (!ret)
/* Driver doesn't want device back. */ /* Driver doesn't want device back. */
ccw_device_do_unreg_rereg((void *)&cdev->dev); ccw_device_do_unreg_rereg((void *)cdev);
else else
wake_up(&cdev->private->wait_q); wake_up(&cdev->private->wait_q);
} }
...@@ -476,6 +477,8 @@ ccw_device_verify_done(struct ccw_device *cdev, int err) ...@@ -476,6 +477,8 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
{ {
cdev->private->flags.doverify = 0; cdev->private->flags.doverify = 0;
switch (err) { switch (err) {
case -EOPNOTSUPP: /* path grouping not supported, just set online. */
cdev->private->options.pgroup = 0;
case 0: case 0:
ccw_device_done(cdev, DEV_STATE_ONLINE); ccw_device_done(cdev, DEV_STATE_ONLINE);
break; break;
......
...@@ -338,6 +338,10 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event) ...@@ -338,6 +338,10 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
* One of those strange devices which claim to be able * One of those strange devices which claim to be able
* to do multipathing but not for Set Path Group ID. * 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; cdev->private->flags.pgid_single = 1;
/* fall through. */ /* fall through. */
case -EAGAIN: /* Try again. */ case -EAGAIN: /* Try again. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment