Skip to content

Commit

Permalink
[S390] cio: fix channel path vary
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Peter Oberparleiter authored and Martin Schwidefsky committed Dec 18, 2009
1 parent 83e56d0 commit d302e1a
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 14 deletions.
3 changes: 0 additions & 3 deletions drivers/s390/cio/ccwreq.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,13 @@ 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)
return;
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);
Expand All @@ -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) {
Expand Down
29 changes: 18 additions & 11 deletions drivers/s390/cio/device_pgid.c
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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;
Expand All @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions drivers/s390/cio/io_sch.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down

0 comments on commit d302e1a

Please sign in to comment.