summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Snitzer <snitzer@redhat.com>2019-02-25 11:07:10 -0500
committerMike Snitzer <snitzer@redhat.com>2019-03-05 14:53:52 -0500
commitde7180ff908b2bc0342e832dbdaa9a5f1ecaa33a (patch)
treedb7c3b6e894a7d5f96ab155500ff5a8275cfad66
parentf87e033b3b923d91194348c11221e1bbc92e51b2 (diff)
dm cache: add support for discard passdown to the origin device
DM cache now defaults to passing discards down to the origin device. User may disable this using the "no_discard_passdown" feature when creating the cache device. If the cache's underlying origin device doesn't support discards then passdown is disabled (with warning). Similarly, if the underlying origin device's max_discard_sectors is less than a cache block discard passdown will be disabled (this is required because sizing of the cache internal discard bitset depends on it). Signed-off-by: Mike Snitzer <snitzer@redhat.com>
-rw-r--r--Documentation/device-mapper/cache.txt3
-rw-r--r--drivers/md/dm-cache-target.c126
2 files changed, 103 insertions, 26 deletions
diff --git a/Documentation/device-mapper/cache.txt b/Documentation/device-mapper/cache.txt
index ff0841711fd5..8ae1cf8e94da 100644
--- a/Documentation/device-mapper/cache.txt
+++ b/Documentation/device-mapper/cache.txt
@@ -206,6 +206,9 @@ Optional feature arguments are:
206 in a separate btree, which improves speed of shutting 206 in a separate btree, which improves speed of shutting
207 down the cache. 207 down the cache.
208 208
209 no_discard_passdown : disable passing down discards from the cache
210 to the origin's data device.
211
209A policy called 'default' is always registered. This is an alias for 212A policy called 'default' is always registered. This is an alias for
210the policy we currently think is giving best all round performance. 213the policy we currently think is giving best all round performance.
211 214
diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c
index adc529f12b6b..d249cf8ac277 100644
--- a/drivers/md/dm-cache-target.c
+++ b/drivers/md/dm-cache-target.c
@@ -353,6 +353,7 @@ struct cache_features {
353 enum cache_metadata_mode mode; 353 enum cache_metadata_mode mode;
354 enum cache_io_mode io_mode; 354 enum cache_io_mode io_mode;
355 unsigned metadata_version; 355 unsigned metadata_version;
356 bool discard_passdown:1;
356}; 357};
357 358
358struct cache_stats { 359struct cache_stats {
@@ -1899,7 +1900,11 @@ static bool process_discard_bio(struct cache *cache, struct bio *bio)
1899 b = to_dblock(from_dblock(b) + 1); 1900 b = to_dblock(from_dblock(b) + 1);
1900 } 1901 }
1901 1902
1902 bio_endio(bio); 1903 if (cache->features.discard_passdown) {
1904 remap_to_origin(cache, bio);
1905 generic_make_request(bio);
1906 } else
1907 bio_endio(bio);
1903 1908
1904 return false; 1909 return false;
1905} 1910}
@@ -2233,13 +2238,14 @@ static void init_features(struct cache_features *cf)
2233 cf->mode = CM_WRITE; 2238 cf->mode = CM_WRITE;
2234 cf->io_mode = CM_IO_WRITEBACK; 2239 cf->io_mode = CM_IO_WRITEBACK;
2235 cf->metadata_version = 1; 2240 cf->metadata_version = 1;
2241 cf->discard_passdown = true;
2236} 2242}
2237 2243
2238static int parse_features(struct cache_args *ca, struct dm_arg_set *as, 2244static int parse_features(struct cache_args *ca, struct dm_arg_set *as,
2239 char **error) 2245 char **error)
2240{ 2246{
2241 static const struct dm_arg _args[] = { 2247 static const struct dm_arg _args[] = {
2242 {0, 2, "Invalid number of cache feature arguments"}, 2248 {0, 3, "Invalid number of cache feature arguments"},
2243 }; 2249 };
2244 2250
2245 int r, mode_ctr = 0; 2251 int r, mode_ctr = 0;
@@ -2274,6 +2280,9 @@ static int parse_features(struct cache_args *ca, struct dm_arg_set *as,
2274 else if (!strcasecmp(arg, "metadata2")) 2280 else if (!strcasecmp(arg, "metadata2"))
2275 cf->metadata_version = 2; 2281 cf->metadata_version = 2;
2276 2282
2283 else if (!strcasecmp(arg, "no_discard_passdown"))
2284 cf->discard_passdown = false;
2285
2277 else { 2286 else {
2278 *error = "Unrecognised cache feature requested"; 2287 *error = "Unrecognised cache feature requested";
2279 return -EINVAL; 2288 return -EINVAL;
@@ -3119,6 +3128,39 @@ static void cache_resume(struct dm_target *ti)
3119 do_waker(&cache->waker.work); 3128 do_waker(&cache->waker.work);
3120} 3129}
3121 3130
3131static void emit_flags(struct cache *cache, char *result,
3132 unsigned maxlen, ssize_t *sz_ptr)
3133{
3134 ssize_t sz = *sz_ptr;
3135 struct cache_features *cf = &cache->features;
3136 unsigned count = (cf->metadata_version == 2) + !cf->discard_passdown + 1;
3137
3138 DMEMIT("%u ", count);
3139
3140 if (cf->metadata_version == 2)
3141 DMEMIT("metadata2 ");
3142
3143 if (writethrough_mode(cache))
3144 DMEMIT("writethrough ");
3145
3146 else if (passthrough_mode(cache))
3147 DMEMIT("passthrough ");
3148
3149 else if (writeback_mode(cache))
3150 DMEMIT("writeback ");
3151
3152 else {
3153 DMEMIT("unknown ");
3154 DMERR("%s: internal error: unknown io mode: %d",
3155 cache_device_name(cache), (int) cf->io_mode);
3156 }
3157
3158 if (!cf->discard_passdown)
3159 DMEMIT("no_discard_passdown ");
3160
3161 *sz_ptr = sz;
3162}
3163
3122/* 3164/*
3123 * Status format: 3165 * Status format:
3124 * 3166 *
@@ -3185,25 +3227,7 @@ static void cache_status(struct dm_target *ti, status_type_t type,
3185 (unsigned) atomic_read(&cache->stats.promotion), 3227 (unsigned) atomic_read(&cache->stats.promotion),
3186 (unsigned long) atomic_read(&cache->nr_dirty)); 3228 (unsigned long) atomic_read(&cache->nr_dirty));
3187 3229
3188 if (cache->features.metadata_version == 2) 3230 emit_flags(cache, result, maxlen, &sz);
3189 DMEMIT("2 metadata2 ");
3190 else
3191 DMEMIT("1 ");
3192
3193 if (writethrough_mode(cache))
3194 DMEMIT("writethrough ");
3195
3196 else if (passthrough_mode(cache))
3197 DMEMIT("passthrough ");
3198
3199 else if (writeback_mode(cache))
3200 DMEMIT("writeback ");
3201
3202 else {
3203 DMERR("%s: internal error: unknown io mode: %d",
3204 cache_device_name(cache), (int) cache->features.io_mode);
3205 goto err;
3206 }
3207 3231
3208 DMEMIT("2 migration_threshold %llu ", (unsigned long long) cache->migration_threshold); 3232 DMEMIT("2 migration_threshold %llu ", (unsigned long long) cache->migration_threshold);
3209 3233
@@ -3432,14 +3456,62 @@ static int cache_iterate_devices(struct dm_target *ti,
3432 return r; 3456 return r;
3433} 3457}
3434 3458
3459static bool origin_dev_supports_discard(struct block_device *origin_bdev)
3460{
3461 struct request_queue *q = bdev_get_queue(origin_bdev);
3462
3463 return q && blk_queue_discard(q);
3464}
3465
3466/*
3467 * If discard_passdown was enabled verify that the origin device
3468 * supports discards. Disable discard_passdown if not.
3469 */
3470static void disable_passdown_if_not_supported(struct cache *cache)
3471{
3472 struct block_device *origin_bdev = cache->origin_dev->bdev;
3473 struct queue_limits *origin_limits = &bdev_get_queue(origin_bdev)->limits;
3474 const char *reason = NULL;
3475 char buf[BDEVNAME_SIZE];
3476
3477 if (!cache->features.discard_passdown)
3478 return;
3479
3480 if (!origin_dev_supports_discard(origin_bdev))
3481 reason = "discard unsupported";
3482
3483 else if (origin_limits->max_discard_sectors < cache->sectors_per_block)
3484 reason = "max discard sectors smaller than a block";
3485
3486 if (reason) {
3487 DMWARN("Origin device (%s) %s: Disabling discard passdown.",
3488 bdevname(origin_bdev, buf), reason);
3489 cache->features.discard_passdown = false;
3490 }
3491}
3492
3435static void set_discard_limits(struct cache *cache, struct queue_limits *limits) 3493static void set_discard_limits(struct cache *cache, struct queue_limits *limits)
3436{ 3494{
3495 struct block_device *origin_bdev = cache->origin_dev->bdev;
3496 struct queue_limits *origin_limits = &bdev_get_queue(origin_bdev)->limits;
3497
3498 if (!cache->features.discard_passdown) {
3499 /* No passdown is done so setting own virtual limits */
3500 limits->max_discard_sectors = min_t(sector_t, cache->discard_block_size * 1024,
3501 cache->origin_sectors);
3502 limits->discard_granularity = cache->discard_block_size << SECTOR_SHIFT;
3503 return;
3504 }
3505
3437 /* 3506 /*
3438 * FIXME: these limits may be incompatible with the cache device 3507 * cache_iterate_devices() is stacking both origin and fast device limits
3508 * but discards aren't passed to fast device, so inherit origin's limits.
3439 */ 3509 */
3440 limits->max_discard_sectors = min_t(sector_t, cache->discard_block_size * 1024, 3510 limits->max_discard_sectors = origin_limits->max_discard_sectors;
3441 cache->origin_sectors); 3511 limits->max_hw_discard_sectors = origin_limits->max_hw_discard_sectors;
3442 limits->discard_granularity = cache->discard_block_size << SECTOR_SHIFT; 3512 limits->discard_granularity = origin_limits->discard_granularity;
3513 limits->discard_alignment = origin_limits->discard_alignment;
3514 limits->discard_misaligned = origin_limits->discard_misaligned;
3443} 3515}
3444 3516
3445static void cache_io_hints(struct dm_target *ti, struct queue_limits *limits) 3517static void cache_io_hints(struct dm_target *ti, struct queue_limits *limits)
@@ -3456,6 +3528,8 @@ static void cache_io_hints(struct dm_target *ti, struct queue_limits *limits)
3456 blk_limits_io_min(limits, cache->sectors_per_block << SECTOR_SHIFT); 3528 blk_limits_io_min(limits, cache->sectors_per_block << SECTOR_SHIFT);
3457 blk_limits_io_opt(limits, cache->sectors_per_block << SECTOR_SHIFT); 3529 blk_limits_io_opt(limits, cache->sectors_per_block << SECTOR_SHIFT);
3458 } 3530 }
3531
3532 disable_passdown_if_not_supported(cache);
3459 set_discard_limits(cache, limits); 3533 set_discard_limits(cache, limits);
3460} 3534}
3461 3535
@@ -3463,7 +3537,7 @@ static void cache_io_hints(struct dm_target *ti, struct queue_limits *limits)
3463 3537
3464static struct target_type cache_target = { 3538static struct target_type cache_target = {
3465 .name = "cache", 3539 .name = "cache",
3466 .version = {2, 0, 0}, 3540 .version = {2, 1, 0},
3467 .module = THIS_MODULE, 3541 .module = THIS_MODULE,
3468 .ctr = cache_ctr, 3542 .ctr = cache_ctr,
3469 .dtr = cache_dtr, 3543 .dtr = cache_dtr,