Commit c729696b authored by Jan Höppner's avatar Jan Höppner Committed by Vasily Gorbik

s390/dasd: Recognise data for ESE volumes

In order to work with Extent Space Efficient (ESE) volumes, certain
viable information about those volumes and the corresponding extent
pool (such as extent size, configured space, allocated space, etc.) can
be provided.

Use the CCW commands Volume Storage Query and Logical Configuration
Query to receive detailed information about ESE volumes and the extent
pool respectively. These information are made accessible via internal
functions for subsequent users, and via sysfs attributes for userpsace
usage.

The new sysfs attributes reside in separate directories called capacity
and extent_pool.

attributes:
ese:
    0/1 depending on whether the volume is an ESE volume

Capacity related attributes:
space_allocated:
    Space currently allocated by the volume (in cyl)
space_configured:
    Remaining space in the extent pool (in cyl)
logical_capacity:
    The entire addressable space for this volume (in cyl)

Extent Pool related attributes:
pool_id:
    ID of the extent pool the volume in question resides in
pool_oos:
    Extent pool is out-of-space
extent_size:
    Size of a single extent in this pool
cap_at_warnlevel
    Extent pool capacity at warn level
warn_threshold:
    Threshold at which percentage of remaining extent pool space a
    warning message is issued
Signed-off-by: default avatarJan Höppner <hoeppner@linux.ibm.com>
Reviewed-by: default avatarStefan Haberland <sth@linux.ibm.com>
Signed-off-by: default avatarVasily Gorbik <gor@linux.ibm.com>
parent 461db0ea
......@@ -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);
}
......
......@@ -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
......
......@@ -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 */
......
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment