[dm-devel] [PATCH 8/8] [dm-cache] cache target

Alasdair G Kergon agk at redhat.com
Tue Feb 12 17:29:09 UTC 2013


On Tue, Feb 12, 2013 at 03:27:33PM +0000, Alasdair G Kergon wrote:
> updated code taken from the all-caches branch of 
> git://github.com/jthornber/linux-2.6
  
File: dm-cache-target.c

> #include <asm/div64.h>
Is this needed anywhere?  We should be using sector_div for sector_t.

> #include <linux/blkdev.h>
Already in dm.h

> #include <linux/list.h>
Already in dm.h

> struct cache_features {
> 	enum cache_mode mode;
> 	bool write_through:1;
> };

We should probably support 'ignore_discard' like thin, so it's possible
to skip internal target discard processing if the user thinks it gives 
them no net benefit.
 
> struct cache {

Rather large - some parts could be moved out into sub structures (stats),
but never mind for now.

> static struct dm_bio_prison_cell *prealloc_get_cell(struct prealloc *p)
> {
> 	struct dm_bio_prison_cell *r = NULL;
> 
> 	if (p->cell1) {
> 		r = p->cell1;
> 		p->cell1 = NULL;
> 
> 	} else if (p->cell2) {
> 		r = p->cell2;
> 		p->cell2 = NULL;
> 	} else
> 		BUG();

Brief comment explaining the assumption here (or at top of fn) to help people
if this BUG() is hit?

> static void prealloc_put_cell(struct prealloc *p, struct dm_bio_prison_cell *cell)
> {
> 	if (!p->cell2)
> 		p->cell2 = cell;
> 
> 	else if (!p->cell1)
> 		p->cell1 = cell;
> 
> 	else
> 		BUG();
> }

Same.

> static dm_dblock_t oblock_to_dblock(struct cache *cache, dm_oblock_t oblock)
> {
> 	sector_t tmp = cache->discard_block_size;
> 	dm_block_t b = from_oblock(oblock);
> 
> 	do_div(tmp, cache->sectors_per_block);

sector_div?  (and elsewhere)

> 	do_div(b, tmp);
> 	return to_dblock(b);
> }


> static void load_stats(struct cache *cache)
> {
> 	struct dm_cache_statistics stats;
> 
> 	dm_cache_get_stats(cache->cmd, &stats);

Make it clearer from the fn name where the stats are "got" from?
e.g. dm_cache_metadata_* or _get_stats_from_*


> static void migration_success_post_commit(struct dm_cache_migration *mg)
> {
> 	unsigned long flags;
> 	struct cache *cache = mg->cache;
> 
> 	if (mg->writeback) {
> 		DMWARN("shouldn't get here");

Explain why.  BUG? or corruption?

> 		return;

> /*
>  * People generally discard large parts of a device, eg, the whole device
>  * when formatting.  Splitting these large discards up into cache block
>  * sized ios and then quiescing (always neccessary for discard) takes too
>  * long.
>  *
>  * We keep it simple, and allow any size of discard to come in, and just
>  * mark off blocks on the discard bitset.  No passdown occurs!
>  *
>  * To implement passdown we need to change the bio_prison such that a cell
>  * can have a key that spans many blocks.  This change is planned for
>  * thin-provisioning.

Re-word the last bit of this slightly so it doesn't become incorrect if thin
provisioning implements this?  (Would we remember to update this comment
otherwise?)

>  */
> static void process_discard_bio(struct cache *cache, struct bio *bio)

> static void cache_dtr(struct dm_target *ti)
> {
> 	struct cache *cache = ti->private;
> 
> 	pr_alert("dm-cache statistics:\n");

alert?!  Just debugging code that you'll be removing, I trust:)

> static int ensure_args__(struct dm_arg_set *as,
> 		       unsigned count, char **error)
> {
> 	if (as->argc < count) {
> 		*error = "Insufficient args";
> 		return -EINVAL;
> 	}
> 
> 	return 0;
> }
> 
> #define ensure_args(n)					\
> 	do {						\
> 		r = ensure_args__(as, n, error);	\
> 		if (r)					\
> 			return r;			\
> 	} while (0)
> 

> static int parse_metadata_dev(struct cache_args *ca, struct dm_arg_set *as,
> 			      char **error)
> {
> 	int r;
> 	sector_t metadata_dev_size;
> 	char b[BDEVNAME_SIZE];
> 
> 	ensure_args(1);

I'm afraid I don't see any benefit of this: it slows down understanding
of the code and has a hidden 'return' that isn't hinted at in the macro 
name to trip people up.

Can we not do something like:

	if (!at_least_one_arg(as, error))
		return -EINVAL;

 
> #define parse(name)					\
> 	do {						\
> 		r = parse_ ## name(ca, &as, error);	\
> 		if (r)					\
> 			return r;			\
> 	} while (0)
> 
> 	parse(metadata_dev);
> 	parse(cache_dev);
> 	parse(origin_dev);
> 	parse(block_size);
> 	parse(features);
> 	parse(policy);
> #undef parse
 
Please just write this out longhand: I don't see the gain from making
the reader deal with a new macro.

> static struct kmem_cache *_migration_cache;

Remove _ prefix I think.

> static int cache_create(struct cache_args *ca, struct cache **result)

Use PTR_ERR maybe?  Maybe not.

> 	ti->num_discard_bios = 1;
> 	ti->discards_supported = true;
> 	ti->discard_zeroes_data_unsupported = true;
> 
> 	if (cache->features.write_through)

Fix:	if (ca->features.write_through)

> 		ti->num_write_bios = cache_num_write_bios;



> static int cache_status(struct dm_target *ti, status_type_t type,
> 			unsigned status_flags, char *result, unsigned maxlen)

Add provision for policy status here too?

> static int process_config_option(struct cache *cache, char **argv)

Document non-standard return value up-front?

> {
> 	if (!strcasecmp(argv[1], "migration_threshold")) {
> 		unsigned long tmp;
> 
> 		if (kstrtoul(argv[2], 10, &tmp))
> 			return -EINVAL;
> 
> 		cache->migration_threshold = tmp;

Does this need any validation or is any value OK/sensible?

> 
> 	} else
> 		return 1; /* Inform caller it's not our option. */

Invert if and return 1 immediately, dropping need for else + indentation?


Document current message(s) supported by this file inline ahead of function here

> static int cache_message(struct dm_target *ti, unsigned argc, char **argv)
> {
> 	int r = 0;
> 	struct cache *cache = ti->private;
> 
> 	if (argc != 3)
> 		return -EINVAL;
> 
> 	r = !strcasecmp(argv[0], "set_config") ? process_config_option(cache, argv) : 1;
> 
> 	if (r == 1) /* Message is for the target -> hand over to policy plugin. */
> 		r = policy_message(cache->policy, argc, argv);
> 

What's responsible for logging that the message wasn't recognised?
  or is EINVAL enough perhaps as we don't really add any info to that?

  Drop 'set_config'?
     => "set" or why not just use the variable name directly as the message?
 

> static int cache_bvec_merge(struct dm_target *ti,
> 			  struct bvec_merge_data *bvm,
> 			  struct bio_vec *biovec, int max_size)

Needs a comment to explain the reasoning here I think.
We act as if the cache dev wasn't present?
Then take the hit and split later if cached?
Have we seen any impact in tests or tried alternatives here?

> static void cache_io_hints(struct dm_target *ti, struct queue_limits *limits)

Check carefully. The thin version of this had to be fixed.

> static void dm_cache_exit(void)

Missing __exit (not used anywhere else)

Alasdair




More information about the dm-devel mailing list