s390/dasd: Add discard support for ESE volumes
authorJan Höppner <hoeppner@linux.ibm.com>
Fri, 27 Apr 2018 14:57:44 +0000 (16:57 +0200)
committerVasily Gorbik <gor@linux.ibm.com>
Thu, 11 Jul 2019 18:39:54 +0000 (20:39 +0200)
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>
drivers/s390/block/dasd_eckd.c

index 3edb35359843e36e2f0a5fc2d299543f7c6d0178..7f7429a99a671d36099d3b8a6f46282ecfef0d8d 100644 (file)
@@ -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 = {