xref: /dflybsd-src/contrib/lvm2/dist/tools/pvmove.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
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