wake_up(&cdev->private->wait_q);
}
-/*
- * Function called from device_pgid.c after sense path ground has completed.
- */
-void
-ccw_device_sense_pgid_done(struct ccw_device *cdev, int err)
-{
- struct subchannel *sch;
-
- sch = to_subchannel(cdev->dev.parent);
- switch (err) {
- case -EOPNOTSUPP: /* path grouping not supported, use nop instead. */
- case 0: /* success */
- case -EACCES: /* partial success, some paths not operational */
- 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);
- return;
- default:
- ccw_device_done(cdev, DEV_STATE_NOT_OPER);
- return;
- }
- /* Start Path Group verification. */
- cdev->private->state = DEV_STATE_VERIFY;
- ccw_device_verify_start(cdev);
-}
-
/*
* Start device recognition.
*/
}
break;
case -ETIME:
+ case -EUSERS:
/* Reset oper notify indication after verify error. */
cdev->private->flags.donotify = 0;
ccw_device_done(cdev, DEV_STATE_BOXED);
dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
return ret;
}
- /* Do we want to do path grouping? */
- if (!cdev->private->options.pgroup) {
- /* Start initial path verification. */
- cdev->private->state = DEV_STATE_VERIFY;
- ccw_device_verify_start(cdev);
- return 0;
- }
- /* Do a SensePGID first. */
- cdev->private->state = DEV_STATE_SENSE_PGID;
- ccw_device_sense_pgid_start(cdev);
+ /* Start initial path verification. */
+ cdev->private->state = DEV_STATE_VERIFY;
+ ccw_device_verify_start(cdev);
return 0;
}
struct ccw_request *req = &cdev->private->req;
u8 fn;
- /* Adjust lpm if paths are not set in pam. */
- req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam);
+ /* Use next available path that is not already in correct state. */
+ req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & ~sch->vpm);
if (!req->lpm)
goto out_nopath;
/* Channel program setup. */
verify_done(cdev, rc);
}
+static void spid_start(struct ccw_device *cdev)
+{
+ struct ccw_request *req = &cdev->private->req;
+
+ /* Initialize request data. */
+ memset(req, 0, sizeof(*req));
+ req->timeout = PGID_TIMEOUT;
+ req->maxretries = PGID_RETRIES;
+ req->lpm = 0x80;
+ req->callback = spid_callback;
+ spid_do(cdev);
+}
+
static int pgid_cmp(struct pgid *p1, struct pgid *p2)
{
return memcmp((char *) p1 + 1, (char *) p2 + 1,
*p = first;
}
+static u8 pgid_to_vpm(struct ccw_device *cdev)
+{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+ struct pgid *pgid;
+ int i;
+ int lpm;
+ u8 vpm = 0;
+
+ /* Set VPM 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)
+ continue;
+ pgid = &cdev->private->pgid[i];
+ if (sch->opm & lpm) {
+ if (pgid->inf.ps.state1 != SNID_STATE1_GROUPED)
+ continue;
+ } else {
+ if (pgid->inf.ps.state1 != SNID_STATE1_UNGROUPED)
+ continue;
+ }
+ if (cdev->private->flags.mpath) {
+ if (pgid->inf.ps.state3 != SNID_STATE3_MULTI_PATH)
+ continue;
+ } else {
+ if (pgid->inf.ps.state3 != SNID_STATE3_SINGLE_PATH)
+ continue;
+ }
+ vpm |= lpm;
+ }
+
+ return vpm;
+}
+
static void pgid_fill(struct ccw_device *cdev, struct pgid *pgid)
{
int i;
static void snid_done(struct ccw_device *cdev, int rc)
{
struct ccw_dev_id *id = &cdev->private->dev_id;
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
struct pgid *pgid;
int mismatch = 0;
int reserved = 0;
if (rc)
goto out;
pgid_analyze(cdev, &pgid, &mismatch, &reserved, &reset);
- if (!mismatch) {
- pgid_fill(cdev, pgid);
- cdev->private->flags.pgid_rdy = 1;
- }
if (reserved)
rc = -EUSERS;
+ else if (mismatch)
+ rc = -EOPNOTSUPP;
+ else {
+ sch->vpm = pgid_to_vpm(cdev);
+ pgid_fill(cdev, pgid);
+ }
out:
- CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x mism=%d "
- "rsvd=%d reset=%d\n", id->ssid, id->devno, rc,
- cdev->private->pgid_valid_mask, mismatch, reserved,
- reset);
- ccw_device_sense_pgid_done(cdev, rc);
+ 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);
+ switch (rc) {
+ case 0:
+ /* Anything left to do? */
+ if (sch->vpm == sch->schib.pmcw.pam) {
+ verify_done(cdev, sch->vpm == 0 ? -EACCES : 0);
+ return;
+ }
+ /* Perform path-grouping. */
+ spid_start(cdev);
+ break;
+ case -EOPNOTSUPP:
+ /* Path-grouping not supported. */
+ cdev->private->flags.pgroup = 0;
+ cdev->private->flags.mpath = 0;
+ verify_start(cdev);
+ break;
+ default:
+ verify_done(cdev, rc);
+ }
}
/*
snid_done(cdev, rc);
}
-/**
- * ccw_device_sense_pgid_start - perform SENSE PGID
- * @cdev: ccw device
- *
- * Execute a SENSE PGID channel program on each path to @cdev to update its
- * PGID information. When finished, call ccw_device_sense_id_done with a
- * return code specifying the result.
- */
-void ccw_device_sense_pgid_start(struct ccw_device *cdev)
-{
- struct ccw_request *req = &cdev->private->req;
-
- CIO_TRACE_EVENT(4, "snid");
- 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->flags.pgid_rdy = 0;
- cdev->private->pgid_valid_mask = 0;
- /* Initialize request data. */
- memset(req, 0, sizeof(*req));
- req->timeout = PGID_TIMEOUT;
- req->maxretries = PGID_RETRIES;
- req->callback = snid_callback;
- req->lpm = 0x80;
- snid_do(cdev);
-}
-
/*
* Perform path verification.
*/
{
struct subchannel *sch = to_subchannel(cdev->dev.parent);
struct ccw_request *req = &cdev->private->req;
+ struct ccw_dev_id *devid = &cdev->private->dev_id;
sch->vpm = 0;
/* Initialize request data. */
req->maxretries = PGID_RETRIES;
req->lpm = 0x80;
if (cdev->private->flags.pgroup) {
- req->callback = spid_callback;
- spid_do(cdev);
+ CIO_TRACE_EVENT(4, "snid");
+ CIO_HEX_EVENT(4, devid, sizeof(*devid));
+ req->callback = snid_callback;
+ snid_do(cdev);
} else {
+ CIO_TRACE_EVENT(4, "nop");
+ CIO_HEX_EVENT(4, devid, sizeof(*devid));
req->filter = nop_filter;
req->callback = nop_callback;
nop_do(cdev);
{
CIO_TRACE_EVENT(4, "vrfy");
CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id));
- if (!cdev->private->flags.pgid_rdy) {
- /* No pathgrouping possible. */
- cdev->private->flags.pgroup = 0;
- cdev->private->flags.mpath = 0;
- } else {
- /*
- * Initialize pathgroup and multipath state with target values.
- * They may change in the course of path verification.
- */
- cdev->private->flags.pgroup = cdev->private->options.pgroup;
- cdev->private->flags.mpath = cdev->private->options.mpath;
-
- }
+ /* Initialize PGID data. */
+ memset(cdev->private->pgid, 0, sizeof(cdev->private->pgid));
+ cdev->private->pgid_valid_mask = 0;
+ /*
+ * Initialize pathgroup and multipath state with target values.
+ * They may change in the course of path verification.
+ */
+ cdev->private->flags.pgroup = cdev->private->options.pgroup;
+ cdev->private->flags.mpath = cdev->private->options.mpath;
cdev->private->flags.doverify = 0;
verify_start(cdev);
}