aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/block
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/block')
-rw-r--r--drivers/s390/block/dasd_devmap.c70
-rw-r--r--drivers/s390/block/dasd_eckd.c276
-rw-r--r--drivers/s390/block/dasd_eckd.h73
-rw-r--r--drivers/s390/block/dasd_int.h15
4 files changed, 430 insertions, 4 deletions
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index 245f33c2f71e..32fc51341d99 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -1642,6 +1642,35 @@ static DEVICE_ATTR(path_interval, 0644, dasd_path_interval_show,
dasd_path_interval_store);
+#define DASD_DEFINE_ATTR(_name, _func) \
+static ssize_t dasd_##_name##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct ccw_device *cdev = to_ccwdev(dev); \
+ struct dasd_device *device = dasd_device_from_cdev(cdev); \
+ int val = 0; \
+ \
+ if (IS_ERR(device)) \
+ return -ENODEV; \
+ if (device->discipline && _func) \
+ val = _func(device); \
+ dasd_put_device(device); \
+ \
+ return snprintf(buf, PAGE_SIZE, "%d\n", val); \
+} \
+static DEVICE_ATTR(_name, 0444, dasd_##_name##_show, NULL); \
+
+DASD_DEFINE_ATTR(ese, device->discipline->is_ese);
+DASD_DEFINE_ATTR(extent_size, device->discipline->ext_size);
+DASD_DEFINE_ATTR(pool_id, device->discipline->ext_pool_id);
+DASD_DEFINE_ATTR(space_configured, device->discipline->space_configured);
+DASD_DEFINE_ATTR(space_allocated, device->discipline->space_allocated);
+DASD_DEFINE_ATTR(logical_capacity, device->discipline->logical_capacity);
+DASD_DEFINE_ATTR(warn_threshold, device->discipline->ext_pool_warn_thrshld);
+DASD_DEFINE_ATTR(cap_at_warnlevel, device->discipline->ext_pool_cap_at_warnlevel);
+DASD_DEFINE_ATTR(pool_oos, device->discipline->ext_pool_oos);
+
static struct attribute * dasd_attrs[] = {
&dev_attr_readonly.attr,
&dev_attr_discipline.attr,
@@ -1667,6 +1696,7 @@ static struct attribute * dasd_attrs[] = {
&dev_attr_path_interval.attr,
&dev_attr_path_reset.attr,
&dev_attr_hpf.attr,
+ &dev_attr_ese.attr,
NULL,
};
@@ -1674,6 +1704,39 @@ static const struct attribute_group dasd_attr_group = {
.attrs = dasd_attrs,
};
+static struct attribute *capacity_attrs[] = {
+ &dev_attr_space_configured.attr,
+ &dev_attr_space_allocated.attr,
+ &dev_attr_logical_capacity.attr,
+ NULL,
+};
+
+static const struct attribute_group capacity_attr_group = {
+ .name = "capacity",
+ .attrs = capacity_attrs,
+};
+
+static struct attribute *ext_pool_attrs[] = {
+ &dev_attr_pool_id.attr,
+ &dev_attr_extent_size.attr,
+ &dev_attr_warn_threshold.attr,
+ &dev_attr_cap_at_warnlevel.attr,
+ &dev_attr_pool_oos.attr,
+ NULL,
+};
+
+static const struct attribute_group ext_pool_attr_group = {
+ .name = "extent_pool",
+ .attrs = ext_pool_attrs,
+};
+
+static const struct attribute_group *dasd_attr_groups[] = {
+ &dasd_attr_group,
+ &capacity_attr_group,
+ &ext_pool_attr_group,
+ NULL,
+};
+
/*
* Return value of the specified feature.
*/
@@ -1715,16 +1778,15 @@ dasd_set_feature(struct ccw_device *cdev, int feature, int flag)
EXPORT_SYMBOL(dasd_set_feature);
-int
-dasd_add_sysfs_files(struct ccw_device *cdev)
+int dasd_add_sysfs_files(struct ccw_device *cdev)
{
- return sysfs_create_group(&cdev->dev.kobj, &dasd_attr_group);
+ return sysfs_create_groups(&cdev->dev.kobj, dasd_attr_groups);
}
void
dasd_remove_sysfs_files(struct ccw_device *cdev)
{
- sysfs_remove_group(&cdev->dev.kobj, &dasd_attr_group);
+ sysfs_remove_groups(&cdev->dev.kobj, dasd_attr_groups);
}
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 9e81f766d938..67156d46c236 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -108,6 +108,7 @@ struct check_attention_work_data {
__u8 lpum;
};
+static int dasd_eckd_ext_pool_id(struct dasd_device *);
static int prepare_itcw(struct itcw *, unsigned int, unsigned int, int,
struct dasd_device *, struct dasd_device *,
unsigned int, int, unsigned int, unsigned int,
@@ -1470,6 +1471,252 @@ static int dasd_eckd_read_features(struct dasd_device *device)
return rc;
}
+/* Read Volume Information - Volume Storage Query */
+static int dasd_eckd_read_vol_info(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private = device->private;
+ struct dasd_psf_prssd_data *prssdp;
+ struct dasd_rssd_vsq *vsq;
+ struct dasd_ccw_req *cqr;
+ struct ccw1 *ccw;
+ int rc;
+
+ /* This command cannot be executed on an alias device */
+ if (private->uid.type == UA_BASE_PAV_ALIAS ||
+ private->uid.type == UA_HYPER_PAV_ALIAS)
+ return 0;
+
+ cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 2 /* PSF + RSSD */,
+ sizeof(*prssdp) + sizeof(*vsq), device, NULL);
+ if (IS_ERR(cqr)) {
+ DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
+ "Could not allocate initialization request");
+ return PTR_ERR(cqr);
+ }
+
+ /* Prepare for Read Subsystem Data */
+ prssdp = cqr->data;
+ prssdp->order = PSF_ORDER_PRSSD;
+ prssdp->suborder = PSF_SUBORDER_VSQ; /* Volume Storage Query */
+ prssdp->lss = private->ned->ID;
+ prssdp->volume = private->ned->unit_addr;
+
+ ccw = cqr->cpaddr;
+ ccw->cmd_code = DASD_ECKD_CCW_PSF;
+ ccw->count = sizeof(*prssdp);
+ ccw->flags |= CCW_FLAG_CC;
+ ccw->cda = (__u32)(addr_t)prssdp;
+
+ /* Read Subsystem Data - Volume Storage Query */
+ vsq = (struct dasd_rssd_vsq *)(prssdp + 1);
+ memset(vsq, 0, sizeof(*vsq));
+
+ ccw++;
+ ccw->cmd_code = DASD_ECKD_CCW_RSSD;
+ ccw->count = sizeof(*vsq);
+ ccw->flags |= CCW_FLAG_SLI;
+ ccw->cda = (__u32)(addr_t)vsq;
+
+ cqr->buildclk = get_tod_clock();
+ cqr->status = DASD_CQR_FILLED;
+ cqr->startdev = device;
+ cqr->memdev = device;
+ cqr->block = NULL;
+ cqr->retries = 256;
+ cqr->expires = device->default_expires * HZ;
+ /* The command might not be supported. Suppress the error output */
+ __set_bit(DASD_CQR_SUPPRESS_CR, &cqr->flags);
+
+ rc = dasd_sleep_on_interruptible(cqr);
+ if (rc == 0) {
+ memcpy(&private->vsq, vsq, sizeof(*vsq));
+ } else {
+ dev_warn(&device->cdev->dev,
+ "Reading the volume storage information failed with rc=%d\n", rc);
+ }
+
+ dasd_sfree_request(cqr, cqr->memdev);
+
+ return rc;
+}
+
+static int dasd_eckd_is_ese(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private = device->private;
+
+ return private->vsq.vol_info.ese;
+}
+
+static int dasd_eckd_ext_pool_id(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private = device->private;
+
+ return private->vsq.extent_pool_id;
+}
+
+/*
+ * This value represents the total amount of available space. As more space is
+ * allocated by ESE volumes, this value will decrease.
+ * The data for this value is therefore updated on any call.
+ */
+static int dasd_eckd_space_configured(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private = device->private;
+ int rc;
+
+ rc = dasd_eckd_read_vol_info(device);
+
+ return rc ? : private->vsq.space_configured;
+}
+
+/*
+ * The value of space allocated by an ESE volume may have changed and is
+ * therefore updated on any call.
+ */
+static int dasd_eckd_space_allocated(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private = device->private;
+ int rc;
+
+ rc = dasd_eckd_read_vol_info(device);
+
+ return rc ? : private->vsq.space_allocated;
+}
+
+static int dasd_eckd_logical_capacity(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private = device->private;
+
+ return private->vsq.logical_capacity;
+}
+
+static void dasd_eckd_cpy_ext_pool_data(struct dasd_device *device,
+ struct dasd_rssd_lcq *lcq)
+{
+ struct dasd_eckd_private *private = device->private;
+ int pool_id = dasd_eckd_ext_pool_id(device);
+ struct dasd_ext_pool_sum eps;
+ int i;
+
+ for (i = 0; i < lcq->pool_count; i++) {
+ eps = lcq->ext_pool_sum[i];
+ if (eps.pool_id == pool_id) {
+ memcpy(&private->eps, &eps,
+ sizeof(struct dasd_ext_pool_sum));
+ }
+ }
+}
+
+/* Read Extent Pool Information - Logical Configuration Query */
+static int dasd_eckd_read_ext_pool_info(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private = device->private;
+ struct dasd_psf_prssd_data *prssdp;
+ struct dasd_rssd_lcq *lcq;
+ struct dasd_ccw_req *cqr;
+ struct ccw1 *ccw;
+ int rc;
+
+ /* This command cannot be executed on an alias device */
+ if (private->uid.type == UA_BASE_PAV_ALIAS ||
+ private->uid.type == UA_HYPER_PAV_ALIAS)
+ return 0;
+
+ cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 2 /* PSF + RSSD */,
+ sizeof(*prssdp) + sizeof(*lcq), device, NULL);
+ if (IS_ERR(cqr)) {
+ DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
+ "Could not allocate initialization request");
+ return PTR_ERR(cqr);
+ }
+
+ /* Prepare for Read Subsystem Data */
+ prssdp = cqr->data;
+ memset(prssdp, 0, sizeof(*prssdp));
+ prssdp->order = PSF_ORDER_PRSSD;
+ prssdp->suborder = PSF_SUBORDER_LCQ; /* Logical Configuration Query */
+
+ ccw = cqr->cpaddr;
+ ccw->cmd_code = DASD_ECKD_CCW_PSF;
+ ccw->count = sizeof(*prssdp);
+ ccw->flags |= CCW_FLAG_CC;
+ ccw->cda = (__u32)(addr_t)prssdp;
+
+ lcq = (struct dasd_rssd_lcq *)(prssdp + 1);
+ memset(lcq, 0, sizeof(*lcq));
+
+ ccw++;
+ ccw->cmd_code = DASD_ECKD_CCW_RSSD;
+ ccw->count = sizeof(*lcq);
+ ccw->flags |= CCW_FLAG_SLI;
+ ccw->cda = (__u32)(addr_t)lcq;
+
+ cqr->buildclk = get_tod_clock();
+ cqr->status = DASD_CQR_FILLED;
+ cqr->startdev = device;
+ cqr->memdev = device;
+ cqr->block = NULL;
+ cqr->retries = 256;
+ cqr->expires = device->default_expires * HZ;
+ /* The command might not be supported. Suppress the error output */
+ __set_bit(DASD_CQR_SUPPRESS_CR, &cqr->flags);
+
+ rc = dasd_sleep_on_interruptible(cqr);
+ if (rc == 0) {
+ dasd_eckd_cpy_ext_pool_data(device, lcq);
+ } else {
+ dev_warn(&device->cdev->dev,
+ "Reading the logical configuration failed with rc=%d\n", rc);
+ }
+
+ dasd_sfree_request(cqr, cqr->memdev);
+
+ return rc;
+}
+
+/*
+ * Depending on the device type, the extent size is specified either as
+ * cylinders per extent (CKD) or size per extent (FBA)
+ * A 1GB size corresponds to 1113cyl, and 16MB to 21cyl.
+ */
+static int dasd_eckd_ext_size(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private = device->private;
+ struct dasd_ext_pool_sum eps = private->eps;
+
+ if (!eps.flags.extent_size_valid)
+ return 0;
+ if (eps.extent_size.size_1G)
+ return 1113;
+ if (eps.extent_size.size_16M)
+ return 21;
+
+ return 0;
+}
+
+static int dasd_eckd_ext_pool_warn_thrshld(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private = device->private;
+
+ return private->eps.warn_thrshld;
+}
+
+static int dasd_eckd_ext_pool_cap_at_warnlevel(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private = device->private;
+
+ return private->eps.flags.capacity_at_warnlevel;
+}
+
+/*
+ * Extent Pool out of space
+ */
+static int dasd_eckd_ext_pool_oos(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private = device->private;
+
+ return private->eps.flags.pool_oos;
+}
/*
* Build CP for Perform Subsystem Function - SSC.
@@ -1700,6 +1947,16 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
/* Read Feature Codes */
dasd_eckd_read_features(device);
+ /* Read Volume Information */
+ rc = dasd_eckd_read_vol_info(device);
+ if (rc)
+ goto out_err3;
+
+ /* Read Extent Pool Information */
+ rc = dasd_eckd_read_ext_pool_info(device);
+ if (rc)
+ goto out_err3;
+
/* Read Device Characteristics */
rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC,
&private->rdc_data, 64);
@@ -4944,6 +5201,16 @@ static int dasd_eckd_restore_device(struct dasd_device *device)
/* Read Feature Codes */
dasd_eckd_read_features(device);
+ /* Read Volume Information */
+ rc = dasd_eckd_read_vol_info(device);
+ if (rc)
+ goto out_err2;
+
+ /* Read Extent Pool Information */
+ rc = dasd_eckd_read_ext_pool_info(device);
+ if (rc)
+ goto out_err2;
+
/* Read Device Characteristics */
rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC,
&temp_rdc_data, 64);
@@ -5785,6 +6052,15 @@ static struct dasd_discipline dasd_eckd_discipline = {
.disable_hpf = dasd_eckd_disable_hpf_device,
.hpf_enabled = dasd_eckd_hpf_enabled,
.reset_path = dasd_eckd_reset_path,
+ .is_ese = dasd_eckd_is_ese,
+ .space_allocated = dasd_eckd_space_allocated,
+ .space_configured = dasd_eckd_space_configured,
+ .logical_capacity = dasd_eckd_logical_capacity,
+ .ext_pool_id = dasd_eckd_ext_pool_id,
+ .ext_size = dasd_eckd_ext_size,
+ .ext_pool_cap_at_warnlevel = dasd_eckd_ext_pool_cap_at_warnlevel,
+ .ext_pool_warn_thrshld = dasd_eckd_ext_pool_warn_thrshld,
+ .ext_pool_oos = dasd_eckd_ext_pool_oos,
};
static int __init
diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h
index 8cd4768edb94..bc5998068ddf 100644
--- a/drivers/s390/block/dasd_eckd.h
+++ b/drivers/s390/block/dasd_eckd.h
@@ -62,6 +62,8 @@
* Perform Subsystem Function / Sub-Orders
*/
#define PSF_SUBORDER_QHA 0x1C /* Query Host Access */
+#define PSF_SUBORDER_VSQ 0x52 /* Volume Storage Query */
+#define PSF_SUBORDER_LCQ 0x53 /* Logical Configuration Query */
/*
* CUIR response condition codes
@@ -368,6 +370,75 @@ struct dasd_rssd_messages {
char messages[4087];
} __packed;
+/*
+ * Read Subsystem Data - Volume Storage Query
+ */
+struct dasd_rssd_vsq {
+ struct {
+ __u8 tse:1;
+ __u8 space_not_available:1;
+ __u8 ese:1;
+ __u8 unused:5;
+ } __packed vol_info;
+ __u8 unused1;
+ __u16 extent_pool_id;
+ __u8 warn_cap_limit;
+ __u8 warn_cap_guaranteed;
+ __u16 unused2;
+ __u32 limit_capacity;
+ __u32 guaranteed_capacity;
+ __u32 space_allocated;
+ __u32 space_configured;
+ __u32 logical_capacity;
+} __packed;
+
+/*
+ * Extent Pool Summary
+ */
+struct dasd_ext_pool_sum {
+ __u16 pool_id;
+ __u8 repo_warn_thrshld;
+ __u8 warn_thrshld;
+ struct {
+ __u8 type:1; /* 0 - CKD / 1 - FB */
+ __u8 track_space_efficient:1;
+ __u8 extent_space_efficient:1;
+ __u8 standard_volume:1;
+ __u8 extent_size_valid:1;
+ __u8 capacity_at_warnlevel:1;
+ __u8 pool_oos:1;
+ __u8 unused0:1;
+ __u8 unused1;
+ } __packed flags;
+ struct {
+ __u8 reserved0:1;
+ __u8 size_1G:1;
+ __u8 reserved1:5;
+ __u8 size_16M:1;
+ } __packed extent_size;
+ __u8 unused;
+} __packed;
+
+/*
+ * Read Subsystem Data-Response - Logical Configuration Query - Header
+ */
+struct dasd_rssd_lcq {
+ __u16 data_length; /* Length of data returned */
+ __u16 pool_count; /* Count of extent pools returned - Max: 448 */
+ struct {
+ __u8 pool_info_valid:1; /* Detailed Information valid */
+ __u8 pool_id_volume:1;
+ __u8 pool_id_cec:1;
+ __u8 unused0:5;
+ __u8 unused1;
+ } __packed header_flags;
+ char sfi_type[6]; /* Storage Facility Image Type (EBCDIC) */
+ char sfi_model[3]; /* Storage Facility Image Model (EBCDIC) */
+ __u8 sfi_seq_num[10]; /* Storage Facility Image Sequence Number */
+ __u8 reserved[7];
+ struct dasd_ext_pool_sum ext_pool_sum[448];
+} __packed;
+
struct dasd_cuir_message {
__u16 length;
__u8 format;
@@ -532,6 +603,8 @@ struct dasd_eckd_private {
int uses_cdl;
struct attrib_data_t attrib; /* e.g. cache operations */
struct dasd_rssd_features features;
+ struct dasd_rssd_vsq vsq;
+ struct dasd_ext_pool_sum eps;
u32 real_cyl;
/* alias managemnet */
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index de6b96036aa4..2f7e79d084ca 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -367,6 +367,21 @@ struct dasd_discipline {
void (*disable_hpf)(struct dasd_device *);
int (*hpf_enabled)(struct dasd_device *);
void (*reset_path)(struct dasd_device *, __u8);
+
+ /*
+ * Extent Space Efficient (ESE) relevant functions
+ */
+ int (*is_ese)(struct dasd_device *);
+ /* Capacity */
+ int (*space_allocated)(struct dasd_device *);
+ int (*space_configured)(struct dasd_device *);
+ int (*logical_capacity)(struct dasd_device *);
+ /* Extent Pool */
+ int (*ext_pool_id)(struct dasd_device *);
+ int (*ext_size)(struct dasd_device *);
+ int (*ext_pool_cap_at_warnlevel)(struct dasd_device *);
+ int (*ext_pool_warn_thrshld)(struct dasd_device *);
+ int (*ext_pool_oos)(struct dasd_device *);
};
extern struct dasd_discipline *dasd_diag_discipline_pointer;