diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 8186473b9aa72..1bfd0e17a4dcc 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1953,6 +1953,16 @@ static void __dasd_device_process_final_queue(struct dasd_device *device, } } +/* + * check if device should be autoquiesced due to too many timeouts + */ +static void __dasd_device_check_autoquiesce_timeout(struct dasd_device *device, + struct dasd_ccw_req *cqr) +{ + if ((device->default_retries - cqr->retries) >= device->aq_timeouts) + dasd_handle_autoquiesce(device, cqr, DASD_EER_TIMEOUTS); +} + /* * Take a look at the first request on the ccw queue and check * if it reached its expire time. If so, terminate the IO. @@ -1987,6 +1997,7 @@ static void __dasd_device_check_expire(struct dasd_device *device) "remaining\n", cqr, (cqr->expires/HZ), cqr->retries); } + __dasd_device_check_autoquiesce_timeout(device, cqr); } } diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index 95c7959c7949b..620fab01b710b 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -1552,6 +1552,52 @@ static ssize_t dasd_aqr_store(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(aq_requeue, 0644, dasd_aqr_show, dasd_aqr_store); +/* + * aq_timeouts controls how much retries have to time out until + * a device gets autoquiesced + */ +static ssize_t +dasd_aq_timeouts_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 = sysfs_emit(buf, "%u\n", device->aq_timeouts); + dasd_put_device(device); + return len; +} + +static ssize_t +dasd_aq_timeouts_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dasd_device *device; + unsigned int val; + + device = dasd_device_from_cdev(to_ccwdev(dev)); + if (IS_ERR(device)) + return -ENODEV; + + if ((kstrtouint(buf, 10, &val) != 0) || + val > DASD_RETRIES_MAX || val == 0) { + dasd_put_device(device); + return -EINVAL; + } + + if (val) + device->aq_timeouts = val; + + dasd_put_device(device); + return count; +} + +static DEVICE_ATTR(aq_timeouts, 0644, dasd_aq_timeouts_show, + dasd_aq_timeouts_store); + /* * expiration time for default requests */ @@ -2403,6 +2449,7 @@ static struct attribute * dasd_attrs[] = { &dev_attr_ping.attr, &dev_attr_aq_mask.attr, &dev_attr_aq_requeue.attr, + &dev_attr_aq_timeouts.attr, NULL, }; diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 1a69f97e88fbb..ade1369fe5ed3 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -2109,6 +2109,7 @@ dasd_eckd_check_characteristics(struct dasd_device *device) device->default_retries = DASD_RETRIES; device->path_thrhld = DASD_ECKD_PATH_THRHLD; device->path_interval = DASD_ECKD_PATH_INTERVAL; + device->aq_timeouts = DASD_RETRIES_MAX; if (private->conf.gneq) { value = 1; diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index c4633a4aeeb15..33f812f0e5150 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -638,6 +638,7 @@ struct dasd_device { struct kset *paths_info; struct dasd_copy_relation *copy; unsigned long aq_mask; + unsigned int aq_timeouts; }; struct dasd_block {