diff options
author | Jan Höppner <hoeppner@linux.ibm.com> | 2018-04-27 10:57:44 -0400 |
---|---|---|
committer | Vasily Gorbik <gor@linux.ibm.com> | 2019-07-11 14:39:54 -0400 |
commit | 7e64db1597fe114b83fe17d0ba96c6aa5fca419a (patch) | |
tree | c6d4b47f6a4f4972f53b59b1370a71eaa51d5210 | |
parent | b54441534e9648f8df24b75a63bb00ed3aa123de (diff) |
s390/dasd: Add discard support for ESE volumes
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 <hoeppner@linux.ibm.com>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
-rw-r--r-- | drivers/s390/block/dasd_eckd.c | 57 |
1 files 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) | |||
1987 | if (readonly) | 1987 | if (readonly) |
1988 | set_bit(DASD_FLAG_DEVICE_RO, &device->flags); | 1988 | set_bit(DASD_FLAG_DEVICE_RO, &device->flags); |
1989 | 1989 | ||
1990 | if (dasd_eckd_is_ese(device)) | ||
1991 | dasd_set_feature(device->cdev, DASD_FEATURE_DISCARD, 1); | ||
1992 | |||
1990 | dev_info(&device->cdev->dev, "New DASD %04X/%02X (CU %04X/%02X) " | 1993 | dev_info(&device->cdev->dev, "New DASD %04X/%02X (CU %04X/%02X) " |
1991 | "with %d cylinders, %d heads, %d sectors%s\n", | 1994 | "with %d cylinders, %d heads, %d sectors%s\n", |
1992 | private->rdc_data.dev_type, | 1995 | private->rdc_data.dev_type, |
@@ -3617,6 +3620,14 @@ static int dasd_eckd_release_space(struct dasd_device *device, | |||
3617 | return -EINVAL; | 3620 | return -EINVAL; |
3618 | } | 3621 | } |
3619 | 3622 | ||
3623 | static struct dasd_ccw_req * | ||
3624 | dasd_eckd_build_cp_discard(struct dasd_device *device, struct dasd_block *block, | ||
3625 | struct request *req, sector_t first_trk, | ||
3626 | sector_t last_trk) | ||
3627 | { | ||
3628 | return dasd_eckd_dso_ras(device, block, req, first_trk, last_trk, 1); | ||
3629 | } | ||
3630 | |||
3620 | static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( | 3631 | static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( |
3621 | struct dasd_device *startdev, | 3632 | struct dasd_device *startdev, |
3622 | struct dasd_block *block, | 3633 | struct dasd_block *block, |
@@ -4361,6 +4372,10 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, | |||
4361 | cmdwtd = private->features.feature[12] & 0x40; | 4372 | cmdwtd = private->features.feature[12] & 0x40; |
4362 | use_prefix = private->features.feature[8] & 0x01; | 4373 | use_prefix = private->features.feature[8] & 0x01; |
4363 | 4374 | ||
4375 | if (req_op(req) == REQ_OP_DISCARD) | ||
4376 | return dasd_eckd_build_cp_discard(startdev, block, req, | ||
4377 | first_trk, last_trk); | ||
4378 | |||
4364 | cqr = NULL; | 4379 | cqr = NULL; |
4365 | if (cdlspecial || dasd_page_cache) { | 4380 | if (cdlspecial || dasd_page_cache) { |
4366 | /* do nothing, just fall through to the cmd mode single case */ | 4381 | /* 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, | |||
4639 | struct dasd_block *block, | 4654 | struct dasd_block *block, |
4640 | struct request *req) | 4655 | struct request *req) |
4641 | { | 4656 | { |
4657 | struct dasd_device *startdev = NULL; | ||
4642 | struct dasd_eckd_private *private; | 4658 | struct dasd_eckd_private *private; |
4643 | struct dasd_device *startdev; | ||
4644 | unsigned long flags; | ||
4645 | struct dasd_ccw_req *cqr; | 4659 | struct dasd_ccw_req *cqr; |
4660 | unsigned long flags; | ||
4646 | 4661 | ||
4647 | startdev = dasd_alias_get_start_dev(base); | 4662 | /* Discard requests can only be processed on base devices */ |
4663 | if (req_op(req) != REQ_OP_DISCARD) | ||
4664 | startdev = dasd_alias_get_start_dev(base); | ||
4648 | if (!startdev) | 4665 | if (!startdev) |
4649 | startdev = base; | 4666 | startdev = base; |
4650 | private = startdev->private; | 4667 | private = startdev->private; |
@@ -6357,8 +6374,20 @@ static void dasd_eckd_setup_blk_queue(struct dasd_block *block) | |||
6357 | unsigned int logical_block_size = block->bp_block; | 6374 | unsigned int logical_block_size = block->bp_block; |
6358 | struct request_queue *q = block->request_queue; | 6375 | struct request_queue *q = block->request_queue; |
6359 | struct dasd_device *device = block->base; | 6376 | struct dasd_device *device = block->base; |
6377 | struct dasd_eckd_private *private; | ||
6378 | unsigned int max_discard_sectors; | ||
6379 | unsigned int max_bytes; | ||
6380 | unsigned int ext_bytes; /* Extent Size in Bytes */ | ||
6381 | int recs_per_trk; | ||
6382 | int trks_per_cyl; | ||
6383 | int ext_limit; | ||
6384 | int ext_size; /* Extent Size in Cylinders */ | ||
6360 | int max; | 6385 | int max; |
6361 | 6386 | ||
6387 | private = device->private; | ||
6388 | trks_per_cyl = private->rdc_data.trk_per_cyl; | ||
6389 | recs_per_trk = recs_per_track(&private->rdc_data, 0, logical_block_size); | ||
6390 | |||
6362 | if (device->features & DASD_FEATURE_USERAW) { | 6391 | if (device->features & DASD_FEATURE_USERAW) { |
6363 | /* | 6392 | /* |
6364 | * the max_blocks value for raw_track access is 256 | 6393 | * 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) | |||
6379 | /* With page sized segments each segment can be translated into one idaw/tidaw */ | 6408 | /* With page sized segments each segment can be translated into one idaw/tidaw */ |
6380 | blk_queue_max_segment_size(q, PAGE_SIZE); | 6409 | blk_queue_max_segment_size(q, PAGE_SIZE); |
6381 | blk_queue_segment_boundary(q, PAGE_SIZE - 1); | 6410 | blk_queue_segment_boundary(q, PAGE_SIZE - 1); |
6411 | |||
6412 | if (dasd_eckd_is_ese(device)) { | ||
6413 | /* | ||
6414 | * Depending on the extent size, up to UINT_MAX bytes can be | ||
6415 | * accepted. However, neither DASD_ECKD_RAS_EXTS_MAX nor the | ||
6416 | * device limits should be exceeded. | ||
6417 | */ | ||
6418 | ext_size = dasd_eckd_ext_size(device); | ||
6419 | ext_limit = min(private->real_cyl / ext_size, DASD_ECKD_RAS_EXTS_MAX); | ||
6420 | ext_bytes = ext_size * trks_per_cyl * recs_per_trk * | ||
6421 | logical_block_size; | ||
6422 | max_bytes = UINT_MAX - (UINT_MAX % ext_bytes); | ||
6423 | if (max_bytes / ext_bytes > ext_limit) | ||
6424 | max_bytes = ext_bytes * ext_limit; | ||
6425 | |||
6426 | max_discard_sectors = max_bytes / 512; | ||
6427 | |||
6428 | blk_queue_max_discard_sectors(q, max_discard_sectors); | ||
6429 | blk_queue_flag_set(QUEUE_FLAG_DISCARD, q); | ||
6430 | q->limits.discard_granularity = ext_bytes; | ||
6431 | q->limits.discard_alignment = ext_bytes; | ||
6432 | } | ||
6382 | } | 6433 | } |
6383 | 6434 | ||
6384 | static struct ccw_driver dasd_eckd_driver = { | 6435 | static struct ccw_driver dasd_eckd_driver = { |