From 7e64db1597fe114b83fe17d0ba96c6aa5fca419a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B6ppner?= Date: Fri, 27 Apr 2018 16:57:44 +0200 Subject: [PATCH] s390/dasd: Add discard support for ESE volumes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ESE (Extent Space Efficient) volumes are thin-provisioned and therefore space is only occupied with real data. In order to make previously used space available for re-allocation again, discard support is enabled for ESE volumes allowing the DASD driver to release said space. Signed-off-by: Jan Höppner Reviewed-by: Stefan Haberland Signed-off-by: Vasily Gorbik --- drivers/s390/block/dasd_eckd.c | 57 ++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 3edb35359843..7f7429a99a67 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1987,6 +1987,9 @@ dasd_eckd_check_characteristics(struct dasd_device *device) if (readonly) set_bit(DASD_FLAG_DEVICE_RO, &device->flags); + if (dasd_eckd_is_ese(device)) + dasd_set_feature(device->cdev, DASD_FEATURE_DISCARD, 1); + dev_info(&device->cdev->dev, "New DASD %04X/%02X (CU %04X/%02X) " "with %d cylinders, %d heads, %d sectors%s\n", private->rdc_data.dev_type, @@ -3617,6 +3620,14 @@ static int dasd_eckd_release_space(struct dasd_device *device, return -EINVAL; } +static struct dasd_ccw_req * +dasd_eckd_build_cp_discard(struct dasd_device *device, struct dasd_block *block, + struct request *req, sector_t first_trk, + sector_t last_trk) +{ + return dasd_eckd_dso_ras(device, block, req, first_trk, last_trk, 1); +} + static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( struct dasd_device *startdev, struct dasd_block *block, @@ -4361,6 +4372,10 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, cmdwtd = private->features.feature[12] & 0x40; use_prefix = private->features.feature[8] & 0x01; + if (req_op(req) == REQ_OP_DISCARD) + return dasd_eckd_build_cp_discard(startdev, block, req, + first_trk, last_trk); + cqr = NULL; if (cdlspecial || dasd_page_cache) { /* do nothing, just fall through to the cmd mode single case */ @@ -4639,12 +4654,14 @@ static struct dasd_ccw_req *dasd_eckd_build_alias_cp(struct dasd_device *base, struct dasd_block *block, struct request *req) { + struct dasd_device *startdev = NULL; struct dasd_eckd_private *private; - struct dasd_device *startdev; - unsigned long flags; struct dasd_ccw_req *cqr; + unsigned long flags; - startdev = dasd_alias_get_start_dev(base); + /* Discard requests can only be processed on base devices */ + if (req_op(req) != REQ_OP_DISCARD) + startdev = dasd_alias_get_start_dev(base); if (!startdev) startdev = base; private = startdev->private; @@ -6357,8 +6374,20 @@ static void dasd_eckd_setup_blk_queue(struct dasd_block *block) unsigned int logical_block_size = block->bp_block; struct request_queue *q = block->request_queue; struct dasd_device *device = block->base; + struct dasd_eckd_private *private; + unsigned int max_discard_sectors; + unsigned int max_bytes; + unsigned int ext_bytes; /* Extent Size in Bytes */ + int recs_per_trk; + int trks_per_cyl; + int ext_limit; + int ext_size; /* Extent Size in Cylinders */ int max; + private = device->private; + trks_per_cyl = private->rdc_data.trk_per_cyl; + recs_per_trk = recs_per_track(&private->rdc_data, 0, logical_block_size); + if (device->features & DASD_FEATURE_USERAW) { /* * the max_blocks value for raw_track access is 256 @@ -6379,6 +6408,28 @@ static void dasd_eckd_setup_blk_queue(struct dasd_block *block) /* With page sized segments each segment can be translated into one idaw/tidaw */ blk_queue_max_segment_size(q, PAGE_SIZE); blk_queue_segment_boundary(q, PAGE_SIZE - 1); + + if (dasd_eckd_is_ese(device)) { + /* + * Depending on the extent size, up to UINT_MAX bytes can be + * accepted. However, neither DASD_ECKD_RAS_EXTS_MAX nor the + * device limits should be exceeded. + */ + ext_size = dasd_eckd_ext_size(device); + ext_limit = min(private->real_cyl / ext_size, DASD_ECKD_RAS_EXTS_MAX); + ext_bytes = ext_size * trks_per_cyl * recs_per_trk * + logical_block_size; + max_bytes = UINT_MAX - (UINT_MAX % ext_bytes); + if (max_bytes / ext_bytes > ext_limit) + max_bytes = ext_bytes * ext_limit; + + max_discard_sectors = max_bytes / 512; + + blk_queue_max_discard_sectors(q, max_discard_sectors); + blk_queue_flag_set(QUEUE_FLAG_DISCARD, q); + q->limits.discard_granularity = ext_bytes; + q->limits.discard_alignment = ext_bytes; + } } static struct ccw_driver dasd_eckd_driver = {