From b94a3ee9f644e6be6f2f70dacefef0f606e2eaa5 Mon Sep 17 00:00:00 2001 From: Jonathan Brassow Date: Tue, 4 Feb 2014 07:59:58 -0600 Subject: [PATCH] cache: Add functions that create/remove cache LVs A cache LV - from LVM's perpective - is a user accessible device that links the cachepool LV and the origin LV. The following functions were added to facilitate the creation and removal of this top-level LV: 1) 'lv_cache_create' - takes a cachepool and an origin device and links them into a new top-level LV of 'cache' segment type. No allocation is necessary in this function, as the sub-LVs contain all of the necessary allocated space. Only the top-level layer needs to be created. 2) 'lv_cache_remove' - this function removes the top-level LV of a cache LV - promoting the cachepool and origin sub-LVs to top-level devices and leaving them exposed to the user. That is, the cachepool is unlinked and free to be used with another origin to form a new cache LV; and the origin is no longer cached. (Currently, if the cache needs to be flushed, it is done in this function and the function waits for it to complete before proceeding. This will be taken out in a future patch in favor of polling.) --- lib/Makefile.in | 1 + lib/metadata/cache_manip.c | 219 +++++++++++++++++++++++++++++++ lib/metadata/metadata-exported.h | 9 +- 3 files changed, 227 insertions(+), 2 deletions(-) create mode 100644 lib/metadata/cache_manip.c diff --git a/lib/Makefile.in b/lib/Makefile.in index 380826997..968ad006b 100644 --- a/lib/Makefile.in +++ b/lib/Makefile.in @@ -87,6 +87,7 @@ SOURCES =\ locking/locking.c \ locking/no_locking.c \ log/log.c \ + metadata/cache_manip.c \ metadata/lv.c \ metadata/lv_manip.c \ metadata/merge.c \ diff --git a/lib/metadata/cache_manip.c b/lib/metadata/cache_manip.c new file mode 100644 index 000000000..24561711e --- /dev/null +++ b/lib/metadata/cache_manip.c @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2014 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU Lesser General Public License v.2.1. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "lib.h" +#include "metadata.h" +#include "locking.h" +#include "pv_map.h" +#include "lvm-string.h" +#include "toolcontext.h" +#include "lv_alloc.h" +#include "pv_alloc.h" +#include "display.h" +#include "segtype.h" +#include "archiver.h" +#include "activate.h" +#include "str_list.h" +#include "defaults.h" +#include "lvm-exec.h" + +/* + * lv_cache_create + * @pool + * @origin + * + * Given a cache_pool and an origin, link the two and create a + * cached LV. + * + * Returns: cache LV on success, NULL on failure + */ +struct logical_volume *lv_cache_create(struct logical_volume *pool, + struct logical_volume *origin) +{ + const struct segment_type *segtype; + struct cmd_context *cmd = pool->vg->cmd; + struct logical_volume *cache_lv; + struct lv_segment *seg; + + if (!lv_is_cache_pool(pool)) { + log_error(INTERNAL_ERROR + "%s is not a cache_pool LV", pool->name); + return NULL; + } + + if (lv_is_cache_type(origin)) { + /* + * FIXME: We can layer caches, insert_layer_for_lv() would + * have to do a better job renaming the LVs in the stack + * first so that there isn't a name collision with _corig. + * The origin under the origin would become *_corig_corig + * before renaming the origin above to *_corig. + */ + log_error(INTERNAL_ERROR + "The origin, %s, cannot be of cache type", + origin->name); + return NULL; + } + + if (!(segtype = get_segtype_from_string(cmd, "cache"))) + return_NULL; + + cache_lv = origin; + if (!(origin = insert_layer_for_lv(cmd, cache_lv, CACHE, "_corig"))) + return_NULL; + + seg = first_seg(cache_lv); + seg->segtype = segtype; + + if (!attach_pool_lv(seg, pool, NULL, NULL)) + return_0; + + return cache_lv; +} + +/* + * lv_cache_remove + * @cache_lv + * + * Given a cache LV, remove the cache layer. This will unlink + * the origin and cache_pool, remove the cache LV layer, and promote + * the origin to a usable non-cached LV of the same name as the + * given cache_lv. + * + * Returns: 1 on success, 0 on failure + */ +int lv_cache_remove(struct logical_volume *cache_lv) +{ + struct cmd_context *cmd = cache_lv->vg->cmd; + char *policy_name; + uint64_t dirty_blocks; + struct segment_type *segtype; + struct lv_segment *cache_seg = first_seg(cache_lv); + struct logical_volume *origin_lv; + struct logical_volume *cache_pool_lv; + + if (!lv_is_cache(cache_lv)) + return_0; + + /* + * FIXME: + * Before the link can be broken, we must ensure that the + * cache has been flushed. This may already be the case + * if the cache mode is writethrough (or the cleaner + * policy is in place from a previous half-finished attempt + * to remove the cache_pool). It could take a long time to + * flush the cache - it should probably be done in the background. + * + * Also, if we do perform the flush in the background and we + * happen to also be removing the cache/origin LV, then we + * could check if the cleaner policy is in place and simply + * remove the cache_pool then without waiting for the flush to + * complete. + */ + if (!lv_cache_policy_info(cache_lv, &policy_name, NULL, NULL)) + return_0; + + if (strcmp(policy_name, "cleaner")) { + /* We must swap in the cleaner to flush the cache */ + log_error("Flushing cache for %s", cache_lv->name); + + /* + * Is there are clean way to free the memory for the name + * and argv when changing the policy? + */ + cache_seg->policy_name = (char *)"cleaner"; + cache_seg->policy_argc = 0; + cache_seg->policy_argv = NULL; + + /* update the kernel to put the cleaner policy in place */ + if (!vg_write(cache_lv->vg)) + return_0; + if (!suspend_lv(cmd, cache_lv)) + return_0; + if (!vg_commit(cache_lv->vg)) + return_0; + if (!resume_lv(cmd, cache_lv)) + return_0; + } + + //FIXME: use polling to do this... + do { + if (!lv_cache_block_info(cache_lv, NULL, + &dirty_blocks, NULL, NULL)) + return_0; + log_error("%" PRIu64 " blocks must still be flushed.", + dirty_blocks); + if (dirty_blocks) + sleep(5); + } while (dirty_blocks); + + cache_pool_lv = first_seg(cache_lv)->pool_lv; + if (!detach_pool_lv(first_seg(cache_lv))) + return_0; + + origin_lv = seg_lv(first_seg(cache_lv), 0); + lv_set_visible(origin_lv); + +//FIXME: We should be able to use 'remove_layer_from_lv', but +// there is a call to 'lv_empty' in there that recursively +// deletes everything down the tree - including the origin_lv +// that we are trying to preserve! +// if (!remove_layer_from_lv(cache_lv, origin_lv)) +// return_0; + + if (!remove_seg_from_segs_using_this_lv(origin_lv, first_seg(cache_lv))) + return_0; + if (!move_lv_segments(cache_lv, origin_lv, 0, 0)) + return_0; + + cache_lv->status &= ~CACHE; + + segtype = get_segtype_from_string(cmd, "error"); + if (!lv_add_virtual_segment(origin_lv, 0, + cache_lv->le_count, segtype, NULL)) + return_0; + + if (!vg_write(cache_lv->vg)) + return_0; + + /* + * suspend_lv on this cache LV will suspend all of the components: + * - the top-level cache LV + * - the origin + * - the cache_pool and all of its sub-LVs + */ + if (!suspend_lv(cmd, cache_lv)) + return_0; + + if (!vg_commit(cache_lv->vg)) + return_0; + + /* + * resume_lv on this (former) cache LV will resume all + * but the cache_pool LV. It must be resumed seperately. + */ + if (!resume_lv(cmd, cache_lv)) + return_0; + if (!resume_lv(cmd, cache_pool_lv)) + return_0; + + if (!activate_lv(cmd, origin_lv)) + return_0; + if (!deactivate_lv(cmd, origin_lv)) + return_0; + if (!lv_remove(origin_lv)) + return_0; + + return 1; +} diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h index 97f05fca1..d8eef2d16 100644 --- a/lib/metadata/metadata-exported.h +++ b/lib/metadata/metadata-exported.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. - * Copyright (C) 2004-2013 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2014 Red Hat, Inc. All rights reserved. * * This file is part of LVM2. * @@ -1016,9 +1016,14 @@ int lv_raid_reshape(struct logical_volume *lv, int lv_raid_replace(struct logical_volume *lv, struct dm_list *remove_pvs, struct dm_list *allocate_pvs); int lv_raid_remove_missing(struct logical_volume *lv); - /* -- metadata/raid_manip.c */ +/* ++ metadata/cache_manip.c */ +struct logical_volume *lv_cache_create(struct logical_volume *pool, + struct logical_volume *origin); +int lv_cache_remove(struct logical_volume *cache_lv); +/* -- metadata/cache_manip.c */ + struct cmd_vg *cmd_vg_add(struct dm_pool *mem, struct dm_list *cmd_vgs, const char *vg_name, const char *vgid, uint32_t flags);