diff options
author | David Teigland <teigland@redhat.com> | 2015-05-13 16:41:24 -0500 |
---|---|---|
committer | David Teigland <teigland@redhat.com> | 2015-05-19 16:37:11 -0500 |
commit | 399cd927940d157e1aec66d4d00ba9fd9d4ec2b7 (patch) | |
tree | 98df4067c7ad7fafc28361abdd8bb3b31f5720e9 | |
parent | cd512fc5ee7b0ec5af29e2dea042838bd2a9fc02 (diff) | |
download | lvm2-399cd927940d157e1aec66d4d00ba9fd9d4ec2b7.tar.gz |
lvconvert, pvmove, polldaemon: add locking for lvmlockd
-rw-r--r-- | tools/lvconvert.c | 114 | ||||
-rw-r--r-- | tools/polldaemon.c | 58 | ||||
-rw-r--r-- | tools/pvmove.c | 59 |
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, |