mirror of
https://github.com/torvalds/linux.git
synced 2024-11-27 22:51:35 +00:00
[S390] cio: fix channel path vary
Channel path vary is currently broken: channel paths which are varied offline are still used by Linux. The reason for this is that: * the path mask indicating which paths of an I/O device can be used is reset by each internal I/O request * the logic that checks if a path group is already in its designated target state incorrectly interprets the result "is correctly set" as "is correctly set and available" Fix this by resetting the path mask only for internal I/O requests which affect the path mask and by correcting the pgid check logic. Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
parent
83e56d0b23
commit
d302e1a5db
@ -49,7 +49,6 @@ static u16 ccwreq_next_path(struct ccw_device *cdev)
|
||||
*/
|
||||
static void ccwreq_stop(struct ccw_device *cdev, int rc)
|
||||
{
|
||||
struct subchannel *sch = to_subchannel(cdev->dev.parent);
|
||||
struct ccw_request *req = &cdev->private->req;
|
||||
|
||||
if (req->done)
|
||||
@ -57,7 +56,6 @@ static void ccwreq_stop(struct ccw_device *cdev, int rc)
|
||||
req->done = 1;
|
||||
ccw_device_set_timeout(cdev, 0);
|
||||
memset(&cdev->private->irb, 0, sizeof(struct irb));
|
||||
sch->lpm = sch->schib.pmcw.pam;
|
||||
if (rc && rc != -ENODEV && req->drc)
|
||||
rc = req->drc;
|
||||
req->callback(cdev, req->data, rc);
|
||||
@ -80,7 +78,6 @@ static void ccwreq_do(struct ccw_device *cdev)
|
||||
continue;
|
||||
}
|
||||
/* Perform start function. */
|
||||
sch->lpm = 0xff;
|
||||
memset(&cdev->private->irb, 0, sizeof(struct irb));
|
||||
rc = cio_start(sch, cp, (u8) req->mask);
|
||||
if (rc == 0) {
|
||||
|
@ -142,7 +142,7 @@ static void spid_do(struct ccw_device *cdev)
|
||||
u8 fn;
|
||||
|
||||
/* Use next available path that is not already in correct state. */
|
||||
req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & ~sch->vpm);
|
||||
req->lpm = lpm_adjust(req->lpm, cdev->private->pgid_todo_mask);
|
||||
if (!req->lpm)
|
||||
goto out_nopath;
|
||||
/* Channel program setup. */
|
||||
@ -254,15 +254,15 @@ static void pgid_analyze(struct ccw_device *cdev, struct pgid **p,
|
||||
*p = first;
|
||||
}
|
||||
|
||||
static u8 pgid_to_vpm(struct ccw_device *cdev)
|
||||
static u8 pgid_to_donepm(struct ccw_device *cdev)
|
||||
{
|
||||
struct subchannel *sch = to_subchannel(cdev->dev.parent);
|
||||
struct pgid *pgid;
|
||||
int i;
|
||||
int lpm;
|
||||
u8 vpm = 0;
|
||||
u8 donepm = 0;
|
||||
|
||||
/* Set VPM bits for paths which are already in the target state. */
|
||||
/* Set bits for paths which are already in the target state. */
|
||||
for (i = 0; i < 8; i++) {
|
||||
lpm = 0x80 >> i;
|
||||
if ((cdev->private->pgid_valid_mask & lpm) == 0)
|
||||
@ -282,10 +282,10 @@ static u8 pgid_to_vpm(struct ccw_device *cdev)
|
||||
if (pgid->inf.ps.state3 != SNID_STATE3_SINGLE_PATH)
|
||||
continue;
|
||||
}
|
||||
vpm |= lpm;
|
||||
donepm |= lpm;
|
||||
}
|
||||
|
||||
return vpm;
|
||||
return donepm;
|
||||
}
|
||||
|
||||
static void pgid_fill(struct ccw_device *cdev, struct pgid *pgid)
|
||||
@ -307,6 +307,7 @@ static void snid_done(struct ccw_device *cdev, int rc)
|
||||
int mismatch = 0;
|
||||
int reserved = 0;
|
||||
int reset = 0;
|
||||
u8 donepm;
|
||||
|
||||
if (rc)
|
||||
goto out;
|
||||
@ -316,18 +317,20 @@ static void snid_done(struct ccw_device *cdev, int rc)
|
||||
else if (mismatch)
|
||||
rc = -EOPNOTSUPP;
|
||||
else {
|
||||
sch->vpm = pgid_to_vpm(cdev);
|
||||
donepm = pgid_to_donepm(cdev);
|
||||
sch->vpm = donepm & sch->opm;
|
||||
cdev->private->pgid_todo_mask &= ~donepm;
|
||||
pgid_fill(cdev, pgid);
|
||||
}
|
||||
out:
|
||||
CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x vpm=%02x "
|
||||
"mism=%d rsvd=%d reset=%d\n", id->ssid, id->devno, rc,
|
||||
cdev->private->pgid_valid_mask, sch->vpm, mismatch,
|
||||
reserved, reset);
|
||||
"todo=%02x mism=%d rsvd=%d reset=%d\n", id->ssid,
|
||||
id->devno, rc, cdev->private->pgid_valid_mask, sch->vpm,
|
||||
cdev->private->pgid_todo_mask, mismatch, reserved, reset);
|
||||
switch (rc) {
|
||||
case 0:
|
||||
/* Anything left to do? */
|
||||
if (sch->vpm == sch->schib.pmcw.pam) {
|
||||
if (cdev->private->pgid_todo_mask == 0) {
|
||||
verify_done(cdev, sch->vpm == 0 ? -EACCES : 0);
|
||||
return;
|
||||
}
|
||||
@ -411,6 +414,7 @@ static void verify_start(struct ccw_device *cdev)
|
||||
struct ccw_dev_id *devid = &cdev->private->dev_id;
|
||||
|
||||
sch->vpm = 0;
|
||||
sch->lpm = sch->schib.pmcw.pam;
|
||||
/* Initialize request data. */
|
||||
memset(req, 0, sizeof(*req));
|
||||
req->timeout = PGID_TIMEOUT;
|
||||
@ -442,11 +446,14 @@ static void verify_start(struct ccw_device *cdev)
|
||||
*/
|
||||
void ccw_device_verify_start(struct ccw_device *cdev)
|
||||
{
|
||||
struct subchannel *sch = to_subchannel(cdev->dev.parent);
|
||||
|
||||
CIO_TRACE_EVENT(4, "vrfy");
|
||||
CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id));
|
||||
/* Initialize PGID data. */
|
||||
memset(cdev->private->pgid, 0, sizeof(cdev->private->pgid));
|
||||
cdev->private->pgid_valid_mask = 0;
|
||||
cdev->private->pgid_todo_mask = sch->schib.pmcw.pam;
|
||||
/*
|
||||
* Initialize pathgroup and multipath state with target values.
|
||||
* They may change in the course of path verification.
|
||||
|
@ -150,6 +150,7 @@ struct ccw_device_private {
|
||||
struct ccw_request req; /* internal I/O request */
|
||||
int iretry;
|
||||
u8 pgid_valid_mask; /* mask of valid PGIDs */
|
||||
u8 pgid_todo_mask; /* mask of PGIDs to be adjusted */
|
||||
struct {
|
||||
unsigned int fast:1; /* post with "channel end" */
|
||||
unsigned int repall:1; /* report every interrupt status */
|
||||
|
Loading…
Reference in New Issue
Block a user