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 da488c0775fc..067ab66209b6 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -48,6 +48,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@@ -2149,6 +2161,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++)@@ -5204,11 +5221,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 */--
2.53.0