diff options
Diffstat (limited to 'drivers/s390/cio')
-rw-r--r-- | drivers/s390/cio/ccwreq.c | 16 | ||||
-rw-r--r-- | drivers/s390/cio/chsc.c | 48 | ||||
-rw-r--r-- | drivers/s390/cio/chsc.h | 2 | ||||
-rw-r--r-- | drivers/s390/cio/device.c | 47 | ||||
-rw-r--r-- | drivers/s390/cio/device_pgid.c | 3 | ||||
-rw-r--r-- | drivers/s390/cio/io_sch.h | 10 |
6 files changed, 115 insertions, 11 deletions
diff --git a/drivers/s390/cio/ccwreq.c b/drivers/s390/cio/ccwreq.c index 7f206ed44fdf..d15f8b4d78bd 100644 --- a/drivers/s390/cio/ccwreq.c +++ b/drivers/s390/cio/ccwreq.c @@ -38,9 +38,13 @@ static u16 ccwreq_next_path(struct ccw_device *cdev) { struct ccw_request *req = &cdev->private->req; + if (!req->singlepath) { + req->mask = 0; + goto out; + } req->retries = req->maxretries; req->mask = lpm_adjust(req->mask >>= 1, req->lpm); - +out: return req->mask; } @@ -113,8 +117,12 @@ void ccw_request_start(struct ccw_device *cdev) { struct ccw_request *req = &cdev->private->req; - /* Try all paths twice to counter link flapping. */ - req->mask = 0x8080; + if (req->singlepath) { + /* Try all paths twice to counter link flapping. */ + req->mask = 0x8080; + } else + req->mask = req->lpm; + req->retries = req->maxretries; req->mask = lpm_adjust(req->mask, req->lpm); req->drc = 0; @@ -182,6 +190,8 @@ static enum io_status ccwreq_status(struct ccw_device *cdev, struct irb *lcirb) /* Ask the driver what to do */ if (cdev->drv && cdev->drv->uc_handler) { todo = cdev->drv->uc_handler(cdev, lcirb); + CIO_TRACE_EVENT(2, "uc_response"); + CIO_HEX_EVENT(2, &todo, sizeof(todo)); switch (todo) { case UC_TODO_RETRY: return IO_STATUS_ERROR; diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 407d0e9adfaf..4cbb1a6ca33c 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -29,6 +29,7 @@ #include "chsc.h" static void *sei_page; +static DEFINE_SPINLOCK(siosl_lock); static DEFINE_SPINLOCK(sda_lock); /** @@ -48,6 +49,7 @@ int chsc_error_from_response(int response) case 0x0007: case 0x0008: case 0x000a: + case 0x0104: return -EINVAL; case 0x0004: return -EOPNOTSUPP; @@ -974,3 +976,49 @@ int chsc_sstpi(void *page, void *result, size_t size) return (rr->response.code == 0x0001) ? 0 : -EIO; } +static struct { + struct chsc_header request; + u32 word1; + struct subchannel_id sid; + u32 word3; + struct chsc_header response; + u32 word[11]; +} __attribute__ ((packed)) siosl_area __attribute__ ((__aligned__(PAGE_SIZE))); + +int chsc_siosl(struct subchannel_id schid) +{ + unsigned long flags; + int ccode; + int rc; + + spin_lock_irqsave(&siosl_lock, flags); + memset(&siosl_area, 0, sizeof(siosl_area)); + siosl_area.request.length = 0x0010; + siosl_area.request.code = 0x0046; + siosl_area.word1 = 0x80000000; + siosl_area.sid = schid; + + ccode = chsc(&siosl_area); + if (ccode > 0) { + if (ccode == 3) + rc = -ENODEV; + else + rc = -EBUSY; + CIO_MSG_EVENT(2, "chsc: chsc failed for 0.%x.%04x (ccode=%d)\n", + schid.ssid, schid.sch_no, ccode); + goto out; + } + rc = chsc_error_from_response(siosl_area.response.code); + if (rc) + CIO_MSG_EVENT(2, "chsc: siosl failed for 0.%x.%04x (rc=%04x)\n", + schid.ssid, schid.sch_no, + siosl_area.response.code); + else + CIO_MSG_EVENT(4, "chsc: siosl succeeded for 0.%x.%04x\n", + schid.ssid, schid.sch_no); +out: + spin_unlock_irqrestore(&siosl_lock, flags); + + return rc; +} +EXPORT_SYMBOL_GPL(chsc_siosl); diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 37aa611d4ac5..5453013f094b 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -80,4 +80,6 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp); int chsc_error_from_response(int response); +int chsc_siosl(struct subchannel_id schid); + #endif diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 6d229f3523a0..51bd3687d163 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -36,6 +36,7 @@ #include "ioasm.h" #include "io_sch.h" #include "blacklist.h" +#include "chsc.h" static struct timer_list recovery_timer; static DEFINE_SPINLOCK(recovery_lock); @@ -486,9 +487,11 @@ static int online_store_handle_offline(struct ccw_device *cdev) spin_lock_irq(cdev->ccwlock); ccw_device_sched_todo(cdev, CDEV_TODO_UNREG_EVAL); spin_unlock_irq(cdev->ccwlock); - } else if (cdev->online && cdev->drv && cdev->drv->set_offline) + return 0; + } + if (cdev->drv && cdev->drv->set_offline) return ccw_device_set_offline(cdev); - return 0; + return -EINVAL; } static int online_store_recog_and_online(struct ccw_device *cdev) @@ -505,8 +508,8 @@ static int online_store_recog_and_online(struct ccw_device *cdev) return -EAGAIN; } if (cdev->drv && cdev->drv->set_online) - ccw_device_set_online(cdev); - return 0; + return ccw_device_set_online(cdev); + return -EINVAL; } static int online_store_handle_online(struct ccw_device *cdev, int force) @@ -598,6 +601,25 @@ available_show (struct device *dev, struct device_attribute *attr, char *buf) } } +static ssize_t +initiate_logging(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct subchannel *sch = to_subchannel(dev); + int rc; + + rc = chsc_siosl(sch->schid); + if (rc < 0) { + pr_warning("Logging for subchannel 0.%x.%04x failed with " + "errno=%d\n", + sch->schid.ssid, sch->schid.sch_no, rc); + return rc; + } + pr_notice("Logging for subchannel 0.%x.%04x was triggered\n", + sch->schid.ssid, sch->schid.sch_no); + return count; +} + static DEVICE_ATTR(chpids, 0444, chpids_show, NULL); static DEVICE_ATTR(pimpampom, 0444, pimpampom_show, NULL); static DEVICE_ATTR(devtype, 0444, devtype_show, NULL); @@ -605,10 +627,12 @@ static DEVICE_ATTR(cutype, 0444, cutype_show, NULL); static DEVICE_ATTR(modalias, 0444, modalias_show, NULL); static DEVICE_ATTR(online, 0644, online_show, online_store); static DEVICE_ATTR(availability, 0444, available_show, NULL); +static DEVICE_ATTR(logging, 0200, NULL, initiate_logging); static struct attribute *io_subchannel_attrs[] = { &dev_attr_chpids.attr, &dev_attr_pimpampom.attr, + &dev_attr_logging.attr, NULL, }; @@ -2036,6 +2060,21 @@ void ccw_device_sched_todo(struct ccw_device *cdev, enum cdev_todo todo) } } +/** + * ccw_device_siosl() - initiate logging + * @cdev: ccw device + * + * This function is used to invoke model-dependent logging within the channel + * subsystem. + */ +int ccw_device_siosl(struct ccw_device *cdev) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + + return chsc_siosl(sch->schid); +} +EXPORT_SYMBOL_GPL(ccw_device_siosl); + MODULE_LICENSE("GPL"); EXPORT_SYMBOL(ccw_device_set_online); EXPORT_SYMBOL(ccw_device_set_offline); diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index 6facb5499a65..82a5ad0d63f6 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -208,6 +208,7 @@ static void spid_start(struct ccw_device *cdev) req->timeout = PGID_TIMEOUT; req->maxretries = PGID_RETRIES; req->lpm = 0x80; + req->singlepath = 1; req->callback = spid_callback; spid_do(cdev); } @@ -420,6 +421,7 @@ static void verify_start(struct ccw_device *cdev) req->timeout = PGID_TIMEOUT; req->maxretries = PGID_RETRIES; req->lpm = 0x80; + req->singlepath = 1; if (cdev->private->flags.pgroup) { CIO_TRACE_EVENT(4, "snid"); CIO_HEX_EVENT(4, devid, sizeof(*devid)); @@ -507,6 +509,7 @@ void ccw_device_disband_start(struct ccw_device *cdev) req->timeout = PGID_TIMEOUT; req->maxretries = PGID_RETRIES; req->lpm = sch->schib.pmcw.pam & sch->opm; + req->singlepath = 1; req->callback = disband_callback; fn = SPID_FUNC_DISBAND; if (cdev->private->flags.mpath) diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index b9ce712a7f25..469ef93f2302 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h @@ -92,11 +92,12 @@ enum io_status { * @filter: optional callback to adjust request status based on IRB data * @callback: final callback * @data: user-defined pointer passed to all callbacks + * @singlepath: if set, use only one path from @lpm per start I/O + * @cancel: non-zero if request was cancelled + * @done: non-zero if request was finished * @mask: current path mask * @retries: current number of retries * @drc: delayed return code - * @cancel: non-zero if request was cancelled - * @done: non-zero if request was finished */ struct ccw_request { struct ccw1 *cp; @@ -108,12 +109,13 @@ struct ccw_request { enum io_status); void (*callback)(struct ccw_device *, void *, int); void *data; + unsigned int singlepath:1; /* These fields are used internally. */ + unsigned int cancel:1; + unsigned int done:1; u16 mask; u16 retries; int drc; - int cancel:1; - int done:1; } __attribute__((packed)); /* |