[dm-devel] [PATCH 24/24] dm cache policy era: add cache block invalidation support
Mike Snitzer
snitzer at redhat.com
Thu Oct 24 18:30:37 UTC 2013
From: Heinz Mauelshagen <heinzm at redhat.com>
This commit adds support for the cache block invalidation API to the
"era" policy shim.
It provides:
- a temporary store containing selected cache blocks filtered based on a
message requesting eras equal to, less than, less equal, greater than
or greater equal to an era number
- provisioning of the entries, to be invalidated in that temporary
store, to the cache core target
Signed-off-by: Heinz Mauelshagen <heinzm at redhat.com>
Signed-off-by: Mike Snitzer <snitzer at redhat.com>
---
drivers/md/dm-cache-policy-era.c | 194 +++++++++++++++++++++++++++++++--------
1 file changed, 154 insertions(+), 40 deletions(-)
diff --git a/drivers/md/dm-cache-policy-era.c b/drivers/md/dm-cache-policy-era.c
index 427514c..486717a 100644
--- a/drivers/md/dm-cache-policy-era.c
+++ b/drivers/md/dm-cache-policy-era.c
@@ -2,6 +2,8 @@
* Copyright 2013 NetApp, Inc. All Rights Reserved, contribution by
* Morgan Mears.
*
+ * Copyright 2013 Red Hat, Inc. All Rights Reserved.
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
@@ -44,6 +46,13 @@ struct era_policy {
era_t *cb_to_era;
era_t era_counter;
+
+ /* Temporary store for unmap information during invalidation. */
+ struct {
+ unsigned long *bitset;
+ dm_oblock_t *oblocks;
+ unsigned long last_cblock;
+ } invalidate;
};
/*----------------------------------------------------------------*/
@@ -53,7 +62,69 @@ static struct era_policy *to_era_policy(struct dm_cache_policy *p)
return container_of(p, struct era_policy, policy);
}
-static int incr_era_counter(struct era_policy *era, const char *curr_era_str)
+static unsigned long *alloc_bitset(unsigned nr_entries)
+{
+ size_t s = sizeof(unsigned long) * dm_div_up(nr_entries, BITS_PER_LONG);
+ return vzalloc(s);
+}
+
+static void free_bitset(unsigned long *bits)
+{
+ vfree(bits);
+}
+
+static dm_oblock_t *alloc_oblocks(unsigned nr_entries)
+{
+ size_t s = sizeof(dm_oblock_t) * nr_entries;
+ return vmalloc(s);
+}
+
+static void free_oblocks(dm_oblock_t *blocks)
+{
+ vfree(blocks);
+}
+
+static void free_invalidate(struct era_policy *era)
+{
+ if (era->invalidate.oblocks) {
+ free_oblocks(era->invalidate.oblocks);
+ era->invalidate.oblocks = NULL;
+ }
+
+ if (era->invalidate.bitset) {
+ free_bitset(era->invalidate.bitset);
+ era->invalidate.bitset = NULL; /* Being checked for! */
+ }
+}
+
+static int alloc_invalidate(struct era_policy *era)
+{
+ /* FIXME: memory consumption! */
+ era->invalidate.oblocks = alloc_oblocks(from_cblock(era->cache_size));
+ if (!era->invalidate.oblocks) {
+ DMERR("failed to allocate original blocks unmap array");
+ goto err;
+ }
+
+ era->invalidate.bitset = alloc_bitset(from_cblock(era->cache_size));
+ if (!era->invalidate.bitset) {
+ DMERR("failed to allocate cache blocks unmap bitset");
+ goto err;
+ }
+
+ era->invalidate.last_cblock = 0;
+ return 0;
+
+err:
+ free_invalidate(era);
+ return -ENOMEM;
+}
+
+
+typedef int (*era_match_fn_t)(era_t, era_t);
+
+static int incr_era_counter(struct era_policy *era, const char *curr_era_str,
+ era_match_fn_t dummy)
{
era_t curr_era_counter;
int r;
@@ -119,8 +190,6 @@ static int era_is_lt_value(era_t era, era_t value)
return era < value;
}
-typedef int (*era_match_fn_t)(era_t, era_t);
-
struct inval_oblocks_ctx {
struct era_policy *era;
era_match_fn_t era_match_fn;
@@ -131,26 +200,16 @@ static int era_inval_oblocks(void *context, dm_cblock_t cblock,
dm_oblock_t oblock, void *unused)
{
struct inval_oblocks_ctx *ctx = (struct inval_oblocks_ctx *)context;
- struct dm_cache_policy *child;
- era_t act_era;
+ era_t act_era = ctx->era->cb_to_era[from_cblock(cblock)];
- act_era = ctx->era->cb_to_era[from_cblock(cblock)];
if (ctx->era_match_fn(act_era, ctx->test_era)) {
#if DEBUG_ERA
DMDEBUG("cblock %u has era %u matching test_era %u; "
"marking mapping to be removed for oblock %llu.",
from_cblock(cblock), act_era, ctx->test_era, oblock);
#endif
- child = ctx->era->policy.child;
-
- /*
- * This deadlocks (lock against self) because child is calling
- * us via the walk_mappings context callback, child's
- * walk_mappings holds child's lock, and child's remove_mappings
- * tries to get it again. Not fixing because I believe the
- * invalidate API is going to change.
- */
- /* child->remove_mapping(child, oblock); */
+ set_bit(from_cblock(cblock), ctx->era->invalidate.bitset);
+ ctx->era->invalidate.oblocks[from_cblock(cblock)] = oblock;
}
return 0;
@@ -164,6 +223,11 @@ static int cond_unmap_by_era(struct era_policy *era, const char *test_era_str,
era_t test_era;
int r;
+ if (era->invalidate.bitset) {
+ DMERR("previous unmap request exists");
+ return -EPERM;
+ }
+
/*
* Unmap blocks with eras matching the given era, according to the
* given matching function.
@@ -172,6 +236,10 @@ static int cond_unmap_by_era(struct era_policy *era, const char *test_era_str,
if (kstrtou32(test_era_str, 10, &test_era))
return -EINVAL;
+ r = alloc_invalidate(era);
+ if (r)
+ return r;
+
io_ctx.era = era;
io_ctx.era_match_fn = era_match_fn;
io_ctx.test_era = test_era;
@@ -197,10 +265,12 @@ static int cond_unmap_by_era(struct era_policy *era, const char *test_era_str,
static void era_destroy(struct dm_cache_policy *p)
{
struct era_policy *era = to_era_policy(p);
+
#if DEBUG_ERA
- DMDEBUG("destroyed era %p", era);
+ DMDEBUG("destroying era %p", era);
#endif
- kfree(era->cb_to_era);
+ free_invalidate(era);
+ vfree(era->cb_to_era);
kfree(era);
}
@@ -216,6 +286,7 @@ static int era_map(struct dm_cache_policy *p, dm_oblock_t oblock,
if (can_block)
mutex_lock(&era->lock);
+
else if (!mutex_trylock(&era->lock))
return -EWOULDBLOCK;
@@ -257,6 +328,7 @@ static int era_load_mapping(struct dm_cache_policy *p,
r = policy_load_mapping(child, oblock, cblock, hint, hint_valid);
+ /* FIXME: recovered area valid on reload called from cache core invalidate mapping error path? */
if (!r && hint_valid &&
(from_cblock(cblock) < from_cblock(era->cache_size))) {
recovered_era = le32_to_cpu(*le32_hint);
@@ -313,28 +385,71 @@ static void era_force_mapping(struct dm_cache_policy *p, dm_oblock_t old_oblock,
mutex_unlock(&era->lock);
}
-static int era_set_config_value(struct dm_cache_policy *p, const char *key,
- const char *value)
+/* Find next block to invalidate. */
+static int __find_invalidate_block(struct era_policy *era, dm_cblock_t *cblock)
+{
+ int bit = find_next_bit(era->invalidate.bitset, from_cblock(era->cache_size),
+ era->invalidate.last_cblock);
+
+ *cblock = to_cblock(bit);
+ era->invalidate.last_cblock = bit;
+ return bit < from_cblock(era->cache_size) ? 0 : -ENODATA;
+}
+
+static int era_invalidate_mapping(struct dm_cache_policy *p,
+ dm_oblock_t *oblock, dm_cblock_t *cblock)
{
struct era_policy *era = to_era_policy(p);
int r;
- if (!strcasecmp(key, "increment_era_counter"))
- r = incr_era_counter(era, value);
- else if (!strcasecmp(key, "unmap_blocks_from_later_eras"))
- r = cond_unmap_by_era(era, value, era_is_gt_value);
- else if (!strcasecmp(key, "unmap_blocks_from_this_era_and_later"))
- r = cond_unmap_by_era(era, value, era_is_gte_value);
- else if (!strcasecmp(key, "unmap_blocks_from_this_era_and_earlier"))
- r = cond_unmap_by_era(era, value, era_is_lte_value);
- else if (!strcasecmp(key, "unmap_blocks_from_earlier_eras"))
- r = cond_unmap_by_era(era, value, era_is_lt_value);
- else
- r = policy_set_config_value(p->child, key, value);
+ if (!era->invalidate.bitset)
+ return -ENODATA;
+
+ r = __find_invalidate_block(era, cblock);
+ if (r < 0)
+ free_invalidate(era);
+ else {
+ BUG_ON(from_cblock(*cblock) >= from_cblock(era->cache_size));
+ BUG_ON(!test_bit(from_cblock(*cblock), era->invalidate.bitset));
+ clear_bit(from_cblock(*cblock), era->invalidate.bitset);
+ *oblock = era->invalidate.oblocks[from_cblock(*cblock)];
+ r = policy_invalidate_mapping(p->child, oblock, cblock);
+#if DEBUG_ERA
+ DMDEBUG("unmapped cblock=%u oblock=%llu", from_cblock(*cblock), from_oblock(*oblock));
+#endif
+ }
return r;
}
+struct config_value_handler {
+ const char *cmd;
+ int (*handler_fn)(struct era_policy *, const char *, era_match_fn_t);
+ era_match_fn_t match_fn;
+};
+
+/* FIXME: is a delete unmap request needed or is reloading the mapping sufficient to achieve it? */
+static int era_set_config_value(struct dm_cache_policy *p, const char *key,
+ const char *value)
+{
+ struct era_policy *era = to_era_policy(p);
+ struct config_value_handler *vh, value_handlers[] = {
+ { "increment_era_counter", incr_era_counter, NULL },
+ { "unmap_blocks_from_later_eras", cond_unmap_by_era, era_is_gt_value },
+ { "unmap_blocks_from_this_era_and_later", cond_unmap_by_era, era_is_gte_value },
+ { "unmap_blocks_from_this_era_and_earlier", cond_unmap_by_era, era_is_lte_value },
+ { "unmap_blocks_from_earlier_eras", cond_unmap_by_era, era_is_lt_value },
+ { NULL }
+ };
+
+ for (vh = value_handlers; vh->cmd; vh++) {
+ if (!strcasecmp(key, vh->cmd))
+ return vh->handler_fn(era, value, vh->match_fn);
+ }
+
+ return policy_set_config_value(p->child, key, value);
+}
+
static int era_emit_config_values(struct dm_cache_policy *p, char *result,
unsigned maxlen)
{
@@ -355,6 +470,7 @@ static void init_policy_functions(struct era_policy *era)
era->policy.load_mapping = era_load_mapping;
era->policy.walk_mappings = era_walk_mappings;
era->policy.force_mapping = era_force_mapping;
+ era->policy.invalidate_mapping = era_invalidate_mapping;
era->policy.emit_config_values = era_emit_config_values;
era->policy.set_config_value = era_set_config_value;
}
@@ -372,15 +488,13 @@ static struct dm_cache_policy *era_create(dm_cblock_t cache_size,
era->cache_size = cache_size;
mutex_init(&era->lock);
- era->cb_to_era = kzalloc(from_cblock(era->cache_size) *
- sizeof(*(era->cb_to_era)), GFP_KERNEL);
- if (!era->cb_to_era)
- goto bad_alloc_cb_to_era;
- era->era_counter = 1;
-
- return &era->policy;
+ era->cb_to_era = vzalloc(from_cblock(era->cache_size) *
+ sizeof(*era->cb_to_era));
+ if (era->cb_to_era) {
+ era->era_counter = 1;
+ return &era->policy;
+ }
-bad_alloc_cb_to_era:
kfree(era);
return NULL;
}
--
1.8.1.4
More information about the dm-devel
mailing list