mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-21 13:34:40 +03:00
pvmove: move poll code in before refactoring
This commit has no impact on functionality. Code required to be visible outside pvmove.c is just moved into new file pvmove_poll.c and some calls are made non-static and declared in new header file pvmove.h
This commit is contained in:
parent
bf2d831e7e
commit
a098aa419f
@ -38,6 +38,7 @@ SOURCES =\
|
|||||||
pvcreate.c \
|
pvcreate.c \
|
||||||
pvdisplay.c \
|
pvdisplay.c \
|
||||||
pvmove.c \
|
pvmove.c \
|
||||||
|
pvmove_poll.c \
|
||||||
pvremove.c \
|
pvremove.c \
|
||||||
pvresize.c \
|
pvresize.c \
|
||||||
pvscan.c \
|
pvscan.c \
|
||||||
|
348
tools/pvmove.c
348
tools/pvmove.c
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
|
* 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.
|
* This file is part of LVM2.
|
||||||
*
|
*
|
||||||
@ -16,46 +16,7 @@
|
|||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
#include "polldaemon.h"
|
#include "polldaemon.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
|
#include "pvmove_poll.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)
|
|
||||||
{
|
|
||||||
const struct segment_type *segtype;
|
|
||||||
unsigned attr = 0;
|
|
||||||
int found = 1;
|
|
||||||
static int _clustered_found = -1;
|
|
||||||
|
|
||||||
if (clustered && _clustered_found >= 0)
|
|
||||||
return _clustered_found;
|
|
||||||
|
|
||||||
if (!(segtype = get_segtype_from_string(cmd, "mirror")))
|
|
||||||
return_0;
|
|
||||||
|
|
||||||
if (activation() && segtype->ops->target_present &&
|
|
||||||
!segtype->ops->target_present(cmd, NULL, clustered ? &attr : NULL))
|
|
||||||
found = 0;
|
|
||||||
|
|
||||||
if (activation() && clustered) {
|
|
||||||
if (found && (attr & MIRROR_LOG_CLUSTERED))
|
|
||||||
_clustered_found = found = 1;
|
|
||||||
else
|
|
||||||
_clustered_found = found = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned _pvmove_is_exclusive(struct cmd_context *cmd,
|
|
||||||
struct volume_group *vg)
|
|
||||||
{
|
|
||||||
if (vg_is_clustered(vg))
|
|
||||||
if (!_pvmove_target_present(cmd, 1))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allow /dev/vgname/lvname, vgname/lvname or lvname */
|
/* Allow /dev/vgname/lvname, vgname/lvname or lvname */
|
||||||
static const char *_extract_lvname(struct cmd_context *cmd, const char *vgname,
|
static const char *_extract_lvname(struct cmd_context *cmd, const char *vgname,
|
||||||
@ -88,13 +49,6 @@ static const char *_extract_lvname(struct cmd_context *cmd, const char *vgname,
|
|||||||
return lvname;
|
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 */
|
/* Create list of PVs for allocation of replacement extents */
|
||||||
static struct dm_list *_get_allocatable_pvs(struct cmd_context *cmd, int argc,
|
static struct dm_list *_get_allocatable_pvs(struct cmd_context *cmd, int argc,
|
||||||
char **argv, struct volume_group *vg,
|
char **argv, struct volume_group *vg,
|
||||||
@ -497,193 +451,6 @@ static struct logical_volume *_set_up_pvmove_lv(struct cmd_context *cmd,
|
|||||||
return lv_mirr;
|
return lv_mirr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _activate_lv(struct cmd_context *cmd, struct logical_volume *lv_mirr,
|
|
||||||
unsigned exclusive)
|
|
||||||
{
|
|
||||||
int r = 0;
|
|
||||||
|
|
||||||
if (exclusive || lv_is_active_exclusive(lv_mirr))
|
|
||||||
r = activate_lv_excl(cmd, lv_mirr);
|
|
||||||
else
|
|
||||||
r = activate_lv(cmd, lv_mirr);
|
|
||||||
|
|
||||||
if (!r)
|
|
||||||
stack;
|
|
||||||
|
|
||||||
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.)
|
|
||||||
*/
|
|
||||||
static int _update_metadata(struct cmd_context *cmd, struct volume_group *vg,
|
|
||||||
struct logical_volume *lv_mirr,
|
|
||||||
struct dm_list *lvs_changed, unsigned flags)
|
|
||||||
{
|
|
||||||
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");
|
|
||||||
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");
|
|
||||||
/* 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;
|
|
||||||
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 (!_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))
|
|
||||||
r = 0;
|
|
||||||
|
|
||||||
if (r)
|
|
||||||
backup(vg);
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _set_up_pvmove(struct cmd_context *cmd, const char *pv_name,
|
static int _set_up_pvmove(struct cmd_context *cmd, const char *pv_name,
|
||||||
int argc, char **argv)
|
int argc, char **argv)
|
||||||
{
|
{
|
||||||
@ -728,13 +495,13 @@ static int _set_up_pvmove(struct cmd_context *cmd, const char *pv_name,
|
|||||||
/* Read VG */
|
/* Read VG */
|
||||||
log_verbose("Finding volume group \"%s\"", pv_vg_name(pv));
|
log_verbose("Finding volume group \"%s\"", pv_vg_name(pv));
|
||||||
|
|
||||||
vg = _get_vg(cmd, pv_vg_name(pv));
|
vg = get_vg(cmd, pv_vg_name(pv));
|
||||||
if (vg_read_error(vg)) {
|
if (vg_read_error(vg)) {
|
||||||
release_vg(vg);
|
release_vg(vg);
|
||||||
return_ECMD_FAILED;
|
return_ECMD_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
exclusive = _pvmove_is_exclusive(cmd, vg);
|
exclusive = pvmove_is_exclusive(cmd, vg);
|
||||||
|
|
||||||
if ((lv_mirr = find_pvmove_lv(vg, pv_dev(pv), PVMOVE))) {
|
if ((lv_mirr = find_pvmove_lv(vg, pv_dev(pv), PVMOVE))) {
|
||||||
log_print_unless_silent("Detected pvmove in progress for %s", pv_name);
|
log_print_unless_silent("Detected pvmove in progress for %s", pv_name);
|
||||||
@ -788,7 +555,7 @@ static int _set_up_pvmove(struct cmd_context *cmd, const char *pv_name,
|
|||||||
if (flags & PVMOVE_FIRST_TIME) {
|
if (flags & PVMOVE_FIRST_TIME) {
|
||||||
if (exclusive)
|
if (exclusive)
|
||||||
flags |= PVMOVE_EXCLUSIVE;
|
flags |= PVMOVE_EXCLUSIVE;
|
||||||
if (!_update_metadata
|
if (!pvmove_update_metadata
|
||||||
(cmd, vg, lv_mirr, lvs_changed, flags))
|
(cmd, vg, lv_mirr, lvs_changed, flags))
|
||||||
goto_out;
|
goto_out;
|
||||||
}
|
}
|
||||||
@ -801,110 +568,13 @@ out:
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _finish_pvmove(struct cmd_context *cmd, struct volume_group *vg,
|
|
||||||
struct logical_volume *lv_mirr,
|
|
||||||
struct dm_list *lvs_changed)
|
|
||||||
{
|
|
||||||
int r = 1;
|
|
||||||
|
|
||||||
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");
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
log_verbose("Removing temporary pvmove LV");
|
|
||||||
if (!lv_remove(lv_mirr)) {
|
|
||||||
log_error("ABORTING: Removal of temporary pvmove LV failed");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FIXME backup positioning */
|
|
||||||
backup(vg);
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct volume_group *_get_move_vg(struct cmd_context *cmd,
|
|
||||||
const char *name,
|
|
||||||
const char *uuid __attribute__((unused)))
|
|
||||||
{
|
|
||||||
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? */
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
vg = _get_vg(cmd, pv_vg_name(pv));
|
|
||||||
free_pv_fid(pv);
|
|
||||||
|
|
||||||
return vg;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct poll_functions _pvmove_fns = {
|
static struct poll_functions _pvmove_fns = {
|
||||||
.get_copy_name_from_lv = get_pvmove_pvname_from_lv_mirr,
|
.get_copy_name_from_lv = get_pvmove_pvname_from_lv_mirr,
|
||||||
.get_copy_vg = _get_move_vg,
|
.get_copy_vg = pvmove_get_copy_vg,
|
||||||
.get_copy_lv = find_pvmove_lv_from_pvname,
|
.get_copy_lv = find_pvmove_lv_from_pvname,
|
||||||
.poll_progress = poll_mirror_progress,
|
.poll_progress = poll_mirror_progress,
|
||||||
.update_metadata = _update_metadata,
|
.update_metadata = pvmove_update_metadata,
|
||||||
.finish_copy = _finish_pvmove,
|
.finish_copy = pvmove_finish,
|
||||||
};
|
};
|
||||||
|
|
||||||
int pvmove_poll(struct cmd_context *cmd, const char *pv_name,
|
int pvmove_poll(struct cmd_context *cmd, const char *pv_name,
|
||||||
@ -924,7 +594,7 @@ int pvmove(struct cmd_context *cmd, int argc, char **argv)
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* dm raid1 target must be present in every case */
|
/* dm raid1 target must be present in every case */
|
||||||
if (!_pvmove_target_present(cmd, 0)) {
|
if (!pvmove_target_present(cmd, 0)) {
|
||||||
log_error("Required device-mapper target(s) not "
|
log_error("Required device-mapper target(s) not "
|
||||||
"detected in your kernel");
|
"detected in your kernel");
|
||||||
return ECMD_FAILED;
|
return ECMD_FAILED;
|
||||||
|
341
tools/pvmove_poll.c
Normal file
341
tools/pvmove_poll.c
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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 "pvmove_poll.h"
|
||||||
|
#include "tools.h"
|
||||||
|
|
||||||
|
int pvmove_target_present(struct cmd_context *cmd, int clustered)
|
||||||
|
{
|
||||||
|
const struct segment_type *segtype;
|
||||||
|
unsigned attr = 0;
|
||||||
|
int found = 1;
|
||||||
|
static int _clustered_found = -1;
|
||||||
|
|
||||||
|
if (clustered && _clustered_found >= 0)
|
||||||
|
return _clustered_found;
|
||||||
|
|
||||||
|
if (!(segtype = get_segtype_from_string(cmd, "mirror")))
|
||||||
|
return_0;
|
||||||
|
|
||||||
|
if (activation() && segtype->ops->target_present &&
|
||||||
|
!segtype->ops->target_present(cmd, NULL, clustered ? &attr : NULL))
|
||||||
|
found = 0;
|
||||||
|
|
||||||
|
if (activation() && clustered) {
|
||||||
|
if (found && (attr & MIRROR_LOG_CLUSTERED))
|
||||||
|
_clustered_found = found = 1;
|
||||||
|
else
|
||||||
|
_clustered_found = found = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned pvmove_is_exclusive(struct cmd_context *cmd, struct volume_group *vg)
|
||||||
|
{
|
||||||
|
if (vg_is_clustered(vg))
|
||||||
|
if (!pvmove_target_present(cmd, 1))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct volume_group *get_vg(struct cmd_context *cmd, const char *vgname)
|
||||||
|
{
|
||||||
|
dev_close_all();
|
||||||
|
|
||||||
|
return vg_read_for_update(cmd, vgname, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int _activate_lv(struct cmd_context *cmd, struct logical_volume *lv_mirr,
|
||||||
|
unsigned exclusive)
|
||||||
|
{
|
||||||
|
int r = 0;
|
||||||
|
|
||||||
|
if (exclusive || lv_is_active_exclusive(lv_mirr))
|
||||||
|
r = activate_lv_excl(cmd, lv_mirr);
|
||||||
|
else
|
||||||
|
r = activate_lv(cmd, lv_mirr);
|
||||||
|
|
||||||
|
if (!r)
|
||||||
|
stack;
|
||||||
|
|
||||||
|
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.)
|
||||||
|
*/
|
||||||
|
int pvmove_update_metadata(struct cmd_context *cmd, struct volume_group *vg,
|
||||||
|
struct logical_volume *lv_mirr,
|
||||||
|
struct dm_list *lvs_changed, unsigned flags)
|
||||||
|
{
|
||||||
|
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");
|
||||||
|
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");
|
||||||
|
/* 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;
|
||||||
|
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 (!_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))
|
||||||
|
r = 0;
|
||||||
|
|
||||||
|
if (r)
|
||||||
|
backup(vg);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pvmove_finish(struct cmd_context *cmd, struct volume_group *vg,
|
||||||
|
struct logical_volume *lv_mirr, struct dm_list *lvs_changed)
|
||||||
|
{
|
||||||
|
int r = 1;
|
||||||
|
|
||||||
|
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");
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_verbose("Removing temporary pvmove LV");
|
||||||
|
if (!lv_remove(lv_mirr)) {
|
||||||
|
log_error("ABORTING: Removal of temporary pvmove LV failed");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME backup positioning */
|
||||||
|
backup(vg);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct volume_group *pvmove_get_copy_vg(struct cmd_context *cmd, const char *name,
|
||||||
|
const char *uuid __attribute__((unused)))
|
||||||
|
{
|
||||||
|
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? */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
vg = get_vg(cmd, pv_vg_name(pv));
|
||||||
|
free_pv_fid(pv);
|
||||||
|
|
||||||
|
return vg;
|
||||||
|
}
|
45
tools/pvmove_poll.h
Normal file
45
tools/pvmove_poll.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _LVM_PVMOVE_H
|
||||||
|
#define _LVM_PVMOVE_H
|
||||||
|
|
||||||
|
#define PVMOVE_FIRST_TIME 0x00000001 /* Called for first time */
|
||||||
|
#define PVMOVE_EXCLUSIVE 0x00000002 /* Require exclusive LV */
|
||||||
|
|
||||||
|
struct cmd_context;
|
||||||
|
struct dm_list;
|
||||||
|
struct logical_volume;
|
||||||
|
struct volume_group;
|
||||||
|
|
||||||
|
int pvmove_target_present(struct cmd_context *cmd, int clustered);
|
||||||
|
|
||||||
|
unsigned pvmove_is_exclusive(struct cmd_context *cmd, struct volume_group *vg);
|
||||||
|
|
||||||
|
struct volume_group *get_vg(struct cmd_context *cmd, const char *vgname);
|
||||||
|
|
||||||
|
int _activate_lv(struct cmd_context *cmd, struct logical_volume *lv_mirr,
|
||||||
|
unsigned exclusive);
|
||||||
|
|
||||||
|
int pvmove_update_metadata(struct cmd_context *cmd, struct volume_group *vg,
|
||||||
|
struct logical_volume *lv_mirr,
|
||||||
|
struct dm_list *lvs_changed, unsigned flags);
|
||||||
|
|
||||||
|
int pvmove_finish(struct cmd_context *cmd, struct volume_group *vg,
|
||||||
|
struct logical_volume *lv_mirr, struct dm_list *lvs_changed);
|
||||||
|
|
||||||
|
struct volume_group *pvmove_get_copy_vg(struct cmd_context *cmd,
|
||||||
|
const char *name, const char *uuid);
|
||||||
|
|
||||||
|
#endif /* _LVM_PVMOVE_H */
|
Loading…
Reference in New Issue
Block a user