summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Teigland <teigland@redhat.com>2015-05-13 16:41:24 -0500
committerDavid Teigland <teigland@redhat.com>2015-05-28 16:34:36 -0500
commit132ee7743bca3417848c3ac605df74647156bdeb (patch)
tree08f67b90719bbd108c13443f84fe2bd9d710726d
parentbacba495ffcf21939c1a377b9ddbd38335fa2290 (diff)
downloadlvm2-132ee7743bca3417848c3ac605df74647156bdeb.tar.gz
lvconvert, pvmove, polldaemon: add locking for lvmlockd
-rw-r--r--tools/lvconvert.c114
-rw-r--r--tools/polldaemon.c58
-rw-r--r--tools/pvmove.c59
3 files changed, 210 insertions, 21 deletions
diff --git a/tools/lvconvert.c b/tools/lvconvert.c
index 6e47f81d2..dc9e4dee3 100644
--- a/tools/lvconvert.c
+++ b/tools/lvconvert.c
@@ -16,6 +16,7 @@
#include "polldaemon.h"
#include "lv_alloc.h"
#include "lvconvert_poll.h"
+#include "lvmpolld-client.h"
struct lvconvert_params {
int cache;
@@ -2524,6 +2525,12 @@ static int _lvconvert_thin(struct cmd_context *cmd,
return 0;
}
+ if (is_lockd_type(lv->vg->lock_type)) {
+ log_error("Can't use lock_type %s LV as external origin.",
+ lv->vg->lock_type);
+ return 0;
+ }
+
dm_list_init(&lvc.tags);
if (!pool_supports_external_origin(first_seg(pool_lv), lv))
@@ -2636,6 +2643,8 @@ static int _lvconvert_pool(struct cmd_context *cmd,
{
int r = 0;
const char *old_name;
+ const char *lock_free_meta_name = NULL;
+ const char *lock_free_data_name = NULL;
struct lv_segment *seg;
struct volume_group *vg = pool_lv->vg;
struct logical_volume *data_lv;
@@ -2974,6 +2983,36 @@ static int _lvconvert_pool(struct cmd_context *cmd,
if (!attach_pool_data_lv(seg, data_lv))
return_0;
+ /*
+ * A thin pool lv adopts the lock from the data lv, and the lock for
+ * the meta lv is unlocked and freed. A cache pool lv has no lock, and
+ * the existing lock for both data and meta lvs are unlocked and freed.
+ */
+ if (is_lockd_type(pool_lv->vg->lock_type)) {
+ if (lp->pool_metadata_name) {
+ char *c;
+ if ((c = strchr(lp->pool_metadata_name, '/')))
+ lock_free_meta_name = c + 1;
+ else
+ lock_free_meta_name = lp->pool_metadata_name;
+ }
+
+ if (segtype_is_cache_pool(lp->segtype)) {
+ lock_free_data_name = pool_lv->name;
+ pool_lv->lock_type = NULL;
+ pool_lv->lock_args = NULL;
+ } else {
+ if (data_lv->lock_type)
+ pool_lv->lock_type = dm_pool_strdup(cmd->mem, data_lv->lock_type);
+ if (data_lv->lock_args)
+ pool_lv->lock_args = dm_pool_strdup(cmd->mem, data_lv->lock_args);
+ }
+ data_lv->lock_type = NULL;
+ data_lv->lock_args = NULL;
+ metadata_lv->lock_type = NULL;
+ metadata_lv->lock_args = NULL;
+ }
+
/* FIXME: revert renamed LVs in fail path? */
/* FIXME: any common code with metadata/thin_manip.c extend_pool() ? */
@@ -3031,6 +3070,22 @@ out:
(segtype_is_cache_pool(lp->segtype)) ?
"cache" : "thin");
+ if (lock_free_meta_name) {
+ if (!lockd_lv_name(cmd, pool_lv->vg, lock_free_meta_name, NULL, "un", LDLV_PERSISTENT)) {
+ log_error("Failed to unlock pool metadata LV %s/%s",
+ pool_lv->vg->name, lock_free_meta_name);
+ }
+ lockd_free_lv(cmd, pool_lv->vg, lock_free_meta_name, NULL);
+ }
+
+ if (lock_free_data_name) {
+ if (!lockd_lv_name(cmd, pool_lv->vg, lock_free_data_name, NULL, "un", LDLV_PERSISTENT)) {
+ log_error("Failed to unlock pool data LV %s/%s",
+ pool_lv->vg->name, lock_free_data_name);
+ }
+ lockd_free_lv(cmd, pool_lv->vg, lock_free_data_name, NULL);
+ }
+
return r;
#if 0
revert_new_lv:
@@ -3250,13 +3305,21 @@ static int lvconvert_single(struct cmd_context *cmd, struct lvconvert_params *lp
struct volume_group *vg;
int ret = ECMD_FAILED;
int saved_ignore_suspended_devices = ignore_suspended_devices();
+ uint32_t lockd_state;
if (arg_count(cmd, repair_ARG)) {
init_ignore_suspended_devices(1);
cmd->handles_missing_pvs = 1;
}
- vg = vg_read(cmd, lp->vg_name, NULL, READ_FOR_UPDATE);
+ /*
+ * The VG lock will be released when the command exits.
+ * Commands that poll the LV will reacquire the VG lock.
+ */
+ if (!lockd_vg(cmd, lp->vg_name, "ex", 0, &lockd_state))
+ goto_out;
+
+ vg = vg_read(cmd, lp->vg_name, NULL, READ_FOR_UPDATE, lockd_state);
if (vg_read_error(vg)) {
release_vg(vg);
goto_out;
@@ -3269,6 +3332,17 @@ static int lvconvert_single(struct cmd_context *cmd, struct lvconvert_params *lp
}
/*
+ * If the lv is inactive before and after the command, the
+ * use of PERSISTENT here means the lv will remain locked as
+ * an effect of running the lvconvert.
+ * To unlock it, it would need to be activated+deactivated.
+ * Or, we could identify the commands for which the lv remains
+ * inactive, and not use PERSISTENT here for those cases.
+ */
+ if (!lockd_lv(cmd, lv, "ex", LDLV_PERSISTENT))
+ goto_bad;
+
+ /*
* lp->pvh holds the list of PVs available for allocation or removal
*/
if (lp->pv_count) {
@@ -3288,6 +3362,12 @@ static int lvconvert_single(struct cmd_context *cmd, struct lvconvert_params *lp
bad:
unlock_vg(cmd, lp->vg_name);
+ /*
+ * The command may sit and monitor progress for some time,
+ * and we do not need or want the VG lock held during that.
+ */
+ lockd_vg(cmd, vg->name, "un", 0, &lockd_state);
+
if (ret == ECMD_PROCESSED && lp->need_polling)
ret = _poll_logical_volume(cmd, lp->lv_to_poll,
lp->wait_completion);
@@ -3306,6 +3386,7 @@ static int _lvconvert_merge_single(struct cmd_context *cmd, struct logical_volum
struct volume_group *vg_fresh;
struct logical_volume *lv_fresh;
int ret = ECMD_FAILED;
+ uint32_t lockd_state = 0; /* dummy placeholder, lvmlockd doesn't use this path */
/*
* FIXME can't trust lv's VG to be current given that caller
@@ -3317,7 +3398,7 @@ static int _lvconvert_merge_single(struct cmd_context *cmd, struct logical_volum
vg_name = lv->vg->name;
unlock_vg(cmd, vg_name);
- vg_fresh = vg_read(cmd, vg_name, NULL, READ_FOR_UPDATE);
+ vg_fresh = vg_read(cmd, vg_name, NULL, READ_FOR_UPDATE, lockd_state);
if (vg_read_error(vg_fresh)) {
log_error("ABORTING: Can't reread VG %s", vg_name);
goto out;
@@ -3356,6 +3437,32 @@ out:
return ret;
}
+/*
+ * process_each_lv locks the VG, reads the VG, calls this which starts the
+ * conversion, then unlocks the VG. The lvpoll command will come along later
+ * and lock the VG, read the VG, check the progress, unlock the VG, sleep and
+ * repeat until done.
+ */
+
+static int _lvconvert_lvmpolld_merge_single(struct cmd_context *cmd, struct logical_volume *lv,
+ struct processing_handle *handle)
+{
+ struct lvconvert_params *lp = (struct lvconvert_params *) handle->custom_handle;
+ int ret;
+
+ lp->lv_to_poll = lv;
+ if ((ret = _lvconvert_single(cmd, lv, lp)) != ECMD_PROCESSED)
+ stack;
+
+ if (ret == ECMD_PROCESSED && lp->need_polling) {
+ if ((ret = _poll_logical_volume(cmd, lp->lv_to_poll,
+ lp->wait_completion)) != ECMD_PROCESSED)
+ stack;
+ }
+
+ return ret;
+}
+
int lvconvert(struct cmd_context * cmd, int argc, char **argv)
{
int ret;
@@ -3379,7 +3486,8 @@ int lvconvert(struct cmd_context * cmd, int argc, char **argv)
if (lp.merge)
ret = process_each_lv(cmd, argc, argv, READ_FOR_UPDATE, handle,
- &_lvconvert_merge_single);
+ lvmpolld_use() ? &_lvconvert_lvmpolld_merge_single :
+ &_lvconvert_merge_single);
else
ret = lvconvert_single(cmd, &lp);
out:
diff --git a/tools/polldaemon.c b/tools/polldaemon.c
index fe2e7fdda..9f6cd137e 100644
--- a/tools/polldaemon.c
+++ b/tools/polldaemon.c
@@ -138,14 +138,20 @@ int wait_for_single_lv(struct cmd_context *cmd, struct poll_operation_id *id,
struct volume_group *vg;
struct logical_volume *lv;
int finished = 0;
+ uint32_t lockd_state;
/* Poll for completion */
while (!finished) {
if (parms->wait_before_testing)
_sleep_and_rescan_devices(parms);
+ if (!lockd_vg(cmd, id->vg_name, "sh", 0, &lockd_state)) {
+ log_error("ABORTING: Can't lock VG for %s.", id->display_name);
+ return 0;
+ }
+
/* Locks the (possibly renamed) VG again */
- vg = vg_read(cmd, id->vg_name, NULL, READ_FOR_UPDATE);
+ vg = vg_read(cmd, id->vg_name, NULL, READ_FOR_UPDATE, lockd_state);
if (vg_read_error(vg)) {
release_vg(vg);
log_error("ABORTING: Can't reread VG for %s.", id->display_name);
@@ -189,6 +195,8 @@ int wait_for_single_lv(struct cmd_context *cmd, struct poll_operation_id *id,
unlock_and_release_vg(cmd, vg, vg->name);
+ lockd_vg(cmd, vg->name, "un", 0, &lockd_state);
+
/*
* FIXME Sleeping after testing, while preferred, also works around
* unreliable "finished" state checking in _percent_run. If the
@@ -360,12 +368,32 @@ static int report_progress(struct cmd_context *cmd, struct poll_operation_id *id
{
struct volume_group *vg;
struct logical_volume *lv;
+ uint32_t lockd_state;
+ int ret;
- vg = vg_read(cmd, id->vg_name, NULL, 0);
+ /*
+ * TODO: we don't really need to take the vg lock here,
+ * because we only report the progress on the same host
+ * where the pvmove/lvconvert is happening. This means
+ * that the local pvmove/lvconvert/lvpoll commands are
+ * updating the local lvmetad with the latest info they
+ * have, and we just need to read the latest info that
+ * they have put into lvmetad about their progress.
+ * No VG lock is needed to protect anything here
+ * (we're just reading the VG), and no VG lock is
+ * needed to force a VG read from disk to get changes
+ * from other hosts, because the only change to the VG
+ * we're interested in is the change done locally.
+ */
+ if (!lockd_vg(cmd, id->vg_name, "sh", 0, &lockd_state))
+ return 0;
+
+ vg = vg_read(cmd, id->vg_name, NULL, 0, lockd_state);
if (vg_read_error(vg)) {
release_vg(vg);
log_error("Can't reread VG for %s", id->display_name);
- return 0;
+ ret = 0;
+ goto out_ret;
}
lv = find_lv(vg, id->lv_name);
@@ -378,31 +406,35 @@ static int report_progress(struct cmd_context *cmd, struct poll_operation_id *id
if (!lv && parms->lv_type == PVMOVE) {
log_print_unless_silent("%s: No pvmove in progress - already finished or aborted.",
id->display_name);
- unlock_and_release_vg(cmd, vg, vg->name);
- return 1;
+ ret = 1;
+ goto out;
}
if (!lv) {
log_warn("Can't find LV in %s for %s. Already finished or removed.",
vg->name, id->display_name);
- unlock_and_release_vg(cmd, vg, vg->name);
- return 1;
+ ret = 1;
+ goto out;
}
if (!lv_is_active_locally(lv)) {
log_print_unless_silent("%s: Interrupted: No longer active.", id->display_name);
- unlock_and_release_vg(cmd, vg, vg->name);
- return 1;
+ ret = 1;
+ goto out;
}
if (parms->poll_fns->poll_progress(cmd, lv, id->display_name, parms) == PROGRESS_CHECK_FAILED) {
- unlock_and_release_vg(cmd, vg, vg->name);
- return_0;
+ ret = 0;
+ goto out;
}
- unlock_and_release_vg(cmd, vg, vg->name);
+ ret = 1;
- return 1;
+out:
+ unlock_and_release_vg(cmd, vg, vg->name);
+out_ret:
+ lockd_vg(cmd, vg->name, "un", 0, &lockd_state);
+ return ret;
}
static int _lvmpolld_init_poll_vg(struct cmd_context *cmd, const char *vgname,
diff --git a/tools/pvmove.c b/tools/pvmove.c
index f4b9d6c39..cc9aeca84 100644
--- a/tools/pvmove.c
+++ b/tools/pvmove.c
@@ -17,6 +17,7 @@
#include "polldaemon.h"
#include "display.h"
#include "pvmove_poll.h"
+#include "lvmpolld-client.h"
#define PVMOVE_FIRST_TIME 0x00000001 /* Called for first time */
@@ -598,6 +599,7 @@ static int _set_up_pvmove(struct cmd_context *cmd, const char *pv_name,
struct dm_list *lvs_changed;
struct physical_volume *pv;
struct logical_volume *lv_mirr;
+ uint32_t lockd_state;
unsigned flags = PVMOVE_FIRST_TIME;
unsigned exclusive;
int r = ECMD_FAILED;
@@ -631,10 +633,13 @@ static int _set_up_pvmove(struct cmd_context *cmd, const char *pv_name,
/* Read VG */
log_verbose("Finding volume group \"%s\"", vg_name);
- vg = vg_read(cmd, vg_name, NULL, READ_FOR_UPDATE);
+ if (!lockd_vg(cmd, vg_name, "ex", 0, &lockd_state))
+ return_ECMD_FAILED;
+
+ vg = vg_read(cmd, vg_name, NULL, READ_FOR_UPDATE, lockd_state);
if (vg_read_error(vg)) {
release_vg(vg);
- return_ECMD_FAILED;
+ goto out_ret;
}
exclusive = _pvmove_is_exclusive(cmd, vg);
@@ -700,6 +705,14 @@ static int _set_up_pvmove(struct cmd_context *cmd, const char *pv_name,
out:
free_pv_fid(pv);
unlock_and_release_vg(cmd, vg, vg_name);
+out_ret:
+ /*
+ * Release explicitly because the command may continue running
+ * for some time monitoring the progress, and we don not want
+ * or need the lockd lock held over that.
+ */
+ lockd_vg(cmd, vg_name, "un", 0, &lockd_state);
+
return r;
}
@@ -712,6 +725,7 @@ static int _read_poll_id_from_pvname(struct cmd_context *cmd, const char *pv_nam
struct logical_volume *lv;
struct physical_volume *pv;
struct volume_group *vg;
+ uint32_t lockd_state;
if (!pv_name) {
log_error(INTERNAL_ERROR "Invalid PV name parameter.");
@@ -723,13 +737,16 @@ static int _read_poll_id_from_pvname(struct cmd_context *cmd, const char *pv_nam
vg_name = pv_vg_name(pv);
+ if (!lockd_vg(cmd, vg_name, "sh", 0, &lockd_state))
+ return_0;
+
/* need read-only access */
- vg = vg_read(cmd, vg_name, NULL, 0);
+ vg = vg_read(cmd, vg_name, NULL, 0, lockd_state);
if (vg_read_error(vg)) {
log_error("ABORTING: Can't read VG for %s.", pv_name);
release_vg(vg);
- free_pv_fid(pv);
- return 0;
+ ret = 0;
+ goto out;
}
if (!(lv = find_pvmove_lv(vg, pv_dev(pv), PVMOVE))) {
@@ -743,6 +760,8 @@ static int _read_poll_id_from_pvname(struct cmd_context *cmd, const char *pv_nam
}
unlock_and_release_vg(cmd, vg, vg_name);
+out:
+ lockd_vg(cmd, vg_name, "un", 0, &lockd_state);
free_pv_fid(pv);
return ret;
}
@@ -828,6 +847,20 @@ int pvmove(struct cmd_context *cmd, int argc, char **argv)
return ECMD_FAILED;
}
+ if (lvmlockd_active() && !lvmpolld_use()) {
+ log_error("Enable lvmpolld when using lvmlockd.");
+ return ECMD_FAILED;
+ }
+
+ if (lvmlockd_active() && !argc) {
+ /*
+ * TODO: move process_each_vg from polldaemon up to here,
+ * then we can remove this limitation.
+ */
+ log_error("Specify pvmove args when using lvmlockd.");
+ return ECMD_FAILED;
+ }
+
if (argc) {
if (!(lvid = dm_pool_alloc(cmd->mem, sizeof(*lvid)))) {
log_error("Failed to allocate lvid.");
@@ -845,6 +878,15 @@ int pvmove(struct cmd_context *cmd, int argc, char **argv)
if (colon)
*colon = '\0';
+ /*
+ * To do a reverse mapping from PV name to VG name, we need the
+ * correct global mapping of PVs to VGs.
+ */
+ if (!lockd_gl(cmd, "sh", 0)) {
+ stack;
+ return ECMD_FAILED;
+ }
+
if (!arg_count(cmd, abort_ARG)) {
if ((ret = _set_up_pvmove(cmd, pv_name, argc, argv, lvid, &vg_name, &lv_name)) != ECMD_PROCESSED) {
stack;
@@ -857,6 +899,13 @@ int pvmove(struct cmd_context *cmd, int argc, char **argv)
if (!in_progress)
return ECMD_PROCESSED;
}
+
+ /*
+ * The command may sit and report progress for some time,
+ * and we do not want or need the lockd locks held during
+ * that time.
+ */
+ lockd_gl(cmd, "un", 0);
}
return pvmove_poll(cmd, pv_name, lvid ? lvid->s : NULL, vg_name, lv_name,