diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index 381d616ad433..035c022255b6 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -1630,6 +1630,44 @@ dasd_expires_store(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(expires, 0644, dasd_expires_show, dasd_expires_store); +/* ESE fulltrack write aggressiveness knob (0..100, see DASD_FT_BIAS_*) */ +static ssize_t +full_track_bias_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->ft_bias); + dasd_put_device(device); + return len; +} + +static ssize_t full_track_bias_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dasd_device *device; + unsigned int val; + + if (kstrtouint(buf, 0, &val) || val > DASD_FT_BIAS_MAX) + return -EINVAL; + + device = dasd_device_from_cdev(to_ccwdev(dev)); + if (IS_ERR(device)) + return -ENODEV; + + device->ft_bias = val; + device->fulltrack = val ? 1 : 0; + + dasd_put_device(device); + return count; +} + +static DEVICE_ATTR_RW(full_track_bias); + static ssize_t dasd_retries_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -2425,6 +2463,7 @@ static struct attribute * dasd_attrs[] = { &dev_attr_erplog.attr, &dev_attr_failfast.attr, &dev_attr_expires.attr, + &dev_attr_full_track_bias.attr, &dev_attr_retries.attr, &dev_attr_timeout.attr, &dev_attr_reservation_policy.attr, diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 7828f564a11b..c34c3afb55d0 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -47,6 +47,18 @@ MODULE_DESCRIPTION("S/390 DASD ECKD Disks device driver"); MODULE_LICENSE("GPL"); +/* + * Default full-track write bias applied to every ESE volume at online time; + * individual volumes can be re-tuned afterwards through their per-device + * full_track_bias sysfs attribute. 0 disables full-track writes, 100 always + * uses them, 50 (the default) enables the adaptive heuristic. Values above + * DASD_FT_BIAS_MAX are capped when applied. + */ +static unsigned int full_track_bias = DASD_FT_BIAS_DEFAULT; +module_param(full_track_bias, uint, 0644); +MODULE_PARM_DESC(full_track_bias, + "Default ESE full-track write bias 0..100 (0=off, 1..99=adaptive, 100=always)"); + static struct dasd_discipline dasd_eckd_discipline; /* The ccw bus type uses this table to find devices that it sends to @@ -2148,6 +2160,11 @@ dasd_eckd_check_characteristics(struct dasd_device *device) device->path_interval = DASD_ECKD_PATH_INTERVAL; device->aq_timeouts = DASD_RETRIES_MAX; + /* default ESE fulltrack write aggressiveness from the module parameter */ + device->ft_bias = min_t(unsigned int, full_track_bias, DASD_FT_BIAS_MAX); + /* only the "always" endpoint forces fulltrack unconditionally here */ + device->fulltrack = (device->ft_bias >= DASD_FT_BIAS_MAX) ? 1 : 0; + if (private->conf.gneq) { value = 1; for (i = 0; i < private->conf.gneq->timeout.value; i++) @@ -5201,11 +5218,20 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, /* do nothing, just fall through to the cmd mode single case */ } else if ((data_size <= private->fcx_max_data) && (fcx_multitrack || (first_trk == last_trk))) { - cqr = dasd_eckd_build_cp_tpm_track(startdev, block, req, - first_rec, last_rec, - first_trk, last_trk, - first_offs, last_offs, - blk_per_trk, blksize); + if (!first_offs && (last_offs + 1 == blk_per_trk) && + rq_data_dir(req) == WRITE && basedev->fulltrack) { + cqr = dasd_eckd_build_cp_tpm_writefulltrack(startdev, block, req, + first_rec, last_rec, + first_trk, last_trk, + first_offs, last_offs, + blk_per_trk, blksize, NULL); + } else { + cqr = dasd_eckd_build_cp_tpm_track(startdev, block, req, + first_rec, last_rec, + first_trk, last_trk, + first_offs, last_offs, + blk_per_trk, blksize); + } if (IS_ERR(cqr) && (PTR_ERR(cqr) != -EAGAIN) && (PTR_ERR(cqr) != -ENOMEM)) cqr = NULL; diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index e1ffa20db10a..b8a3190c9c93 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -629,6 +629,10 @@ struct dasd_device { struct dasd_copy_relation *copy; unsigned long aq_mask; unsigned int aq_timeouts; + + /* ESE fulltrack write control (see full_track_bias sysfs attribute) */ + unsigned int ft_bias; /* aggressiveness 0..100: 0=off, 100=always */ + unsigned int fulltrack; /* internal: use WRITE_FULL_TRACK for aligned writes */ }; struct dasd_block { @@ -686,6 +690,20 @@ struct dasd_queue { #define DASD_STOPPED_PPRC 32 /* PPRC swap */ #define DASD_STOPPED_NOSPC 128 /* no space left */ +/* + * ESE fulltrack write aggressiveness (full_track_bias sysfs attribute), 0..100: + * 0 - never use proactively WRITE_FULL_TRACK + * 100 - always use proactively WRITE_FULL_TRACK, no probing + * 1..99 - adaptive; higher means switch to ft more eagerly + * WRITE_FULL_TRACK has an advantage on sparse formatted ESE devices + * but it has an overall penalty for maximum throughput for fully + * formatted devices. + * The default of 50 tries to balance both and do some probing in between + * to choose the best mode for default IO. + */ +#define DASD_FT_BIAS_MAX 100 +#define DASD_FT_BIAS_DEFAULT 50 + /* per device flags */ #define DASD_FLAG_OFFLINE 3 /* device is in offline processing */ #define DASD_FLAG_EER_SNSS 4 /* A SNSS is required */