diff options
Diffstat (limited to 'tools/pvmove.c')
-rw-r--r-- | tools/pvmove.c | 456 |
1 files changed, 211 insertions, 245 deletions
diff --git a/tools/pvmove.c b/tools/pvmove.c index 30725844c..038056370 100644 --- a/tools/pvmove.c +++ b/tools/pvmove.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved. - * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved. * * This file is part of LVM2. * @@ -14,11 +14,13 @@ */ #include "tools.h" + #include "polldaemon.h" #include "display.h" +#include "pvmove_poll.h" +#include "lvmpolld-client.h" #define PVMOVE_FIRST_TIME 0x00000001 /* Called for first time */ -#define PVMOVE_EXCLUSIVE 0x00000002 /* Require exclusive LV */ static int _pvmove_target_present(struct cmd_context *cmd, int clustered) { @@ -30,7 +32,7 @@ static int _pvmove_target_present(struct cmd_context *cmd, int clustered) if (clustered && _clustered_found >= 0) return _clustered_found; - if (!(segtype = get_segtype_from_string(cmd, "mirror"))) + if (!(segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_MIRROR))) return_0; if (activation() && segtype->ops->target_present && @@ -88,13 +90,6 @@ static const char *_extract_lvname(struct cmd_context *cmd, const char *vgname, return lvname; } -static struct volume_group *_get_vg(struct cmd_context *cmd, const char *vgname) -{ - dev_close_all(); - - return vg_read_for_update(cmd, vgname, NULL, 0); -} - /* Create list of PVs for allocation of replacement extents */ static struct dm_list *_get_allocatable_pvs(struct cmd_context *cmd, int argc, char **argv, struct volume_group *vg, @@ -513,170 +508,62 @@ static int _activate_lv(struct cmd_context *cmd, struct logical_volume *lv_mirr, return r; } -static int _is_pvmove_image_removable(struct logical_volume *mimage_lv, - void *baton) -{ - uint32_t mimage_to_remove = *((uint32_t *)baton); - struct lv_segment *mirror_seg; - - if (!(mirror_seg = get_only_segment_using_this_lv(mimage_lv))) { - log_error(INTERNAL_ERROR "%s is not a proper mirror image", - mimage_lv->name); - return 0; - } - - if (seg_type(mirror_seg, 0) != AREA_LV) { - log_error(INTERNAL_ERROR "%s is not a pvmove mirror of LV-type", - mirror_seg->lv->name); - return 0; - } - - if (mimage_to_remove > mirror_seg->area_count) { - log_error(INTERNAL_ERROR "Mirror image %" PRIu32 " not found in segment", - mimage_to_remove); - return 0; - } - - if (seg_lv(mirror_seg, mimage_to_remove) == mimage_lv) - return 1; - - return 0; -} - -static int _detach_pvmove_mirror(struct cmd_context *cmd, - struct logical_volume *lv_mirr) -{ - uint32_t mimage_to_remove = 0; - struct dm_list lvs_completed; - struct lv_list *lvl; - - /* Update metadata to remove mirror segments and break dependencies */ - dm_list_init(&lvs_completed); - - if (arg_is_set(cmd, abort_ARG) && - (seg_type(first_seg(lv_mirr), 0) == AREA_LV)) - mimage_to_remove = 1; /* remove the second mirror leg */ - - if (!lv_remove_mirrors(cmd, lv_mirr, 1, 0, _is_pvmove_image_removable, &mimage_to_remove, PVMOVE) || - !remove_layers_for_segments_all(cmd, lv_mirr, PVMOVE, - &lvs_completed)) { - return 0; - } - - dm_list_iterate_items(lvl, &lvs_completed) - /* FIXME Assumes only one pvmove at a time! */ - lvl->lv->status &= ~LOCKED; - - return 1; -} - -static int _suspend_lvs(struct cmd_context *cmd, unsigned first_time, - struct logical_volume *lv_mirr, - struct dm_list *lvs_changed, - struct volume_group *vg_to_revert) -{ - /* - * Suspend lvs_changed the first time. - * Suspend mirrors on subsequent calls. - */ - if (first_time) { - if (!suspend_lvs(cmd, lvs_changed, vg_to_revert)) - return_0; - } else if (!suspend_lv(cmd, lv_mirr)) { - if (vg_to_revert) - vg_revert(vg_to_revert); - return_0; - } - - return 1; -} - -static int _resume_lvs(struct cmd_context *cmd, unsigned first_time, - struct logical_volume *lv_mirr, - struct dm_list *lvs_changed) -{ - /* - * Suspend lvs_changed the first time. - * Suspend mirrors on subsequent calls. - */ - - if (first_time) { - if (!resume_lvs(cmd, lvs_changed)) { - log_error("Unable to resume logical volumes"); - return 0; - } - } else if (!resume_lv(cmd, lv_mirr)) { - log_error("Unable to reactivate logical volume \"%s\"", - lv_mirr->name); - return 0; - } - - return 1; -} - /* - * Called to set up initial pvmove LV and to advance the mirror - * to successive sections of it. - * (Not called after the last section completes.) + * Called to set up initial pvmove LV only. + * (Not called after first or any other section completes.) */ static int _update_metadata(struct cmd_context *cmd, struct volume_group *vg, struct logical_volume *lv_mirr, - struct dm_list *lvs_changed, unsigned flags) + struct dm_list *lvs_changed, unsigned exclusive) { - unsigned exclusive = (flags & PVMOVE_EXCLUSIVE) ? 1 : 0; - unsigned first_time = (flags & PVMOVE_FIRST_TIME) ? 1 : 0; int r = 0; - log_verbose("Updating volume group metadata"); + log_verbose("Setting up pvmove in on-disk volume group metadata."); if (!vg_write(vg)) { log_error("ABORTING: Volume group metadata update failed."); return 0; } - if (!_suspend_lvs(cmd, first_time, lv_mirr, lvs_changed, vg)) { - log_error("ABORTING: Temporary pvmove mirror %s failed.", first_time ? "activation" : "reload"); + if (!suspend_lvs(cmd, lvs_changed, vg)) { + log_error("ABORTING: Temporary pvmove mirror activation failed."); /* FIXME Add a recovery path for first time too. */ - if (!first_time && !revert_lv(cmd, lv_mirr)) - stack; return 0; } /* Commit on-disk metadata */ if (!vg_commit(vg)) { log_error("ABORTING: Volume group metadata update failed."); - if (!_resume_lvs(cmd, first_time, lv_mirr, lvs_changed)) - stack; - if (!first_time && !revert_lv(cmd, lv_mirr)) - stack; + if (!resume_lvs(cmd, lvs_changed)) + log_error("Unable to resume logical volumes."); return 0; } /* Activate the temporary mirror LV */ /* Only the first mirror segment gets activated as a mirror */ /* FIXME: Add option to use a log */ - if (first_time) { - if (!exclusive && _pvmove_is_exclusive(cmd, vg)) - exclusive = 1; + if (!exclusive && _pvmove_is_exclusive(cmd, vg)) + exclusive = 1; - if (!_activate_lv(cmd, lv_mirr, exclusive)) { - if (test_mode()) { - r = 1; - goto out; - } - - /* - * FIXME Run --abort internally here. - */ - log_error("ABORTING: Temporary pvmove mirror activation failed. Run pvmove --abort."); + if (!_activate_lv(cmd, lv_mirr, exclusive)) { + if (test_mode()) { + r = 1; goto out; } + + /* + * FIXME Run --abort internally here. + */ + log_error("ABORTING: Temporary pvmove mirror activation failed. Run pvmove --abort."); + goto out; } r = 1; out: - if (!_resume_lvs(cmd, first_time, lv_mirr, lvs_changed)) + if (!resume_lvs(cmd, lvs_changed)) { + log_error("Unable to resume logical volumes."); r = 0; + } if (r) backup(vg); @@ -684,10 +571,27 @@ out: return r; } +static int _copy_id_components(struct cmd_context *cmd, + const struct logical_volume *lv, char **vg_name, + char **lv_name, union lvid *lvid) +{ + if (!(*vg_name = dm_pool_strdup(cmd->mem, lv->vg->name)) || + !(*lv_name = dm_pool_strdup(cmd->mem, lv->name))) { + log_error("Failed to clone VG or LV name."); + return 0; + } + + *lvid = lv->lvid; + + return 1; +} + static int _set_up_pvmove(struct cmd_context *cmd, const char *pv_name, - int argc, char **argv) + int argc, char **argv, union lvid *lvid, char **vg_name_copy, + char **lv_mirr_name) { const char *lv_name = NULL; + const char *vg_name; char *pv_name_arg; struct volume_group *vg; struct dm_list *source_pvl; @@ -696,6 +600,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 = 0; unsigned flags = PVMOVE_FIRST_TIME; unsigned exclusive; int r = ECMD_FAILED; @@ -710,9 +615,10 @@ static int _set_up_pvmove(struct cmd_context *cmd, const char *pv_name, return EINVALID_CMD_LINE; } + vg_name = pv_vg_name(pv); + if (arg_count(cmd, name_ARG)) { - if (!(lv_name = _extract_lvname(cmd, pv_vg_name(pv), - arg_value(cmd, name_ARG)))) { + if (!(lv_name = _extract_lvname(cmd, vg_name, arg_value(cmd, name_ARG)))) { stack; free_pv_fid(pv); return EINVALID_CMD_LINE; @@ -726,12 +632,15 @@ static int _set_up_pvmove(struct cmd_context *cmd, const char *pv_name, } /* Read VG */ - log_verbose("Finding volume group \"%s\"", pv_vg_name(pv)); + log_verbose("Finding volume group \"%s\"", vg_name); + + if (!lockd_vg(cmd, vg_name, "ex", 0, &lockd_state)) + return_ECMD_FAILED; - vg = _get_vg(cmd, pv_vg_name(pv)); + 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); @@ -785,143 +694,154 @@ static int _set_up_pvmove(struct cmd_context *cmd, const char *pv_name, /* init_pvmove(1); */ /* vg->status |= PVMOVE; */ - if (flags & PVMOVE_FIRST_TIME) { - if (exclusive) - flags |= PVMOVE_EXCLUSIVE; - if (!_update_metadata - (cmd, vg, lv_mirr, lvs_changed, flags)) + if (!_copy_id_components(cmd, lv_mirr, vg_name_copy, lv_mirr_name, lvid)) + goto out; + + if (flags & PVMOVE_FIRST_TIME) + if (!_update_metadata(cmd, vg, lv_mirr, lvs_changed, exclusive)) goto_out; - } /* LVs are all in status LOCKED */ r = ECMD_PROCESSED; out: free_pv_fid(pv); - unlock_and_release_vg(cmd, vg, pv_vg_name(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. + */ + if (!lockd_vg(cmd, vg_name, "un", 0, &lockd_state)) + stack; + return r; } -static int _finish_pvmove(struct cmd_context *cmd, struct volume_group *vg, - struct logical_volume *lv_mirr, - struct dm_list *lvs_changed) +static int _read_poll_id_from_pvname(struct cmd_context *cmd, const char *pv_name, + union lvid *lvid, char **vg_name_copy, + char **lv_name_copy, unsigned *in_progress) { - int r = 1; + int ret = 0; + const char *vg_name; + struct logical_volume *lv; + struct physical_volume *pv; + struct volume_group *vg; + uint32_t lockd_state = 0; - if (!dm_list_empty(lvs_changed) && - (!_detach_pvmove_mirror(cmd, lv_mirr) || - !replace_lv_with_error_segment(lv_mirr))) { - log_error("ABORTING: Removal of temporary mirror failed"); + if (!pv_name) { + log_error(INTERNAL_ERROR "Invalid PV name parameter."); return 0; } - /* Store metadata without dependencies on mirror segments */ - if (!vg_write(vg)) { - log_error("ABORTING: Failed to write new data locations " - "to disk."); - return 0; - } + if (!(pv = find_pv_by_name(cmd, pv_name, 0, 0))) + return_0; - /* Suspend LVs changed (implicitly suspends lv_mirr) */ - if (!suspend_lvs(cmd, lvs_changed, vg)) { - log_error("ABORTING: Locking LVs to remove temporary mirror failed"); - if (!revert_lv(cmd, lv_mirr)) - stack; - return 0; - } + vg_name = pv_vg_name(pv); - /* Store metadata without dependencies on mirror segments */ - if (!vg_commit(vg)) { - log_error("ABORTING: Failed to write new data locations " - "to disk."); - if (!revert_lv(cmd, lv_mirr)) - stack; - if (!revert_lvs(cmd, lvs_changed)) - stack; - return 0; - } + if (!lockd_vg(cmd, vg_name, "sh", 0, &lockd_state)) + return_0; - /* Release mirror LV. (No pending I/O because it's been suspended.) */ - if (!resume_lv(cmd, lv_mirr)) { - log_error("Unable to reactivate logical volume \"%s\"", - lv_mirr->name); - r = 0; + /* need read-only access */ + 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); + ret = 0; + goto out; } - /* Unsuspend LVs */ - if (!resume_lvs(cmd, lvs_changed)) - stack; - - /* Deactivate mirror LV */ - if (!deactivate_lv(cmd, lv_mirr)) { - log_error("ABORTING: Unable to deactivate temporary logical " - "volume \"%s\"", lv_mirr->name); - r = 0; + if (!(lv = find_pvmove_lv(vg, pv_dev(pv), PVMOVE))) { + log_print_unless_silent("%s: No pvmove in progress - already finished or aborted.", + pv_name); + ret = 1; + *in_progress = 0; + } else if (_copy_id_components(cmd, lv, vg_name_copy, lv_name_copy, lvid)) { + ret = 1; + *in_progress = 1; } - log_verbose("Removing temporary pvmove LV"); - if (!lv_remove(lv_mirr)) { - log_error("ABORTING: Removal of temporary pvmove LV failed"); - return 0; - } + unlock_and_release_vg(cmd, vg, vg_name); +out: + if (!lockd_vg(cmd, vg_name, "un", 0, &lockd_state)) + stack; + free_pv_fid(pv); + return ret; +} - /* Store it on disks */ - log_verbose("Writing out final volume group after pvmove"); - if (!vg_write(vg) || !vg_commit(vg)) { - log_error("ABORTING: Failed to write new data locations " - "to disk."); - return 0; - } +static struct poll_functions _pvmove_fns = { + .get_copy_name_from_lv = get_pvmove_pvname_from_lv_mirr, + .poll_progress = poll_mirror_progress, + .update_metadata = pvmove_update_metadata, + .finish_copy = pvmove_finish, +}; - /* FIXME backup positioning */ - backup(vg); +static void _destroy_id(struct cmd_context *cmd, struct poll_operation_id *id) +{ + if (!id) + return; - return r; + dm_pool_free(cmd->mem, id); } -static struct volume_group *_get_move_vg(struct cmd_context *cmd, - const char *name, - const char *uuid __attribute__((unused))) +static struct poll_operation_id *_create_id(struct cmd_context *cmd, + const char *pv_name, + const char *vg_name, + const char *lv_name, + const char *uuid) { - struct physical_volume *pv; - struct volume_group *vg; - - /* Reread all metadata in case it got changed */ - if (!(pv = find_pv_by_name(cmd, name, 0, 0))) { - log_error("ABORTING: Can't reread PV %s", name); - /* What more could we do here? */ + struct poll_operation_id *id = dm_pool_alloc(cmd->mem, sizeof(struct poll_operation_id)); + if (!id) { + log_error("Poll operation ID allocation failed."); return NULL; } - vg = _get_vg(cmd, pv_vg_name(pv)); - free_pv_fid(pv); + id->vg_name = vg_name ? dm_pool_strdup(cmd->mem, vg_name) : NULL; + id->lv_name = lv_name ? dm_pool_strdup(cmd->mem, lv_name) : NULL; + id->display_name = pv_name ? dm_pool_strdup(cmd->mem, pv_name) : NULL; + id->uuid = uuid ? dm_pool_strdup(cmd->mem, uuid) : NULL; - return vg; -} + if (!id->vg_name || !id->lv_name || !id->display_name || !id->uuid) { + log_error("Failed to copy one or more poll operation ID members."); + _destroy_id(cmd, id); + id = NULL; + } -static struct poll_functions _pvmove_fns = { - .get_copy_name_from_lv = get_pvmove_pvname_from_lv_mirr, - .get_copy_vg = _get_move_vg, - .get_copy_lv = find_pvmove_lv_from_pvname, - .poll_progress = poll_mirror_progress, - .update_metadata = _update_metadata, - .finish_copy = _finish_pvmove, -}; + return id; +} int pvmove_poll(struct cmd_context *cmd, const char *pv_name, - unsigned background) + const char *uuid, const char *vg_name, + const char *lv_name, unsigned background) { + int r; + struct poll_operation_id *id = NULL; + if (test_mode()) return ECMD_PROCESSED; - return poll_daemon(cmd, pv_name, NULL, background, PVMOVE, &_pvmove_fns, - "Moved"); + if (uuid) { + id = _create_id(cmd, pv_name, vg_name, lv_name, uuid); + if (!id) { + log_error("Failed to allocate poll identifier for pvmove."); + return ECMD_FAILED; + } + } + + r = poll_daemon(cmd, background, PVMOVE, &_pvmove_fns, "Moved", id); + + _destroy_id(cmd, id); + + return r; } int pvmove(struct cmd_context *cmd, int argc, char **argv) { - char *pv_name = NULL; char *colon; int ret; + unsigned in_progress = 1; + union lvid *lvid = NULL; + char *pv_name = NULL, *vg_name = NULL, *lv_name = NULL; /* dm raid1 target must be present in every case */ if (!_pvmove_target_present(cmd, 0)) { @@ -930,9 +850,32 @@ int pvmove(struct cmd_context *cmd, int argc, char **argv) return ECMD_FAILED; } + if (lvmlockd_use() && !lvmpolld_use()) { + /* + * Don't want to spend the time making lvmlockd + * work without lvmpolld. + */ + log_error("Enable lvmpolld when using lvmlockd."); + return ECMD_FAILED; + } + + if (lvmlockd_use() && !argc) { + /* + * FIXME: 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."); + return ECMD_FAILED; + } + if (!(pv_name = dm_pool_strdup(cmd->mem, argv[0]))) { - log_error("Failed to clone PV name"); + log_error("Failed to clone PV name."); return ECMD_FAILED; } @@ -942,13 +885,36 @@ int pvmove(struct cmd_context *cmd, int argc, char **argv) if (colon) *colon = '\0'; - if (!arg_count(cmd, abort_ARG) && - (ret = _set_up_pvmove(cmd, pv_name, argc, argv)) != - ECMD_PROCESSED) { + /* + * 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 ret; + 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; + return ret; + } + } else { + if (!_read_poll_id_from_pvname(cmd, pv_name, lvid, &vg_name, &lv_name, &in_progress)) + return_ECMD_FAILED; + + 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, arg_is_set(cmd, background_ARG)); + return pvmove_poll(cmd, pv_name, lvid ? lvid->s : NULL, vg_name, lv_name, + arg_is_set(cmd, background_ARG)); } |