diff options
| author | Mike Snitzer <snitzer@redhat.com> | 2019-02-25 19:07:10 +0300 | 
|---|---|---|
| committer | Mike Snitzer <snitzer@redhat.com> | 2019-03-05 22:53:52 +0300 | 
| commit | de7180ff908b2bc0342e832dbdaa9a5f1ecaa33a (patch) | |
| tree | db7c3b6e894a7d5f96ab155500ff5a8275cfad66 | |
| parent | f87e033b3b923d91194348c11221e1bbc92e51b2 (diff) | |
| download | linux-de7180ff908b2bc0342e832dbdaa9a5f1ecaa33a.tar.xz | |
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.txt | 3 | ||||
| -rw-r--r-- | drivers/md/dm-cache-target.c | 126 | 
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:                    in a separate btree, which improves speed of shutting  		  down the cache. +   no_discard_passdown	: disable passing down discards from the cache +			  to the origin's data device. +  A policy called 'default' is always registered.  This is an alias for  the policy we currently think is giving best all round performance. 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 {  	enum cache_metadata_mode mode;  	enum cache_io_mode io_mode;  	unsigned metadata_version; +	bool discard_passdown:1;  };  struct cache_stats { @@ -1899,7 +1900,11 @@ static bool process_discard_bio(struct cache *cache, struct bio *bio)  		b = to_dblock(from_dblock(b) + 1);  	} -	bio_endio(bio); +	if (cache->features.discard_passdown) { +		remap_to_origin(cache, bio); +		generic_make_request(bio); +	} else +		bio_endio(bio);  	return false;  } @@ -2233,13 +2238,14 @@ static void init_features(struct cache_features *cf)  	cf->mode = CM_WRITE;  	cf->io_mode = CM_IO_WRITEBACK;  	cf->metadata_version = 1; +	cf->discard_passdown = true;  }  static int parse_features(struct cache_args *ca, struct dm_arg_set *as,  			  char **error)  {  	static const struct dm_arg _args[] = { -		{0, 2, "Invalid number of cache feature arguments"}, +		{0, 3, "Invalid number of cache feature arguments"},  	};  	int r, mode_ctr = 0; @@ -2274,6 +2280,9 @@ static int parse_features(struct cache_args *ca, struct dm_arg_set *as,  		else if (!strcasecmp(arg, "metadata2"))  			cf->metadata_version = 2; +		else if (!strcasecmp(arg, "no_discard_passdown")) +			cf->discard_passdown = false; +  		else {  			*error = "Unrecognised cache feature requested";  			return -EINVAL; @@ -3119,6 +3128,39 @@ static void cache_resume(struct dm_target *ti)  	do_waker(&cache->waker.work);  } +static void emit_flags(struct cache *cache, char *result, +		       unsigned maxlen, ssize_t *sz_ptr) +{ +	ssize_t sz = *sz_ptr; +	struct cache_features *cf = &cache->features; +	unsigned count = (cf->metadata_version == 2) + !cf->discard_passdown + 1; + +	DMEMIT("%u ", count); + +	if (cf->metadata_version == 2) +		DMEMIT("metadata2 "); + +	if (writethrough_mode(cache)) +		DMEMIT("writethrough "); + +	else if (passthrough_mode(cache)) +		DMEMIT("passthrough "); + +	else if (writeback_mode(cache)) +		DMEMIT("writeback "); + +	else { +		DMEMIT("unknown "); +		DMERR("%s: internal error: unknown io mode: %d", +		      cache_device_name(cache), (int) cf->io_mode); +	} + +	if (!cf->discard_passdown) +		DMEMIT("no_discard_passdown "); + +	*sz_ptr = sz; +} +  /*   * Status format:   * @@ -3185,25 +3227,7 @@ static void cache_status(struct dm_target *ti, status_type_t type,  		       (unsigned) atomic_read(&cache->stats.promotion),  		       (unsigned long) atomic_read(&cache->nr_dirty)); -		if (cache->features.metadata_version == 2) -			DMEMIT("2 metadata2 "); -		else -			DMEMIT("1 "); - -		if (writethrough_mode(cache)) -			DMEMIT("writethrough "); - -		else if (passthrough_mode(cache)) -			DMEMIT("passthrough "); - -		else if (writeback_mode(cache)) -			DMEMIT("writeback "); - -		else { -			DMERR("%s: internal error: unknown io mode: %d", -			      cache_device_name(cache), (int) cache->features.io_mode); -			goto err; -		} +		emit_flags(cache, result, maxlen, &sz);  		DMEMIT("2 migration_threshold %llu ", (unsigned long long) cache->migration_threshold); @@ -3432,14 +3456,62 @@ static int cache_iterate_devices(struct dm_target *ti,  	return r;  } +static bool origin_dev_supports_discard(struct block_device *origin_bdev) +{ +	struct request_queue *q = bdev_get_queue(origin_bdev); + +	return q && blk_queue_discard(q); +} + +/* + * If discard_passdown was enabled verify that the origin device + * supports discards.  Disable discard_passdown if not. + */ +static void disable_passdown_if_not_supported(struct cache *cache) +{ +	struct block_device *origin_bdev = cache->origin_dev->bdev; +	struct queue_limits *origin_limits = &bdev_get_queue(origin_bdev)->limits; +	const char *reason = NULL; +	char buf[BDEVNAME_SIZE]; + +	if (!cache->features.discard_passdown) +		return; + +	if (!origin_dev_supports_discard(origin_bdev)) +		reason = "discard unsupported"; + +	else if (origin_limits->max_discard_sectors < cache->sectors_per_block) +		reason = "max discard sectors smaller than a block"; + +	if (reason) { +		DMWARN("Origin device (%s) %s: Disabling discard passdown.", +		       bdevname(origin_bdev, buf), reason); +		cache->features.discard_passdown = false; +	} +} +  static void set_discard_limits(struct cache *cache, struct queue_limits *limits)  { +	struct block_device *origin_bdev = cache->origin_dev->bdev; +	struct queue_limits *origin_limits = &bdev_get_queue(origin_bdev)->limits; + +	if (!cache->features.discard_passdown) { +		/* No passdown is done so setting own virtual limits */ +		limits->max_discard_sectors = min_t(sector_t, cache->discard_block_size * 1024, +						    cache->origin_sectors); +		limits->discard_granularity = cache->discard_block_size << SECTOR_SHIFT; +		return; +	} +  	/* -	 * FIXME: these limits may be incompatible with the cache device +	 * cache_iterate_devices() is stacking both origin and fast device limits +	 * but discards aren't passed to fast device, so inherit origin's limits.  	 */ -	limits->max_discard_sectors = min_t(sector_t, cache->discard_block_size * 1024, -					    cache->origin_sectors); -	limits->discard_granularity = cache->discard_block_size << SECTOR_SHIFT; +	limits->max_discard_sectors = origin_limits->max_discard_sectors; +	limits->max_hw_discard_sectors = origin_limits->max_hw_discard_sectors; +	limits->discard_granularity = origin_limits->discard_granularity; +	limits->discard_alignment = origin_limits->discard_alignment; +	limits->discard_misaligned = origin_limits->discard_misaligned;  }  static 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)  		blk_limits_io_min(limits, cache->sectors_per_block << SECTOR_SHIFT);  		blk_limits_io_opt(limits, cache->sectors_per_block << SECTOR_SHIFT);  	} + +	disable_passdown_if_not_supported(cache);  	set_discard_limits(cache, limits);  } @@ -3463,7 +3537,7 @@ static void cache_io_hints(struct dm_target *ti, struct queue_limits *limits)  static struct target_type cache_target = {  	.name = "cache", -	.version = {2, 0, 0}, +	.version = {2, 1, 0},  	.module = THIS_MODULE,  	.ctr = cache_ctr,  	.dtr = cache_dtr, | 
