diff options
Diffstat (limited to 'drivers')
34 files changed, 1089 insertions, 412 deletions
diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c index 46a7b738f61f..ea3fa90d020a 100644 --- a/drivers/pci/hotplug/s390_pci_hpc.c +++ b/drivers/pci/hotplug/s390_pci_hpc.c @@ -41,6 +41,28 @@ struct slot { struct zpci_dev *zdev; }; +static inline int slot_configure(struct slot *slot) +{ + int ret = sclp_pci_configure(slot->zdev->fid); + + zpci_dbg(3, "conf fid:%x, rc:%d\n", slot->zdev->fid, ret); + if (!ret) + slot->zdev->state = ZPCI_FN_STATE_CONFIGURED; + + return ret; +} + +static inline int slot_deconfigure(struct slot *slot) +{ + int ret = sclp_pci_deconfigure(slot->zdev->fid); + + zpci_dbg(3, "deconf fid:%x, rc:%d\n", slot->zdev->fid, ret); + if (!ret) + slot->zdev->state = ZPCI_FN_STATE_STANDBY; + + return ret; +} + static int enable_slot(struct hotplug_slot *hotplug_slot) { struct slot *slot = hotplug_slot->private; @@ -49,14 +71,23 @@ static int enable_slot(struct hotplug_slot *hotplug_slot) if (slot->zdev->state != ZPCI_FN_STATE_STANDBY) return -EIO; - rc = sclp_pci_configure(slot->zdev->fid); - zpci_dbg(3, "conf fid:%x, rc:%d\n", slot->zdev->fid, rc); - if (!rc) { - slot->zdev->state = ZPCI_FN_STATE_CONFIGURED; - /* automatically scan the device after is was configured */ - zpci_enable_device(slot->zdev); - zpci_scan_device(slot->zdev); - } + rc = slot_configure(slot); + if (rc) + return rc; + + rc = zpci_enable_device(slot->zdev); + if (rc) + goto out_deconfigure; + + slot->zdev->state = ZPCI_FN_STATE_ONLINE; + + pci_scan_slot(slot->zdev->bus, ZPCI_DEVFN); + pci_bus_add_devices(slot->zdev->bus); + + return rc; + +out_deconfigure: + slot_deconfigure(slot); return rc; } @@ -68,17 +99,14 @@ static int disable_slot(struct hotplug_slot *hotplug_slot) if (!zpci_fn_configured(slot->zdev->state)) return -EIO; + if (slot->zdev->pdev) + pci_stop_and_remove_bus_device(slot->zdev->pdev); + rc = zpci_disable_device(slot->zdev); if (rc) return rc; - /* TODO: we rely on the user to unbind/remove the device, is that plausible - * or do we need to trigger that here? - */ - rc = sclp_pci_deconfigure(slot->zdev->fid); - zpci_dbg(3, "deconf fid:%x, rc:%d\n", slot->zdev->fid, rc); - if (!rc) - slot->zdev->state = ZPCI_FN_STATE_STANDBY; - return rc; + + return slot_deconfigure(slot); } static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index a899d8bb190d..95057a925d80 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1335,6 +1335,16 @@ int __weak pcibios_add_device (struct pci_dev *dev) } /** + * pcibios_release_device - provide arch specific hooks when releasing device dev + * @dev: the PCI device being released + * + * Permits the platform to provide architecture specific functionality when + * devices are released. This is the default implementation. Architecture + * implementations can override this. + */ +void __weak pcibios_release_device(struct pci_dev *dev) {} + +/** * pcibios_disable_device - disable arch specific PCI resources for device dev * @dev: the PCI device to disable * diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 70f10fa3c1b2..58cc0a8a0979 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1132,6 +1132,7 @@ static void pci_release_dev(struct device *dev) pci_dev = to_pci_dev(dev); pci_release_capabilities(pci_dev); pci_release_of_node(pci_dev); + pcibios_release_device(pci_dev); kfree(pci_dev); } diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index d72a9216ee2e..17150a778984 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -38,9 +38,6 @@ */ #define DASD_CHANQ_MAX_SIZE 4 -#define DASD_SLEEPON_START_TAG (void *) 1 -#define DASD_SLEEPON_END_TAG (void *) 2 - /* * SECTION: exported variables of dasd.c */ @@ -1787,11 +1784,11 @@ static void __dasd_device_process_ccw_queue(struct dasd_device *device, list_for_each_safe(l, n, &device->ccw_queue) { cqr = list_entry(l, struct dasd_ccw_req, devlist); - /* Stop list processing at the first non-final request. */ + /* Skip any non-final request. */ if (cqr->status == DASD_CQR_QUEUED || cqr->status == DASD_CQR_IN_IO || cqr->status == DASD_CQR_CLEAR_PENDING) - break; + continue; if (cqr->status == DASD_CQR_ERROR) { __dasd_device_recovery(device, cqr); } @@ -2183,7 +2180,7 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible) test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && (!dasd_eer_enabled(device))) { cqr->status = DASD_CQR_FAILED; - cqr->intrc = -EAGAIN; + cqr->intrc = -ENOLINK; continue; } /* Don't try to start requests if device is stopped */ @@ -2402,8 +2399,7 @@ int dasd_sleep_on_immediatly(struct dasd_ccw_req *cqr) * Cancels a request that was started with dasd_sleep_on_req. * This is useful to timeout requests. The request will be * terminated if it is currently in i/o. - * Returns 1 if the request has been terminated. - * 0 if there was no need to terminate the request (not started yet) + * Returns 0 if request termination was successful * negative error code if termination failed * Cancellation of a request is an asynchronous operation! The calling * function has to wait until the request is properly returned via callback. @@ -2440,7 +2436,6 @@ int dasd_cancel_req(struct dasd_ccw_req *cqr) return rc; } - /* * SECTION: Operations of the dasd_block layer. */ @@ -2537,6 +2532,16 @@ static void __dasd_process_request_queue(struct dasd_block *block) __blk_end_request_all(req, -EIO); continue; } + if (test_bit(DASD_FLAG_ABORTALL, &basedev->flags) && + (basedev->features & DASD_FEATURE_FAILFAST || + blk_noretry_request(req))) { + DBF_DEV_EVENT(DBF_ERR, basedev, + "Rejecting failfast request %p", + req); + blk_start_request(req); + __blk_end_request_all(req, -ETIMEDOUT); + continue; + } cqr = basedev->discipline->build_cp(basedev, block, req); if (IS_ERR(cqr)) { if (PTR_ERR(cqr) == -EBUSY) @@ -2575,8 +2580,10 @@ static void __dasd_process_request_queue(struct dasd_block *block) */ cqr->callback_data = (void *) req; cqr->status = DASD_CQR_FILLED; + req->completion_data = cqr; blk_start_request(req); list_add_tail(&cqr->blocklist, &block->ccw_queue); + INIT_LIST_HEAD(&cqr->devlist); dasd_profile_start(block, cqr, req); } } @@ -2590,8 +2597,17 @@ static void __dasd_cleanup_cqr(struct dasd_ccw_req *cqr) req = (struct request *) cqr->callback_data; dasd_profile_end(cqr->block, cqr, req); status = cqr->block->base->discipline->free_cp(cqr, req); - if (status <= 0) - error = status ? status : -EIO; + if (status < 0) + error = status; + else if (status == 0) { + if (cqr->intrc == -EPERM) + error = -EBADE; + else if (cqr->intrc == -ENOLINK || + cqr->intrc == -ETIMEDOUT) + error = cqr->intrc; + else + error = -EIO; + } __blk_end_request_all(req, error); } @@ -2692,6 +2708,7 @@ static void __dasd_block_start_head(struct dasd_block *block) test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && (!dasd_eer_enabled(block->base))) { cqr->status = DASD_CQR_FAILED; + cqr->intrc = -ENOLINK; dasd_schedule_block_bh(block); continue; } @@ -2864,6 +2881,82 @@ static void do_dasd_request(struct request_queue *queue) } /* + * Block timeout callback, called from the block layer + * + * request_queue lock is held on entry. + * + * Return values: + * BLK_EH_RESET_TIMER if the request should be left running + * BLK_EH_NOT_HANDLED if the request is handled or terminated + * by the driver. + */ +enum blk_eh_timer_return dasd_times_out(struct request *req) +{ + struct dasd_ccw_req *cqr = req->completion_data; + struct dasd_block *block = req->q->queuedata; + struct dasd_device *device; + int rc = 0; + + if (!cqr) + return BLK_EH_NOT_HANDLED; + + device = cqr->startdev ? cqr->startdev : block->base; + if (!device->blk_timeout) + return BLK_EH_RESET_TIMER; + DBF_DEV_EVENT(DBF_WARNING, device, + " dasd_times_out cqr %p status %x", + cqr, cqr->status); + + spin_lock(&block->queue_lock); + spin_lock(get_ccwdev_lock(device->cdev)); + cqr->retries = -1; + cqr->intrc = -ETIMEDOUT; + if (cqr->status >= DASD_CQR_QUEUED) { + spin_unlock(get_ccwdev_lock(device->cdev)); + rc = dasd_cancel_req(cqr); + } else if (cqr->status == DASD_CQR_FILLED || + cqr->status == DASD_CQR_NEED_ERP) { + cqr->status = DASD_CQR_TERMINATED; + spin_unlock(get_ccwdev_lock(device->cdev)); + } else if (cqr->status == DASD_CQR_IN_ERP) { + struct dasd_ccw_req *searchcqr, *nextcqr, *tmpcqr; + + list_for_each_entry_safe(searchcqr, nextcqr, + &block->ccw_queue, blocklist) { + tmpcqr = searchcqr; + while (tmpcqr->refers) + tmpcqr = tmpcqr->refers; + if (tmpcqr != cqr) + continue; + /* searchcqr is an ERP request for cqr */ + searchcqr->retries = -1; + searchcqr->intrc = -ETIMEDOUT; + if (searchcqr->status >= DASD_CQR_QUEUED) { + spin_unlock(get_ccwdev_lock(device->cdev)); + rc = dasd_cancel_req(searchcqr); + spin_lock(get_ccwdev_lock(device->cdev)); + } else if ((searchcqr->status == DASD_CQR_FILLED) || + (searchcqr->status == DASD_CQR_NEED_ERP)) { + searchcqr->status = DASD_CQR_TERMINATED; + rc = 0; + } else if (searchcqr->status == DASD_CQR_IN_ERP) { + /* + * Shouldn't happen; most recent ERP + * request is at the front of queue + */ + continue; + } + break; + } + spin_unlock(get_ccwdev_lock(device->cdev)); + } + dasd_schedule_block_bh(block); + spin_unlock(&block->queue_lock); + + return rc ? BLK_EH_RESET_TIMER : BLK_EH_NOT_HANDLED; +} + +/* * Allocate and initialize request queue and default I/O scheduler. */ static int dasd_alloc_queue(struct dasd_block *block) diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index a71bb8aaca1d..58bc6eb49de1 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -1240,6 +1240,101 @@ dasd_expires_store(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(expires, 0644, dasd_expires_show, dasd_expires_store); +static ssize_t +dasd_retries_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct dasd_device *device; + int len; + + device = dasd_device_from_cdev(to_ccwdev(dev)); + if (IS_ERR(device)) + return -ENODEV; + len = snprintf(buf, PAGE_SIZE, "%lu\n", device->default_retries); + dasd_put_device(device); + return len; +} + +static ssize_t +dasd_retries_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dasd_device *device; + unsigned long val; + + device = dasd_device_from_cdev(to_ccwdev(dev)); + if (IS_ERR(device)) + return -ENODEV; + + if ((strict_strtoul(buf, 10, &val) != 0) || + (val > DASD_RETRIES_MAX)) { + dasd_put_device(device); + return -EINVAL; + } + + if (val) + device->default_retries = val; + + dasd_put_device(device); + return count; +} + +static DEVICE_ATTR(retries, 0644, dasd_retries_show, dasd_retries_store); + +static ssize_t +dasd_timeout_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct dasd_device *device; + int len; + + device = dasd_device_from_cdev(to_ccwdev(dev)); + if (IS_ERR(device)) + return -ENODEV; + len = snprintf(buf, PAGE_SIZE, "%lu\n", device->blk_timeout); + dasd_put_device(device); + return len; +} + +static ssize_t +dasd_timeout_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dasd_device *device; + struct request_queue *q; + unsigned long val, flags; + + device = dasd_device_from_cdev(to_ccwdev(dev)); + if (IS_ERR(device) || !device->block) + return -ENODEV; + + if ((strict_strtoul(buf, 10, &val) != 0) || + val > UINT_MAX / HZ) { + dasd_put_device(device); + return -EINVAL; + } + q = device->block->request_queue; + if (!q) { + dasd_put_device(device); + return -ENODEV; + } + spin_lock_irqsave(&device->block->request_queue_lock, flags); + if (!val) + blk_queue_rq_timed_out(q, NULL); + else + blk_queue_rq_timed_out(q, dasd_times_out); + + device->blk_timeout = val; + + blk_queue_rq_timeout(q, device->blk_timeout * HZ); + spin_unlock_irqrestore(&device->block->request_queue_lock, flags); + + dasd_put_device(device); + return count; +} + +static DEVICE_ATTR(timeout, 0644, + dasd_timeout_show, dasd_timeout_store); + static ssize_t dasd_reservation_policy_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1350,6 +1445,8 @@ static struct attribute * dasd_attrs[] = { &dev_attr_erplog.attr, &dev_attr_failfast.attr, &dev_attr_expires.attr, + &dev_attr_retries.attr, + &dev_attr_timeout.attr, &dev_attr_reservation_policy.attr, &dev_attr_last_known_reservation_state.attr, &dev_attr_safe_offline.attr, diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index cc0603358522..feca317b33de 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -359,6 +359,7 @@ dasd_diag_check_device(struct dasd_device *device) } device->default_expires = DIAG_TIMEOUT; + device->default_retries = DIAG_MAX_RETRIES; /* Figure out position of label block */ switch (private->rdc_data.vdev_class) { @@ -555,7 +556,7 @@ static struct dasd_ccw_req *dasd_diag_build_cp(struct dasd_device *memdev, recid++; } } - cqr->retries = DIAG_MAX_RETRIES; + cqr->retries = memdev->default_retries; cqr->buildclk = get_tod_clock(); if (blk_noretry_request(req) || block->base->features & DASD_FEATURE_FAILFAST) @@ -582,7 +583,10 @@ dasd_diag_free_cp(struct dasd_ccw_req *cqr, struct request *req) static void dasd_diag_handle_terminated_request(struct dasd_ccw_req *cqr) { - cqr->status = DASD_CQR_FILLED; + if (cqr->retries < 0) + cqr->status = DASD_CQR_FAILED; + else + cqr->status = DASD_CQR_FILLED; }; /* Fill in IOCTL data for device. */ diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 6a44b27623ed..e61a6deea3c0 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1682,6 +1682,9 @@ dasd_eckd_check_characteristics(struct dasd_device *device) /* set default timeout */ device->default_expires = DASD_EXPIRES; + /* set default retry count */ + device->default_retries = DASD_RETRIES; + if (private->gneq) { value = 1; for (i = 0; i < private->gneq->timeout.value; i++) @@ -2378,6 +2381,10 @@ sleep: static void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr) { + if (cqr->retries < 0) { + cqr->status = DASD_CQR_FAILED; + return; + } cqr->status = DASD_CQR_FILLED; if (cqr->block && (cqr->startdev != cqr->block->base)) { dasd_eckd_reset_ccw_to_base_io(cqr); @@ -2659,7 +2666,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( cqr->block = block; cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ cqr->lpm = startdev->path_data.ppm; - cqr->retries = 256; + cqr->retries = startdev->default_retries; cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; @@ -2834,7 +2841,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track( cqr->block = block; cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ cqr->lpm = startdev->path_data.ppm; - cqr->retries = 256; + cqr->retries = startdev->default_retries; cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; @@ -2968,7 +2975,7 @@ static int prepare_itcw(struct itcw *itcw, dcw = itcw_add_dcw(itcw, pfx_cmd, 0, &pfxdata, sizeof(pfxdata), total_data_size); - return IS_ERR(dcw) ? PTR_ERR(dcw) : 0; + return PTR_RET(dcw); } static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( @@ -3127,7 +3134,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( cqr->block = block; cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ cqr->lpm = startdev->path_data.ppm; - cqr->retries = 256; + cqr->retries = startdev->default_retries; cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; @@ -3330,7 +3337,7 @@ static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev, cqr->block = block; cqr->expires = startdev->default_expires * HZ; cqr->lpm = startdev->path_data.ppm; - cqr->retries = 256; + cqr->retries = startdev->default_retries; cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; diff --git a/drivers/s390/block/dasd_erp.c b/drivers/s390/block/dasd_erp.c index 3250cb471f78..8d11f773a752 100644 --- a/drivers/s390/block/dasd_erp.c +++ b/drivers/s390/block/dasd_erp.c @@ -159,6 +159,14 @@ dasd_log_sense(struct dasd_ccw_req *cqr, struct irb *irb) struct dasd_device *device; device = cqr->startdev; + if (cqr->intrc == -ETIMEDOUT) { + dev_err(&device->cdev->dev, "cqr %p timeout error", cqr); + return; + } + if (cqr->intrc == -ENOLINK) { + dev_err(&device->cdev->dev, "cqr %p transport error", cqr); + return; + } /* dump sense data */ if (device->discipline && device->discipline->dump_sense) device->discipline->dump_sense(device, cqr, irb); diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index 4dd0e2f6047e..9cbc8c32ba59 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -29,6 +29,8 @@ #endif /* PRINTK_HEADER */ #define PRINTK_HEADER "dasd(fba):" +#define FBA_DEFAULT_RETRIES 32 + #define DASD_FBA_CCW_WRITE 0x41 #define DASD_FBA_CCW_READ 0x42 #define DASD_FBA_CCW_LOCATE 0x43 @@ -167,6 +169,7 @@ dasd_fba_check_characteristics(struct dasd_device *device) } device->default_expires = DASD_EXPIRES; + device->default_retries = FBA_DEFAULT_RETRIES; device->path_data.opm = LPM_ANYPATH; readonly = dasd_device_is_ro(device); @@ -369,7 +372,7 @@ static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev, cqr->memdev = memdev; cqr->block = block; cqr->expires = memdev->default_expires * HZ; /* default 5 minutes */ - cqr->retries = 32; + cqr->retries = memdev->default_retries; cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; @@ -425,7 +428,10 @@ out: static void dasd_fba_handle_terminated_request(struct dasd_ccw_req *cqr) { - cqr->status = DASD_CQR_FILLED; + if (cqr->retries < 0) + cqr->status = DASD_CQR_FAILED; + else + cqr->status = DASD_CQR_FILLED; }; static int diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 0785bd9bd5b6..690001af0d09 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -224,6 +224,8 @@ struct dasd_ccw_req { /* default expiration time*/ #define DASD_EXPIRES 300 #define DASD_EXPIRES_MAX 40000000 +#define DASD_RETRIES 256 +#define DASD_RETRIES_MAX 32768 /* per dasd_ccw_req flags */ #define DASD_CQR_FLAGS_USE_ERP 0 /* use ERP for this request */ @@ -466,6 +468,9 @@ struct dasd_device { /* default expiration time in s */ unsigned long default_expires; + unsigned long default_retries; + + unsigned long blk_timeout; struct dentry *debugfs_dentry; struct dasd_profile profile; @@ -519,7 +524,10 @@ struct dasd_block { #define DASD_FLAG_SUSPENDED 9 /* The device was suspended */ #define DASD_FLAG_SAFE_OFFLINE 10 /* safe offline processing requested*/ #define DASD_FLAG_SAFE_OFFLINE_RUNNING 11 /* safe offline running */ +#define DASD_FLAG_ABORTALL 12 /* Abort all noretry requests */ +#define DASD_SLEEPON_START_TAG ((void *) 1) +#define DASD_SLEEPON_END_TAG ((void *) 2) void dasd_put_device_wake(struct dasd_device *); @@ -660,6 +668,8 @@ void dasd_free_device(struct dasd_device *); struct dasd_block *dasd_alloc_block(void); void dasd_free_block(struct dasd_block *); +enum blk_eh_timer_return dasd_times_out(struct request *req); + void dasd_enable_device(struct dasd_device *); void dasd_set_target_state(struct dasd_device *, int); void dasd_kick_device(struct dasd_device *); diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index 8be1b51e9311..25a0f2f8b0b9 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -141,6 +141,59 @@ static int dasd_ioctl_resume(struct dasd_block *block) } /* + * Abort all failfast I/O on a device. + */ +static int dasd_ioctl_abortio(struct dasd_block *block) +{ + unsigned long flags; + struct dasd_device *base; + struct dasd_ccw_req *cqr, *n; + + base = block->base; + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if (test_and_set_bit(DASD_FLAG_ABORTALL, &base->flags)) + return 0; + DBF_DEV_EVENT(DBF_NOTICE, base, "%s", "abortall flag set"); + + spin_lock_irqsave(&block->request_queue_lock, flags); + spin_lock(&block->queue_lock); + list_for_each_entry_safe(cqr, n, &block->ccw_queue, blocklist) { + if (test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && + cqr->callback_data && + cqr->callback_data != DASD_SLEEPON_START_TAG && + cqr->callback_data != DASD_SLEEPON_END_TAG) { + spin_unlock(&block->queue_lock); + blk_abort_request(cqr->callback_data); + spin_lock(&block->queue_lock); + } + } + spin_unlock(&block->queue_lock); + spin_unlock_irqrestore(&block->request_queue_lock, flags); + + dasd_schedule_block_bh(block); + return 0; +} + +/* + * Allow I/O on a device + */ +static int dasd_ioctl_allowio(struct dasd_block *block) +{ + struct dasd_device *base; + + base = block->base; + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if (test_and_clear_bit(DASD_FLAG_ABORTALL, &base->flags)) + DBF_DEV_EVENT(DBF_NOTICE, base, "%s", "abortall flag unset"); + + return 0; +} + +/* * performs formatting of _device_ according to _fdata_ * Note: The discipline's format_function is assumed to deliver formatting * commands to format multiple units of the device. In terms of the ECKD @@ -458,6 +511,12 @@ int dasd_ioctl(struct block_device *bdev, fmode_t mode, case BIODASDRESUME: rc = dasd_ioctl_resume(block); break; + case BIODASDABORTIO: + rc = dasd_ioctl_abortio(block); + break; + case BIODASDALLOWIO: + rc = dasd_ioctl_allowio(block); + break; case BIODASDFMT: rc = dasd_ioctl_format(bdev, argp); break; diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index f3c325207445..17821a026c9c 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -3,7 +3,7 @@ # obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \ - sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o + sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o sclp_ctl.o obj-$(CONFIG_TN3270) += raw3270.o obj-$(CONFIG_TN3270_CONSOLE) += con3270.o diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index bd6871bf545a..3e4fb4e858da 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -50,11 +50,42 @@ static char sclp_init_sccb[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); /* Suspend request */ static DECLARE_COMPLETION(sclp_request_queue_flushed); +/* Number of console pages to allocate, used by sclp_con.c and sclp_vt220.c */ +int sclp_console_pages = SCLP_CONSOLE_PAGES; +/* Flag to indicate if buffer pages are dropped on buffer full condition */ +int sclp_console_drop = 0; +/* Number of times the console dropped buffer pages */ +unsigned long sclp_console_full; + static void sclp_suspend_req_cb(struct sclp_req *req, void *data) { complete(&sclp_request_queue_flushed); } +static int __init sclp_setup_console_pages(char *str) +{ + int pages, rc; + + rc = kstrtoint(str, 0, &pages); + if (!rc && pages >= SCLP_CONSOLE_PAGES) + sclp_console_pages = pages; + return 1; +} + +__setup("sclp_con_pages=", sclp_setup_console_pages); + +static int __init sclp_setup_console_drop(char *str) +{ + int drop, rc; + + rc = kstrtoint(str, 0, &drop); + if (!rc && drop) + sclp_console_drop = 1; + return 1; +} + +__setup("sclp_con_drop=", sclp_setup_console_drop); + static struct sclp_req sclp_suspend_req; /* Timer for request retries. */ @@ -117,14 +148,19 @@ static int sclp_init(void); int sclp_service_call(sclp_cmdw_t command, void *sccb) { - int cc; + int cc = 4; /* Initialize for program check handling */ asm volatile( - " .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */ - " ipm %0\n" - " srl %0,28" - : "=&d" (cc) : "d" (command), "a" (__pa(sccb)) + "0: .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */ + "1: ipm %0\n" + " srl %0,28\n" + "2:\n" + EX_TABLE(0b, 2b) + EX_TABLE(1b, 2b) + : "+&d" (cc) : "d" (command), "a" (__pa(sccb)) : "cc", "memory"); + if (cc == 4) + return -EINVAL; if (cc == 3) return -EIO; if (cc == 2) @@ -1013,11 +1049,47 @@ static const struct dev_pm_ops sclp_pm_ops = { .restore = sclp_restore, }; +static ssize_t sclp_show_console_pages(struct device_driver *dev, char *buf) +{ + return sprintf(buf, "%i\n", sclp_console_pages); +} + +static DRIVER_ATTR(con_pages, S_IRUSR, sclp_show_console_pages, NULL); + +static ssize_t sclp_show_con_drop(struct device_driver *dev, char *buf) +{ + return sprintf(buf, "%i\n", sclp_console_drop); +} + +static DRIVER_ATTR(con_drop, S_IRUSR, sclp_show_con_drop, NULL); + +static ssize_t sclp_show_console_full(struct device_driver *dev, char *buf) +{ + return sprintf(buf, "%lu\n", sclp_console_full); +} + +static DRIVER_ATTR(con_full, S_IRUSR, sclp_show_console_full, NULL); + +static struct attribute *sclp_drv_attrs[] = { + &driver_attr_con_pages.attr, + &driver_attr_con_drop.attr, + &driver_attr_con_full.attr, + NULL, +}; +static struct attribute_group sclp_drv_attr_group = { + .attrs = sclp_drv_attrs, +}; +static const struct attribute_group *sclp_drv_attr_groups[] = { + &sclp_drv_attr_group, + NULL, +}; + static struct platform_driver sclp_pdrv = { .driver = { .name = "sclp", .owner = THIS_MODULE, .pm = &sclp_pm_ops, + .groups = sclp_drv_attr_groups, }, }; @@ -1096,10 +1168,12 @@ static __init int sclp_initcall(void) rc = platform_driver_register(&sclp_pdrv); if (rc) return rc; + sclp_pdev = platform_device_register_simple("sclp", -1, NULL, 0); - rc = IS_ERR(sclp_pdev) ? PTR_ERR(sclp_pdev) : 0; + rc = PTR_RET(sclp_pdev); if (rc) goto fail_platform_driver_unregister; + rc = atomic_notifier_chain_register(&panic_notifier_list, &sclp_on_panic_nb); if (rc) diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h index 25bcd4c0ed82..40d1406289ed 100644 --- a/drivers/s390/char/sclp.h +++ b/drivers/s390/char/sclp.h @@ -15,7 +15,7 @@ /* maximum number of pages concerning our own memory management */ #define MAX_KMEM_PAGES (sizeof(unsigned long) << 3) -#define MAX_CONSOLE_PAGES 6 +#define SCLP_CONSOLE_PAGES 6 #define EVTYP_OPCMD 0x01 #define EVTYP_MSG 0x02 @@ -171,10 +171,15 @@ int sclp_remove_processed(struct sccb_header *sccb); int sclp_deactivate(void); int sclp_reactivate(void); int sclp_service_call(sclp_cmdw_t command, void *sccb); +int sclp_sync_request(sclp_cmdw_t command, void *sccb); int sclp_sdias_init(void); void sclp_sdias_exit(void); +extern int sclp_console_pages; +extern int sclp_console_drop; +extern unsigned long sclp_console_full; + /* useful inlines */ /* VM uses EBCDIC 037, LPAR+native(SE+HMC) use EBCDIC 500 */ diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index bf07c3a188d4..8cd34bf644b3 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -195,7 +195,7 @@ static void sclp_sync_callback(struct sclp_req *req, void *data) complete(completion); } -static int do_sync_request(sclp_cmdw_t cmd, void *sccb) +int sclp_sync_request(sclp_cmdw_t cmd, void *sccb) { struct completion completion; struct sclp_req *request; @@ -270,7 +270,7 @@ int sclp_get_cpu_info(struct sclp_cpu_info *info) if (!sccb) return -ENOMEM; sccb->header.length = sizeof(*sccb); - rc = do_sync_request(SCLP_CMDW_READ_CPU_INFO, sccb); + rc = sclp_sync_request(SCLP_CMDW_READ_CPU_INFO, sccb); if (rc) goto out; if (sccb->header.response_code != 0x0010) { @@ -304,7 +304,7 @@ static int do_cpu_configure(sclp_cmdw_t cmd) if (!sccb) return -ENOMEM; sccb->header.length = sizeof(*sccb); - rc = do_sync_request(cmd, sccb); + rc = sclp_sync_request(cmd, sccb); if (rc) goto out; switch (sccb->header.response_code) { @@ -374,7 +374,7 @@ static int do_assign_storage(sclp_cmdw_t cmd, u16 rn) return -ENOMEM; sccb->header.length = PAGE_SIZE; sccb->rn = rn; - rc = do_sync_request(cmd, sccb); + rc = sclp_sync_request(cmd, sccb); if (rc) goto out; switch (sccb->header.response_code) { @@ -429,7 +429,7 @@ static int sclp_attach_storage(u8 id) if (!sccb) return -ENOMEM; sccb->header.length = PAGE_SIZE; - rc = do_sync_request(0x00080001 | id << 8, sccb); + rc = sclp_sync_request(0x00080001 | id << 8, sccb); if (rc) goto out; switch (sccb->header.response_code) { @@ -627,7 +627,7 @@ static int __init sclp_detect_standby_memory(void) for (id = 0; id <= sclp_max_storage_id; id++) { memset(sccb, 0, PAGE_SIZE); sccb->header.length = PAGE_SIZE; - rc = do_sync_request(0x00040001 | id << 8, sccb); + rc = sclp_sync_request(0x00040001 | id << 8, sccb); if (rc) goto out; switch (sccb->header.response_code) { @@ -668,7 +668,7 @@ static int __init sclp_detect_standby_memory(void) if (rc) goto out; sclp_pdev = platform_device_register_simple("sclp_mem", -1, NULL, 0); - rc = IS_ERR(sclp_pdev) ? PTR_ERR(sclp_pdev) : 0; + rc = PTR_RET(sclp_pdev); if (rc) goto out_driver; sclp_add_standby_memory(); @@ -714,7 +714,7 @@ static int do_pci_configure(sclp_cmdw_t cmd, u32 fid) sccb->header.length = PAGE_SIZE; sccb->atype = SCLP_RECONFIG_PCI_ATPYE; sccb->aid = fid; - rc = do_sync_request(cmd, sccb); + rc = sclp_sync_request(cmd, sccb); if (rc) goto out; switch (sccb->header.response_code) { @@ -771,7 +771,7 @@ static int do_chp_configure(sclp_cmdw_t cmd) if (!sccb) return -ENOMEM; sccb->header.length = sizeof(*sccb); - rc = do_sync_request(cmd, sccb); + rc = sclp_sync_request(cmd, sccb); if (rc) goto out; switch (sccb->header.response_code) { @@ -846,7 +846,7 @@ int sclp_chp_read_info(struct sclp_chp_info *info) if (!sccb) return -ENOMEM; sccb->header.length = sizeof(*sccb); - rc = do_sync_request(SCLP_CMDW_READ_CHPATH_INFORMATION, sccb); + rc = sclp_sync_request(SCLP_CMDW_READ_CHPATH_INFORMATION, sccb); if (rc) goto out; if (sccb->header.response_code != 0x0010) { diff --git a/drivers/s390/char/sclp_con.c b/drivers/s390/char/sclp_con.c index ecf45c54f8c4..5880def98fc1 100644 --- a/drivers/s390/char/sclp_con.c +++ b/drivers/s390/char/sclp_con.c @@ -130,6 +130,31 @@ sclp_console_timeout(unsigned long data) } /* + * Drop oldest console buffer if sclp_con_drop is set + */ +static int +sclp_console_drop_buffer(void) +{ + struct list_head *list; + struct sclp_buffer *buffer; + void *page; + + if (!sclp_console_drop) + return 0; + list = sclp_con_outqueue.next; + if (sclp_con_queue_running) + /* The first element is in I/O */ + list = list->next; + if (list == &sclp_con_outqueue) + return 0; + list_del(list); + buffer = list_entry(list, struct sclp_buffer, list); + page = sclp_unmake_buffer(buffer); + list_add_tail((struct list_head *) page, &sclp_con_pages); + return 1; +} + +/* * Writes the given message to S390 system console */ static void @@ -150,9 +175,13 @@ sclp_console_write(struct console *console, const char *message, do { /* make sure we have a console output buffer */ if (sclp_conbuf == NULL) { + if (list_empty(&sclp_con_pages)) + sclp_console_full++; while (list_empty(&sclp_con_pages)) { if (sclp_con_suspended) goto out; + if (sclp_console_drop_buffer()) + break; spin_unlock_irqrestore(&sclp_con_lock, flags); sclp_sync_wait(); spin_lock_irqsave(&sclp_con_lock, flags); @@ -297,7 +326,7 @@ sclp_console_init(void) return rc; /* Allocate pages for output buffering */ INIT_LIST_HEAD(&sclp_con_pages); - for (i = 0; i < MAX_CONSOLE_PAGES; i++) { + for (i = 0; i < sclp_console_pages; i++) { page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); list_add_tail(page, &sclp_con_pages); } diff --git a/drivers/s390/char/sclp_ctl.c b/drivers/s390/char/sclp_ctl.c new file mode 100644 index 000000000000..648cb86afd42 --- /dev/null +++ b/drivers/s390/char/sclp_ctl.c @@ -0,0 +1,144 @@ +/* + * IOCTL interface for SCLP + * + * Copyright IBM Corp. 2012 + * + * Author: Michael Holzheu <holzheu@linux.vnet.ibm.com> + */ + +#include <linux/compat.h> +#include <linux/uaccess.h> +#include <linux/miscdevice.h> +#include <linux/gfp.h> +#include <linux/module.h> +#include <linux/ioctl.h> +#include <linux/fs.h> +#include <asm/compat.h> +#include <asm/sclp_ctl.h> +#include <asm/sclp.h> + +#include "sclp.h" + +/* + * Supported command words + */ +static unsigned int sclp_ctl_sccb_wlist[] = { + 0x00400002, + 0x00410002, +}; + +/* + * Check if command word is supported + */ +static int sclp_ctl_cmdw_supported(unsigned int cmdw) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sclp_ctl_sccb_wlist); i++) { + if (cmdw == sclp_ctl_sccb_wlist[i]) + return 1; + } + return 0; +} + +static void __user *u64_to_uptr(u64 value) +{ + if (is_compat_task()) + return compat_ptr(value); + else + return (void __user *)(unsigned long)value; +} + +/* + * Start SCLP request + */ +static int sclp_ctl_ioctl_sccb(void __user *user_area) +{ + struct sclp_ctl_sccb ctl_sccb; + struct sccb_header *sccb; + int rc; + + if (copy_from_user(&ctl_sccb, user_area, sizeof(ctl_sccb))) + return -EFAULT; + if (!sclp_ctl_cmdw_supported(ctl_sccb.cmdw)) + return -EOPNOTSUPP; + sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!sccb) + return -ENOMEM; + if (copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), sizeof(*sccb))) { + rc = -EFAULT; + goto out_free; + } + if (sccb->length > PAGE_SIZE || sccb->length < 8) + return -EINVAL; + if (copy_from_user(sccb, u64_to_uptr(ctl_sccb.sccb), sccb->length)) { + rc = -EFAULT; + goto out_free; + } + rc = sclp_sync_request(ctl_sccb.cmdw, sccb); + if (rc) + goto out_free; + if (copy_to_user(u64_to_uptr(ctl_sccb.sccb), sccb, sccb->length)) + rc = -EFAULT; +out_free: + free_page((unsigned long) sccb); + return rc; +} + +/* + * SCLP SCCB ioctl function + */ +static long sclp_ctl_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + void __user *argp; + + if (is_compat_task()) + argp = compat_ptr(arg); + else + argp = (void __user *) arg; + switch (cmd) { + case SCLP_CTL_SCCB: + return sclp_ctl_ioctl_sccb(argp); + default: /* unknown ioctl number */ + return -ENOTTY; + } +} + +/* + * File operations + */ +static const struct file_operations sclp_ctl_fops = { + .owner = THIS_MODULE, + .open = nonseekable_open, + .unlocked_ioctl = sclp_ctl_ioctl, + .compat_ioctl = sclp_ctl_ioctl, + .llseek = no_llseek, +}; + +/* + * Misc device definition + */ +static struct miscdevice sclp_ctl_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "sclp", + .fops = &sclp_ctl_fops, +}; + +/* + * Register sclp_ctl misc device + */ +static int __init sclp_ctl_init(void) +{ + return misc_register(&sclp_ctl_device); +} +module_init(sclp_ctl_init); + +/* + * Deregister sclp_ctl misc device + */ +static void __exit sclp_ctl_exit(void) +{ + misc_deregister(&sclp_ctl_device); +} +module_exit(sclp_ctl_exit); diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index 5aaaa2ec8df4..4eed38cd0af6 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -362,6 +362,31 @@ sclp_vt220_timeout(unsigned long data) #define BUFFER_MAX_DELAY HZ/20 +/* + * Drop oldest console buffer if sclp_con_drop is set + */ +static int +sclp_vt220_drop_buffer(void) +{ + struct list_head *list; + struct sclp_vt220_request *request; + void *page; + + if (!sclp_console_drop) + return 0; + list = sclp_vt220_outqueue.next; + if (sclp_vt220_queue_running) + /* The first element is in I/O */ + list = list->next; + if (list == &sclp_vt220_outqueue) + return 0; + list_del(list); + request = list_entry(list, struct sclp_vt220_request, list); + page = request->sclp_req.sccb; + list_add_tail((struct list_head *) page, &sclp_vt220_empty); + return 1; +} + /* * Internal implementation of the write function. Write COUNT bytes of data * from memory at BUF @@ -390,12 +415,16 @@ __sclp_vt220_write(const unsigned char *buf, int count, int do_schedule, do { /* Create an sclp output buffer if none exists yet */ if (sclp_vt220_current_request == NULL) { + if (list_empty(&sclp_vt220_empty)) + sclp_console_full++; while (list_empty(&sclp_vt220_empty)) { - spin_unlock_irqrestore(&sclp_vt220_lock, flags); if (may_fail || sclp_vt220_suspended) goto out; - else - sclp_sync_wait(); + if (sclp_vt220_drop_buffer()) + break; + spin_unlock_irqrestore(&sclp_vt220_lock, flags); + + sclp_sync_wait(); spin_lock_irqsave(&sclp_vt220_lock, flags); } page = (void *) sclp_vt220_empty.next; @@ -428,8 +457,8 @@ __sclp_vt220_write(const unsigned char *buf, int count, int do_schedule, sclp_vt220_timer.expires = jiffies + BUFFER_MAX_DELAY; add_timer(&sclp_vt220_timer); } - spin_unlock_irqrestore(&sclp_vt220_lock, flags); out: + spin_unlock_irqrestore(&sclp_vt220_lock, flags); return overall_written; } @@ -803,7 +832,7 @@ sclp_vt220_con_init(void) if (!CONSOLE_IS_SCLP) return 0; - rc = __sclp_vt220_init(MAX_CONSOLE_PAGES); + rc = __sclp_vt220_init(sclp_console_pages); if (rc) return rc; /* Attach linux console */ diff --git a/drivers/s390/char/tape_class.c b/drivers/s390/char/tape_class.c index 54b3c79203f5..91c3c642c76e 100644 --- a/drivers/s390/char/tape_class.c +++ b/drivers/s390/char/tape_class.c @@ -77,7 +77,7 @@ struct tape_class_device *register_tape_dev( tcd->class_device = device_create(tape_class, device, tcd->char_device->dev, NULL, "%s", tcd->device_name); - rc = IS_ERR(tcd->class_device) ? PTR_ERR(tcd->class_device) : 0; + rc = PTR_RET(tcd->class_device); if (rc) goto fail_with_cdev; rc = sysfs_create_link( diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c index e9b72311e254..d5eac985976b 100644 --- a/drivers/s390/char/vmwatchdog.c +++ b/drivers/s390/char/vmwatchdog.c @@ -112,7 +112,8 @@ static int vmwdt_keepalive(void) static int vmwdt_disable(void) { - int ret = __diag288(wdt_cancel, 0, "", 0); + char cmd[] = {'\0'}; + int ret = __diag288(wdt_cancel, 0, cmd, 0); WARN_ON(ret != 0); clear_bit(VMWDT_RUNNING, &vmwdt_is_open); return ret; @@ -124,7 +125,7 @@ static int __init vmwdt_probe(void) * so we try initializing it with a NOP command ("BEGIN") * that won't cause any harm even if the following disable * fails for some reason */ - static char __initdata ebc_begin[] = { + char ebc_begin[] = { 194, 197, 199, 201, 213 }; if (__diag288(wdt_init, 15, ebc_begin, sizeof(ebc_begin)) != 0) diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c index bc10220f6847..91edbd7ee806 100644 --- a/drivers/s390/cio/airq.c +++ b/drivers/s390/cio/airq.c @@ -9,142 +9,87 @@ */ #include <linux/init.h> +#include <linux/irq.h> +#include <linux/kernel_stat.h> #include <linux/module.h> +#include <linux/mutex.h> +#include <linux/rculist.h> #include <linux/slab.h> -#include <linux/rcupdate.h> #include <asm/airq.h> #include <asm/isc.h> #include "cio.h" #include "cio_debug.h" +#include "ioasm.h" -#define NR_AIRQS 32 -#define NR_AIRQS_PER_WORD sizeof(unsigned long) -#define NR_AIRQ_WORDS (NR_AIRQS / NR_AIRQS_PER_WORD) - -union indicator_t { - unsigned long word[NR_AIRQ_WORDS]; - unsigned char byte[NR_AIRQS]; -} __attribute__((packed)); - -struct airq_t { - adapter_int_handler_t handler; - void *drv_data; -}; - -static union indicator_t indicators[MAX_ISC+1]; -static struct airq_t *airqs[MAX_ISC+1][NR_AIRQS]; - -static int register_airq(struct airq_t *airq, u8 isc) -{ - int i; - - for (i = 0; i < NR_AIRQS; i++) - if (!cmpxchg(&airqs[isc][i], NULL, airq)) - return i; - return -ENOMEM; -} +static DEFINE_SPINLOCK(airq_lists_lock); +static struct hlist_head airq_lists[MAX_ISC+1]; /** - * s390_register_adapter_interrupt() - register adapter interrupt handler - * @handler: adapter handler to be registered - * @drv_data: driver data passed with each call to the handler - * @isc: isc for which the handler should be called + * register_adapter_interrupt() - register adapter interrupt handler + * @airq: pointer to adapter interrupt descriptor * - * Returns: - * Pointer to the indicator to be used on success - * ERR_PTR() if registration failed + * Returns 0 on success, or -EINVAL. */ -void *s390_register_adapter_interrupt(adapter_int_handler_t handler, - void *drv_data, u8 isc) +int register_adapter_interrupt(struct airq_struct *airq) { - struct airq_t *airq; - char dbf_txt[16]; - int ret; - - if (isc > MAX_ISC) - return ERR_PTR(-EINVAL); - airq = kmalloc(sizeof(struct airq_t), GFP_KERNEL); - if (!airq) { - ret = -ENOMEM; - goto out; + char dbf_txt[32]; + + if (!airq->handler || airq->isc > MAX_ISC) + return -EINVAL; + if (!airq->lsi_ptr) { + airq->lsi_ptr = kzalloc(1, GFP_KERNEL); + if (!airq->lsi_ptr) + return -ENOMEM; + airq->flags |= AIRQ_PTR_ALLOCATED; } - airq->handler = handler; - airq->drv_data = drv_data; - - ret = register_airq(airq, isc); -out: - snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%d", ret); + if (!airq->lsi_mask) + airq->lsi_mask = 0xff; + snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%p", airq); CIO_TRACE_EVENT(4, dbf_txt); - if (ret < 0) { - kfree(airq); - return ERR_PTR(ret); - } else - return &indicators[isc].byte[ret]; + isc_register(airq->isc); + spin_lock(&airq_lists_lock); + hlist_add_head_rcu(&airq->list, &airq_lists[airq->isc]); + spin_unlock(&airq_lists_lock); + return 0; } -EXPORT_SYMBOL(s390_register_adapter_interrupt); +EXPORT_SYMBOL(register_adapter_interrupt); /** - * s390_unregister_adapter_interrupt - unregister adapter interrupt handler - * @ind: indicator for which the handler is to be unregistered - * @isc: interruption subclass + * unregister_adapter_interrupt - unregister adapter interrupt handler + * @airq: pointer to adapter interrupt descriptor */ -void s390_unregister_adapter_interrupt(void *ind, u8 isc) +void unregister_adapter_interrupt(struct airq_struct *airq) { - struct airq_t *airq; - char dbf_txt[16]; - int i; + char dbf_txt[32]; - i = (int) ((addr_t) ind) - ((addr_t) &indicators[isc].byte[0]); - snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%d", i); + if (hlist_unhashed(&airq->list)) + return; + snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%p", airq); CIO_TRACE_EVENT(4, dbf_txt); - indicators[isc].byte[i] = 0; - airq = xchg(&airqs[isc][i], NULL); - /* - * Allow interrupts to complete. This will ensure that the airq handle - * is no longer referenced by any interrupt handler. - */ - synchronize_sched(); - kfree(airq); + spin_lock(&airq_lists_lock); + hlist_del_rcu(&airq->list); + spin_unlock(&airq_lists_lock); + synchronize_rcu(); + isc_unregister(airq->isc); + if (airq->flags & AIRQ_PTR_ALLOCATED) { + kfree(airq->lsi_ptr); + airq->lsi_ptr = NULL; + airq->flags &= ~AIRQ_PTR_ALLOCATED; + } } -EXPORT_SYMBOL(s390_unregister_adapter_interrupt); - -#define INDICATOR_MASK (0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8)) +EXPORT_SYMBOL(unregister_adapter_interrupt); void do_adapter_IO(u8 isc) { - int w; - int i; - unsigned long word; - struct airq_t *airq; - - /* - * Access indicator array in word-sized chunks to minimize storage - * fetch operations. - */ - for (w = 0; w < NR_AIRQ_WORDS; w++) { - word = indicators[isc].word[w]; - i = w * NR_AIRQS_PER_WORD; - /* - * Check bytes within word for active indicators. - */ - while (word) { - if (word & INDICATOR_MASK) { - airq = airqs[isc][i]; - /* Make sure gcc reads from airqs only once. */ - barrier(); - if (likely(airq)) - airq->handler(&indicators[isc].byte[i], - airq->drv_data); - else - /* - * Reset ill-behaved indicator. - */ - indicators[isc].byte[i] = 0; - } - word <<= 8; - i++; - } - } + struct airq_struct *airq; + struct hlist_head *head; + + head = &airq_lists[isc]; + rcu_read_lock(); + hlist_for_each_entry_rcu(airq, head, list) + if ((*airq->lsi_ptr & airq->lsi_mask) != 0) + airq->handler(airq); + rcu_read_unlock(); } diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 8ea7d9b2c671..13299f902676 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -20,6 +20,7 @@ #include <asm/chpid.h> #include <asm/chsc.h> #include <asm/crw.h> +#include <asm/isc.h> #include "css.h" #include "cio.h" @@ -144,6 +145,65 @@ out: return ret; } +/** + * chsc_ssqd() - store subchannel QDIO data (SSQD) + * @schid: id of the subchannel on which SSQD is performed + * @ssqd: request and response block for SSQD + * + * Returns 0 on success. + */ +int chsc_ssqd(struct subchannel_id schid, struct chsc_ssqd_area *ssqd) +{ + memset(ssqd, 0, sizeof(*ssqd)); + ssqd->request.length = 0x0010; + ssqd->request.code = 0x0024; + ssqd->first_sch = schid.sch_no; + ssqd->last_sch = schid.sch_no; + ssqd->ssid = schid.ssid; + + if (chsc(ssqd)) + return -EIO; + + return chsc_error_from_response(ssqd->response.code); +} +EXPORT_SYMBOL_GPL(chsc_ssqd); + +/** + * chsc_sadc() - set adapter device controls (SADC) + * @schid: id of the subchannel on which SADC is performed + * @scssc: request and response block for SADC + * @summary_indicator_addr: summary indicator address + * @subchannel_indicator_addr: subchannel indicator address + * + * Returns 0 on success. + */ +int chsc_sadc(struct subchannel_id schid, struct chsc_scssc_area *scssc, + u64 summary_indicator_addr, u64 subchannel_indicator_addr) +{ + memset(scssc, 0, sizeof(*scssc)); + scssc->request.length = 0x0fe0; + scssc->request.code = 0x0021; + scssc->operation_code = 0; + + scssc->summary_indicator_addr = summary_indicator_addr; + scssc->subchannel_indicator_addr = subchannel_indicator_addr; + + scssc->ks = PAGE_DEFAULT_KEY >> 4; + scssc->kc = PAGE_DEFAULT_KEY >> 4; + scssc->isc = QDIO_AIRQ_ISC; + scssc->schid = schid; + + /* enable the time delay disablement facility */ + if (css_general_characteristics.aif_tdd) + scssc->word_with_d_bit = 0x10000000; + + if (chsc(scssc)) + return -EIO; + + return chsc_error_from_response(scssc->response.code); +} +EXPORT_SYMBOL_GPL(chsc_sadc); + static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data) { spin_lock_irq(sch->lock); diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index e7ef2a683b8f..23d072e70eb2 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -7,14 +7,10 @@ #include <asm/chpid.h> #include <asm/chsc.h> #include <asm/schid.h> +#include <asm/qdio.h> #define CHSC_SDA_OC_MSS 0x2 -struct chsc_header { - u16 length; - u16 code; -} __attribute__ ((packed)); - #define NR_MEASUREMENT_CHARS 5 struct cmg_chars { u32 values[NR_MEASUREMENT_CHARS]; @@ -77,6 +73,40 @@ struct chsc_ssd_info { u16 fla[8]; }; +struct chsc_ssqd_area { + struct chsc_header request; + u16:10; + u8 ssid:2; + u8 fmt:4; + u16 first_sch; + u16:16; + u16 last_sch; + u32:32; + struct chsc_header response; + u32:32; + struct qdio_ssqd_desc qdio_ssqd; +} __packed; + +struct chsc_scssc_area { + struct chsc_header request; + u16 operation_code; + u16:16; + u32:32; + u32:32; + u64 summary_indicator_addr; + u64 subchannel_indicator_addr; + u32 ks:4; + u32 kc:4; + u32:21; + u32 isc:3; + u32 word_with_d_bit; + u32:32; + struct subchannel_id schid; + u32 reserved[1004]; + struct chsc_header response; + u32:32; +} __packed; + struct chsc_scpd { struct chsc_header request; u32:2; @@ -116,7 +146,9 @@ int chsc_determine_fmt1_channel_path_desc(struct chp_id chpid, void chsc_chp_online(struct chp_id chpid); void chsc_chp_offline(struct chp_id chpid); int chsc_get_channel_measurement_chars(struct channel_path *chp); - +int chsc_ssqd(struct subchannel_id schid, struct chsc_ssqd_area *ssqd); +int chsc_sadc(struct subchannel_id schid, struct chsc_scssc_area *scssc, + u64 summary_indicator_addr, u64 subchannel_indicator_addr); int chsc_error_from_response(int response); int chsc_siosl(struct subchannel_id schid); diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c index facdf809113f..7b29d0be0ca3 100644 --- a/drivers/s390/cio/chsc_sch.c +++ b/drivers/s390/cio/chsc_sch.c @@ -29,6 +29,10 @@ static debug_info_t *chsc_debug_msg_id; static debug_info_t *chsc_debug_log_id; +static struct chsc_request *on_close_request; +static struct chsc_async_area *on_close_chsc_area; +static DEFINE_MUTEX(on_close_mutex); + #define CHSC_MSG(imp, args...) do { \ debug_sprintf_event(chsc_debug_msg_id, imp , ##args); \ } while (0) @@ -258,7 +262,7 @@ static int chsc_async(struct chsc_async_area *chsc_area, CHSC_LOG(2, "schid"); CHSC_LOG_HEX(2, &sch->schid, sizeof(sch->schid)); cc = chsc(chsc_area); - sprintf(dbf, "cc:%d", cc); + snprintf(dbf, sizeof(dbf), "cc:%d", cc); CHSC_LOG(2, dbf); switch (cc) { case 0: @@ -287,11 +291,11 @@ static int chsc_async(struct chsc_async_area *chsc_area, return ret; } -static void chsc_log_command(struct chsc_async_area *chsc_area) +static void chsc_log_command(void *chsc_area) { char dbf[10]; - sprintf(dbf, "CHSC:%x", chsc_area->header.code); + snprintf(dbf, sizeof(dbf), "CHSC:%x", ((uint16_t *)chsc_area)[1]); CHSC_LOG(0, dbf); CHSC_LOG_HEX(0, chsc_area, 32); } @@ -355,13 +359,106 @@ static int chsc_ioctl_start(void __user *user_area) if (copy_to_user(user_area, chsc_area, PAGE_SIZE)) ret = -EFAULT; out_free: - sprintf(dbf, "ret:%d", ret); + snprintf(dbf, sizeof(dbf), "ret:%d", ret); CHSC_LOG(0, dbf); kfree(request); free_page((unsigned long)chsc_area); return ret; } +static int chsc_ioctl_on_close_set(void __user *user_area) +{ + char dbf[13]; + int ret; + + mutex_lock(&on_close_mutex); + if (on_close_chsc_area) { + ret = -EBUSY; + goto out_unlock; + } + on_close_request = kzalloc(sizeof(*on_close_request), GFP_KERNEL); + if (!on_close_request) { + ret = -ENOMEM; + goto out_unlock; + } + on_close_chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL); + if (!on_close_chsc_area) { + ret = -ENOMEM; + goto out_free_request; + } + if (copy_from_user(on_close_chsc_area, user_area, PAGE_SIZE)) { + ret = -EFAULT; + goto out_free_chsc; + } + ret = 0; + goto out_unlock; + +out_free_chsc: + free_page((unsigned long)on_close_chsc_area); + on_close_chsc_area = NULL; +out_free_request: + kfree(on_close_request); + on_close_request = NULL; +out_unlock: + mutex_unlock(&on_close_mutex); + snprintf(dbf, sizeof(dbf), "ocsret:%d", ret); + CHSC_LOG(0, dbf); + return ret; +} + +static int chsc_ioctl_on_close_remove(void) +{ + char dbf[13]; + int ret; + + mutex_lock(&on_close_mutex); + if (!on_close_chsc_area) { + ret = -ENOENT; + goto out_unlock; + } + free_page((unsigned long)on_close_chsc_area); + on_close_chsc_area = NULL; + kfree(on_close_request); + on_close_request = NULL; + ret = 0; +out_unlock: + mutex_unlock(&on_close_mutex); + snprintf(dbf, sizeof(dbf), "ocrret:%d", ret); + CHSC_LOG(0, dbf); + return ret; +} + +static int chsc_ioctl_start_sync(void __user *user_area) +{ + struct chsc_sync_area *chsc_area; + int ret, ccode; + + chsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!chsc_area) + return -ENOMEM; + if (copy_from_user(chsc_area, user_area, PAGE_SIZE)) { + ret = -EFAULT; + goto out_free; + } + if (chsc_area->header.code & 0x4000) { + ret = -EINVAL; + goto out_free; + } + chsc_log_command(chsc_area); + ccode = chsc(chsc_area); + if (ccode != 0) { + ret = -EIO; + goto out_free; + } + if (copy_to_user(user_area, chsc_area, PAGE_SIZE)) + ret = -EFAULT; + else + ret = 0; +out_free: + free_page((unsigned long)chsc_area); + return ret; +} + static int chsc_ioctl_info_channel_path(void __user *user_cd) { struct chsc_chp_cd *cd; @@ -795,6 +892,8 @@ static long chsc_ioctl(struct file *filp, unsigned int cmd, switch (cmd) { case CHSC_START: return chsc_ioctl_start(argp); + case CHSC_START_SYNC: + return chsc_ioctl_start_sync(argp); case CHSC_INFO_CHANNEL_PATH: return chsc_ioctl_info_channel_path(argp); case CHSC_INFO_CU: @@ -809,14 +908,60 @@ static long chsc_ioctl(struct file *filp, unsigned int cmd, return chsc_ioctl_chpd(argp); case CHSC_INFO_DCAL: return chsc_ioctl_dcal(argp); + case CHSC_ON_CLOSE_SET: + return chsc_ioctl_on_close_set(argp); + case CHSC_ON_CLOSE_REMOVE: + return chsc_ioctl_on_close_remove(); default: /* unknown ioctl number */ return -ENOIOCTLCMD; } } +static atomic_t chsc_ready_for_use = ATOMIC_INIT(1); + +static int chsc_open(struct inode *inode, struct file *file) +{ + if (!atomic_dec_and_test(&chsc_ready_for_use)) { + atomic_inc(&chsc_ready_for_use); + return -EBUSY; + } + return nonseekable_open(inode, file); +} + +static int chsc_release(struct inode *inode, struct file *filp) +{ + char dbf[13]; + int ret; + + mutex_lock(&on_close_mutex); + if (!on_close_chsc_area) + goto out_unlock; + init_completion(&on_close_request->completion); + CHSC_LOG(0, "on_close"); + chsc_log_command(on_close_chsc_area); + spin_lock_irq(&chsc_lock); + ret = chsc_async(on_close_chsc_area, on_close_request); + spin_unlock_irq(&chsc_lock); + if (ret == -EINPROGRESS) { + wait_for_completion(&on_close_request->completion); + ret = chsc_examine_irb(on_close_request); + } + snprintf(dbf, sizeof(dbf), "relret:%d", ret); + CHSC_LOG(0, dbf); + free_page((unsigned long)on_close_chsc_area); + on_close_chsc_area = NULL; + kfree(on_close_request); + on_close_request = NULL; +out_unlock: + mutex_unlock(&on_close_mutex); + atomic_inc(&chsc_ready_for_use); + return 0; +} + static const struct file_operations chsc_fops = { .owner = THIS_MODULE, - .open = nonseekable_open, + .open = chsc_open, + .release = chsc_release, .unlocked_ioctl = chsc_ioctl, .compat_ioctl = chsc_ioctl, .llseek = no_llseek, diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 935d80b4e9ce..4eeb4a6bf207 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -568,7 +568,7 @@ out: */ void __irq_entry do_IRQ(struct pt_regs *regs) { - struct tpi_info *tpi_info; + struct tpi_info *tpi_info = (struct tpi_info *) ®s->int_code; struct subchannel *sch; struct irb *irb; struct pt_regs *old_regs; @@ -579,46 +579,34 @@ void __irq_entry do_IRQ(struct pt_regs *regs) if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator) /* Serve timer interrupts first. */ clock_comparator_work(); - /* - * Get interrupt information from lowcore - */ - tpi_info = (struct tpi_info *)&S390_lowcore.subchannel_id; - irb = (struct irb *)&S390_lowcore.irb; - do { - kstat_incr_irqs_this_cpu(IO_INTERRUPT, NULL); - if (tpi_info->adapter_IO) { - do_adapter_IO(tpi_info->isc); - continue; - } - sch = (struct subchannel *)(unsigned long)tpi_info->intparm; - if (!sch) { - /* Clear pending interrupt condition. */ - inc_irq_stat(IRQIO_CIO); - tsch(tpi_info->schid, irb); - continue; - } - spin_lock(sch->lock); - /* Store interrupt response block to lowcore. */ - if (tsch(tpi_info->schid, irb) == 0) { - /* Keep subchannel information word up to date. */ - memcpy (&sch->schib.scsw, &irb->scsw, - sizeof (irb->scsw)); - /* Call interrupt handler if there is one. */ - if (sch->driver && sch->driver->irq) - sch->driver->irq(sch); - else - inc_irq_stat(IRQIO_CIO); - } else + + kstat_incr_irqs_this_cpu(IO_INTERRUPT, NULL); + irb = (struct irb *) &S390_lowcore.irb; + if (tpi_info->adapter_IO) { + do_adapter_IO(tpi_info->isc); + goto out; + } + sch = (struct subchannel *)(unsigned long) tpi_info->intparm; + if (!sch) { + /* Clear pending interrupt condition. */ + inc_irq_stat(IRQIO_CIO); + tsch(tpi_info->schid, irb); + goto out; + } + spin_lock(sch->lock); + /* Store interrupt response block to lowcore. */ + if (tsch(tpi_info->schid, irb) == 0) { + /* Keep subchannel information word up to date. */ + memcpy (&sch->schib.scsw, &irb->scsw, sizeof (irb->scsw)); + /* Call interrupt handler if there is one. */ + if (sch->driver && sch->driver->irq) + sch->driver->irq(sch); + else inc_irq_stat(IRQIO_CIO); - spin_unlock(sch->lock); - /* - * Are more interrupts pending? - * If so, the tpi instruction will update the lowcore - * to hold the info for the next interrupt. - * We don't do this for VM because a tpi drops the cpu - * out of the sie which costs more cycles than it saves. - */ - } while (MACHINE_IS_LPAR && tpi(NULL) != 0); + } else + inc_irq_stat(IRQIO_CIO); + spin_unlock(sch->lock); +out: irq_exit(); set_irq_regs(old_regs); } diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 5132554d7917..8acaae18bd11 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -140,40 +140,6 @@ struct siga_flag { u8:3; } __attribute__ ((packed)); -struct chsc_ssqd_area { - struct chsc_header request; - u16:10; - u8 ssid:2; - u8 fmt:4; - u16 first_sch; - u16:16; - u16 last_sch; - u32:32; - struct chsc_header response; - u32:32; - struct qdio_ssqd_desc qdio_ssqd; -} __attribute__ ((packed)); - -struct scssc_area { - struct chsc_header request; - u16 operation_code; - u16:16; - u32:32; - u32:32; - u64 summary_indicator_addr; - u64 subchannel_indicator_addr; - u32 ks:4; - u32 kc:4; - u32:21; - u32 isc:3; - u32 word_with_d_bit; - u32:32; - struct subchannel_id schid; - u32 reserved[1004]; - struct chsc_header response; - u32:32; -} __attribute__ ((packed)); - struct qdio_dev_perf_stat { unsigned int adapter_int; unsigned int qdio_int; diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 843051bc20f1..fb1c1e0483ed 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -608,50 +608,6 @@ static inline int contains_aobs(struct qdio_q *q) return !q->is_input_q && q->u.out.use_cq; } -static inline void qdio_trace_aob(struct qdio_irq *irq, struct qdio_q *q, - int i, struct qaob *aob) -{ - int tmp; - - DBF_DEV_EVENT(DBF_INFO, irq, "AOB%d:%lx", i, - (unsigned long) virt_to_phys(aob)); - DBF_DEV_EVENT(DBF_INFO, irq, "RES00:%lx", - (unsigned long) aob->res0[0]); - DBF_DEV_EVENT(DBF_INFO, irq, "RES01:%lx", - (unsigned long) aob->res0[1]); - DBF_DEV_EVENT(DBF_INFO, irq, "RES02:%lx", - (unsigned long) aob->res0[2]); - DBF_DEV_EVENT(DBF_INFO, irq, "RES03:%lx", - (unsigned long) aob->res0[3]); - DBF_DEV_EVENT(DBF_INFO, irq, "RES04:%lx", - (unsigned long) aob->res0[4]); - DBF_DEV_EVENT(DBF_INFO, irq, "RES05:%lx", - (unsigned long) aob->res0[5]); - DBF_DEV_EVENT(DBF_INFO, irq, "RES1:%x", aob->res1); - DBF_DEV_EVENT(DBF_INFO, irq, "RES2:%x", aob->res2); - DBF_DEV_EVENT(DBF_INFO, irq, "RES3:%x", aob->res3); - DBF_DEV_EVENT(DBF_INFO, irq, "AORC:%u", aob->aorc); - DBF_DEV_EVENT(DBF_INFO, irq, "FLAGS:%u", aob->flags); - DBF_DEV_EVENT(DBF_INFO, irq, "CBTBS:%u", aob->cbtbs); - DBF_DEV_EVENT(DBF_INFO, irq, "SBC:%u", aob->sb_count); - for (tmp = 0; tmp < QDIO_MAX_ELEMENTS_PER_BUFFER; ++tmp) { - DBF_DEV_EVENT(DBF_INFO, irq, "SBA%d:%lx", tmp, - (unsigned long) aob->sba[tmp]); - DBF_DEV_EVENT(DBF_INFO, irq, "rSBA%d:%lx", tmp, - (unsigned long) q->sbal[i]->element[tmp].addr); - DBF_DEV_EVENT(DBF_INFO, irq, "DC%d:%u", tmp, aob->dcount[tmp]); - DBF_DEV_EVENT(DBF_INFO, irq, "rDC%d:%u", tmp, - q->sbal[i]->element[tmp].length); - } - DBF_DEV_EVENT(DBF_INFO, irq, "USER0:%lx", (unsigned long) aob->user0); - for (tmp = 0; tmp < 2; ++tmp) { - DBF_DEV_EVENT(DBF_INFO, irq, "RES4%d:%lx", tmp, - (unsigned long) aob->res4[tmp]); - } - DBF_DEV_EVENT(DBF_INFO, irq, "USER1:%lx", (unsigned long) aob->user1); - DBF_DEV_EVENT(DBF_INFO, irq, "USER2:%lx", (unsigned long) aob->user2); -} - static inline void qdio_handle_aobs(struct qdio_q *q, int start, int count) { unsigned char state = 0; diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c index 16ecd35b8e51..f5f4a91fab44 100644 --- a/drivers/s390/cio/qdio_setup.c +++ b/drivers/s390/cio/qdio_setup.c @@ -254,40 +254,31 @@ int qdio_setup_get_ssqd(struct qdio_irq *irq_ptr, int rc; DBF_EVENT("getssqd:%4x", schid->sch_no); - if (irq_ptr != NULL) - ssqd = (struct chsc_ssqd_area *)irq_ptr->chsc_page; - else + if (!irq_ptr) { ssqd = (struct chsc_ssqd_area *)__get_free_page(GFP_KERNEL); - memset(ssqd, 0, PAGE_SIZE); - - ssqd->request = (struct chsc_header) { - .length = 0x0010, - .code = 0x0024, - }; - ssqd->first_sch = schid->sch_no; - ssqd->last_sch = schid->sch_no; - ssqd->ssid = schid->ssid; - - if (chsc(ssqd)) - return -EIO; - rc = chsc_error_from_response(ssqd->response.code); + if (!ssqd) + return -ENOMEM; + } else { + ssqd = (struct chsc_ssqd_area *)irq_ptr->chsc_page; + } + + rc = chsc_ssqd(*schid, ssqd); if (rc) - return rc; + goto out; if (!(ssqd->qdio_ssqd.flags & CHSC_FLAG_QDIO_CAPABILITY) || !(ssqd->qdio_ssqd.flags & CHSC_FLAG_VALIDITY) || (ssqd->qdio_ssqd.sch != schid->sch_no)) - return -EINVAL; - - if (irq_ptr != NULL) - memcpy(&irq_ptr->ssqd_desc, &ssqd->qdio_ssqd, - sizeof(struct qdio_ssqd_desc)); - else { - memcpy(data, &ssqd->qdio_ssqd, - sizeof(struct qdio_ssqd_desc)); + rc = -EINVAL; + + if (!rc) + memcpy(data, &ssqd->qdio_ssqd, sizeof(*data)); + +out: + if (!irq_ptr) free_page((unsigned long)ssqd); - } - return 0; + + return rc; } void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr) @@ -295,7 +286,7 @@ void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr) unsigned char qdioac; int rc; - rc = qdio_setup_get_ssqd(irq_ptr, &irq_ptr->schid, NULL); + rc = qdio_setup_get_ssqd(irq_ptr, &irq_ptr->schid, &irq_ptr->ssqd_desc); if (rc) { DBF_ERROR("%4x ssqd ERR", irq_ptr->schid.sch_no); DBF_ERROR("rc:%x", rc); diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index bde5255200dc..5d06253c2a7a 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c @@ -36,8 +36,13 @@ struct indicator_t { static LIST_HEAD(tiq_list); static DEFINE_MUTEX(tiq_list_lock); -/* adapter local summary indicator */ -static u8 *tiqdio_alsi; +/* Adapter interrupt definitions */ +static void tiqdio_thinint_handler(struct airq_struct *airq); + +static struct airq_struct tiqdio_airq = { + .handler = tiqdio_thinint_handler, + .isc = QDIO_AIRQ_ISC, +}; static struct indicator_t *q_indicators; @@ -176,7 +181,7 @@ static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq) * @alsi: pointer to adapter local summary indicator * @data: NULL */ -static void tiqdio_thinint_handler(void *alsi, void *data) +static void tiqdio_thinint_handler(struct airq_struct *airq) { u32 si_used = clear_shared_ind(); struct qdio_q *q; @@ -208,51 +213,31 @@ static void tiqdio_thinint_handler(void *alsi, void *data) static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) { - struct scssc_area *scssc_area; + struct chsc_scssc_area *scssc = (void *)irq_ptr->chsc_page; + u64 summary_indicator_addr, subchannel_indicator_addr; int rc; - scssc_area = (struct scssc_area *)irq_ptr->chsc_page; - memset(scssc_area, 0, PAGE_SIZE); - if (reset) { - scssc_area->summary_indicator_addr = 0; - scssc_area->subchannel_indicator_addr = 0; + summary_indicator_addr = 0; + subchannel_indicator_addr = 0; } else { - scssc_area->summary_indicator_addr = virt_to_phys(tiqdio_alsi); - scssc_area->subchannel_indicator_addr = - virt_to_phys(irq_ptr->dsci); + summary_indicator_addr = virt_to_phys(tiqdio_airq.lsi_ptr); + subchannel_indicator_addr = virt_to_phys(irq_ptr->dsci); } - scssc_area->request = (struct chsc_header) { - .length = 0x0fe0, - .code = 0x0021, - }; - scssc_area->operation_code = 0; - scssc_area->ks = PAGE_DEFAULT_KEY >> 4; - scssc_area->kc = PAGE_DEFAULT_KEY >> 4; - scssc_area->isc = QDIO_AIRQ_ISC; - scssc_area->schid = irq_ptr->schid; - - /* enable the time delay disablement facility */ - if (css_general_characteristics.aif_tdd) - scssc_area->word_with_d_bit = 0x10000000; - - rc = chsc(scssc_area); - if (rc) - return -EIO; - - rc = chsc_error_from_response(scssc_area->response.code); + rc = chsc_sadc(irq_ptr->schid, scssc, summary_indicator_addr, + subchannel_indicator_addr); if (rc) { DBF_ERROR("%4x SSI r:%4x", irq_ptr->schid.sch_no, - scssc_area->response.code); - DBF_ERROR_HEX(&scssc_area->response, sizeof(void *)); - return rc; + scssc->response.code); + goto out; } DBF_EVENT("setscind"); - DBF_HEX(&scssc_area->summary_indicator_addr, sizeof(unsigned long)); - DBF_HEX(&scssc_area->subchannel_indicator_addr, sizeof(unsigned long)); - return 0; + DBF_HEX(&summary_indicator_addr, sizeof(summary_indicator_addr)); + DBF_HEX(&subchannel_indicator_addr, sizeof(subchannel_indicator_addr)); +out: + return rc; } /* allocate non-shared indicators and shared indicator */ @@ -272,14 +257,12 @@ void tiqdio_free_memory(void) int __init tiqdio_register_thinints(void) { - isc_register(QDIO_AIRQ_ISC); - tiqdio_alsi = s390_register_adapter_interrupt(&tiqdio_thinint_handler, - NULL, QDIO_AIRQ_ISC); - if (IS_ERR(tiqdio_alsi)) { - DBF_EVENT("RTI:%lx", PTR_ERR(tiqdio_alsi)); - tiqdio_alsi = NULL; - isc_unregister(QDIO_AIRQ_ISC); - return -ENOMEM; + int rc; + + rc = register_adapter_interrupt(&tiqdio_airq); + if (rc) { + DBF_EVENT("RTI:%x", rc); + return rc; } return 0; } @@ -312,9 +295,5 @@ void qdio_shutdown_thinint(struct qdio_irq *irq_ptr) void __exit tiqdio_unregister_thinints(void) { WARN_ON(!list_empty(&tiq_list)); - - if (tiqdio_alsi) { - s390_unregister_adapter_interrupt(tiqdio_alsi, QDIO_AIRQ_ISC); - isc_unregister(QDIO_AIRQ_ISC); - } + unregister_adapter_interrupt(&tiqdio_airq); } diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 9de41aa14896..f446a7705c3b 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -58,7 +58,7 @@ static inline void ap_schedule_poll_timer(void); static int __ap_poll_device(struct ap_device *ap_dev, unsigned long *flags); static int ap_device_remove(struct device *dev); static int ap_device_probe(struct device *dev); -static void ap_interrupt_handler(void *unused1, void *unused2); +static void ap_interrupt_handler(struct airq_struct *airq); static void ap_reset(struct ap_device *ap_dev); static void ap_config_timeout(unsigned long ptr); static int ap_select_domain(void); @@ -106,7 +106,6 @@ static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait); static struct task_struct *ap_poll_kthread = NULL; static DEFINE_MUTEX(ap_poll_thread_mutex); static DEFINE_SPINLOCK(ap_poll_timer_lock); -static void *ap_interrupt_indicator; static struct hrtimer ap_poll_timer; /* In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds. * If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.*/ @@ -120,13 +119,21 @@ static int ap_suspend_flag; static int user_set_domain = 0; static struct bus_type ap_bus_type; +/* Adapter interrupt definitions */ +static int ap_airq_flag; + +static struct airq_struct ap_airq = { + .handler = ap_interrupt_handler, + .isc = AP_ISC, +}; + /** * ap_using_interrupts() - Returns non-zero if interrupt support is * available. */ static inline int ap_using_interrupts(void) { - return ap_interrupt_indicator != NULL; + return ap_airq_flag; } /** @@ -588,7 +595,7 @@ static int ap_init_queue(ap_qid_t qid) } } if (rc == 0 && ap_using_interrupts()) { - rc = ap_queue_enable_interruption(qid, ap_interrupt_indicator); + rc = ap_queue_enable_interruption(qid, ap_airq.lsi_ptr); /* If interruption mode is supported by the machine, * but an AP can not be enabled for interruption then * the AP will be discarded. */ @@ -821,13 +828,22 @@ static int ap_bus_suspend(struct device *dev, pm_message_t state) static int ap_bus_resume(struct device *dev) { - int rc = 0; struct ap_device *ap_dev = to_ap_dev(dev); + int rc; if (ap_suspend_flag) { ap_suspend_flag = 0; - if (!ap_interrupts_available()) - ap_interrupt_indicator = NULL; + if (ap_interrupts_available()) { + if (!ap_using_interrupts()) { + rc = register_adapter_interrupt(&ap_airq); + ap_airq_flag = (rc == 0); + } + } else { + if (ap_using_interrupts()) { + unregister_adapter_interrupt(&ap_airq); + ap_airq_flag = 0; + } + } ap_query_configuration(); if (!user_set_domain) { ap_domain_index = -1; @@ -848,7 +864,10 @@ static int ap_bus_resume(struct device *dev) tasklet_schedule(&ap_tasklet); if (ap_thread_flag) rc = ap_poll_thread_start(); - } + else + rc = 0; + } else + rc = 0; if (AP_QID_QUEUE(ap_dev->qid) != ap_domain_index) { spin_lock_bh(&ap_dev->lock); ap_dev->qid = AP_MKQID(AP_QID_DEVICE(ap_dev->qid), @@ -1266,7 +1285,7 @@ out: return rc; } -static void ap_interrupt_handler(void *unused1, void *unused2) +static void ap_interrupt_handler(struct airq_struct *airq) { inc_irq_stat(IRQIO_APB); tasklet_schedule(&ap_tasklet); @@ -1722,7 +1741,7 @@ static void ap_poll_all(unsigned long dummy) * important that no requests on any AP get lost. */ if (ap_using_interrupts()) - xchg((u8 *)ap_interrupt_indicator, 0); + xchg(ap_airq.lsi_ptr, 0); do { flags = 0; spin_lock(&ap_device_list_lock); @@ -1795,7 +1814,7 @@ static int ap_poll_thread_start(void) mutex_lock(&ap_poll_thread_mutex); if (!ap_poll_kthread) { ap_poll_kthread = kthread_run(ap_poll_thread, NULL, "appoll"); - rc = IS_ERR(ap_poll_kthread) ? PTR_ERR(ap_poll_kthread) : 0; + rc = PTR_RET(ap_poll_kthread); if (rc) ap_poll_kthread = NULL; } @@ -1881,13 +1900,8 @@ int __init ap_module_init(void) return -ENODEV; } if (ap_interrupts_available()) { - isc_register(AP_ISC); - ap_interrupt_indicator = s390_register_adapter_interrupt( - &ap_interrupt_handler, NULL, AP_ISC); - if (IS_ERR(ap_interrupt_indicator)) { - ap_interrupt_indicator = NULL; - isc_unregister(AP_ISC); - } + rc = register_adapter_interrupt(&ap_airq); + ap_airq_flag = (rc == 0); } register_reset_call(&ap_reset_call); @@ -1904,7 +1918,7 @@ int __init ap_module_init(void) /* Create /sys/devices/ap. */ ap_root_device = root_device_register("ap"); - rc = IS_ERR(ap_root_device) ? PTR_ERR(ap_root_device) : 0; + rc = PTR_RET(ap_root_device); if (rc) goto out_bus; @@ -1955,10 +1969,8 @@ out_bus: bus_unregister(&ap_bus_type); out: unregister_reset_call(&ap_reset_call); - if (ap_using_interrupts()) { - s390_unregister_adapter_interrupt(ap_interrupt_indicator, AP_ISC); - isc_unregister(AP_ISC); - } + if (ap_using_interrupts()) + unregister_adapter_interrupt(&ap_airq); return rc; } @@ -1994,10 +2006,8 @@ void ap_module_exit(void) bus_remove_file(&ap_bus_type, ap_bus_attrs[i]); bus_unregister(&ap_bus_type); unregister_reset_call(&ap_reset_call); - if (ap_using_interrupts()) { - s390_unregister_adapter_interrupt(ap_interrupt_indicator, AP_ISC); - isc_unregister(AP_ISC); - } + if (ap_using_interrupts()) + unregister_adapter_interrupt(&ap_airq); } module_init(ap_module_init); diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c index 83bc9c5fa0c1..fd7b3bd80789 100644 --- a/drivers/s390/net/claw.c +++ b/drivers/s390/net/claw.c @@ -3348,7 +3348,7 @@ static int __init claw_init(void) } CLAW_DBF_TEXT(2, setup, "init_mod"); claw_root_dev = root_device_register("claw"); - ret = IS_ERR(claw_root_dev) ? PTR_ERR(claw_root_dev) : 0; + ret = PTR_RET(claw_root_dev); if (ret) goto register_err; ret = ccw_driver_register(&claw_ccw_driver); diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index 676f12049a36..70b3a023100e 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -1837,7 +1837,7 @@ static int __init ctcm_init(void) if (ret) goto out_err; ctcm_root_dev = root_device_register("ctcm"); - ret = IS_ERR(ctcm_root_dev) ? PTR_ERR(ctcm_root_dev) : 0; + ret = PTR_RET(ctcm_root_dev); if (ret) goto register_err; ret = ccw_driver_register(&ctcm_ccw_driver); diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index c645dc9e98af..f404f55b3191 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -2441,7 +2441,7 @@ __init lcs_init_module(void) if (rc) goto out_err; lcs_root_dev = root_device_register("lcs"); - rc = IS_ERR(lcs_root_dev) ? PTR_ERR(lcs_root_dev) : 0; + rc = PTR_RET(lcs_root_dev); if (rc) goto register_err; rc = ccw_driver_register(&lcs_ccw_driver); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 6cd0fc1b203a..70ce6b6fce3b 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -5705,7 +5705,7 @@ static int __init qeth_core_init(void) if (rc) goto out_err; qeth_core_root_dev = root_device_register("qeth"); - rc = IS_ERR(qeth_core_root_dev) ? PTR_ERR(qeth_core_root_dev) : 0; + rc = PTR_RET(qeth_core_root_dev); if (rc) goto register_err; qeth_core_header_cache = kmem_cache_create("qeth_hdr", |