Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+103 -26
+3
Documentation/device-mapper/cache.txt
··· 206 206 in a separate btree, which improves speed of shutting 207 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 + 209 212 A policy called 'default' is always registered. This is an alias for 210 213 the policy we currently think is giving best all round performance. 211 214
+100 -26
drivers/md/dm-cache-target.c
··· 353 353 enum cache_metadata_mode mode; 354 354 enum cache_io_mode io_mode; 355 355 unsigned metadata_version; 356 + bool discard_passdown:1; 356 357 }; 357 358 358 359 struct cache_stats { ··· 1900 1899 b = to_dblock(from_dblock(b) + 1); 1901 1900 } 1902 1901 1903 - bio_endio(bio); 1902 + if (cache->features.discard_passdown) { 1903 + remap_to_origin(cache, bio); 1904 + generic_make_request(bio); 1905 + } else 1906 + bio_endio(bio); 1904 1907 1905 1908 return false; 1906 1909 } ··· 2238 2233 cf->mode = CM_WRITE; 2239 2234 cf->io_mode = CM_IO_WRITEBACK; 2240 2235 cf->metadata_version = 1; 2236 + cf->discard_passdown = true; 2241 2237 } 2242 2238 2243 2239 static int parse_features(struct cache_args *ca, struct dm_arg_set *as, 2244 2240 char **error) 2245 2241 { 2246 2242 static const struct dm_arg _args[] = { 2247 - {0, 2, "Invalid number of cache feature arguments"}, 2243 + {0, 3, "Invalid number of cache feature arguments"}, 2248 2244 }; 2249 2245 2250 2246 int r, mode_ctr = 0; ··· 2279 2273 2280 2274 else if (!strcasecmp(arg, "metadata2")) 2281 2275 cf->metadata_version = 2; 2276 + 2277 + else if (!strcasecmp(arg, "no_discard_passdown")) 2278 + cf->discard_passdown = false; 2282 2279 2283 2280 else { 2284 2281 *error = "Unrecognised cache feature requested"; ··· 3128 3119 do_waker(&cache->waker.work); 3129 3120 } 3130 3121 3122 + static void emit_flags(struct cache *cache, char *result, 3123 + unsigned maxlen, ssize_t *sz_ptr) 3124 + { 3125 + ssize_t sz = *sz_ptr; 3126 + struct cache_features *cf = &cache->features; 3127 + unsigned count = (cf->metadata_version == 2) + !cf->discard_passdown + 1; 3128 + 3129 + DMEMIT("%u ", count); 3130 + 3131 + if (cf->metadata_version == 2) 3132 + DMEMIT("metadata2 "); 3133 + 3134 + if (writethrough_mode(cache)) 3135 + DMEMIT("writethrough "); 3136 + 3137 + else if (passthrough_mode(cache)) 3138 + DMEMIT("passthrough "); 3139 + 3140 + else if (writeback_mode(cache)) 3141 + DMEMIT("writeback "); 3142 + 3143 + else { 3144 + DMEMIT("unknown "); 3145 + DMERR("%s: internal error: unknown io mode: %d", 3146 + cache_device_name(cache), (int) cf->io_mode); 3147 + } 3148 + 3149 + if (!cf->discard_passdown) 3150 + DMEMIT("no_discard_passdown "); 3151 + 3152 + *sz_ptr = sz; 3153 + } 3154 + 3131 3155 /* 3132 3156 * Status format: 3133 3157 * ··· 3227 3185 (unsigned) atomic_read(&cache->stats.promotion), 3228 3186 (unsigned long) atomic_read(&cache->nr_dirty)); 3229 3187 3230 - if (cache->features.metadata_version == 2) 3231 - DMEMIT("2 metadata2 "); 3232 - else 3233 - DMEMIT("1 "); 3234 - 3235 - if (writethrough_mode(cache)) 3236 - DMEMIT("writethrough "); 3237 - 3238 - else if (passthrough_mode(cache)) 3239 - DMEMIT("passthrough "); 3240 - 3241 - else if (writeback_mode(cache)) 3242 - DMEMIT("writeback "); 3243 - 3244 - else { 3245 - DMERR("%s: internal error: unknown io mode: %d", 3246 - cache_device_name(cache), (int) cache->features.io_mode); 3247 - goto err; 3248 - } 3188 + emit_flags(cache, result, maxlen, &sz); 3249 3189 3250 3190 DMEMIT("2 migration_threshold %llu ", (unsigned long long) cache->migration_threshold); 3251 3191 ··· 3456 3432 return r; 3457 3433 } 3458 3434 3435 + static bool origin_dev_supports_discard(struct block_device *origin_bdev) 3436 + { 3437 + struct request_queue *q = bdev_get_queue(origin_bdev); 3438 + 3439 + return q && blk_queue_discard(q); 3440 + } 3441 + 3442 + /* 3443 + * If discard_passdown was enabled verify that the origin device 3444 + * supports discards. Disable discard_passdown if not. 3445 + */ 3446 + static void disable_passdown_if_not_supported(struct cache *cache) 3447 + { 3448 + struct block_device *origin_bdev = cache->origin_dev->bdev; 3449 + struct queue_limits *origin_limits = &bdev_get_queue(origin_bdev)->limits; 3450 + const char *reason = NULL; 3451 + char buf[BDEVNAME_SIZE]; 3452 + 3453 + if (!cache->features.discard_passdown) 3454 + return; 3455 + 3456 + if (!origin_dev_supports_discard(origin_bdev)) 3457 + reason = "discard unsupported"; 3458 + 3459 + else if (origin_limits->max_discard_sectors < cache->sectors_per_block) 3460 + reason = "max discard sectors smaller than a block"; 3461 + 3462 + if (reason) { 3463 + DMWARN("Origin device (%s) %s: Disabling discard passdown.", 3464 + bdevname(origin_bdev, buf), reason); 3465 + cache->features.discard_passdown = false; 3466 + } 3467 + } 3468 + 3459 3469 static void set_discard_limits(struct cache *cache, struct queue_limits *limits) 3460 3470 { 3471 + struct block_device *origin_bdev = cache->origin_dev->bdev; 3472 + struct queue_limits *origin_limits = &bdev_get_queue(origin_bdev)->limits; 3473 + 3474 + if (!cache->features.discard_passdown) { 3475 + /* No passdown is done so setting own virtual limits */ 3476 + limits->max_discard_sectors = min_t(sector_t, cache->discard_block_size * 1024, 3477 + cache->origin_sectors); 3478 + limits->discard_granularity = cache->discard_block_size << SECTOR_SHIFT; 3479 + return; 3480 + } 3481 + 3461 3482 /* 3462 - * FIXME: these limits may be incompatible with the cache device 3483 + * cache_iterate_devices() is stacking both origin and fast device limits 3484 + * but discards aren't passed to fast device, so inherit origin's limits. 3463 3485 */ 3464 - limits->max_discard_sectors = min_t(sector_t, cache->discard_block_size * 1024, 3465 - cache->origin_sectors); 3466 - limits->discard_granularity = cache->discard_block_size << SECTOR_SHIFT; 3486 + limits->max_discard_sectors = origin_limits->max_discard_sectors; 3487 + limits->max_hw_discard_sectors = origin_limits->max_hw_discard_sectors; 3488 + limits->discard_granularity = origin_limits->discard_granularity; 3489 + limits->discard_alignment = origin_limits->discard_alignment; 3490 + limits->discard_misaligned = origin_limits->discard_misaligned; 3467 3491 } 3468 3492 3469 3493 static void cache_io_hints(struct dm_target *ti, struct queue_limits *limits) ··· 3528 3456 blk_limits_io_min(limits, cache->sectors_per_block << SECTOR_SHIFT); 3529 3457 blk_limits_io_opt(limits, cache->sectors_per_block << SECTOR_SHIFT); 3530 3458 } 3459 + 3460 + disable_passdown_if_not_supported(cache); 3531 3461 set_discard_limits(cache, limits); 3532 3462 } 3533 3463 ··· 3537 3463 3538 3464 static struct target_type cache_target = { 3539 3465 .name = "cache", 3540 - .version = {2, 0, 0}, 3466 + .version = {2, 1, 0}, 3541 3467 .module = THIS_MODULE, 3542 3468 .ctr = cache_ctr, 3543 3469 .dtr = cache_dtr,