186d7f5d3SJohn Marino /* $NetBSD: pvmove.c,v 1.1.1.2 2009/12/02 00:25:54 haad Exp $ */
286d7f5d3SJohn Marino
386d7f5d3SJohn Marino /*
486d7f5d3SJohn Marino * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
586d7f5d3SJohn Marino * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
686d7f5d3SJohn Marino *
786d7f5d3SJohn Marino * This file is part of LVM2.
886d7f5d3SJohn Marino *
986d7f5d3SJohn Marino * This copyrighted material is made available to anyone wishing to use,
1086d7f5d3SJohn Marino * modify, copy, or redistribute it subject to the terms and conditions
1186d7f5d3SJohn Marino * of the GNU Lesser General Public License v.2.1.
1286d7f5d3SJohn Marino *
1386d7f5d3SJohn Marino * You should have received a copy of the GNU Lesser General Public License
1486d7f5d3SJohn Marino * along with this program; if not, write to the Free Software Foundation,
1586d7f5d3SJohn Marino * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1686d7f5d3SJohn Marino */
1786d7f5d3SJohn Marino
1886d7f5d3SJohn Marino #include "tools.h"
1986d7f5d3SJohn Marino #include "polldaemon.h"
2086d7f5d3SJohn Marino #include "display.h"
2186d7f5d3SJohn Marino
2286d7f5d3SJohn Marino #define PVMOVE_FIRST_TIME 0x00000001 /* Called for first time */
2386d7f5d3SJohn Marino
_pvmove_target_present(struct cmd_context * cmd,int clustered)2486d7f5d3SJohn Marino static int _pvmove_target_present(struct cmd_context *cmd, int clustered)
2586d7f5d3SJohn Marino {
2686d7f5d3SJohn Marino const struct segment_type *segtype;
2786d7f5d3SJohn Marino unsigned attr = 0;
2886d7f5d3SJohn Marino int found = 1;
2986d7f5d3SJohn Marino static int _clustered_found = -1;
3086d7f5d3SJohn Marino
3186d7f5d3SJohn Marino if (clustered && _clustered_found >= 0)
3286d7f5d3SJohn Marino return _clustered_found;
3386d7f5d3SJohn Marino
3486d7f5d3SJohn Marino if (!(segtype = get_segtype_from_string(cmd, "mirror")))
3586d7f5d3SJohn Marino return_0;
3686d7f5d3SJohn Marino
3786d7f5d3SJohn Marino if (activation() && segtype->ops->target_present &&
3886d7f5d3SJohn Marino !segtype->ops->target_present(cmd, NULL, clustered ? &attr : NULL))
3986d7f5d3SJohn Marino found = 0;
4086d7f5d3SJohn Marino
4186d7f5d3SJohn Marino if (activation() && clustered) {
4286d7f5d3SJohn Marino if (found && (attr & MIRROR_LOG_CLUSTERED))
4386d7f5d3SJohn Marino _clustered_found = found = 1;
4486d7f5d3SJohn Marino else
4586d7f5d3SJohn Marino _clustered_found = found = 0;
4686d7f5d3SJohn Marino }
4786d7f5d3SJohn Marino
4886d7f5d3SJohn Marino return found;
4986d7f5d3SJohn Marino }
5086d7f5d3SJohn Marino
_pvmove_is_exclusive(struct cmd_context * cmd,struct volume_group * vg)5186d7f5d3SJohn Marino static unsigned _pvmove_is_exclusive(struct cmd_context *cmd,
5286d7f5d3SJohn Marino struct volume_group *vg)
5386d7f5d3SJohn Marino {
5486d7f5d3SJohn Marino if (vg_is_clustered(vg))
5586d7f5d3SJohn Marino if (!_pvmove_target_present(cmd, 1))
5686d7f5d3SJohn Marino return 1;
5786d7f5d3SJohn Marino
5886d7f5d3SJohn Marino return 0;
5986d7f5d3SJohn Marino }
6086d7f5d3SJohn Marino
6186d7f5d3SJohn Marino /* Allow /dev/vgname/lvname, vgname/lvname or lvname */
_extract_lvname(struct cmd_context * cmd,const char * vgname,const char * arg)6286d7f5d3SJohn Marino static const char *_extract_lvname(struct cmd_context *cmd, const char *vgname,
6386d7f5d3SJohn Marino const char *arg)
6486d7f5d3SJohn Marino {
6586d7f5d3SJohn Marino const char *lvname;
6686d7f5d3SJohn Marino
6786d7f5d3SJohn Marino /* Is an lvname supplied directly? */
6886d7f5d3SJohn Marino if (!strchr(arg, '/'))
6986d7f5d3SJohn Marino return arg;
7086d7f5d3SJohn Marino
7186d7f5d3SJohn Marino lvname = skip_dev_dir(cmd, arg, NULL);
7286d7f5d3SJohn Marino while (*lvname == '/')
7386d7f5d3SJohn Marino lvname++;
7486d7f5d3SJohn Marino if (!strchr(lvname, '/')) {
7586d7f5d3SJohn Marino log_error("--name takes a logical volume name");
7686d7f5d3SJohn Marino return NULL;
7786d7f5d3SJohn Marino }
7886d7f5d3SJohn Marino if (strncmp(vgname, lvname, strlen(vgname)) ||
7986d7f5d3SJohn Marino (lvname += strlen(vgname), *lvname != '/')) {
8086d7f5d3SJohn Marino log_error("Named LV and old PV must be in the same VG");
8186d7f5d3SJohn Marino return NULL;
8286d7f5d3SJohn Marino }
8386d7f5d3SJohn Marino while (*lvname == '/')
8486d7f5d3SJohn Marino lvname++;
8586d7f5d3SJohn Marino if (!*lvname) {
8686d7f5d3SJohn Marino log_error("Incomplete LV name supplied with --name");
8786d7f5d3SJohn Marino return NULL;
8886d7f5d3SJohn Marino }
8986d7f5d3SJohn Marino return lvname;
9086d7f5d3SJohn Marino }
9186d7f5d3SJohn Marino
_get_vg(struct cmd_context * cmd,const char * vgname)9286d7f5d3SJohn Marino static struct volume_group *_get_vg(struct cmd_context *cmd, const char *vgname)
9386d7f5d3SJohn Marino {
9486d7f5d3SJohn Marino dev_close_all();
9586d7f5d3SJohn Marino
9686d7f5d3SJohn Marino return vg_read_for_update(cmd, vgname, NULL, 0);
9786d7f5d3SJohn Marino }
9886d7f5d3SJohn Marino
9986d7f5d3SJohn Marino /* Create list of PVs for allocation of replacement extents */
_get_allocatable_pvs(struct cmd_context * cmd,int argc,char ** argv,struct volume_group * vg,struct physical_volume * pv,alloc_policy_t alloc)10086d7f5d3SJohn Marino static struct dm_list *_get_allocatable_pvs(struct cmd_context *cmd, int argc,
10186d7f5d3SJohn Marino char **argv, struct volume_group *vg,
10286d7f5d3SJohn Marino struct physical_volume *pv,
10386d7f5d3SJohn Marino alloc_policy_t alloc)
10486d7f5d3SJohn Marino {
10586d7f5d3SJohn Marino struct dm_list *allocatable_pvs, *pvht, *pvh;
10686d7f5d3SJohn Marino struct pv_list *pvl;
10786d7f5d3SJohn Marino
10886d7f5d3SJohn Marino if (argc)
10986d7f5d3SJohn Marino allocatable_pvs = create_pv_list(cmd->mem, vg, argc, argv, 1);
11086d7f5d3SJohn Marino else
11186d7f5d3SJohn Marino allocatable_pvs = clone_pv_list(cmd->mem, &vg->pvs);
11286d7f5d3SJohn Marino
11386d7f5d3SJohn Marino if (!allocatable_pvs)
11486d7f5d3SJohn Marino return_NULL;
11586d7f5d3SJohn Marino
11686d7f5d3SJohn Marino dm_list_iterate_safe(pvh, pvht, allocatable_pvs) {
11786d7f5d3SJohn Marino pvl = dm_list_item(pvh, struct pv_list);
11886d7f5d3SJohn Marino
11986d7f5d3SJohn Marino /* Don't allocate onto the PV we're clearing! */
12086d7f5d3SJohn Marino if ((alloc != ALLOC_ANYWHERE) && (pvl->pv->dev == pv_dev(pv))) {
12186d7f5d3SJohn Marino dm_list_del(&pvl->list);
12286d7f5d3SJohn Marino continue;
12386d7f5d3SJohn Marino }
12486d7f5d3SJohn Marino
12586d7f5d3SJohn Marino /* Remove PV if full */
12686d7f5d3SJohn Marino if ((pvl->pv->pe_count == pvl->pv->pe_alloc_count))
12786d7f5d3SJohn Marino dm_list_del(&pvl->list);
12886d7f5d3SJohn Marino }
12986d7f5d3SJohn Marino
13086d7f5d3SJohn Marino if (dm_list_empty(allocatable_pvs)) {
13186d7f5d3SJohn Marino log_error("No extents available for allocation");
13286d7f5d3SJohn Marino return NULL;
13386d7f5d3SJohn Marino }
13486d7f5d3SJohn Marino
13586d7f5d3SJohn Marino return allocatable_pvs;
13686d7f5d3SJohn Marino }
13786d7f5d3SJohn Marino
13886d7f5d3SJohn Marino /*
13986d7f5d3SJohn Marino * Replace any LV segments on given PV with temporary mirror.
14086d7f5d3SJohn Marino * Returns list of LVs changed.
14186d7f5d3SJohn Marino */
_insert_pvmove_mirrors(struct cmd_context * cmd,struct logical_volume * lv_mirr,struct dm_list * source_pvl,struct logical_volume * lv,struct dm_list * lvs_changed)14286d7f5d3SJohn Marino static int _insert_pvmove_mirrors(struct cmd_context *cmd,
14386d7f5d3SJohn Marino struct logical_volume *lv_mirr,
14486d7f5d3SJohn Marino struct dm_list *source_pvl,
14586d7f5d3SJohn Marino struct logical_volume *lv,
14686d7f5d3SJohn Marino struct dm_list *lvs_changed)
14786d7f5d3SJohn Marino
14886d7f5d3SJohn Marino {
14986d7f5d3SJohn Marino struct pv_list *pvl;
15086d7f5d3SJohn Marino uint32_t prev_le_count;
15186d7f5d3SJohn Marino
15286d7f5d3SJohn Marino /* Only 1 PV may feature in source_pvl */
15386d7f5d3SJohn Marino pvl = dm_list_item(source_pvl->n, struct pv_list);
15486d7f5d3SJohn Marino
15586d7f5d3SJohn Marino prev_le_count = lv_mirr->le_count;
15686d7f5d3SJohn Marino if (!insert_layer_for_segments_on_pv(cmd, lv, lv_mirr, PVMOVE,
15786d7f5d3SJohn Marino pvl, lvs_changed))
15886d7f5d3SJohn Marino return_0;
15986d7f5d3SJohn Marino
16086d7f5d3SJohn Marino /* check if layer was inserted */
16186d7f5d3SJohn Marino if (lv_mirr->le_count - prev_le_count) {
16286d7f5d3SJohn Marino lv->status |= LOCKED;
16386d7f5d3SJohn Marino
16486d7f5d3SJohn Marino log_verbose("Moving %u extents of logical volume %s/%s",
16586d7f5d3SJohn Marino lv_mirr->le_count - prev_le_count,
16686d7f5d3SJohn Marino lv->vg->name, lv->name);
16786d7f5d3SJohn Marino }
16886d7f5d3SJohn Marino
16986d7f5d3SJohn Marino return 1;
17086d7f5d3SJohn Marino }
17186d7f5d3SJohn Marino
17286d7f5d3SJohn Marino /* Create new LV with mirror segments for the required copies */
_set_up_pvmove_lv(struct cmd_context * cmd,struct volume_group * vg,struct dm_list * source_pvl,const char * lv_name,struct dm_list * allocatable_pvs,alloc_policy_t alloc,struct dm_list ** lvs_changed)17386d7f5d3SJohn Marino static struct logical_volume *_set_up_pvmove_lv(struct cmd_context *cmd,
17486d7f5d3SJohn Marino struct volume_group *vg,
17586d7f5d3SJohn Marino struct dm_list *source_pvl,
17686d7f5d3SJohn Marino const char *lv_name,
17786d7f5d3SJohn Marino struct dm_list *allocatable_pvs,
17886d7f5d3SJohn Marino alloc_policy_t alloc,
17986d7f5d3SJohn Marino struct dm_list **lvs_changed)
18086d7f5d3SJohn Marino {
18186d7f5d3SJohn Marino struct logical_volume *lv_mirr, *lv;
18286d7f5d3SJohn Marino struct lv_list *lvl;
18386d7f5d3SJohn Marino uint32_t log_count = 0;
18486d7f5d3SJohn Marino int lv_found = 0;
18586d7f5d3SJohn Marino
18686d7f5d3SJohn Marino /* FIXME Cope with non-contiguous => splitting existing segments */
18786d7f5d3SJohn Marino if (!(lv_mirr = lv_create_empty("pvmove%d", NULL,
18886d7f5d3SJohn Marino LVM_READ | LVM_WRITE,
18986d7f5d3SJohn Marino ALLOC_CONTIGUOUS, vg))) {
19086d7f5d3SJohn Marino log_error("Creation of temporary pvmove LV failed");
19186d7f5d3SJohn Marino return NULL;
19286d7f5d3SJohn Marino }
19386d7f5d3SJohn Marino
19486d7f5d3SJohn Marino lv_mirr->status |= (PVMOVE | LOCKED);
19586d7f5d3SJohn Marino
19686d7f5d3SJohn Marino if (!(*lvs_changed = dm_pool_alloc(cmd->mem, sizeof(**lvs_changed)))) {
19786d7f5d3SJohn Marino log_error("lvs_changed list struct allocation failed");
19886d7f5d3SJohn Marino return NULL;
19986d7f5d3SJohn Marino }
20086d7f5d3SJohn Marino
20186d7f5d3SJohn Marino dm_list_init(*lvs_changed);
20286d7f5d3SJohn Marino
20386d7f5d3SJohn Marino /* Find segments to be moved and set up mirrors */
20486d7f5d3SJohn Marino dm_list_iterate_items(lvl, &vg->lvs) {
20586d7f5d3SJohn Marino lv = lvl->lv;
20686d7f5d3SJohn Marino if ((lv == lv_mirr))
20786d7f5d3SJohn Marino continue;
20886d7f5d3SJohn Marino if (lv_name) {
20986d7f5d3SJohn Marino if (strcmp(lv->name, lv_name))
21086d7f5d3SJohn Marino continue;
21186d7f5d3SJohn Marino lv_found = 1;
21286d7f5d3SJohn Marino }
21386d7f5d3SJohn Marino if (lv_is_origin(lv) || lv_is_cow(lv)) {
21486d7f5d3SJohn Marino log_print("Skipping snapshot-related LV %s", lv->name);
21586d7f5d3SJohn Marino continue;
21686d7f5d3SJohn Marino }
21786d7f5d3SJohn Marino if (lv->status & MIRRORED) {
21886d7f5d3SJohn Marino log_print("Skipping mirror LV %s", lv->name);
21986d7f5d3SJohn Marino continue;
22086d7f5d3SJohn Marino }
22186d7f5d3SJohn Marino if (lv->status & MIRROR_LOG) {
22286d7f5d3SJohn Marino log_print("Skipping mirror log LV %s", lv->name);
22386d7f5d3SJohn Marino continue;
22486d7f5d3SJohn Marino }
22586d7f5d3SJohn Marino if (lv->status & MIRROR_IMAGE) {
22686d7f5d3SJohn Marino log_print("Skipping mirror image LV %s", lv->name);
22786d7f5d3SJohn Marino continue;
22886d7f5d3SJohn Marino }
22986d7f5d3SJohn Marino if (lv->status & LOCKED) {
23086d7f5d3SJohn Marino log_print("Skipping locked LV %s", lv->name);
23186d7f5d3SJohn Marino continue;
23286d7f5d3SJohn Marino }
23386d7f5d3SJohn Marino if (!_insert_pvmove_mirrors(cmd, lv_mirr, source_pvl, lv,
23486d7f5d3SJohn Marino *lvs_changed))
23586d7f5d3SJohn Marino return_NULL;
23686d7f5d3SJohn Marino }
23786d7f5d3SJohn Marino
23886d7f5d3SJohn Marino if (lv_name && !lv_found) {
23986d7f5d3SJohn Marino log_error("Logical volume %s not found.", lv_name);
24086d7f5d3SJohn Marino return NULL;
24186d7f5d3SJohn Marino }
24286d7f5d3SJohn Marino
24386d7f5d3SJohn Marino /* Is temporary mirror empty? */
24486d7f5d3SJohn Marino if (!lv_mirr->le_count) {
24586d7f5d3SJohn Marino log_error("No data to move for %s", vg->name);
24686d7f5d3SJohn Marino return NULL;
24786d7f5d3SJohn Marino }
24886d7f5d3SJohn Marino
24986d7f5d3SJohn Marino if (!lv_add_mirrors(cmd, lv_mirr, 1, 1, 0, log_count,
25086d7f5d3SJohn Marino allocatable_pvs, alloc, MIRROR_BY_SEG)) {
25186d7f5d3SJohn Marino log_error("Failed to convert pvmove LV to mirrored");
25286d7f5d3SJohn Marino return_NULL;
25386d7f5d3SJohn Marino }
25486d7f5d3SJohn Marino
25586d7f5d3SJohn Marino if (!split_parent_segments_for_layer(cmd, lv_mirr)) {
25686d7f5d3SJohn Marino log_error("Failed to split segments being moved");
25786d7f5d3SJohn Marino return_NULL;
25886d7f5d3SJohn Marino }
25986d7f5d3SJohn Marino
26086d7f5d3SJohn Marino return lv_mirr;
26186d7f5d3SJohn Marino }
26286d7f5d3SJohn Marino
_activate_lv(struct cmd_context * cmd,struct logical_volume * lv_mirr,unsigned exclusive)26386d7f5d3SJohn Marino static int _activate_lv(struct cmd_context *cmd, struct logical_volume *lv_mirr,
26486d7f5d3SJohn Marino unsigned exclusive)
26586d7f5d3SJohn Marino {
26686d7f5d3SJohn Marino if (exclusive)
26786d7f5d3SJohn Marino return activate_lv_excl(cmd, lv_mirr);
26886d7f5d3SJohn Marino
26986d7f5d3SJohn Marino return activate_lv(cmd, lv_mirr);
27086d7f5d3SJohn Marino }
27186d7f5d3SJohn Marino
27286d7f5d3SJohn Marino static int _finish_pvmove(struct cmd_context *cmd, struct volume_group *vg,
27386d7f5d3SJohn Marino struct logical_volume *lv_mirr,
27486d7f5d3SJohn Marino struct dm_list *lvs_changed);
27586d7f5d3SJohn Marino
_update_metadata(struct cmd_context * cmd,struct volume_group * vg,struct logical_volume * lv_mirr,struct dm_list * lvs_changed,unsigned flags)27686d7f5d3SJohn Marino static int _update_metadata(struct cmd_context *cmd, struct volume_group *vg,
27786d7f5d3SJohn Marino struct logical_volume *lv_mirr,
27886d7f5d3SJohn Marino struct dm_list *lvs_changed, unsigned flags)
27986d7f5d3SJohn Marino {
28086d7f5d3SJohn Marino unsigned exclusive = _pvmove_is_exclusive(cmd, vg);
28186d7f5d3SJohn Marino unsigned first_time = (flags & PVMOVE_FIRST_TIME) ? 1 : 0;
28286d7f5d3SJohn Marino int r = 0;
28386d7f5d3SJohn Marino
28486d7f5d3SJohn Marino log_verbose("Updating volume group metadata");
28586d7f5d3SJohn Marino if (!vg_write(vg)) {
28686d7f5d3SJohn Marino log_error("ABORTING: Volume group metadata update failed.");
28786d7f5d3SJohn Marino return 0;
28886d7f5d3SJohn Marino }
28986d7f5d3SJohn Marino
29086d7f5d3SJohn Marino /* Suspend lvs_changed */
29186d7f5d3SJohn Marino if (!suspend_lvs(cmd, lvs_changed))
29286d7f5d3SJohn Marino goto_out;
29386d7f5d3SJohn Marino
29486d7f5d3SJohn Marino /* Suspend mirrors on subsequent calls */
29586d7f5d3SJohn Marino if (!first_time) {
29686d7f5d3SJohn Marino if (!suspend_lv(cmd, lv_mirr)) {
29786d7f5d3SJohn Marino resume_lvs(cmd, lvs_changed);
29886d7f5d3SJohn Marino vg_revert(vg);
29986d7f5d3SJohn Marino goto_out;
30086d7f5d3SJohn Marino }
30186d7f5d3SJohn Marino }
30286d7f5d3SJohn Marino
30386d7f5d3SJohn Marino /* Commit on-disk metadata */
30486d7f5d3SJohn Marino if (!vg_commit(vg)) {
30586d7f5d3SJohn Marino log_error("ABORTING: Volume group metadata update failed.");
30686d7f5d3SJohn Marino if (!first_time)
30786d7f5d3SJohn Marino resume_lv(cmd, lv_mirr);
30886d7f5d3SJohn Marino resume_lvs(cmd, lvs_changed);
30986d7f5d3SJohn Marino goto out;
31086d7f5d3SJohn Marino }
31186d7f5d3SJohn Marino
31286d7f5d3SJohn Marino /* Activate the temporary mirror LV */
31386d7f5d3SJohn Marino /* Only the first mirror segment gets activated as a mirror */
31486d7f5d3SJohn Marino /* FIXME: Add option to use a log */
31586d7f5d3SJohn Marino if (first_time) {
31686d7f5d3SJohn Marino if (!_activate_lv(cmd, lv_mirr, exclusive)) {
31786d7f5d3SJohn Marino if (test_mode())
31886d7f5d3SJohn Marino goto out;
31986d7f5d3SJohn Marino
32086d7f5d3SJohn Marino /*
32186d7f5d3SJohn Marino * Nothing changed yet, try to revert pvmove.
32286d7f5d3SJohn Marino */
32386d7f5d3SJohn Marino log_error("Temporary pvmove mirror activation failed.");
32486d7f5d3SJohn Marino if (!_finish_pvmove(cmd, vg, lv_mirr, lvs_changed))
32586d7f5d3SJohn Marino log_error("ABORTING: Restoring original configuration "
32686d7f5d3SJohn Marino "before pvmove failed. Run pvmove --abort.");
32786d7f5d3SJohn Marino goto out;
32886d7f5d3SJohn Marino }
32986d7f5d3SJohn Marino } else if (!resume_lv(cmd, lv_mirr)) {
33086d7f5d3SJohn Marino log_error("Unable to reactivate logical volume \"%s\"",
33186d7f5d3SJohn Marino lv_mirr->name);
33286d7f5d3SJohn Marino resume_lvs(cmd, lvs_changed);
33386d7f5d3SJohn Marino goto out;
33486d7f5d3SJohn Marino }
33586d7f5d3SJohn Marino
33686d7f5d3SJohn Marino /* Unsuspend LVs */
33786d7f5d3SJohn Marino if (!resume_lvs(cmd, lvs_changed)) {
33886d7f5d3SJohn Marino log_error("Unable to resume logical volumes");
33986d7f5d3SJohn Marino goto out;
34086d7f5d3SJohn Marino }
34186d7f5d3SJohn Marino
34286d7f5d3SJohn Marino r = 1;
34386d7f5d3SJohn Marino out:
34486d7f5d3SJohn Marino backup(vg);
34586d7f5d3SJohn Marino return r;
34686d7f5d3SJohn Marino }
34786d7f5d3SJohn Marino
_set_up_pvmove(struct cmd_context * cmd,const char * pv_name,int argc,char ** argv)34886d7f5d3SJohn Marino static int _set_up_pvmove(struct cmd_context *cmd, const char *pv_name,
34986d7f5d3SJohn Marino int argc, char **argv)
35086d7f5d3SJohn Marino {
35186d7f5d3SJohn Marino const char *lv_name = NULL;
35286d7f5d3SJohn Marino char *pv_name_arg;
35386d7f5d3SJohn Marino struct volume_group *vg;
35486d7f5d3SJohn Marino struct dm_list *source_pvl;
35586d7f5d3SJohn Marino struct dm_list *allocatable_pvs;
35686d7f5d3SJohn Marino alloc_policy_t alloc;
35786d7f5d3SJohn Marino struct dm_list *lvs_changed;
35886d7f5d3SJohn Marino struct physical_volume *pv;
35986d7f5d3SJohn Marino struct logical_volume *lv_mirr;
36086d7f5d3SJohn Marino unsigned first_time = 1;
36186d7f5d3SJohn Marino unsigned exclusive;
36286d7f5d3SJohn Marino int r = ECMD_FAILED;
36386d7f5d3SJohn Marino
36486d7f5d3SJohn Marino pv_name_arg = argv[0];
36586d7f5d3SJohn Marino argc--;
36686d7f5d3SJohn Marino argv++;
36786d7f5d3SJohn Marino
36886d7f5d3SJohn Marino /* Find PV (in VG) */
36986d7f5d3SJohn Marino if (!(pv = find_pv_by_name(cmd, pv_name))) {
37086d7f5d3SJohn Marino stack;
37186d7f5d3SJohn Marino return EINVALID_CMD_LINE;
37286d7f5d3SJohn Marino }
37386d7f5d3SJohn Marino
37486d7f5d3SJohn Marino if (arg_count(cmd, name_ARG)) {
37586d7f5d3SJohn Marino if (!(lv_name = _extract_lvname(cmd, pv_vg_name(pv),
37686d7f5d3SJohn Marino arg_value(cmd, name_ARG)))) {
37786d7f5d3SJohn Marino stack;
37886d7f5d3SJohn Marino return EINVALID_CMD_LINE;
37986d7f5d3SJohn Marino }
38086d7f5d3SJohn Marino
38186d7f5d3SJohn Marino if (!validate_name(lv_name)) {
38286d7f5d3SJohn Marino log_error("Logical volume name %s is invalid", lv_name);
38386d7f5d3SJohn Marino return EINVALID_CMD_LINE;
38486d7f5d3SJohn Marino }
38586d7f5d3SJohn Marino }
38686d7f5d3SJohn Marino
38786d7f5d3SJohn Marino /* Read VG */
38886d7f5d3SJohn Marino log_verbose("Finding volume group \"%s\"", pv_vg_name(pv));
38986d7f5d3SJohn Marino
39086d7f5d3SJohn Marino vg = _get_vg(cmd, pv_vg_name(pv));
39186d7f5d3SJohn Marino if (vg_read_error(vg)) {
39286d7f5d3SJohn Marino vg_release(vg);
39386d7f5d3SJohn Marino stack;
39486d7f5d3SJohn Marino return ECMD_FAILED;
39586d7f5d3SJohn Marino }
39686d7f5d3SJohn Marino
39786d7f5d3SJohn Marino exclusive = _pvmove_is_exclusive(cmd, vg);
39886d7f5d3SJohn Marino
39986d7f5d3SJohn Marino if ((lv_mirr = find_pvmove_lv(vg, pv_dev(pv), PVMOVE))) {
40086d7f5d3SJohn Marino log_print("Detected pvmove in progress for %s", pv_name);
40186d7f5d3SJohn Marino if (argc || lv_name)
40286d7f5d3SJohn Marino log_error("Ignoring remaining command line arguments");
40386d7f5d3SJohn Marino
40486d7f5d3SJohn Marino if (!(lvs_changed = lvs_using_lv(cmd, vg, lv_mirr))) {
40586d7f5d3SJohn Marino log_error("ABORTING: Failed to generate list of moving LVs");
40686d7f5d3SJohn Marino goto out;
40786d7f5d3SJohn Marino }
40886d7f5d3SJohn Marino
40986d7f5d3SJohn Marino /* Ensure mirror LV is active */
41086d7f5d3SJohn Marino if (!_activate_lv(cmd, lv_mirr, exclusive)) {
41186d7f5d3SJohn Marino log_error("ABORTING: Temporary mirror activation failed.");
41286d7f5d3SJohn Marino goto out;
41386d7f5d3SJohn Marino }
41486d7f5d3SJohn Marino
41586d7f5d3SJohn Marino first_time = 0;
41686d7f5d3SJohn Marino } else {
41786d7f5d3SJohn Marino /* Determine PE ranges to be moved */
41886d7f5d3SJohn Marino if (!(source_pvl = create_pv_list(cmd->mem, vg, 1,
41986d7f5d3SJohn Marino &pv_name_arg, 0)))
42086d7f5d3SJohn Marino goto_out;
42186d7f5d3SJohn Marino
42286d7f5d3SJohn Marino alloc = arg_uint_value(cmd, alloc_ARG, ALLOC_INHERIT);
42386d7f5d3SJohn Marino if (alloc == ALLOC_INHERIT)
42486d7f5d3SJohn Marino alloc = vg->alloc;
42586d7f5d3SJohn Marino
42686d7f5d3SJohn Marino /* Get PVs we can use for allocation */
42786d7f5d3SJohn Marino if (!(allocatable_pvs = _get_allocatable_pvs(cmd, argc, argv,
42886d7f5d3SJohn Marino vg, pv, alloc)))
42986d7f5d3SJohn Marino goto_out;
43086d7f5d3SJohn Marino
43186d7f5d3SJohn Marino if (!archive(vg))
43286d7f5d3SJohn Marino goto_out;
43386d7f5d3SJohn Marino
43486d7f5d3SJohn Marino if (!(lv_mirr = _set_up_pvmove_lv(cmd, vg, source_pvl, lv_name,
43586d7f5d3SJohn Marino allocatable_pvs, alloc,
43686d7f5d3SJohn Marino &lvs_changed)))
43786d7f5d3SJohn Marino goto_out;
43886d7f5d3SJohn Marino }
43986d7f5d3SJohn Marino
44086d7f5d3SJohn Marino /* Lock lvs_changed and activate (with old metadata) */
44186d7f5d3SJohn Marino if (!activate_lvs(cmd, lvs_changed, exclusive))
44286d7f5d3SJohn Marino goto_out;
44386d7f5d3SJohn Marino
44486d7f5d3SJohn Marino /* FIXME Presence of a mirror once set PVMOVE - now remove associated logic */
44586d7f5d3SJohn Marino /* init_pvmove(1); */
44686d7f5d3SJohn Marino /* vg->status |= PVMOVE; */
44786d7f5d3SJohn Marino
44886d7f5d3SJohn Marino if (first_time) {
44986d7f5d3SJohn Marino if (!_update_metadata
45086d7f5d3SJohn Marino (cmd, vg, lv_mirr, lvs_changed, PVMOVE_FIRST_TIME))
45186d7f5d3SJohn Marino goto_out;
45286d7f5d3SJohn Marino }
45386d7f5d3SJohn Marino
45486d7f5d3SJohn Marino /* LVs are all in status LOCKED */
45586d7f5d3SJohn Marino r = ECMD_PROCESSED;
45686d7f5d3SJohn Marino out:
45786d7f5d3SJohn Marino unlock_and_release_vg(cmd, vg, pv_vg_name(pv));
45886d7f5d3SJohn Marino return r;
45986d7f5d3SJohn Marino }
46086d7f5d3SJohn Marino
_finish_pvmove(struct cmd_context * cmd,struct volume_group * vg,struct logical_volume * lv_mirr,struct dm_list * lvs_changed)46186d7f5d3SJohn Marino static int _finish_pvmove(struct cmd_context *cmd, struct volume_group *vg,
46286d7f5d3SJohn Marino struct logical_volume *lv_mirr,
46386d7f5d3SJohn Marino struct dm_list *lvs_changed)
46486d7f5d3SJohn Marino {
46586d7f5d3SJohn Marino int r = 1;
46686d7f5d3SJohn Marino struct dm_list lvs_completed;
46786d7f5d3SJohn Marino struct lv_list *lvl;
46886d7f5d3SJohn Marino
46986d7f5d3SJohn Marino /* Update metadata to remove mirror segments and break dependencies */
47086d7f5d3SJohn Marino dm_list_init(&lvs_completed);
47186d7f5d3SJohn Marino if (!lv_remove_mirrors(cmd, lv_mirr, 1, 0, NULL, PVMOVE) ||
47286d7f5d3SJohn Marino !remove_layers_for_segments_all(cmd, lv_mirr, PVMOVE,
47386d7f5d3SJohn Marino &lvs_completed)) {
47486d7f5d3SJohn Marino log_error("ABORTING: Removal of temporary mirror failed");
47586d7f5d3SJohn Marino return 0;
47686d7f5d3SJohn Marino }
47786d7f5d3SJohn Marino
47886d7f5d3SJohn Marino dm_list_iterate_items(lvl, &lvs_completed)
47986d7f5d3SJohn Marino /* FIXME Assumes only one pvmove at a time! */
48086d7f5d3SJohn Marino lvl->lv->status &= ~LOCKED;
48186d7f5d3SJohn Marino
48286d7f5d3SJohn Marino /* Store metadata without dependencies on mirror segments */
48386d7f5d3SJohn Marino if (!vg_write(vg)) {
48486d7f5d3SJohn Marino log_error("ABORTING: Failed to write new data locations "
48586d7f5d3SJohn Marino "to disk.");
48686d7f5d3SJohn Marino return 0;
48786d7f5d3SJohn Marino }
48886d7f5d3SJohn Marino
48986d7f5d3SJohn Marino /* Suspend LVs changed */
49086d7f5d3SJohn Marino if (!suspend_lvs(cmd, lvs_changed)) {
49186d7f5d3SJohn Marino log_error("Locking LVs to remove temporary mirror failed");
49286d7f5d3SJohn Marino r = 0;
49386d7f5d3SJohn Marino }
49486d7f5d3SJohn Marino
49586d7f5d3SJohn Marino /* Suspend mirror LV to flush pending I/O */
49686d7f5d3SJohn Marino if (!suspend_lv(cmd, lv_mirr)) {
49786d7f5d3SJohn Marino log_error("Suspension of temporary mirror LV failed");
49886d7f5d3SJohn Marino r = 0;
49986d7f5d3SJohn Marino }
50086d7f5d3SJohn Marino
50186d7f5d3SJohn Marino /* Store metadata without dependencies on mirror segments */
50286d7f5d3SJohn Marino if (!vg_commit(vg)) {
50386d7f5d3SJohn Marino log_error("ABORTING: Failed to write new data locations "
50486d7f5d3SJohn Marino "to disk.");
50586d7f5d3SJohn Marino vg_revert(vg);
50686d7f5d3SJohn Marino resume_lv(cmd, lv_mirr);
50786d7f5d3SJohn Marino resume_lvs(cmd, lvs_changed);
50886d7f5d3SJohn Marino return 0;
50986d7f5d3SJohn Marino }
51086d7f5d3SJohn Marino
51186d7f5d3SJohn Marino /* Release mirror LV. (No pending I/O because it's been suspended.) */
51286d7f5d3SJohn Marino if (!resume_lv(cmd, lv_mirr)) {
51386d7f5d3SJohn Marino log_error("Unable to reactivate logical volume \"%s\"",
51486d7f5d3SJohn Marino lv_mirr->name);
51586d7f5d3SJohn Marino r = 0;
51686d7f5d3SJohn Marino }
51786d7f5d3SJohn Marino
51886d7f5d3SJohn Marino /* Unsuspend LVs */
51986d7f5d3SJohn Marino resume_lvs(cmd, lvs_changed);
52086d7f5d3SJohn Marino
52186d7f5d3SJohn Marino /* Deactivate mirror LV */
52286d7f5d3SJohn Marino if (!deactivate_lv(cmd, lv_mirr)) {
52386d7f5d3SJohn Marino log_error("ABORTING: Unable to deactivate temporary logical "
52486d7f5d3SJohn Marino "volume \"%s\"", lv_mirr->name);
52586d7f5d3SJohn Marino r = 0;
52686d7f5d3SJohn Marino }
52786d7f5d3SJohn Marino
52886d7f5d3SJohn Marino log_verbose("Removing temporary pvmove LV");
52986d7f5d3SJohn Marino if (!lv_remove(lv_mirr)) {
53086d7f5d3SJohn Marino log_error("ABORTING: Removal of temporary pvmove LV failed");
53186d7f5d3SJohn Marino return 0;
53286d7f5d3SJohn Marino }
53386d7f5d3SJohn Marino
53486d7f5d3SJohn Marino /* Store it on disks */
53586d7f5d3SJohn Marino log_verbose("Writing out final volume group after pvmove");
53686d7f5d3SJohn Marino if (!vg_write(vg) || !vg_commit(vg)) {
53786d7f5d3SJohn Marino log_error("ABORTING: Failed to write new data locations "
53886d7f5d3SJohn Marino "to disk.");
53986d7f5d3SJohn Marino return 0;
54086d7f5d3SJohn Marino }
54186d7f5d3SJohn Marino
54286d7f5d3SJohn Marino /* FIXME backup positioning */
54386d7f5d3SJohn Marino backup(vg);
54486d7f5d3SJohn Marino
54586d7f5d3SJohn Marino return r;
54686d7f5d3SJohn Marino }
54786d7f5d3SJohn Marino
_get_move_vg(struct cmd_context * cmd,const char * name,const char * uuid)54886d7f5d3SJohn Marino static struct volume_group *_get_move_vg(struct cmd_context *cmd,
54986d7f5d3SJohn Marino const char *name, const char *uuid)
55086d7f5d3SJohn Marino {
55186d7f5d3SJohn Marino struct physical_volume *pv;
55286d7f5d3SJohn Marino
55386d7f5d3SJohn Marino /* Reread all metadata in case it got changed */
55486d7f5d3SJohn Marino if (!(pv = find_pv_by_name(cmd, name))) {
55586d7f5d3SJohn Marino log_error("ABORTING: Can't reread PV %s", name);
55686d7f5d3SJohn Marino /* What more could we do here? */
55786d7f5d3SJohn Marino return NULL;
55886d7f5d3SJohn Marino }
55986d7f5d3SJohn Marino
56086d7f5d3SJohn Marino return _get_vg(cmd, pv_vg_name(pv));
56186d7f5d3SJohn Marino }
56286d7f5d3SJohn Marino
56386d7f5d3SJohn Marino static struct poll_functions _pvmove_fns = {
56486d7f5d3SJohn Marino .get_copy_name_from_lv = get_pvmove_pvname_from_lv_mirr,
56586d7f5d3SJohn Marino .get_copy_vg = _get_move_vg,
56686d7f5d3SJohn Marino .get_copy_lv = find_pvmove_lv_from_pvname,
56786d7f5d3SJohn Marino .poll_progress = poll_mirror_progress,
56886d7f5d3SJohn Marino .update_metadata = _update_metadata,
56986d7f5d3SJohn Marino .finish_copy = _finish_pvmove,
57086d7f5d3SJohn Marino };
57186d7f5d3SJohn Marino
pvmove_poll(struct cmd_context * cmd,const char * pv_name,unsigned background)57286d7f5d3SJohn Marino int pvmove_poll(struct cmd_context *cmd, const char *pv_name,
57386d7f5d3SJohn Marino unsigned background)
57486d7f5d3SJohn Marino {
57586d7f5d3SJohn Marino return poll_daemon(cmd, pv_name, NULL, background, PVMOVE, &_pvmove_fns,
57686d7f5d3SJohn Marino "Moved");
57786d7f5d3SJohn Marino }
57886d7f5d3SJohn Marino
pvmove(struct cmd_context * cmd,int argc,char ** argv)57986d7f5d3SJohn Marino int pvmove(struct cmd_context *cmd, int argc, char **argv)
58086d7f5d3SJohn Marino {
58186d7f5d3SJohn Marino char *pv_name = NULL;
58286d7f5d3SJohn Marino char *colon;
58386d7f5d3SJohn Marino int ret;
58486d7f5d3SJohn Marino
58586d7f5d3SJohn Marino /* dm raid1 target must be present in every case */
58686d7f5d3SJohn Marino if (!_pvmove_target_present(cmd, 0)) {
58786d7f5d3SJohn Marino log_error("Required device-mapper target(s) not "
58886d7f5d3SJohn Marino "detected in your kernel");
58986d7f5d3SJohn Marino return ECMD_FAILED;
59086d7f5d3SJohn Marino }
59186d7f5d3SJohn Marino
59286d7f5d3SJohn Marino if (argc) {
59386d7f5d3SJohn Marino pv_name = argv[0];
59486d7f5d3SJohn Marino
59586d7f5d3SJohn Marino /* Drop any PE lists from PV name */
59686d7f5d3SJohn Marino if ((colon = strchr(pv_name, ':'))) {
59786d7f5d3SJohn Marino if (!(pv_name = dm_pool_strndup(cmd->mem, pv_name,
59886d7f5d3SJohn Marino (unsigned) (colon -
59986d7f5d3SJohn Marino pv_name)))) {
60086d7f5d3SJohn Marino log_error("Failed to clone PV name");
60186d7f5d3SJohn Marino return ECMD_FAILED;
60286d7f5d3SJohn Marino }
60386d7f5d3SJohn Marino }
60486d7f5d3SJohn Marino
60586d7f5d3SJohn Marino if (!arg_count(cmd, abort_ARG) &&
60686d7f5d3SJohn Marino (ret = _set_up_pvmove(cmd, pv_name, argc, argv)) !=
60786d7f5d3SJohn Marino ECMD_PROCESSED) {
60886d7f5d3SJohn Marino stack;
60986d7f5d3SJohn Marino return ret;
61086d7f5d3SJohn Marino }
61186d7f5d3SJohn Marino }
61286d7f5d3SJohn Marino
61386d7f5d3SJohn Marino return pvmove_poll(cmd, pv_name, arg_is_set(cmd, background_ARG));
61486d7f5d3SJohn Marino }
615