Commit d302e1a5 authored by Peter Oberparleiter's avatar Peter Oberparleiter Committed by Martin Schwidefsky

[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: default avatarPeter Oberparleiter <peter.oberparleiter@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 83e56d0b
...@@ -49,7 +49,6 @@ static u16 ccwreq_next_path(struct ccw_device *cdev) ...@@ -49,7 +49,6 @@ static u16 ccwreq_next_path(struct ccw_device *cdev)
*/ */
static void ccwreq_stop(struct ccw_device *cdev, int rc) 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; struct ccw_request *req = &cdev->private->req;
if (req->done) if (req->done)
...@@ -57,7 +56,6 @@ static void ccwreq_stop(struct ccw_device *cdev, int rc) ...@@ -57,7 +56,6 @@ static void ccwreq_stop(struct ccw_device *cdev, int rc)
req->done = 1; req->done = 1;
ccw_device_set_timeout(cdev, 0); ccw_device_set_timeout(cdev, 0);
memset(&cdev->private->irb, 0, sizeof(struct irb)); memset(&cdev->private->irb, 0, sizeof(struct irb));
sch->lpm = sch->schib.pmcw.pam;
if (rc && rc != -ENODEV && req->drc) if (rc && rc != -ENODEV && req->drc)
rc = req->drc; rc = req->drc;
req->callback(cdev, req->data, rc); req->callback(cdev, req->data, rc);
...@@ -80,7 +78,6 @@ static void ccwreq_do(struct ccw_device *cdev) ...@@ -80,7 +78,6 @@ static void ccwreq_do(struct ccw_device *cdev)
continue; continue;
} }
/* Perform start function. */ /* Perform start function. */
sch->lpm = 0xff;
memset(&cdev->private->irb, 0, sizeof(struct irb)); memset(&cdev->private->irb, 0, sizeof(struct irb));
rc = cio_start(sch, cp, (u8) req->mask); rc = cio_start(sch, cp, (u8) req->mask);
if (rc == 0) { if (rc == 0) {
......
...@@ -142,7 +142,7 @@ static void spid_do(struct ccw_device *cdev) ...@@ -142,7 +142,7 @@ static void spid_do(struct ccw_device *cdev)
u8 fn; u8 fn;
/* Use next available path that is not already in correct state. */ /* 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) if (!req->lpm)
goto out_nopath; goto out_nopath;
/* Channel program setup. */ /* Channel program setup. */
...@@ -254,15 +254,15 @@ static void pgid_analyze(struct ccw_device *cdev, struct pgid **p, ...@@ -254,15 +254,15 @@ static void pgid_analyze(struct ccw_device *cdev, struct pgid **p,
*p = first; *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 subchannel *sch = to_subchannel(cdev->dev.parent);
struct pgid *pgid; struct pgid *pgid;
int i; int i;
int lpm; 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++) { for (i = 0; i < 8; i++) {
lpm = 0x80 >> i; lpm = 0x80 >> i;
if ((cdev->private->pgid_valid_mask & lpm) == 0) if ((cdev->private->pgid_valid_mask & lpm) == 0)
...@@ -282,10 +282,10 @@ static u8 pgid_to_vpm(struct ccw_device *cdev) ...@@ -282,10 +282,10 @@ static u8 pgid_to_vpm(struct ccw_device *cdev)
if (pgid->inf.ps.state3 != SNID_STATE3_SINGLE_PATH) if (pgid->inf.ps.state3 != SNID_STATE3_SINGLE_PATH)
continue; continue;
} }
vpm |= lpm; donepm |= lpm;
} }
return vpm; return donepm;
} }
static void pgid_fill(struct ccw_device *cdev, struct pgid *pgid) 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) ...@@ -307,6 +307,7 @@ static void snid_done(struct ccw_device *cdev, int rc)
int mismatch = 0; int mismatch = 0;
int reserved = 0; int reserved = 0;
int reset = 0; int reset = 0;
u8 donepm;
if (rc) if (rc)
goto out; goto out;
...@@ -316,18 +317,20 @@ static void snid_done(struct ccw_device *cdev, int rc) ...@@ -316,18 +317,20 @@ static void snid_done(struct ccw_device *cdev, int rc)
else if (mismatch) else if (mismatch)
rc = -EOPNOTSUPP; rc = -EOPNOTSUPP;
else { 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); pgid_fill(cdev, pgid);
} }
out: out:
CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x vpm=%02x " 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, "todo=%02x mism=%d rsvd=%d reset=%d\n", id->ssid,
cdev->private->pgid_valid_mask, sch->vpm, mismatch, id->devno, rc, cdev->private->pgid_valid_mask, sch->vpm,
reserved, reset); cdev->private->pgid_todo_mask, mismatch, reserved, reset);
switch (rc) { switch (rc) {
case 0: case 0:
/* Anything left to do? */ /* 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); verify_done(cdev, sch->vpm == 0 ? -EACCES : 0);
return; return;
} }
...@@ -411,6 +414,7 @@ static void verify_start(struct ccw_device *cdev) ...@@ -411,6 +414,7 @@ static void verify_start(struct ccw_device *cdev)
struct ccw_dev_id *devid = &cdev->private->dev_id; struct ccw_dev_id *devid = &cdev->private->dev_id;
sch->vpm = 0; sch->vpm = 0;
sch->lpm = sch->schib.pmcw.pam;
/* Initialize request data. */ /* Initialize request data. */
memset(req, 0, sizeof(*req)); memset(req, 0, sizeof(*req));
req->timeout = PGID_TIMEOUT; req->timeout = PGID_TIMEOUT;
...@@ -442,11 +446,14 @@ static void verify_start(struct ccw_device *cdev) ...@@ -442,11 +446,14 @@ static void verify_start(struct ccw_device *cdev)
*/ */
void ccw_device_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_TRACE_EVENT(4, "vrfy");
CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id));
/* Initialize PGID data. */ /* Initialize PGID data. */
memset(cdev->private->pgid, 0, sizeof(cdev->private->pgid)); memset(cdev->private->pgid, 0, sizeof(cdev->private->pgid));
cdev->private->pgid_valid_mask = 0; cdev->private->pgid_valid_mask = 0;
cdev->private->pgid_todo_mask = sch->schib.pmcw.pam;
/* /*
* Initialize pathgroup and multipath state with target values. * Initialize pathgroup and multipath state with target values.
* They may change in the course of path verification. * They may change in the course of path verification.
......
...@@ -150,6 +150,7 @@ struct ccw_device_private { ...@@ -150,6 +150,7 @@ struct ccw_device_private {
struct ccw_request req; /* internal I/O request */ struct ccw_request req; /* internal I/O request */
int iretry; int iretry;
u8 pgid_valid_mask; /* mask of valid PGIDs */ u8 pgid_valid_mask; /* mask of valid PGIDs */
u8 pgid_todo_mask; /* mask of PGIDs to be adjusted */
struct { struct {
unsigned int fast:1; /* post with "channel end" */ unsigned int fast:1; /* post with "channel end" */
unsigned int repall:1; /* report every interrupt status */ unsigned int repall:1; /* report every interrupt status */
......
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