[dm-devel] [PATCH 19/24] dm cache: support for stackable caching policies
Mike Snitzer
snitzer at redhat.com
Thu Oct 24 18:30:32 UTC 2013
From: Morgan Mears <morgan.mears at netapp.com>
This commit implements support for stacking caching policies by
concatenating policy names.
A policy stack includes zero or more non-terminal policies, or shims,
followed by exactly one terminal policy which actually maintains the
cache-to-origin block mappings.
Non-terminal policy shims will be added in later patches. Each policy
shim must set the DM_CACHE_POLICY_SHIM feature flag in the "features"
member of the dm_cache_policy_type structure.
Signed-off-by: Morgan Mears <morgan.mears at netapp.com>
Signed-off-by: Heinz Mauelshagen <heinzm at redhat.com>
Signed-off-by: Mike Snitzer <snitzer at redhat.com>
---
drivers/md/Makefile | 3 +-
drivers/md/dm-cache-policy-internal.h | 6 +
drivers/md/dm-cache-policy.c | 47 ++++++-
drivers/md/dm-cache-policy.h | 17 +++
drivers/md/dm-cache-shim-utils.c | 210 +++++++++++++++++++++++++++++
drivers/md/dm-cache-shim-utils.h | 73 +++++++++++
drivers/md/dm-cache-stack-utils.c | 239 ++++++++++++++++++++++++++++++++++
drivers/md/dm-cache-stack-utils.h | 34 +++++
8 files changed, 626 insertions(+), 3 deletions(-)
create mode 100644 drivers/md/dm-cache-shim-utils.c
create mode 100644 drivers/md/dm-cache-shim-utils.h
create mode 100644 drivers/md/dm-cache-stack-utils.c
create mode 100644 drivers/md/dm-cache-stack-utils.h
diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index 2acc43f..5f6dfc3 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -11,7 +11,8 @@ dm-mirror-y += dm-raid1.o
dm-log-userspace-y \
+= dm-log-userspace-base.o dm-log-userspace-transfer.o
dm-thin-pool-y += dm-thin.o dm-thin-metadata.o
-dm-cache-y += dm-cache-target.o dm-cache-metadata.o dm-cache-policy.o
+dm-cache-y += dm-cache-target.o dm-cache-metadata.o dm-cache-policy.o \
+ dm-cache-shim-utils.o dm-cache-stack-utils.o
dm-cache-mq-y += dm-cache-policy-mq.o
dm-cache-cleaner-y += dm-cache-policy-cleaner.o
md-mod-y += md.o bitmap.o
diff --git a/drivers/md/dm-cache-policy-internal.h b/drivers/md/dm-cache-policy-internal.h
index 9b1473b..996b2b5 100644
--- a/drivers/md/dm-cache-policy-internal.h
+++ b/drivers/md/dm-cache-policy-internal.h
@@ -124,6 +124,12 @@ const unsigned *dm_cache_policy_get_version(struct dm_cache_policy *p);
int dm_cache_policy_set_hint_size(struct dm_cache_policy *p, unsigned hint_size);
size_t dm_cache_policy_get_hint_size(struct dm_cache_policy *p);
+/*
+ * Return bool that reflects whether or not policy is only a shim
+ * layer in a policy stack.
+ */
+bool dm_cache_policy_is_shim(struct dm_cache_policy *p);
+
/*----------------------------------------------------------------*/
#endif /* DM_CACHE_POLICY_INTERNAL_H */
diff --git a/drivers/md/dm-cache-policy.c b/drivers/md/dm-cache-policy.c
index 8e84d08..f2d8d33 100644
--- a/drivers/md/dm-cache-policy.c
+++ b/drivers/md/dm-cache-policy.c
@@ -5,6 +5,7 @@
*/
#include "dm-cache-policy-internal.h"
+#include "dm-cache-stack-utils.h"
#include "dm.h"
#include <linux/module.h>
@@ -54,6 +55,9 @@ static struct dm_cache_policy_type *get_policy_once(const char *name)
static struct dm_cache_policy_type *get_policy(const char *name)
{
struct dm_cache_policy_type *t;
+ char name_wo_delim[CACHE_POLICY_NAME_SIZE];
+ char *p_delim;
+ int n;
t = get_policy_once(name);
if (IS_ERR(t))
@@ -68,6 +72,28 @@ static struct dm_cache_policy_type *get_policy(const char *name)
if (IS_ERR(t))
return NULL;
+ if (t)
+ return t;
+
+ /*
+ * We also need to check for dm-cache-<@name> with no trailing
+ * DM_CACHE_POLICY_STACK_DELIM if @name has one, in order to
+ * support loadable policy shims.
+ */
+ n = strlcpy(name_wo_delim, name, sizeof(name_wo_delim));
+ if (n >= sizeof(name_wo_delim))
+ return NULL;
+ p_delim = strchr(name_wo_delim, DM_CACHE_POLICY_STACK_DELIM);
+ if (!p_delim || (p_delim[1] != '\0'))
+ return NULL;
+ p_delim[0] = '\0';
+
+ request_module("dm-cache-%s", name_wo_delim);
+
+ t = get_policy_once(name);
+ if (IS_ERR(t))
+ return NULL;
+
return t;
}
@@ -117,6 +143,11 @@ struct dm_cache_policy *dm_cache_policy_create(const char *name,
struct dm_cache_policy *p = NULL;
struct dm_cache_policy_type *type;
+ if (dm_cache_stack_utils_string_is_policy_stack(name))
+ return dm_cache_stack_utils_policy_stack_create(name, cache_size,
+ origin_size,
+ cache_block_size);
+
type = get_policy(name);
if (!type) {
DMWARN("unknown policy type");
@@ -138,8 +169,12 @@ void dm_cache_policy_destroy(struct dm_cache_policy *p)
{
struct dm_cache_policy_type *t = p->private;
- p->destroy(p);
- put_policy(t);
+ if (dm_cache_stack_utils_string_is_policy_stack(t->name))
+ dm_cache_stack_utils_policy_stack_destroy(p);
+ else {
+ p->destroy(p);
+ put_policy(t);
+ }
}
EXPORT_SYMBOL_GPL(dm_cache_policy_destroy);
@@ -179,4 +214,12 @@ int dm_cache_policy_set_hint_size(struct dm_cache_policy *p, unsigned hint_size)
}
EXPORT_SYMBOL_GPL(dm_cache_policy_set_hint_size);
+bool dm_cache_policy_is_shim(struct dm_cache_policy *p)
+{
+ struct dm_cache_policy_type *t = p->private;
+
+ return (t->features & DM_CACHE_POLICY_SHIM) ? true : false;
+}
+EXPORT_SYMBOL_GPL(dm_cache_policy_is_shim);
+
/*----------------------------------------------------------------*/
diff --git a/drivers/md/dm-cache-policy.h b/drivers/md/dm-cache-policy.h
index 6779ea7..83ec775 100644
--- a/drivers/md/dm-cache-policy.h
+++ b/drivers/md/dm-cache-policy.h
@@ -190,11 +190,26 @@ struct dm_cache_policy {
* Book keeping ptr for the policy register, not for general use.
*/
void *private;
+
+ /*
+ * Support for stackable policies. A policy stack consists of 0 or more
+ * "non-terminal" policies (which can intercept requests to provide
+ * additional functionality, but ultimately hand them down the stack)
+ * followed by one "terminal" policy which actually runs a caching
+ * algorithm. This is the pointer to the "next" policy in a
+ * non-terminal policy. It will always be NULL in a terminal policy.
+ */
+ struct dm_cache_policy *child;
};
/*----------------------------------------------------------------*/
/*
+ * Indicates that a policy is only a shim layer in a policy stack.
+ */
+#define DM_CACHE_POLICY_SHIM (1 << 0)
+
+/*
* We maintain a little register of the different policy types.
*/
#define CACHE_POLICY_NAME_SIZE 16
@@ -222,6 +237,8 @@ struct dm_cache_policy_type {
struct dm_cache_policy *(*create)(dm_cblock_t cache_size,
sector_t origin_size,
sector_t block_size);
+
+ unsigned long features;
};
int dm_cache_policy_register(struct dm_cache_policy_type *type);
diff --git a/drivers/md/dm-cache-shim-utils.c b/drivers/md/dm-cache-shim-utils.c
new file mode 100644
index 0000000..4151883
--- /dev/null
+++ b/drivers/md/dm-cache-shim-utils.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2013 NetApp, Inc. All Rights Reserved, contribution by
+ * Morgan Mears.
+ *
+ * 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
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details
+ *
+ */
+
+#include "dm-cache-policy.h"
+#include "dm-cache-policy-internal.h"
+#include "dm-cache-shim-utils.h"
+#include "dm.h"
+
+#include <linux/module.h>
+
+#define DM_MSG_PREFIX "cache-shim-utils"
+
+/*----------------------------------------------------------------*/
+
+static int shim_nested_walk_apply(void *context, dm_cblock_t cblock,
+ dm_oblock_t oblock, void *hint)
+{
+ struct shim_walk_map_ctx *ctx = context;
+ struct dm_cache_policy *p;
+ int child_hint_size;
+ void *my_hint;
+
+ /* Save off our child's hint */
+ if (ctx->child_hint_buf) {
+ p = ctx->my_policy;
+ child_hint_size = dm_cache_policy_get_hint_size(p->child);
+ if (child_hint_size && hint)
+ memcpy(&ctx->child_hint_buf[0], hint, child_hint_size);
+ }
+
+ /* Provide my hint or NULL up the stack */
+ my_hint = ctx->cblock_to_hint_fn ?
+ ctx->cblock_to_hint_fn(ctx, cblock, oblock) : NULL;
+
+ /* Reverse recurse, unless short-circuted */
+ return (ctx->parent_fn) ?
+ (*ctx->parent_fn)(ctx->parent_ctx, cblock, oblock, my_hint) : 0;
+}
+
+/*----------------------------------------------------------------*/
+
+/*
+ * Public interface, via the policy struct. See dm-cache-policy.h for a
+ * description of these.
+ */
+
+static void shim_destroy(struct dm_cache_policy *p)
+{
+ kfree(p);
+}
+
+static int shim_map(struct dm_cache_policy *p, dm_oblock_t oblock,
+ bool can_block, bool can_migrate, bool discarded_oblock,
+ struct bio *bio, struct policy_result *result)
+{
+ return policy_map(p->child, oblock, can_block, can_migrate,
+ discarded_oblock, bio, result);
+}
+
+static int shim_lookup(struct dm_cache_policy *p, dm_oblock_t oblock,
+ dm_cblock_t *cblock)
+{
+ return policy_lookup(p->child, oblock, cblock);
+}
+
+static void shim_set_dirty(struct dm_cache_policy *p, dm_oblock_t oblock)
+{
+ policy_set_dirty(p->child, oblock);
+}
+
+static void shim_clear_dirty(struct dm_cache_policy *p, dm_oblock_t oblock)
+{
+ policy_clear_dirty(p->child, oblock);
+}
+
+static int shim_load_mapping(struct dm_cache_policy *p,
+ dm_oblock_t oblock, dm_cblock_t cblock,
+ void *hint, bool hint_valid)
+{
+ return policy_load_mapping(p->child, oblock, cblock, hint, hint_valid);
+}
+
+static int shim_walk_mappings(struct dm_cache_policy *p, policy_walk_fn fn,
+ void *context)
+{
+ struct shim_walk_map_ctx my_ctx, *parent_ctx;
+ int my_hint_size;
+
+ parent_ctx = (struct shim_walk_map_ctx *)context;
+ my_hint_size = dm_cache_policy_get_hint_size(p);
+
+ my_ctx.parent_ctx = parent_ctx;
+ my_ctx.parent_fn = fn;
+ my_ctx.my_policy = p;
+ my_ctx.child_hint_buf = (parent_ctx->child_hint_buf) ?
+ &parent_ctx->child_hint_buf[my_hint_size] : NULL;
+ my_ctx.cblock_to_hint_fn = NULL;
+
+ return policy_walk_mappings(p->child, shim_nested_walk_apply, &my_ctx);
+}
+
+static void shim_remove_mapping(struct dm_cache_policy *p, dm_oblock_t oblock)
+{
+ policy_remove_mapping(p->child, oblock);
+}
+
+static int shim_writeback_work(struct dm_cache_policy *p, dm_oblock_t *oblock,
+ dm_cblock_t *cblock)
+{
+ return policy_writeback_work(p->child, oblock, cblock);
+}
+
+static void shim_force_mapping(struct dm_cache_policy *p,
+ dm_oblock_t current_oblock,
+ dm_oblock_t new_oblock)
+{
+ policy_force_mapping(p->child, current_oblock, new_oblock);
+}
+
+static dm_cblock_t shim_residency(struct dm_cache_policy *p)
+{
+ return policy_residency(p->child);
+}
+
+static void shim_tick(struct dm_cache_policy *p)
+{
+ policy_tick(p->child);
+}
+
+static int shim_set_config_value(struct dm_cache_policy *p,
+ const char *key, const char *value)
+{
+ return policy_set_config_value(p->child, key, value);
+}
+
+static int shim_emit_config_values(struct dm_cache_policy *p, char *result,
+ unsigned maxlen)
+{
+ return policy_emit_config_values(p->child, result, maxlen);
+}
+
+void dm_cache_shim_utils_init_shim_policy(struct dm_cache_policy *p)
+{
+ p->destroy = shim_destroy;
+ p->map = shim_map;
+ p->lookup = shim_lookup;
+ p->set_dirty = shim_set_dirty;
+ p->clear_dirty = shim_clear_dirty;
+ p->load_mapping = shim_load_mapping;
+ p->walk_mappings = shim_walk_mappings;
+ p->remove_mapping = shim_remove_mapping;
+ p->writeback_work = shim_writeback_work;
+ p->force_mapping = shim_force_mapping;
+ p->residency = shim_residency;
+ p->tick = shim_tick;
+ p->emit_config_values = shim_emit_config_values;
+ p->set_config_value = shim_set_config_value;
+}
+EXPORT_SYMBOL_GPL(dm_cache_shim_utils_init_shim_policy);
+
+int dm_cache_shim_utils_walk_map_with_ctx(struct shim_walk_map_ctx *ctx)
+{
+ struct dm_cache_policy *p = ctx->my_policy;
+
+ /*
+ * Used by the stack root policy in its walk_mappings implementation,
+ * to provide the top-level context that contains the buffer used to
+ * consolidate hint data from all of the shims and the terminal policy.
+ */
+ return policy_walk_mappings(p->child, shim_nested_walk_apply, ctx);
+}
+EXPORT_SYMBOL_GPL(dm_cache_shim_utils_walk_map_with_ctx);
+
+int dm_cache_shim_utils_walk_map(struct dm_cache_policy *p, policy_walk_fn fn,
+ void *context, cblock_to_hint_fn_t hint_fn)
+{
+ struct shim_walk_map_ctx my_ctx, *parent_ctx;
+ int my_hint_size;
+
+ /*
+ * Used by shim policies for their walk_mappings implementations.
+ * Handles packing up the hint data, in conjunction with
+ * shim_nested_walk_apply.
+ */
+ parent_ctx = (struct shim_walk_map_ctx *)context;
+ my_hint_size = dm_cache_policy_get_hint_size(p);
+
+ my_ctx.parent_ctx = parent_ctx;
+ my_ctx.parent_fn = fn;
+ my_ctx.my_policy = p;
+ my_ctx.child_hint_buf = (parent_ctx && parent_ctx->child_hint_buf) ?
+ &parent_ctx->child_hint_buf[my_hint_size] : NULL;
+ my_ctx.cblock_to_hint_fn = hint_fn;
+
+ return policy_walk_mappings(p->child, shim_nested_walk_apply, &my_ctx);
+}
+EXPORT_SYMBOL_GPL(dm_cache_shim_utils_walk_map);
diff --git a/drivers/md/dm-cache-shim-utils.h b/drivers/md/dm-cache-shim-utils.h
new file mode 100644
index 0000000..92f2f21
--- /dev/null
+++ b/drivers/md/dm-cache-shim-utils.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2013 NetApp, Inc. All Rights Reserved, contribution by
+ * Morgan Mears.
+ *
+ * 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
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details
+ *
+ */
+
+#ifndef DM_CACHE_SHIM_UTILS_H
+#define DM_CACHE_SHIM_UTILS_H
+
+#include "dm-cache-policy.h"
+
+struct shim_walk_map_ctx;
+
+typedef void* (*cblock_to_hint_fn_t)(struct shim_walk_map_ctx *,
+ dm_cblock_t,
+ dm_oblock_t);
+
+/*
+ * For walk_mappings to work with a policy stack, every non-terminal policy
+ * has to start its context with one of these. There are no requirements for
+ * the context used by the terminal policy.
+ */
+struct shim_walk_map_ctx {
+ void *parent_ctx;
+ policy_walk_fn parent_fn;
+ struct dm_cache_policy *my_policy;
+ char *child_hint_buf;
+ cblock_to_hint_fn_t cblock_to_hint_fn;
+ union {
+ __le64 le64_buf;
+ __le32 le32_buf;
+ __le16 le16_buf;
+ };
+};
+
+/*
+ * Populate a shim (non-terminal) policy structure with functions that just
+ * hand off to the child policy. Caller can then override just those
+ * functions of interest.
+ */
+void dm_cache_shim_utils_init_shim_policy(struct dm_cache_policy *p);
+
+/*
+ * Launch a "walk_mappings" leg using the context provided by our caller.
+ * Typically used at the bottom of a policy stack, so caller can provide
+ * the hint buffer.
+ */
+int dm_cache_shim_utils_walk_map_with_ctx(struct shim_walk_map_ctx *ctx);
+
+/*
+ * Initialize a context appropriately and Launch a "walk_mappings" leg.
+ * Typically used to implement walk_mappings in shim policies. The
+ * framework will call hint_fn at the appropriate point, and it should
+ * return a pointer to the disk-ready hint for the given cblock. The
+ * leXX_bufs in the shim_walk_map_ctx structure can be used to store the
+ * disk-ready hint if it will fit.
+ */
+int dm_cache_shim_utils_walk_map(struct dm_cache_policy *p,
+ policy_walk_fn fn,
+ void *context,
+ cblock_to_hint_fn_t hint_fn);
+
+#endif /* DM_CACHE_SHIM_UTILS_H */
diff --git a/drivers/md/dm-cache-stack-utils.c b/drivers/md/dm-cache-stack-utils.c
new file mode 100644
index 0000000..82cc3af
--- /dev/null
+++ b/drivers/md/dm-cache-stack-utils.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2013 NetApp, Inc. All Rights Reserved, contribution by
+ * Morgan Mears.
+ *
+ * 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
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details
+ *
+ */
+
+#include "dm-cache-policy-internal.h"
+#include "dm-cache-shim-utils.h"
+#include "dm-cache-stack-utils.h"
+#include "dm-cache-policy.h"
+#include "dm.h"
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#define DM_MSG_PREFIX "cache-stack-utils"
+
+struct stack_root_policy {
+ struct dm_cache_policy policy;
+ struct dm_cache_policy_type type;
+};
+
+/*----------------------------------------------------------------*/
+
+static void *stack_root_cblock_to_hint(struct shim_walk_map_ctx *ctx,
+ dm_cblock_t cblock, dm_oblock_t oblock)
+{
+ return ctx->child_hint_buf;
+}
+
+static int stack_root_walk_mappings(struct dm_cache_policy *p,
+ policy_walk_fn fn, void *context)
+{
+ struct shim_walk_map_ctx ctx;
+ size_t hint_size;
+ int r;
+
+ ctx.parent_ctx = context;
+ ctx.parent_fn = fn;
+ ctx.my_policy = p;
+ ctx.child_hint_buf = NULL;
+ ctx.cblock_to_hint_fn = stack_root_cblock_to_hint;
+
+ hint_size = dm_cache_policy_get_hint_size(p);
+ if (hint_size) {
+ ctx.child_hint_buf = kzalloc(hint_size, GFP_KERNEL);
+ if (!ctx.child_hint_buf)
+ return -ENOMEM;
+ }
+
+ r = dm_cache_shim_utils_walk_map_with_ctx(&ctx);
+
+ kfree(ctx.child_hint_buf);
+
+ return r;
+}
+
+static struct dm_cache_policy *stack_root_create(const char *policy_stack_str,
+ struct dm_cache_policy *head)
+{
+ struct stack_root_policy *p = kzalloc(sizeof(*p), GFP_KERNEL);
+ struct dm_cache_policy *child;
+ struct dm_cache_policy_type *t;
+ const unsigned *version;
+ const char *seg_name;
+ size_t canonical_name_len, hint_size;
+ int i;
+
+ if (!p)
+ return NULL;
+
+ t = &p->type;
+ dm_cache_shim_utils_init_shim_policy(&p->policy);
+ p->policy.walk_mappings = stack_root_walk_mappings;
+ p->policy.child = head;
+
+ /*
+ * We compose the canonical name for this policy stack by removing
+ * any shim policies that do not have hint data. This is intended
+ * to allow for a class of shim policies that can be inserted into,
+ * or removed from, the policy stack without causing the in-flash
+ * metadata to be invalidated. The thought is to allow debug or
+ * tracing shims to be inserted or removed without dropping the cache.
+ * The composite version numbers of a policy stack do not include the
+ * versions of the hintless policies for the same reason.
+ */
+ canonical_name_len = 0;
+ for (child = head; child; child = child->child) {
+ hint_size = dm_cache_policy_get_hint_size(child);
+
+#if 0
+ /* FIXME: avoids policy name in t->name, thus leaving an non-destroyable stack. */
+ if (!hint_size && child->child)
+ continue;
+#endif
+
+ t->hint_size += hint_size;
+
+ seg_name = dm_cache_policy_get_name(child);
+ canonical_name_len += strlen(seg_name) + (dm_cache_policy_is_shim(child) ? 1 : 0);
+
+ if (canonical_name_len >= sizeof(t->name)) {
+ DMWARN("policy stack string '%s' is too long",
+ policy_stack_str);
+ kfree(p);
+ return NULL;
+ }
+
+ strcat(t->name, seg_name);
+
+ if (dm_cache_policy_is_shim(child)) {
+ t->name[canonical_name_len - 1] = DM_CACHE_POLICY_STACK_DELIM;
+ t->name[canonical_name_len] = '\0';
+ }
+
+ version = dm_cache_policy_get_version(child);
+
+ for (i = 0; i < CACHE_POLICY_VERSION_SIZE; i++)
+ t->version[i] += version[i];
+ }
+
+ p->policy.private = t;
+ return &p->policy;
+}
+
+static void stack_root_destroy(struct dm_cache_policy *p)
+{
+ kfree(p);
+}
+
+/*----------------------------------------------------------------*/
+
+int dm_cache_stack_utils_string_is_policy_stack(const char *string)
+{
+ const char *delim;
+
+ /*
+ * A string specifies a policy stack instead of a policy if it
+ * contains a policy delimiter (+) anywhere but at the end. The
+ * latter is needed to properly distinguish between policy stacks and
+ * individual shim policies, since this function is called on them
+ * when the policy stack is constructed from the specified string.
+ */
+ delim = strchr(string, DM_CACHE_POLICY_STACK_DELIM);
+ if (!delim || (delim[1] == '\0'))
+ return false;
+
+ return true;
+}
+
+static void __policy_destroy_stack(struct dm_cache_policy *head_p)
+{
+ struct dm_cache_policy *cur_p, *next_p;
+
+ for (cur_p = head_p; cur_p; cur_p = next_p) {
+ next_p = cur_p->child;
+ dm_cache_policy_destroy(cur_p);
+ }
+}
+
+struct dm_cache_policy *
+dm_cache_stack_utils_policy_stack_create(const char *policy_stack_str,
+ dm_cblock_t cache_size,
+ sector_t origin_size,
+ sector_t cache_block_size)
+{
+ char policy_name_buf[CACHE_POLICY_NAME_SIZE];
+ struct dm_cache_policy *p, *head_p, *next_p;
+ char *policy_name, *delim, uninitialized_var(saved_char);
+ int n;
+
+ n = strlcpy(policy_name_buf, policy_stack_str, sizeof(policy_name_buf));
+ if (n >= sizeof(policy_name_buf)) {
+ DMWARN("policy stack string is too long");
+ return NULL;
+ }
+
+ policy_name = policy_name_buf;
+ p = head_p = next_p = NULL;
+
+ do {
+ delim = strchr(policy_name, DM_CACHE_POLICY_STACK_DELIM);
+ if (delim)
+ *delim = '\0';
+
+ next_p = dm_cache_policy_create(policy_name, cache_size,
+ origin_size, cache_block_size);
+ if (!next_p)
+ goto cleanup;
+
+ next_p->child = NULL;
+ if (p)
+ p->child = next_p;
+ else
+ head_p = next_p;
+ p = next_p;
+
+ if (delim) {
+ if (!dm_cache_policy_is_shim(next_p)) {
+ DMERR("%s is no shim policy", policy_name);
+ goto cleanup;
+ }
+
+ *delim = DM_CACHE_POLICY_STACK_DELIM;
+ policy_name = delim + 1;
+ }
+ } while (delim);
+
+ if (head_p->child) {
+ next_p = stack_root_create(policy_stack_str, head_p);
+ if (!next_p)
+ goto cleanup;
+
+ head_p = next_p;
+ }
+
+ return head_p;
+
+cleanup:
+ __policy_destroy_stack(head_p);
+ return NULL;
+}
+
+void dm_cache_stack_utils_policy_stack_destroy(struct dm_cache_policy *p)
+{
+ __policy_destroy_stack(p->child);
+ stack_root_destroy(p);
+}
diff --git a/drivers/md/dm-cache-stack-utils.h b/drivers/md/dm-cache-stack-utils.h
new file mode 100644
index 0000000..54c767e
--- /dev/null
+++ b/drivers/md/dm-cache-stack-utils.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2013 NetApp, Inc. All Rights Reserved, contribution by
+ * Morgan Mears.
+ *
+ * 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
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details
+ *
+ */
+
+#ifndef DM_CACHE_STACK_UTILS_H
+#define DM_CACHE_STACK_UTILS_H
+
+#include "dm-cache-policy.h"
+
+#define DM_CACHE_POLICY_STACK_DELIM '+'
+
+int dm_cache_stack_utils_string_is_policy_stack(const char *string);
+
+struct dm_cache_policy *dm_cache_stack_utils_policy_stack_create(
+ const char *policy_stack_string,
+ dm_cblock_t cache_size,
+ sector_t origin_size,
+ sector_t cache_block_size);
+
+void dm_cache_stack_utils_policy_stack_destroy(struct dm_cache_policy *p);
+
+#endif /* DM_CACHE_STACK_UTILS_H */
--
1.8.1.4
More information about the dm-devel
mailing list