From 006d7a809b21ed831206f47135a8b038699bd9f0 Mon Sep 17 00:00:00 2001 From: Heinz Mauelshagen Date: Mon, 14 Oct 2013 18:32:26 +0200 Subject: [PATCH] dm-cache-policy-{era,trc}.c: support policy_invalidate_mapping() --- drivers/md/dm-cache-policy-era.c | 204 ++++++++++++++++++++++++++++++--------- drivers/md/dm-cache-policy-trc.c | 10 + 2 files changed, 171 insertions(+), 43 deletions(-) Index: linux/drivers/md/dm-cache-policy-era.c =================================================================== --- linux.orig/drivers/md/dm-cache-policy-era.c +++ linux/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 @@ -37,11 +39,18 @@ struct era_policy { struct mutex lock; /* FIXME: spinlock? */ + era_t era_counter; + dm_cblock_t cache_size; 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; }; /*----------------------------------------------------------------*/ @@ -51,7 +60,70 @@ static struct era_policy *to_era_policy( 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; @@ -115,8 +187,6 @@ static int era_is_lt_value(era_t era, er 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; @@ -127,25 +197,15 @@ static int era_inval_oblocks(void *conte 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)) { DMDEBUG("cblock %u has era %u matching test_era %u; " - "removing mapping for oblock %llu.", - from_cblock(cblock), act_era, ctx->test_era, - oblock); - child = ctx->era->policy.child; + "marking mapping to be removed for oblock %llu.", + from_cblock(cblock), act_era, ctx->test_era, oblock); - /* - * 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; @@ -160,6 +220,11 @@ static int cond_unmap_by_era(struct era_ 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. @@ -168,6 +233,10 @@ static int cond_unmap_by_era(struct era_ 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; @@ -193,9 +262,12 @@ static int cond_unmap_by_era(struct era_ static void era_destroy(struct dm_cache_policy *p) { struct era_policy *era = to_era_policy(p); - DMDEBUG("destroyed era %p", era); - kfree(era->cb_to_era); + free_invalidate(era); + vfree(era->cb_to_era); kfree(era); + + /* FIXME: remove this */ + DMDEBUG("destroyed era %p", era); } static int era_map(struct dm_cache_policy *p, dm_oblock_t oblock, @@ -210,6 +282,7 @@ static int era_map(struct dm_cache_polic if (can_block) mutex_lock(&era->lock); + else if (!mutex_trylock(&era->lock)) return -EWOULDBLOCK; @@ -221,6 +294,7 @@ static int era_map(struct dm_cache_polic if (!r && (bio_data_dir(bio) == WRITE) && (result->op == POLICY_HIT)) { cb_idx = from_cblock(result->cblock); BUG_ON(cb_idx >= from_cblock(era->cache_size)); + smp_rmb(); /* FIXME: remove this */ DMDEBUG("assigning era %u to cblock %u, oblock %llu due to write hit.", era->era_counter, result->cblock, oblock); @@ -249,6 +323,7 @@ static int era_load_mapping(struct dm_ca 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); @@ -259,6 +334,7 @@ static int era_load_mapping(struct dm_ca * Make sure the era counter starts higher than the highest * persisted era. */ + smp_rmb(); if (recovered_era >= era->era_counter) { era->era_counter = recovered_era; if (era->era_counter < ERA_MAX_ERA) @@ -289,6 +365,7 @@ static void era_force_mapping(struct dm_ "(old_oblock %llu) due to force_mapping.", era->era_counter, cblock, new_oblock, old_oblock); + smp_rmb(); era->cb_to_era[from_cblock(cblock)] = era->era_counter; } @@ -297,28 +374,70 @@ static void era_force_mapping(struct dm_ 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); + DMDEBUG("unmapped cblock=%u oblock=%llu", from_cblock(*cblock), from_oblock(*oblock)); + } 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) { @@ -337,6 +456,7 @@ static void init_policy_functions(struct 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; } @@ -354,15 +474,13 @@ static struct dm_cache_policy *era_creat 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; } Index: linux/drivers/md/dm-cache-policy-trc.c =================================================================== --- linux.orig/drivers/md/dm-cache-policy-trc.c +++ linux/drivers/md/dm-cache-policy-trc.c @@ -14,6 +14,8 @@ * */ +/* FIXME: use from_[oc]block() */ + #include "dm-cache-policy.h" #include "dm-cache-policy-internal.h" #include "dm-cache-shim-utils.h" @@ -135,6 +137,13 @@ static void trc_force_mapping(struct dm_ policy_force_mapping(p->child, old_oblock, new_oblock); } +static int trc_invalidate_mapping(struct dm_cache_policy *p, + dm_oblock_t *oblock, dm_cblock_t *cblock) +{ + DM_TRC_OUT(DM_TRC_LEV_NORMAL, p, "%p %llu %u", p, from_oblock(*oblock), from_cblock(*cblock)); + return policy_invalidate_mapping(p->child, oblock, cblock); +} + static dm_cblock_t trc_residency(struct dm_cache_policy *p) { DM_TRC_OUT(DM_TRC_LEV_NORMAL, p, "%p", p); @@ -189,6 +198,7 @@ static void init_policy_functions(struct trc->policy.remove_mapping = trc_remove_mapping; trc->policy.writeback_work = trc_writeback_work; trc->policy.force_mapping = trc_force_mapping; + trc->policy.invalidate_mapping = trc_invalidate_mapping; trc->policy.residency = trc_residency; trc->policy.tick = trc_tick; trc->policy.emit_config_values = trc_emit_config_values;