diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index 245f33c2f71e5cc202d0edc7187ace888a3ec515..32fc51341d99859172c035535e774f672c8a1149 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 9e81f766d938287d554343af10ac7d87d3b605d2..67156d46c236fbdb7fd89a80321b183d2ccc913d 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 8cd4768edb948f62e08ee036c04e7e840ac33d1a..bc5998068ddffe514223e034813fb8208dc3dc29 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 de6b96036aa40fb104e84c6c9e58ba89beebb232..2f7e79d084ca165f86651a09b9ec63ee345549fb 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;