From 7c4d964fa4e857d6fb6b63159a898a5c63c173bf Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:22 +0100 Subject: [S390] cio: handle error during path verification consistently Handle verification errors consistently through the existing callback ccw_device_done to reduce cleanup code duplication. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device_pgid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/s390/cio/device_pgid.c') diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index fc5ca1dd52b3..cb27bd4cc231 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -423,7 +423,7 @@ __ccw_device_verify_start(struct ccw_device *cdev) /* Permanent path failure, try next. */ } /* Done with all paths. */ - ccw_device_verify_done(cdev, (sch->vpm != 0) ? 0 : -ENODEV); + ccw_device_verify_done(cdev, (sch->vpm != 0) ? 0 : -EACCES); } /* -- cgit v1.2.2 From 9679baaf85b6e4dc662160bbbca344287ea6580d Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:27 +0100 Subject: [S390] cio: use ccw request infrastructure for pgid Use the newly introduced ccw request infrastructure to implement pgid related operations: sense pgid, set pgid and disband pg. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device_pgid.c | 876 +++++++++++++++++------------------------ 1 file changed, 371 insertions(+), 505 deletions(-) (limited to 'drivers/s390/cio/device_pgid.c') diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index cb27bd4cc231..ce493144b054 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -1,594 +1,460 @@ /* - * drivers/s390/cio/device_pgid.c + * CCW device PGID and path verification I/O handling. * - * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, - * IBM Corporation - * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com) - * Martin Schwidefsky (schwidefsky@de.ibm.com) - * - * Path Group ID functions. + * Copyright IBM Corp. 2002,2009 + * Author(s): Cornelia Huck + * Martin Schwidefsky + * Peter Oberparleiter */ -#include -#include - +#include +#include +#include +#include +#include #include #include -#include -#include #include "cio.h" #include "cio_debug.h" -#include "css.h" #include "device.h" -#include "ioasm.h" #include "io_sch.h" +#define PGID_RETRIES 5 +#define PGID_TIMEOUT (10 * HZ) + /* - * Helper function called from interrupt context to decide whether an - * operation should be tried again. + * Process path verification data and report result. */ -static int __ccw_device_should_retry(union scsw *scsw) +static void verify_done(struct ccw_device *cdev, int rc) { - /* CC is only valid if start function bit is set. */ - if ((scsw->cmd.fctl & SCSW_FCTL_START_FUNC) && scsw->cmd.cc == 1) - return 1; - /* No more activity. For sense and set PGID we stubbornly try again. */ - if (!scsw->cmd.actl) - return 1; - return 0; + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_dev_id *id = &cdev->private->dev_id; + int mpath = !cdev->private->flags.pgid_single; + int pgroup = cdev->private->options.pgroup; + + if (rc) + goto out; + /* Ensure consistent multipathing state at device and channel. */ + if (sch->config.mp != mpath) { + sch->config.mp = mpath; + rc = cio_commit_config(sch); + } +out: + CIO_MSG_EVENT(2, "vrfy: device 0.%x.%04x: rc=%d pgroup=%d mpath=%d " + "vpm=%02x\n", id->ssid, id->devno, rc, pgroup, mpath, + sch->vpm); + ccw_device_verify_done(cdev, rc); } /* - * Start Sense Path Group ID helper function. Used in ccw_device_recog - * and ccw_device_sense_pgid. + * Create channel program to perform a NOOP. */ -static int -__ccw_device_sense_pgid_start(struct ccw_device *cdev) +static void nop_build_cp(struct ccw_device *cdev) { - struct subchannel *sch; - struct ccw1 *ccw; - int ret; - int i; - - sch = to_subchannel(cdev->dev.parent); - /* Return if we already checked on all paths. */ - if (cdev->private->imask == 0) - return (sch->lpm == 0) ? -ENODEV : -EACCES; - i = 8 - ffs(cdev->private->imask); - - /* Setup sense path group id channel program. */ - ccw = cdev->private->iccws; - ccw->cmd_code = CCW_CMD_SENSE_PGID; - ccw->count = sizeof (struct pgid); - ccw->flags = CCW_FLAG_SLI; - - /* Reset device status. */ - memset(&cdev->private->irb, 0, sizeof(struct irb)); - /* Try on every path. */ - ret = -ENODEV; - while (cdev->private->imask != 0) { - /* Try every path multiple times. */ - ccw->cda = (__u32) __pa (&cdev->private->pgid[i]); - if (cdev->private->iretry > 0) { - cdev->private->iretry--; - /* Reset internal retry indication. */ - cdev->private->flags.intretry = 0; - ret = cio_start (sch, cdev->private->iccws, - cdev->private->imask); - /* ret is 0, -EBUSY, -EACCES or -ENODEV */ - if (ret != -EACCES) - return ret; - CIO_MSG_EVENT(3, "SNID - Device %04x on Subchannel " - "0.%x.%04x, lpm %02X, became 'not " - "operational'\n", - cdev->private->dev_id.devno, - sch->schid.ssid, - sch->schid.sch_no, cdev->private->imask); - - } - cdev->private->imask >>= 1; - cdev->private->iretry = 5; - i++; - } - - return ret; + struct ccw_request *req = &cdev->private->req; + struct ccw1 *cp = cdev->private->iccws; + + cp->cmd_code = CCW_CMD_NOOP; + cp->cda = 0; + cp->count = 0; + cp->flags = CCW_FLAG_SLI; + req->cp = cp; } -void -ccw_device_sense_pgid_start(struct ccw_device *cdev) +/* + * Perform NOOP on a single path. + */ +static void nop_do(struct ccw_device *cdev) { - int ret; - - /* Set a timeout of 60s */ - ccw_device_set_timeout(cdev, 60*HZ); - - cdev->private->state = DEV_STATE_SENSE_PGID; - cdev->private->imask = 0x80; - cdev->private->iretry = 5; - memset (&cdev->private->pgid, 0, sizeof (cdev->private->pgid)); - ret = __ccw_device_sense_pgid_start(cdev); - if (ret && ret != -EBUSY) - ccw_device_sense_pgid_done(cdev, ret); + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + + /* Adjust lpm. */ + req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & sch->opm); + if (!req->lpm) + goto out_nopath; + nop_build_cp(cdev); + ccw_request_start(cdev); + return; + +out_nopath: + verify_done(cdev, sch->vpm ? 0 : -EACCES); } /* - * Called from interrupt context to check if a valid answer - * to Sense Path Group ID was received. + * Adjust NOOP I/O status. */ -static int -__ccw_device_check_sense_pgid(struct ccw_device *cdev) +static enum io_status nop_filter(struct ccw_device *cdev, void *data, + struct irb *irb, enum io_status status) { - struct subchannel *sch; - struct irb *irb; - int i; - - sch = to_subchannel(cdev->dev.parent); - irb = &cdev->private->irb; - if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { - /* Retry Sense PGID if requested. */ - if (cdev->private->flags.intretry) { - cdev->private->flags.intretry = 0; - return -EAGAIN; - } - return -ETIME; - } - if (irb->esw.esw0.erw.cons && - (irb->ecw[0]&(SNS0_CMD_REJECT|SNS0_INTERVENTION_REQ))) { - /* - * If the device doesn't support the Sense Path Group ID - * command further retries wouldn't help ... - */ - return -EOPNOTSUPP; - } - if (irb->esw.esw0.erw.cons) { - CIO_MSG_EVENT(2, "SNID - device 0.%x.%04x, unit check, " - "lpum %02X, cnt %02d, sns : " - "%02X%02X%02X%02X %02X%02X%02X%02X ...\n", - cdev->private->dev_id.ssid, - cdev->private->dev_id.devno, - irb->esw.esw0.sublog.lpum, - irb->esw.esw0.erw.scnt, - irb->ecw[0], irb->ecw[1], - irb->ecw[2], irb->ecw[3], - irb->ecw[4], irb->ecw[5], - irb->ecw[6], irb->ecw[7]); - return -EAGAIN; - } - if (irb->scsw.cmd.cc == 3) { - u8 lpm; - - lpm = to_io_private(sch)->orb.cmd.lpm; - CIO_MSG_EVENT(3, "SNID - Device %04x on Subchannel 0.%x.%04x," - " lpm %02X, became 'not operational'\n", - cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no, lpm); - return -EACCES; - } - i = 8 - ffs(cdev->private->imask); - if (cdev->private->pgid[i].inf.ps.state2 == SNID_STATE2_RESVD_ELSE) { - CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x " - "is reserved by someone else\n", - cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no); - return -EUSERS; - } - return 0; + /* Only subchannel status might indicate a path error. */ + if (status == IO_STATUS_ERROR && irb->scsw.cmd.cstat == 0) + return IO_DONE; + return status; } /* - * Got interrupt for Sense Path Group ID. + * Process NOOP request result for a single path. */ -void -ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event) +static void nop_callback(struct ccw_device *cdev, void *data, int rc) { - struct subchannel *sch; - struct irb *irb; - int ret; - - irb = (struct irb *) __LC_IRB; - - if (irb->scsw.cmd.stctl == - (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { - if (__ccw_device_should_retry(&irb->scsw)) { - ret = __ccw_device_sense_pgid_start(cdev); - if (ret && ret != -EBUSY) - ccw_device_sense_pgid_done(cdev, ret); - } - return; - } - if (ccw_device_accumulate_and_sense(cdev, irb) != 0) - return; - sch = to_subchannel(cdev->dev.parent); - ret = __ccw_device_check_sense_pgid(cdev); - memset(&cdev->private->irb, 0, sizeof(struct irb)); - switch (ret) { - /* 0, -ETIME, -EOPNOTSUPP, -EAGAIN, -EACCES or -EUSERS */ - case -EOPNOTSUPP: /* Sense Path Group ID not supported */ - ccw_device_sense_pgid_done(cdev, -EOPNOTSUPP); - break; - case -ETIME: /* Sense path group id stopped by timeout. */ - ccw_device_sense_pgid_done(cdev, -ETIME); - break; - case -EACCES: /* channel is not operational. */ - sch->lpm &= ~cdev->private->imask; - /* Fall through. */ - case 0: /* Sense Path Group ID successful. */ - cdev->private->imask >>= 1; - cdev->private->iretry = 5; - /* Fall through. */ - case -EAGAIN: /* Try again. */ - ret = __ccw_device_sense_pgid_start(cdev); - if (ret != 0 && ret != -EBUSY) - ccw_device_sense_pgid_done(cdev, ret); - break; - case -EUSERS: /* device is reserved for someone else. */ - ccw_device_sense_pgid_done(cdev, -EUSERS); - break; - } + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + + if (rc == 0) + sch->vpm |= req->lpm; + else if (rc != -EACCES) + goto err; + req->lpm >>= 1; + nop_do(cdev); + return; + +err: + verify_done(cdev, rc); } /* - * Path Group ID helper function. + * Create channel program to perform SET PGID on a single path. */ -static int -__ccw_device_do_pgid(struct ccw_device *cdev, __u8 func) +static void spid_build_cp(struct ccw_device *cdev, u8 fn) { - struct subchannel *sch; - struct ccw1 *ccw; - int ret; - - sch = to_subchannel(cdev->dev.parent); - - /* Setup sense path group id channel program. */ - cdev->private->pgid[0].inf.fc = func; - ccw = cdev->private->iccws; - if (cdev->private->flags.pgid_single) - cdev->private->pgid[0].inf.fc |= SPID_FUNC_SINGLE_PATH; - else - cdev->private->pgid[0].inf.fc |= SPID_FUNC_MULTI_PATH; - ccw->cmd_code = CCW_CMD_SET_PGID; - ccw->cda = (__u32) __pa (&cdev->private->pgid[0]); - ccw->count = sizeof (struct pgid); - ccw->flags = CCW_FLAG_SLI; - - /* Reset device status. */ - memset(&cdev->private->irb, 0, sizeof(struct irb)); - - /* Try multiple times. */ - ret = -EACCES; - if (cdev->private->iretry > 0) { - cdev->private->iretry--; - /* Reset internal retry indication. */ - cdev->private->flags.intretry = 0; - ret = cio_start (sch, cdev->private->iccws, - cdev->private->imask); - /* We expect an interrupt in case of success or busy - * indication. */ - if ((ret == 0) || (ret == -EBUSY)) - return ret; - } - /* PGID command failed on this path. */ - CIO_MSG_EVENT(3, "SPID - Device %04x on Subchannel " - "0.%x.%04x, lpm %02X, became 'not operational'\n", - cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no, cdev->private->imask); - return ret; + struct ccw_request *req = &cdev->private->req; + struct ccw1 *cp = cdev->private->iccws; + int i = 8 - ffs(req->lpm); + struct pgid *pgid = &cdev->private->pgid[i]; + + pgid->inf.fc = fn; + cp->cmd_code = CCW_CMD_SET_PGID; + cp->cda = (u32) (addr_t) pgid; + cp->count = sizeof(*pgid); + cp->flags = CCW_FLAG_SLI; + req->cp = cp; } /* - * Helper function to send a nop ccw down a path. + * Perform establish/resign SET PGID on a single path. */ -static int __ccw_device_do_nop(struct ccw_device *cdev) +static void spid_do(struct ccw_device *cdev) { - struct subchannel *sch; - struct ccw1 *ccw; - int ret; - - sch = to_subchannel(cdev->dev.parent); - - /* Setup nop channel program. */ - ccw = cdev->private->iccws; - ccw->cmd_code = CCW_CMD_NOOP; - ccw->cda = 0; - ccw->count = 0; - ccw->flags = CCW_FLAG_SLI; - - /* Reset device status. */ - memset(&cdev->private->irb, 0, sizeof(struct irb)); - - /* Try multiple times. */ - ret = -EACCES; - if (cdev->private->iretry > 0) { - cdev->private->iretry--; - /* Reset internal retry indication. */ - cdev->private->flags.intretry = 0; - ret = cio_start (sch, cdev->private->iccws, - cdev->private->imask); - /* We expect an interrupt in case of success or busy - * indication. */ - if ((ret == 0) || (ret == -EBUSY)) - return ret; - } - /* nop command failed on this path. */ - CIO_MSG_EVENT(3, "NOP - Device %04x on Subchannel " - "0.%x.%04x, lpm %02X, became 'not operational'\n", - cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no, cdev->private->imask); - return ret; + struct subchannel *sch = to_subchannel(cdev->dev.parent); + 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); + if (!req->lpm) + goto out_nopath; + /* Channel program setup. */ + if (req->lpm & sch->opm) + fn = SPID_FUNC_ESTABLISH; + else + fn = SPID_FUNC_RESIGN; + if (!cdev->private->flags.pgid_single) + fn |= SPID_FUNC_MULTI_PATH; + spid_build_cp(cdev, fn); + ccw_request_start(cdev); + return; + +out_nopath: + verify_done(cdev, sch->vpm ? 0 : -EACCES); } +static void verify_start(struct ccw_device *cdev); /* - * Called from interrupt context to check if a valid answer - * to Set Path Group ID was received. + * Process SET PGID request result for a single path. */ -static int -__ccw_device_check_pgid(struct ccw_device *cdev) +static void spid_callback(struct ccw_device *cdev, void *data, int rc) { - struct subchannel *sch; - struct irb *irb; - - sch = to_subchannel(cdev->dev.parent); - irb = &cdev->private->irb; - if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { - /* Retry Set PGID if requested. */ - if (cdev->private->flags.intretry) { - cdev->private->flags.intretry = 0; - return -EAGAIN; + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + + switch (rc) { + case 0: + sch->vpm |= req->lpm & sch->opm; + break; + case -EACCES: + break; + case -EOPNOTSUPP: + if (!cdev->private->flags.pgid_single) { + /* Try without multipathing. */ + cdev->private->flags.pgid_single = 1; + goto out_restart; } - return -ETIME; - } - if (irb->esw.esw0.erw.cons) { - if (irb->ecw[0] & SNS0_CMD_REJECT) - return -EOPNOTSUPP; - /* Hmm, whatever happened, try again. */ - CIO_MSG_EVENT(2, "SPID - device 0.%x.%04x, unit check, " - "cnt %02d, " - "sns : %02X%02X%02X%02X %02X%02X%02X%02X ...\n", - cdev->private->dev_id.ssid, - cdev->private->dev_id.devno, - irb->esw.esw0.erw.scnt, - irb->ecw[0], irb->ecw[1], - irb->ecw[2], irb->ecw[3], - irb->ecw[4], irb->ecw[5], - irb->ecw[6], irb->ecw[7]); - return -EAGAIN; - } - if (irb->scsw.cmd.cc == 3) { - CIO_MSG_EVENT(3, "SPID - Device %04x on Subchannel 0.%x.%04x," - " lpm %02X, became 'not operational'\n", - cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no, cdev->private->imask); - return -EACCES; + /* Try without pathgrouping. */ + cdev->private->options.pgroup = 0; + goto out_restart; + default: + goto err; } - return 0; + req->lpm >>= 1; + spid_do(cdev); + return; + +out_restart: + verify_start(cdev); + return; +err: + verify_done(cdev, rc); +} + +static int pgid_cmp(struct pgid *p1, struct pgid *p2) +{ + return memcmp((char *) p1 + 1, (char *) p2 + 1, + sizeof(struct pgid) - 1); } /* - * Called from interrupt context to check the path status after a nop has - * been send. + * Determine pathgroup state from PGID data. */ -static int __ccw_device_check_nop(struct ccw_device *cdev) +static void pgid_analyze(struct ccw_device *cdev, struct pgid **p, + int *mismatch, int *reserved, int *reset) { - struct subchannel *sch; - struct irb *irb; - - sch = to_subchannel(cdev->dev.parent); - irb = &cdev->private->irb; - if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { - /* Retry NOP if requested. */ - if (cdev->private->flags.intretry) { - cdev->private->flags.intretry = 0; - return -EAGAIN; + struct pgid *pgid = &cdev->private->pgid[0]; + struct pgid *first = NULL; + int lpm; + int i; + + *mismatch = 0; + *reserved = 0; + *reset = 0; + for (i = 0, lpm = 0x80; i < 8; i++, pgid++, lpm >>= 1) { + if ((cdev->private->pgid_valid_mask & lpm) == 0) + continue; + if (pgid->inf.ps.state2 == SNID_STATE2_RESVD_ELSE) + *reserved = 1; + if (pgid->inf.ps.state1 == SNID_STATE1_RESET) { + /* A PGID was reset. */ + *reset = 1; + continue; } - return -ETIME; - } - if (irb->scsw.cmd.cc == 3) { - CIO_MSG_EVENT(3, "NOP - Device %04x on Subchannel 0.%x.%04x," - " lpm %02X, became 'not operational'\n", - cdev->private->dev_id.devno, sch->schid.ssid, - sch->schid.sch_no, cdev->private->imask); - return -EACCES; + if (!first) { + first = pgid; + continue; + } + if (pgid_cmp(pgid, first) != 0) + *mismatch = 1; } - return 0; + if (!first) + first = &channel_subsystems[0]->global_pgid; + *p = first; } -static void -__ccw_device_verify_start(struct ccw_device *cdev) +static void pgid_fill(struct ccw_device *cdev, struct pgid *pgid) { - struct subchannel *sch; - __u8 func; - int ret; - - sch = to_subchannel(cdev->dev.parent); - /* Repeat for all paths. */ - for (; cdev->private->imask; cdev->private->imask >>= 1, - cdev->private->iretry = 5) { - if ((cdev->private->imask & sch->schib.pmcw.pam) == 0) - /* Path not available, try next. */ - continue; - if (cdev->private->options.pgroup) { - if (sch->opm & cdev->private->imask) - func = SPID_FUNC_ESTABLISH; - else - func = SPID_FUNC_RESIGN; - ret = __ccw_device_do_pgid(cdev, func); - } else - ret = __ccw_device_do_nop(cdev); - /* We expect an interrupt in case of success or busy - * indication. */ - if (ret == 0 || ret == -EBUSY) - return; - /* Permanent path failure, try next. */ + int i; + + for (i = 0; i < 8; i++) + memcpy(&cdev->private->pgid[i], pgid, sizeof(struct pgid)); +} + +/* + * Process SENSE PGID data and report result. + */ +static void snid_done(struct ccw_device *cdev, int rc) +{ + struct ccw_dev_id *id = &cdev->private->dev_id; + struct pgid *pgid; + int mismatch = 0; + int reserved = 0; + int reset = 0; + + if (rc) + goto out; + pgid_analyze(cdev, &pgid, &mismatch, &reserved, &reset); + if (!mismatch) { + pgid_fill(cdev, pgid); + cdev->private->flags.pgid_rdy = 1; } - /* Done with all paths. */ - ccw_device_verify_done(cdev, (sch->vpm != 0) ? 0 : -EACCES); + if (reserved) + rc = -EUSERS; +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); } - + /* - * Got interrupt for Set Path Group ID. + * Create channel program to perform a SENSE PGID on a single path. */ -void -ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event) +static void snid_build_cp(struct ccw_device *cdev) { - struct subchannel *sch; - struct irb *irb; - int ret; + struct ccw_request *req = &cdev->private->req; + struct ccw1 *cp = cdev->private->iccws; + int i = 8 - ffs(req->lpm); + + /* Channel program setup. */ + cp->cmd_code = CCW_CMD_SENSE_PGID; + cp->cda = (u32) (addr_t) &cdev->private->pgid[i]; + cp->count = sizeof(struct pgid); + cp->flags = CCW_FLAG_SLI; + req->cp = cp; +} - irb = (struct irb *) __LC_IRB; +/* + * Perform SENSE PGID on a single path. + */ +static void snid_do(struct ccw_device *cdev) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + + /* Adjust lpm if paths are not set in pam. */ + req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam); + if (!req->lpm) + goto out_nopath; + snid_build_cp(cdev); + ccw_request_start(cdev); + return; + +out_nopath: + snid_done(cdev, cdev->private->pgid_valid_mask ? 0 : -EACCES); +} - if (irb->scsw.cmd.stctl == - (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { - if (__ccw_device_should_retry(&irb->scsw)) - __ccw_device_verify_start(cdev); - return; - } - if (ccw_device_accumulate_and_sense(cdev, irb) != 0) - return; - sch = to_subchannel(cdev->dev.parent); - if (cdev->private->options.pgroup) - ret = __ccw_device_check_pgid(cdev); - else - ret = __ccw_device_check_nop(cdev); - memset(&cdev->private->irb, 0, sizeof(struct irb)); +/* + * Process SENSE PGID request result for single path. + */ +static void snid_callback(struct ccw_device *cdev, void *data, int rc) +{ + struct ccw_request *req = &cdev->private->req; + + if (rc == 0) + cdev->private->pgid_valid_mask |= req->lpm; + else if (rc != -EACCES) + goto err; + req->lpm >>= 1; + snid_do(cdev); + return; + +err: + snid_done(cdev, rc); +} - switch (ret) { - /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */ - case 0: - /* Path verification ccw finished successfully, update lpm. */ - sch->vpm |= sch->opm & cdev->private->imask; - /* Go on with next path. */ - cdev->private->imask >>= 1; - cdev->private->iretry = 5; - __ccw_device_verify_start(cdev); - break; - case -EOPNOTSUPP: - /* - * One of those strange devices which claim to be able - * to do multipathing but not for Set Path Group ID. - */ - if (cdev->private->flags.pgid_single) - cdev->private->options.pgroup = 0; - else - cdev->private->flags.pgid_single = 1; - /* Retry */ - sch->vpm = 0; - cdev->private->imask = 0x80; - cdev->private->iretry = 5; - /* fall through. */ - case -EAGAIN: /* Try again. */ - __ccw_device_verify_start(cdev); - break; - case -ETIME: /* Set path group id stopped by timeout. */ - ccw_device_verify_done(cdev, -ETIME); - break; - case -EACCES: /* channel is not operational. */ - cdev->private->imask >>= 1; - cdev->private->iretry = 5; - __ccw_device_verify_start(cdev); - break; - } +/** + * 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); } -void -ccw_device_verify_start(struct ccw_device *cdev) +/* + * Perform path verification. + */ +static void verify_start(struct ccw_device *cdev) { struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; - cdev->private->flags.pgid_single = 0; - cdev->private->imask = 0x80; - cdev->private->iretry = 5; - - /* Start with empty vpm. */ sch->vpm = 0; - - /* Get current pam. */ - if (cio_update_schib(sch)) { - ccw_device_verify_done(cdev, -ENODEV); - return; + /* Initialize request data. */ + memset(req, 0, sizeof(*req)); + req->timeout = PGID_TIMEOUT; + req->maxretries = PGID_RETRIES; + req->lpm = 0x80; + if (cdev->private->options.pgroup) { + req->callback = spid_callback; + spid_do(cdev); + } else { + req->filter = nop_filter; + req->callback = nop_callback; + nop_do(cdev); } - /* After 60s path verification is considered to have failed. */ - ccw_device_set_timeout(cdev, 60*HZ); - __ccw_device_verify_start(cdev); } -static void -__ccw_device_disband_start(struct ccw_device *cdev) +/** + * ccw_device_verify_start - perform path verification + * @cdev: ccw device + * + * Perform an I/O on each available channel path to @cdev to determine which + * paths are operational. The resulting path mask is stored in sch->vpm. + * If device options specify pathgrouping, establish a pathgroup for the + * operational paths. When finished, call ccw_device_verify_done with a + * return code specifying the result. + */ +void ccw_device_verify_start(struct ccw_device *cdev) { - struct subchannel *sch; - int ret; - - sch = to_subchannel(cdev->dev.parent); - while (cdev->private->imask != 0) { - if (sch->lpm & cdev->private->imask) { - ret = __ccw_device_do_pgid(cdev, SPID_FUNC_DISBAND); - if (ret == 0) - return; - } - cdev->private->iretry = 5; - cdev->private->imask >>= 1; - } - ccw_device_disband_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV); + 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->options.pgroup = 0; + cdev->private->flags.pgid_single = 1; + } else + cdev->private->flags.pgid_single = 0; + cdev->private->flags.doverify = 0; + verify_start(cdev); } /* - * Got interrupt for Unset Path Group ID. + * Process disband SET PGID request result. */ -void -ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event) +static void disband_callback(struct ccw_device *cdev, void *data, int rc) { - struct subchannel *sch; - struct irb *irb; - int ret; - - irb = (struct irb *) __LC_IRB; - - if (irb->scsw.cmd.stctl == - (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) { - if (__ccw_device_should_retry(&irb->scsw)) - __ccw_device_disband_start(cdev); - return; - } - if (ccw_device_accumulate_and_sense(cdev, irb) != 0) - return; - sch = to_subchannel(cdev->dev.parent); - ret = __ccw_device_check_pgid(cdev); - memset(&cdev->private->irb, 0, sizeof(struct irb)); - switch (ret) { - /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */ - case 0: /* disband successful. */ - ccw_device_disband_done(cdev, ret); - break; - case -EOPNOTSUPP: - /* - * One of those strange devices which claim to be able - * to do multipathing but not for Unset Path Group ID. - */ - cdev->private->flags.pgid_single = 1; - /* fall through. */ - case -EAGAIN: /* Try again. */ - __ccw_device_disband_start(cdev); - break; - case -ETIME: /* Set path group id stopped by timeout. */ - ccw_device_disband_done(cdev, -ETIME); - break; - case -EACCES: /* channel is not operational. */ - cdev->private->imask >>= 1; - cdev->private->iretry = 5; - __ccw_device_disband_start(cdev); - break; + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_dev_id *id = &cdev->private->dev_id; + + if (rc) + goto out; + /* Ensure consistent multipathing state at device and channel. */ + cdev->private->flags.pgid_single = 1; + if (sch->config.mp) { + sch->config.mp = 0; + rc = cio_commit_config(sch); } +out: + CIO_MSG_EVENT(0, "disb: device 0.%x.%04x: rc=%d\n", id->ssid, id->devno, + rc); + ccw_device_disband_done(cdev, rc); } -void -ccw_device_disband_start(struct ccw_device *cdev) +/** + * ccw_device_disband_start - disband pathgroup + * @cdev: ccw device + * + * Execute a SET PGID channel program on @cdev to disband a previously + * established pathgroup. When finished, call ccw_device_disband_done with + * a return code specifying the result. + */ +void ccw_device_disband_start(struct ccw_device *cdev) { - /* After 60s disbanding is considered to have failed. */ - ccw_device_set_timeout(cdev, 60*HZ); - - cdev->private->flags.pgid_single = 0; - cdev->private->iretry = 5; - cdev->private->imask = 0x80; - __ccw_device_disband_start(cdev); + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + u8 fn; + + CIO_TRACE_EVENT(4, "disb"); + CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); + /* Request setup. */ + memset(req, 0, sizeof(*req)); + req->timeout = PGID_TIMEOUT; + req->maxretries = PGID_RETRIES; + req->lpm = sch->schib.pmcw.pam & sch->opm; + req->callback = disband_callback; + fn = SPID_FUNC_DISBAND; + if (!cdev->private->flags.pgid_single) + fn |= SPID_FUNC_MULTI_PATH; + spid_build_cp(cdev, fn); + ccw_request_start(cdev); } -- cgit v1.2.2 From 454e1fa1ebae7cff707b2e3f12b775c263c8408b Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:30 +0100 Subject: [S390] cio: split PGID settings and status Split setting (driver wants feature enabled) and status (feature setup was successful) for PGID related ccw device features so that setup errors can be detected. Previously, incorrectly handled setup errors could in rare cases lead to erratic I/O behavior and permanently unusuable devices. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device_pgid.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) (limited to 'drivers/s390/cio/device_pgid.c') diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index ce493144b054..3323042ba755 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -30,8 +30,8 @@ static void verify_done(struct ccw_device *cdev, int rc) { struct subchannel *sch = to_subchannel(cdev->dev.parent); struct ccw_dev_id *id = &cdev->private->dev_id; - int mpath = !cdev->private->flags.pgid_single; - int pgroup = cdev->private->options.pgroup; + int mpath = cdev->private->flags.mpath; + int pgroup = cdev->private->flags.pgroup; if (rc) goto out; @@ -150,7 +150,7 @@ static void spid_do(struct ccw_device *cdev) fn = SPID_FUNC_ESTABLISH; else fn = SPID_FUNC_RESIGN; - if (!cdev->private->flags.pgid_single) + if (cdev->private->flags.mpath) fn |= SPID_FUNC_MULTI_PATH; spid_build_cp(cdev, fn); ccw_request_start(cdev); @@ -177,13 +177,13 @@ static void spid_callback(struct ccw_device *cdev, void *data, int rc) case -EACCES: break; case -EOPNOTSUPP: - if (!cdev->private->flags.pgid_single) { + if (cdev->private->flags.mpath) { /* Try without multipathing. */ - cdev->private->flags.pgid_single = 1; + cdev->private->flags.mpath = 0; goto out_restart; } /* Try without pathgrouping. */ - cdev->private->options.pgroup = 0; + cdev->private->flags.pgroup = 0; goto out_restart; default: goto err; @@ -374,7 +374,7 @@ static void verify_start(struct ccw_device *cdev) req->timeout = PGID_TIMEOUT; req->maxretries = PGID_RETRIES; req->lpm = 0x80; - if (cdev->private->options.pgroup) { + if (cdev->private->flags.pgroup) { req->callback = spid_callback; spid_do(cdev); } else { @@ -400,10 +400,17 @@ void ccw_device_verify_start(struct ccw_device *cdev) CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); if (!cdev->private->flags.pgid_rdy) { /* No pathgrouping possible. */ - cdev->private->options.pgroup = 0; - cdev->private->flags.pgid_single = 1; - } else - cdev->private->flags.pgid_single = 0; + 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; + + } cdev->private->flags.doverify = 0; verify_start(cdev); } @@ -419,7 +426,7 @@ static void disband_callback(struct ccw_device *cdev, void *data, int rc) if (rc) goto out; /* Ensure consistent multipathing state at device and channel. */ - cdev->private->flags.pgid_single = 1; + cdev->private->flags.mpath = 0; if (sch->config.mp) { sch->config.mp = 0; rc = cio_commit_config(sch); @@ -453,7 +460,7 @@ void ccw_device_disband_start(struct ccw_device *cdev) req->lpm = sch->schib.pmcw.pam & sch->opm; req->callback = disband_callback; fn = SPID_FUNC_DISBAND; - if (!cdev->private->flags.pgid_single) + if (cdev->private->flags.mpath) fn |= SPID_FUNC_MULTI_PATH; spid_build_cp(cdev, fn); ccw_request_start(cdev); -- cgit v1.2.2 From 52ef0608e3ee4a511725e443c4b572fece22b353 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:31 +0100 Subject: [S390] cio: use sense-pgid operation for path verification Set-pgid operations fail for some device types under z/VM for which the hypervisor has already set the pgid. Also reserved devices or changed pgids are not correctly recognized. Fix these problems by using a combination of sense-pgid and set-pgid and by also accepting pre-defined pgid settings. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device_pgid.c | 148 ++++++++++++++++++++++++++--------------- 1 file changed, 95 insertions(+), 53 deletions(-) (limited to 'drivers/s390/cio/device_pgid.c') diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index 3323042ba755..4d54abd82b8c 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -141,8 +141,8 @@ static void spid_do(struct ccw_device *cdev) 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. */ @@ -199,6 +199,19 @@ err: 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, @@ -241,6 +254,40 @@ static void pgid_analyze(struct ccw_device *cdev, struct pgid **p, *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; @@ -255,6 +302,7 @@ static void pgid_fill(struct ccw_device *cdev, struct pgid *pgid) 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; @@ -263,18 +311,38 @@ static void snid_done(struct ccw_device *cdev, int rc) 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); + } } /* @@ -333,33 +401,6 @@ err: 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. */ @@ -367,6 +408,7 @@ static void verify_start(struct ccw_device *cdev) { 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. */ @@ -375,9 +417,13 @@ static void verify_start(struct ccw_device *cdev) 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); @@ -398,19 +444,15 @@ void ccw_device_verify_start(struct ccw_device *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); } -- cgit v1.2.2 From d7d12ef2befac4fed0dccaddff11338b654804df Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:32 +0100 Subject: [S390] cio: make steal lock procedure more robust An Unconditional Reserve + Release operation (steal lock) for a boxed device may fail when encountering special error cases (e.g. unit checks or path errors). Fix this by using the more robust ccw_request infrastructure for performing the steal lock CCW program. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device_pgid.c | 52 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) (limited to 'drivers/s390/cio/device_pgid.c') diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index 4d54abd82b8c..5bcefeaff744 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -507,3 +507,55 @@ void ccw_device_disband_start(struct ccw_device *cdev) spid_build_cp(cdev, fn); ccw_request_start(cdev); } + +static void stlck_build_cp(struct ccw_device *cdev, void *buf1, void *buf2) +{ + struct ccw_request *req = &cdev->private->req; + struct ccw1 *cp = cdev->private->iccws; + + cp[0].cmd_code = CCW_CMD_STLCK; + cp[0].cda = (u32) (addr_t) buf1; + cp[0].count = 32; + cp[0].flags = CCW_FLAG_CC; + cp[1].cmd_code = CCW_CMD_RELEASE; + cp[1].cda = (u32) (addr_t) buf2; + cp[1].count = 32; + cp[1].flags = 0; + req->cp = cp; +} + +static void stlck_callback(struct ccw_device *cdev, void *data, int rc) +{ + ccw_device_stlck_done(cdev, data, rc); +} + +/** + * ccw_device_stlck_start - perform unconditional release + * @cdev: ccw device + * @data: data pointer to be passed to ccw_device_stlck_done + * @buf1: data pointer used in channel program + * @buf2: data pointer used in channel program + * + * Execute a channel program on @cdev to release an existing PGID reservation. + * When finished, call ccw_device_stlck_done with a return code specifying the + * result. + */ +void ccw_device_stlck_start(struct ccw_device *cdev, void *data, void *buf1, + void *buf2) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_request *req = &cdev->private->req; + + CIO_TRACE_EVENT(4, "stlck"); + CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); + /* Request setup. */ + memset(req, 0, sizeof(*req)); + req->timeout = PGID_TIMEOUT; + req->maxretries = PGID_RETRIES; + req->lpm = sch->schib.pmcw.pam & sch->opm; + req->data = data; + req->callback = stlck_callback; + stlck_build_cp(cdev, buf1, buf2); + ccw_request_start(cdev); +} + -- cgit v1.2.2 From de1b04388f63cbddf91d9f6c50c29be7232881ca Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 7 Dec 2009 12:51:40 +0100 Subject: [S390] cio: improve error recovery for internal I/Os Improve error recovery for internal I/Os by repeating each I/O 256 times per path to cope with long-running non-permanent error conditions. Also retry each path twice to cope with link flapping, i.e. single paths becoming unavailable in the order in which they are tried. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device_pgid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/s390/cio/device_pgid.c') diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index 5bcefeaff744..aad188e43b4f 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -20,7 +20,7 @@ #include "device.h" #include "io_sch.h" -#define PGID_RETRIES 5 +#define PGID_RETRIES 256 #define PGID_TIMEOUT (10 * HZ) /* -- cgit v1.2.2 From d302e1a5dbe1677a495033a2d310656a55139cdf Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Fri, 18 Dec 2009 17:43:23 +0100 Subject: [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 Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device_pgid.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) (limited to 'drivers/s390/cio/device_pgid.c') diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index aad188e43b4f..6facb5499a65 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -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. -- cgit v1.2.2