summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlasdair Kergon <agk@redhat.com>2008-09-19 06:42:00 +0000
committerAlasdair Kergon <agk@redhat.com>2008-09-19 06:42:00 +0000
commit8c5bcdabab5b40498f2051715639cf07f14d877b (patch)
tree4cadcdff3b23747c3ea869f3851bedf4d95dc3d4
parent86fb36e2b08f667bc06ac7d0b160a0247bb382a0 (diff)
downloadlvm2-8c5bcdabab5b40498f2051715639cf07f14d877b.tar.gz
lvm2-8c5bcdabab5b40498f2051715639cf07f14d877b.tar.xz
lvm2-8c5bcdabab5b40498f2051715639cf07f14d877b.zip
Improve the way VGs with PVs missing are handled so manual intervention
is required in fewer circumstances. (mornfall)
-rw-r--r--WHATS_NEW6
-rw-r--r--daemons/clvmd/lvm-functions.c6
-rw-r--r--doc/example.conf12
-rw-r--r--lib/activate/activate.c6
-rw-r--r--lib/activate/dev_manager.c81
-rw-r--r--lib/commands/toolcontext.c20
-rw-r--r--lib/commands/toolcontext.h7
-rw-r--r--lib/config/defaults.h2
-rw-r--r--lib/display/display.c10
-rw-r--r--lib/format1/disk-rep.h2
-rw-r--r--lib/format1/format1.c16
-rw-r--r--lib/format1/import-export.c9
-rw-r--r--lib/format_text/archiver.c6
-rw-r--r--lib/format_text/flags.c16
-rw-r--r--lib/format_text/import_vsn1.c13
-rw-r--r--lib/locking/cluster_locking.c3
-rw-r--r--lib/locking/locking.h1
-rw-r--r--lib/log/log.c11
-rw-r--r--lib/log/log.h2
-rw-r--r--lib/metadata/metadata-exported.h8
-rw-r--r--lib/metadata/metadata.c216
-rw-r--r--lib/metadata/metadata.h8
-rw-r--r--lib/report/report.c2
-rw-r--r--man/lvm.conf.516
-rw-r--r--man/vgreduce.810
-rw-r--r--tools/commands.h4
-rw-r--r--tools/lvmcmdline.c7
-rw-r--r--tools/lvremove.c2
-rw-r--r--tools/pvcreate.c3
-rw-r--r--tools/vgcfgbackup.c3
-rw-r--r--tools/vgreduce.c102
31 files changed, 430 insertions, 180 deletions
diff --git a/WHATS_NEW b/WHATS_NEW
index 2a27861b..5abf6dc3 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,11 @@
Version 2.02.40 -
================================
+ In VG with PVs missing, by default allow activation of LVs that are complete.
+ Track PARTIAL_LV and MISSING_PV flags internally.
+ Require --force with --removemissing in vgreduce to remove partial LVs.
+ No longer write out PARTIAL flag into metadata backups.
+ Treat new default activation/missing_stripe_filler "error" as an error target.
+ Remove internal partial_mode.
Add device/md_chunk_alignment to lvm.conf.
Pass struct physical_volume to pe_align and adjust for md chunk size.
Store sysfs location in struct cmd_context.
diff --git a/daemons/clvmd/lvm-functions.c b/daemons/clvmd/lvm-functions.c
index 977cc428..c312949a 100644
--- a/daemons/clvmd/lvm-functions.c
+++ b/daemons/clvmd/lvm-functions.c
@@ -413,9 +413,6 @@ int do_lock_lv(unsigned char command, unsigned char lock_flags, char *resource)
}
}
- if (lock_flags & LCK_PARTIAL_MODE)
- init_partial(1);
-
if (lock_flags & LCK_MIRROR_NOSYNC_MODE)
init_mirror_in_sync(1);
@@ -454,9 +451,6 @@ int do_lock_lv(unsigned char command, unsigned char lock_flags, char *resource)
break;
}
- if (lock_flags & LCK_PARTIAL_MODE)
- init_partial(0);
-
if (lock_flags & LCK_MIRROR_NOSYNC_MODE)
init_mirror_in_sync(0);
diff --git a/doc/example.conf b/doc/example.conf
index ca85d32a..16cefb2f 100644
--- a/doc/example.conf
+++ b/doc/example.conf
@@ -271,11 +271,13 @@ global {
}
activation {
- # Device used in place of missing stripes if activating incomplete volume.
- # For now, you need to set this up yourself first (e.g. with 'dmsetup')
- # For example, you could make it return I/O errors using the 'error'
- # target or make it return zeros.
- missing_stripe_filler = "/dev/ioerror"
+ # How to fill in missing stripes if activating an incomplete volume.
+ # Using "error" will make inaccessible parts of the device return
+ # I/O errors on access. You can instead use a device path, in which
+ # case, that device will be used to in place of missing stripes.
+ # But note that using anything other than "error" with mirrored
+ # or snapshotted volumes is likely to result in data corruption.
+ missing_stripe_filler = "error"
# How much stack (in KB) to reserve for use while devices suspended
reserved_stack = 256
diff --git a/lib/activate/activate.c b/lib/activate/activate.c
index fa1aafd5..06954d16 100644
--- a/lib/activate/activate.c
+++ b/lib/activate/activate.c
@@ -1027,6 +1027,12 @@ static int _lv_activate(struct cmd_context *cmd, const char *lvid_s,
return 0;
}
+ if ((!lv->vg->cmd->partial_activate) && (lv->status & PARTIAL_LV)) {
+ log_error("Refusing activation of partial LV %s. Use --partial to override.",
+ lv->name);
+ return_0;
+ }
+
if (test_mode()) {
_skip("Activating '%s'.", lv->name);
return 1;
diff --git a/lib/activate/dev_manager.c b/lib/activate/dev_manager.c
index a081dad7..41731922 100644
--- a/lib/activate/dev_manager.c
+++ b/lib/activate/dev_manager.c
@@ -47,7 +47,6 @@ struct dev_manager {
struct cmd_context *cmd;
- const char *stripe_filler;
void *target_state;
uint32_t pvmove_mirror_count;
@@ -59,8 +58,6 @@ struct lv_layer {
const char *old_name;
};
-static const char *stripe_filler = NULL;
-
static char *_build_dlid(struct dm_pool *mem, const char *lvid, const char *layer)
{
char *dlid;
@@ -443,13 +440,6 @@ struct dev_manager *dev_manager_create(struct cmd_context *cmd,
dm->cmd = cmd;
dm->mem = mem;
- if (!stripe_filler) {
- stripe_filler = find_config_tree_str(cmd,
- "activation/missing_stripe_filler",
- DEFAULT_STRIPE_FILLER);
- }
- dm->stripe_filler = stripe_filler;
-
if (!(dm->vg_name = dm_pool_strdup(dm->mem, vg_name)))
goto_bad;
@@ -699,6 +689,68 @@ bad:
return NULL;
}
+static char *_add_error_device(struct dev_manager *dm, struct dm_tree *dtree,
+ struct lv_segment *seg, int s)
+{
+ char *id, *name;
+ char errid[32];
+ struct dm_tree_node *node;
+ struct lv_segment *seg_i;
+ int segno = -1, i = 0;;
+ uint64_t size = seg->len * seg->lv->vg->extent_size;
+
+ list_iterate_items(seg_i, &seg->lv->segments) {
+ if (seg == seg_i)
+ segno = i;
+ ++i;
+ }
+
+ if (segno < 0) {
+ log_error("_add_error_device called with bad segment");
+ return_NULL;
+ }
+
+ sprintf(errid, "missing_%d_%d", segno, s);
+
+ if (!(id = build_dlid(dm, seg->lv->lvid.s, errid)))
+ return_NULL;
+
+ if (!(name = build_dm_name(dm->mem, seg->lv->vg->name,
+ seg->lv->name, errid)))
+ return_NULL;
+ if (!(node = dm_tree_add_new_dev(dtree, name, id, 0, 0, 0, 0, 0)))
+ return_NULL;
+ if (!dm_tree_node_add_error_target(node, size))
+ return_NULL;
+
+ return id;
+}
+
+static int _add_error_area(struct dev_manager *dm, struct dm_tree_node *node,
+ struct lv_segment *seg, int s)
+{
+ char *dlid;
+ uint64_t extent_size = seg->lv->vg->extent_size;
+
+ if (!strcmp(dm->cmd->stripe_filler, "error")) {
+ /*
+ * FIXME, the tree pointer is first field of dm_tree_node, but
+ * we don't have the struct definition available.
+ */
+ struct dm_tree **tree = (struct dm_tree **) node;
+ dlid = _add_error_device(dm, *tree, seg, s);
+ if (!dlid)
+ return_0;
+ dm_tree_node_add_target_area(node, NULL, dlid,
+ extent_size * seg_le(seg, s));
+ } else {
+ dm_tree_node_add_target_area(node,
+ dm->cmd->stripe_filler,
+ NULL, UINT64_C(0));
+ }
+ return 1;
+}
+
int add_areas_line(struct dev_manager *dm, struct lv_segment *seg,
struct dm_tree_node *node, uint32_t start_area,
uint32_t areas)
@@ -712,11 +764,10 @@ int add_areas_line(struct dev_manager *dm, struct lv_segment *seg,
(!seg_pvseg(seg, s) ||
!seg_pv(seg, s) ||
!seg_dev(seg, s))) ||
- (seg_type(seg, s) == AREA_LV && !seg_lv(seg, s)))
- dm_tree_node_add_target_area(node,
- dm->stripe_filler,
- NULL, UINT64_C(0));
- else if (seg_type(seg, s) == AREA_PV)
+ (seg_type(seg, s) == AREA_LV && !seg_lv(seg, s))) {
+ if (!_add_error_area(dm, node, seg, s))
+ return_0;
+ } else if (seg_type(seg, s) == AREA_PV)
dm_tree_node_add_target_area(node,
dev_name(seg_dev(seg, s)),
NULL,
diff --git a/lib/commands/toolcontext.c b/lib/commands/toolcontext.c
index cbf63df4..8f89fee3 100644
--- a/lib/commands/toolcontext.c
+++ b/lib/commands/toolcontext.c
@@ -197,6 +197,7 @@ static int _process_config(struct cmd_context *cmd)
{
mode_t old_umask;
const char *read_ahead;
+ struct stat st;
/* umask */
cmd->default_settings.umask = find_config_tree_int(cmd,
@@ -263,6 +264,24 @@ static int _process_config(struct cmd_context *cmd)
return 0;
}
+ cmd->stripe_filler = find_config_tree_str(cmd,
+ "activation/missing_stripe_filler",
+ DEFAULT_STRIPE_FILLER);
+ if (strcmp(cmd->stripe_filler, "error")) {
+ if (stat(cmd->stripe_filler, &st)) {
+ log_warn("WARNING: activation/missing_stripe_filler = \"%s\" "
+ "is invalid,", cmd->stripe_filler);
+ log_warn(" stat failed: %s", strerror(errno));
+ log_warn("Falling back to \"error\" missing_stripe_filler.");
+ cmd->stripe_filler = "error";
+ } else if (!S_ISBLK(st.st_mode)) {
+ log_warn("WARNING: activation/missing_stripe_filler = \"%s\" "
+ "is not a block device.", cmd->stripe_filler);
+ log_warn("Falling back to \"error\" missing_stripe_filler.");
+ cmd->stripe_filler = "error";
+ }
+ }
+
return 1;
}
@@ -981,6 +1000,7 @@ struct cmd_context *create_toolcontext(struct arg *the_args, unsigned is_static,
cmd->args = the_args;
cmd->is_static = is_static;
cmd->is_long_lived = is_long_lived;
+ cmd->handles_missing_pvs = 0;
cmd->hosttags = 0;
list_init(&cmd->formats);
list_init(&cmd->segtypes);
diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h
index 0d9d844f..8f6cfc7a 100644
--- a/lib/commands/toolcontext.h
+++ b/lib/commands/toolcontext.h
@@ -66,8 +66,10 @@ struct cmd_context {
struct command *command;
struct arg *args;
char **argv;
- unsigned is_static; /* Static binary? */
- unsigned is_long_lived; /* Optimises persistent_filter handling */
+ unsigned is_static:1; /* Static binary? */
+ unsigned is_long_lived:1; /* Optimises persistent_filter handling */
+ unsigned handles_missing_pvs:1;
+ unsigned partial_activate:1;
struct dev_filter *filter;
int dump_filter; /* Dump filter when exiting? */
@@ -81,6 +83,7 @@ struct cmd_context {
struct archive_params *archive_params;
struct backup_params *backup_params;
+ const char *stripe_filler;
/* List of defined tags */
struct list tags;
diff --git a/lib/config/defaults.h b/lib/config/defaults.h
index 9566c6ea..605bc497 100644
--- a/lib/config/defaults.h
+++ b/lib/config/defaults.h
@@ -92,7 +92,7 @@
# define DEFAULT_ACTIVATION 0
#endif
-#define DEFAULT_STRIPE_FILLER "/dev/ioerror"
+#define DEFAULT_STRIPE_FILLER "error"
#define DEFAULT_MIRROR_REGION_SIZE 512 /* KB */
#define DEFAULT_INTERVAL 15
diff --git a/lib/display/display.c b/lib/display/display.c
index 6f8bdebd..d9358fb8 100644
--- a/lib/display/display.c
+++ b/lib/display/display.c
@@ -578,10 +578,7 @@ void vgdisplay_full(const struct volume_group *vg)
struct lv_list *lvl;
char uuid[64] __attribute((aligned(8)));
- if (vg->status & PARTIAL_VG)
- active_pvs = list_size(&vg->pvs);
- else
- active_pvs = vg->pv_count;
+ active_pvs = vg->pv_count - vg_missing_pv_count(vg);
log_print("--- Volume group ---");
log_print("VG Name %s", vg->name);
@@ -664,10 +661,7 @@ void vgdisplay_colons(const struct volume_group *vg)
const char *access;
char uuid[64] __attribute((aligned(8)));
- if (vg->status & PARTIAL_VG)
- active_pvs = list_size(&vg->pvs);
- else
- active_pvs = vg->pv_count;
+ active_pvs = vg->pv_count - vg_missing_pv_count(vg);
list_iterate_items(lvl, &vg->lvs)
if (lv_is_visible(lvl->lv) && !(lvl->lv->status & SNAPSHOT))
diff --git a/lib/format1/disk-rep.h b/lib/format1/disk-rep.h
index 9a04c7b8..0ef57190 100644
--- a/lib/format1/disk-rep.h
+++ b/lib/format1/disk-rep.h
@@ -212,7 +212,7 @@ int export_pv(struct cmd_context *cmd, struct dm_pool *mem,
struct pv_disk *pvd, struct physical_volume *pv);
int import_vg(struct dm_pool *mem,
- struct volume_group *vg, struct disk_list *dl, int partial);
+ struct volume_group *vg, struct disk_list *dl);
int export_vg(struct vg_disk *vgd, struct volume_group *vg);
int import_lv(struct dm_pool *mem, struct logical_volume *lv, struct lv_disk *lvd);
diff --git a/lib/format1/format1.c b/lib/format1/format1.c
index b38cf6f3..34207914 100644
--- a/lib/format1/format1.c
+++ b/lib/format1/format1.c
@@ -23,7 +23,7 @@
#include "segtype.h"
/* VG consistency checks */
-static int _check_vgs(struct list *pvs, int *partial)
+static int _check_vgs(struct list *pvs)
{
struct list *pvh, *t;
struct disk_list *dl = NULL;
@@ -33,8 +33,6 @@ static int _check_vgs(struct list *pvs, int *partial)
uint32_t exported = 0;
int first_time = 1;
- *partial = 0;
-
/*
* If there are exported and unexported PVs, ignore exported ones.
* This means an active VG won't be affected if disks are inserted
@@ -98,10 +96,6 @@ static int _check_vgs(struct list *pvs, int *partial)
dl->vgd.pe_total, dl->vgd.pe_allocated,
dl->vgd.pvg_total);
list_del(pvh);
- if (partial_mode()) {
- *partial = 1;
- continue;
- }
return 0;
}
pv_count++;
@@ -111,9 +105,6 @@ static int _check_vgs(struct list *pvs, int *partial)
if (pv_count != first->vgd.pv_cur) {
log_error("%d PV(s) found for VG %s: expected %d",
pv_count, first->pvd.vg_name, first->vgd.pv_cur);
- if (!partial_mode())
- return 0;
- *partial = 1;
}
return 1;
@@ -125,7 +116,6 @@ static struct volume_group *_build_vg(struct format_instance *fid,
struct dm_pool *mem = fid->fmt->cmd->mem;
struct volume_group *vg = dm_pool_alloc(mem, sizeof(*vg));
struct disk_list *dl;
- int partial;
if (!vg)
goto_bad;
@@ -142,12 +132,12 @@ static struct volume_group *_build_vg(struct format_instance *fid,
list_init(&vg->lvs);
list_init(&vg->tags);
- if (!_check_vgs(pvs, &partial))
+ if (!_check_vgs(pvs))
goto_bad;
dl = list_item(pvs->n, struct disk_list);
- if (!import_vg(mem, vg, dl, partial))
+ if (!import_vg(mem, vg, dl))
goto_bad;
if (!import_pvs(fid->fmt, mem, vg, pvs, &vg->pvs, &vg->pv_count))
diff --git a/lib/format1/import-export.c b/lib/format1/import-export.c
index 4bb51160..c80011d9 100644
--- a/lib/format1/import-export.c
+++ b/lib/format1/import-export.c
@@ -214,7 +214,7 @@ int export_pv(struct cmd_context *cmd, struct dm_pool *mem __attribute((unused))
}
int import_vg(struct dm_pool *mem,
- struct volume_group *vg, struct disk_list *dl, int partial)
+ struct volume_group *vg, struct disk_list *dl)
{
struct vg_disk *vgd = &dl->vgd;
memcpy(vg->id.uuid, vgd->vg_uuid, ID_LEN);
@@ -236,10 +236,10 @@ int import_vg(struct dm_pool *mem,
if (vgd->vg_status & VG_EXTENDABLE)
vg->status |= RESIZEABLE_VG;
- if (partial || (vgd->vg_access & VG_READ))
+ if (vgd->vg_access & VG_READ)
vg->status |= LVM_READ;
- if (!partial && (vgd->vg_access & VG_WRITE))
+ if (vgd->vg_access & VG_WRITE)
vg->status |= LVM_WRITE;
if (vgd->vg_access & VG_CLUSTERED)
@@ -255,9 +255,6 @@ int import_vg(struct dm_pool *mem,
vg->max_pv = vgd->pv_max;
vg->alloc = ALLOC_NORMAL;
- if (partial)
- vg->status |= PARTIAL_VG;
-
return 1;
}
diff --git a/lib/format_text/archiver.c b/lib/format_text/archiver.c
index 1d2c5ede..e54535c9 100644
--- a/lib/format_text/archiver.c
+++ b/lib/format_text/archiver.c
@@ -134,10 +134,8 @@ int archive_display(struct cmd_context *cmd, const char *vg_name)
{
int r1, r2;
- init_partial(1);
r1 = archive_list(cmd, cmd->archive_params->dir, vg_name);
r2 = backup_list(cmd, cmd->backup_params->dir, vg_name);
- init_partial(0);
return r1 && r2;
}
@@ -146,9 +144,7 @@ int archive_display_file(struct cmd_context *cmd, const char *file)
{
int r;
- init_partial(1);
r = archive_list_file(cmd, file);
- init_partial(0);
return r;
}
@@ -393,7 +389,7 @@ void check_current_backup(struct volume_group *vg)
char path[PATH_MAX];
struct volume_group *vg_backup;
- if ((vg->status & PARTIAL_VG) || (vg->status & EXPORTED_VG))
+ if (vg->status & EXPORTED_VG)
return;
if (dm_snprintf(path, sizeof(path), "%s/%s",
diff --git a/lib/format_text/flags.c b/lib/format_text/flags.c
index 86854242..eb281b6b 100644
--- a/lib/format_text/flags.c
+++ b/lib/format_text/flags.c
@@ -31,12 +31,12 @@ struct flag {
static struct flag _vg_flags[] = {
{EXPORTED_VG, "EXPORTED", STATUS_FLAG},
{RESIZEABLE_VG, "RESIZEABLE", STATUS_FLAG},
- {PARTIAL_VG, "PARTIAL", STATUS_FLAG},
{PVMOVE, "PVMOVE", STATUS_FLAG},
{LVM_READ, "READ", STATUS_FLAG},
{LVM_WRITE, "WRITE", STATUS_FLAG},
{CLUSTERED, "CLUSTERED", STATUS_FLAG},
{SHARED, "SHARED", STATUS_FLAG},
+ {PARTIAL_VG, NULL, 0},
{PRECOMMITTED, NULL, 0},
{0, NULL, 0}
};
@@ -44,6 +44,7 @@ static struct flag _vg_flags[] = {
static struct flag _pv_flags[] = {
{ALLOCATABLE_PV, "ALLOCATABLE", STATUS_FLAG},
{EXPORTED_VG, "EXPORTED", STATUS_FLAG},
+ {MISSING_PV, "MISSING", COMPATIBLE_FLAG},
{0, NULL, 0}
};
@@ -62,6 +63,8 @@ static struct flag _lv_flags[] = {
{SNAPSHOT, NULL, 0},
{ACTIVATE_EXCL, NULL, 0},
{CONVERTING, NULL, 0},
+ {PARTIAL_LV, NULL, 0},
+ {POSTORDER_FLAG, NULL, 0},
{0, NULL, 0}
};
@@ -155,7 +158,16 @@ int read_flags(uint32_t *status, int type, struct config_value *cv)
break;
}
- if (!flags[f].description && (type & STATUS_FLAG)) {
+ if (type == VG_FLAGS && !strcmp(cv->v.str, "PARTIAL")) {
+ /*
+ * Exception: We no longer write this flag out, but it
+ * might be encountered in old backup files, so restore
+ * it in that case. It is never part of live metadata
+ * though, so only vgcfgrestore needs to be concerned
+ * by this case.
+ */
+ s |= PARTIAL_VG;
+ } else if (!flags[f].description && (type & STATUS_FLAG)) {
log_err("Unknown status flag '%s'.", cv->v.str);
return 0;
}
diff --git a/lib/format_text/import_vsn1.c b/lib/format_text/import_vsn1.c
index f32573a9..653be29d 100644
--- a/lib/format_text/import_vsn1.c
+++ b/lib/format_text/import_vsn1.c
@@ -194,11 +194,6 @@ static int _read_pv(struct format_instance *fid, struct dm_pool *mem,
else
log_error("Couldn't find device with uuid '%s'.",
buffer);
-
- if (partial_mode())
- vg->status |= PARTIAL_VG;
- else
- return 0;
}
if (!(pv->vg_name = dm_pool_strdup(mem, vg->name)))
@@ -211,6 +206,9 @@ static int _read_pv(struct format_instance *fid, struct dm_pool *mem,
return 0;
}
+ if (!pv->dev)
+ pv->status |= MISSING_PV;
+
/* Late addition */
_read_int64(pvn, "dev_size", &pv->size);
@@ -800,11 +798,6 @@ static struct volume_group *_read_vg(struct format_instance *fid,
dm_hash_destroy(pv_hash);
- if (vg->status & PARTIAL_VG) {
- vg->status &= ~LVM_WRITE;
- vg->status |= LVM_READ;
- }
-
/*
* Finished.
*/
diff --git a/lib/locking/cluster_locking.c b/lib/locking/cluster_locking.c
index 3cbc9de9..94b39de5 100644
--- a/lib/locking/cluster_locking.c
+++ b/lib/locking/cluster_locking.c
@@ -315,9 +315,6 @@ static int _lock_for_cluster(unsigned char cmd, uint32_t flags, const char *name
args[0] = flags & 0x7F; /* Maskoff lock flags */
args[1] = flags & 0xC0; /* Bitmap flags */
- if (partial_mode())
- args[1] |= LCK_PARTIAL_MODE;
-
if (mirror_in_sync())
args[1] |= LCK_MIRROR_NOSYNC_MODE;
diff --git a/lib/locking/locking.h b/lib/locking/locking.h
index 6a34a6d3..c900e54f 100644
--- a/lib/locking/locking.h
+++ b/lib/locking/locking.h
@@ -83,7 +83,6 @@ int check_lvm1_vg_inactive(struct cmd_context *cmd, const char *vgname);
/*
* Additional lock bits for cluster communication
*/
-#define LCK_PARTIAL_MODE 0x00000001U /* Running in partial mode */
#define LCK_MIRROR_NOSYNC_MODE 0x00000002U /* Mirrors don't require sync */
#define LCK_DMEVENTD_MONITOR_MODE 0x00000004U /* Register with dmeventd */
diff --git a/lib/log/log.c b/lib/log/log.c
index 78686817..c8b80a28 100644
--- a/lib/log/log.c
+++ b/lib/log/log.c
@@ -29,7 +29,6 @@ static struct str_list _log_dev_alias;
static int _verbose_level = VERBOSE_BASE_LEVEL;
static int _test = 0;
-static int _partial = 0;
static int _md_filtering = 0;
static int _pvmove = 0;
static int _full_scan_done = 0; /* Restrict to one full scan during each cmd */
@@ -154,11 +153,6 @@ void init_test(int level)
_test = level;
}
-void init_partial(int level)
-{
- _partial = level;
-}
-
void init_md_filtering(int level)
{
_md_filtering = level;
@@ -254,11 +248,6 @@ int test_mode()
return _test;
}
-int partial_mode()
-{
- return _partial;
-}
-
int md_filtering()
{
return _md_filtering;
diff --git a/lib/log/log.h b/lib/log/log.h
index 8b85758b..133b170a 100644
--- a/lib/log/log.h
+++ b/lib/log/log.h
@@ -64,7 +64,6 @@ void fin_syslog(void);
void init_verbose(int level);
void init_test(int level);
-void init_partial(int level);
void init_md_filtering(int level);
void init_pvmove(int level);
void init_full_scan_done(int level);
@@ -84,7 +83,6 @@ void init_error_message_produced(int error_message_produced);
void set_cmd_name(const char *cmd_name);
int test_mode(void);
-int partial_mode(void);
int md_filtering(void);
int pvmove_mode(void);
int full_scan_done(void);
diff --git a/lib/metadata/metadata-exported.h b/lib/metadata/metadata-exported.h
index 280646a2..6cdfdb4a 100644
--- a/lib/metadata/metadata-exported.h
+++ b/lib/metadata/metadata-exported.h
@@ -71,6 +71,13 @@ struct pv_segment;
//#define PRECOMMITTED 0x00200000U /* VG - internal use only */
#define CONVERTING 0x00400000U /* LV */
+#define MISSING_PV 0x00800000U /* PV */
+#define PARTIAL_LV 0x01000000U /* LV - derived flag, not
+ written out in metadata*/
+
+//#define POSTORDER_FLAG 0x02000000U /* Not a real flag, reserved for
+// temporary use inside vg_read. */
+
#define LVM_READ 0x00000100U /* LV VG */
#define LVM_WRITE 0x00000200U /* LV VG */
#define CLUSTERED 0x00000400U /* VG */
@@ -564,6 +571,7 @@ uint64_t pv_pe_start(const pv_t *pv);
uint32_t pv_pe_count(const pv_t *pv);
uint32_t pv_pe_alloc_count(const pv_t *pv);
+int vg_missing_pv_count(const vg_t *vg);
uint32_t vg_status(const vg_t *vg);
#define vg_is_clustered(vg) (vg_status((vg)) & CLUSTERED)
diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c
index ecf4152a..e9e944c5 100644
--- a/lib/metadata/metadata.c
+++ b/lib/metadata/metadata.c
@@ -334,9 +334,9 @@ int vg_remove_single(struct cmd_context *cmd, const char *vg_name,
struct pv_list *pvl;
int ret = 1;
- if (!vg || !consistent || (vg_status(vg) & PARTIAL_VG)) {
- log_error("Volume group \"%s\" not found or inconsistent.",
- vg_name);
+ if (!vg || !consistent || vg_missing_pv_count(vg)) {
+ log_error("Volume group \"%s\" not found, is inconsistent "
+ "or has PVs missing.", vg_name);
log_error("Consider vgreduce --removemissing if metadata "
"is inconsistent.");
return 0;
@@ -488,19 +488,15 @@ struct volume_group *vg_create(struct cmd_context *cmd, const char *vg_name,
struct volume_group *vg;
struct dm_pool *mem = cmd->mem;
int consistent = 0;
- int old_partial;
if (!(vg = dm_pool_zalloc(mem, sizeof(*vg))))
return_NULL;
/* is this vg name already in use ? */
- old_partial = partial_mode();
- init_partial(1);
if (vg_read(cmd, vg_name, NULL, &consistent)) {
log_err("A volume group called '%s' already exists.", vg_name);
goto bad;
}
- init_partial(old_partial);
if (!id_create(&vg->id)) {
log_err("Couldn't create uuid for volume group '%s'.", vg_name);
@@ -1191,6 +1187,157 @@ int vgs_are_compatible(struct cmd_context *cmd __attribute((unused)),
return 1;
}
+struct _lv_postorder_baton {
+ int (*fn)(struct logical_volume *lv, void *data);
+ void *data;
+};
+
+static int _lv_postorder_visit(struct logical_volume *,
+ int (*fn)(struct logical_volume *lv, void *data),
+ void *data);
+
+static int _lv_postorder_level(struct logical_volume *lv, void *data)
+{
+ struct _lv_postorder_baton *baton = data;
+ int r =_lv_postorder_visit(lv, baton->fn, baton->data);
+ lv->status |= POSTORDER_FLAG;
+ return r;
+};
+
+static int _lv_each_dependency(struct logical_volume *lv,
+ int (*fn)(struct logical_volume *lv, void *data),
+ void *data)
+{
+ int i, s;
+ struct lv_segment *lvseg;
+
+ struct logical_volume *deps[] = {
+ lv->snapshot ? lv->snapshot->origin : 0,
+ lv->snapshot ? lv->snapshot->cow : 0 };
+ for (i = 0; i < sizeof(deps) / sizeof(*deps); ++i) {
+ if (deps[i] && !fn(deps[i], data))
+ return_0;
+ }
+
+ list_iterate_items(lvseg, &lv->segments) {
+ if (lvseg->log_lv && !fn(lvseg->log_lv, data))
+ return_0;
+ for (s = 0; s < lvseg->area_count; ++s) {
+ if (seg_type(lvseg, s) == AREA_LV && !fn(seg_lv(lvseg,s), data))
+ return_0;
+ }
+ }
+ return 1;
+}
+
+static int _lv_postorder_cleanup(struct logical_volume *lv, void *data)
+{
+ if (!(lv->status & POSTORDER_FLAG))
+ return 1;
+ lv->status &= ~POSTORDER_FLAG;
+
+ if (!_lv_each_dependency(lv, _lv_postorder_cleanup, data))
+ return_0;
+ return 1;
+}
+
+static int _lv_postorder_visit(struct logical_volume *lv,
+ int (*fn)(struct logical_volume *lv, void *data),
+ void *data)
+{
+ struct _lv_postorder_baton baton;
+ int r;
+
+ if (lv->status & POSTORDER_FLAG)
+ return 1;
+
+ baton.fn = fn;
+ baton.data = data;
+ r = _lv_each_dependency(lv, _lv_postorder_level, &baton);
+ if (r) {
+ r = fn(lv, data);
+ log_verbose("visited %s", lv->name);
+ }
+ return r;
+}
+
+/*
+ * This will walk the LV dependency graph in depth-first order and in the
+ * postorder, call a callback function "fn". The void *data is passed along all
+ * the calls. The callback may return zero to indicate an error and terminate
+ * the depth-first walk. The error is propagated to return value of
+ * _lv_postorder.
+ */
+static int _lv_postorder(struct logical_volume *lv,
+ int (*fn)(struct logical_volume *lv, void *data),
+ void *data)
+{
+ int r;
+ r = _lv_postorder_visit(lv, fn, data);
+ _lv_postorder_cleanup(lv, 0);
+ return r;
+}
+
+struct _lv_mark_if_partial_baton {
+ int partial;
+};
+
+static int _lv_mark_if_partial_collect(struct logical_volume *lv, void *data)
+{
+ struct _lv_mark_if_partial_baton *baton = data;
+ if (lv->status & PARTIAL_LV)
+ baton->partial = 1;
+
+ return 1;
+}
+
+static int _lv_mark_if_partial_single(struct logical_volume *lv, void *data)
+{
+ int s;
+ struct _lv_mark_if_partial_baton baton;
+ struct lv_segment *lvseg;
+
+ list_iterate_items(lvseg, &lv->segments) {
+ for (s = 0; s < lvseg->area_count; ++s) {
+ if (seg_type(lvseg, s) == AREA_PV) {
+ if (seg_pv(lvseg, s)->status & MISSING_PV)
+ lv->status |= PARTIAL_LV;
+ }
+ }
+ }
+
+ baton.partial = 0;
+ _lv_each_dependency(lv, _lv_mark_if_partial_collect, &baton);
+
+ if (baton.partial)
+ lv->status |= PARTIAL_LV;
+
+ return 1;
+}
+
+static int _lv_mark_if_partial(struct logical_volume *lv)
+{
+ return _lv_postorder(lv, _lv_mark_if_partial_single, NULL);
+}
+
+/*
+ * Mark LVs with missing PVs using PARTIAL_LV status flag. The flag is
+ * propagated transitively, so LVs referencing other LVs are marked
+ * partial as well, if any of their referenced LVs are marked partial.
+ */
+static int _vg_mark_partial_lvs(struct volume_group *vg)
+{
+ struct logical_volume *lv;
+ struct lv_list *lvl;
+
+ list_iterate_items(lvl, &vg->lvs) {
+ lv = lvl->lv;
+ if (!_lv_mark_if_partial(lv))
+ return_0;
+ }
+ return 1;
+}
+
int vg_validate(struct volume_group *vg)
{
struct pv_list *pvl, *pvl2;
@@ -1295,8 +1442,13 @@ int vg_write(struct volume_group *vg)
return_0;
if (vg->status & PARTIAL_VG) {
- log_error("Cannot change metadata for partial volume group %s",
- vg->name);
+ log_error("Cannot update partial volume group %s.", vg->name);
+ return 0;
+ }
+
+ if (vg_missing_pv_count(vg) && !vg->cmd->handles_missing_pvs) {
+ log_error("Cannot update volume group %s while physical "
+ "volumes are missing.", vg->name);
return 0;
}
@@ -1495,9 +1647,20 @@ static int _update_pv_list(struct list *all_pvs, struct volume_group *vg)
return 1;
}
+int vg_missing_pv_count(const vg_t *vg)
+{
+ int ret = 0;
+ struct pv_list *pvl;
+ list_iterate_items(pvl, &vg->pvs) {
+ if (pvl->pv->status & MISSING_PV)
+ ++ ret;
+ }
+ return ret;
+}
+
/* Caller sets consistent to 1 if it's safe for vg_read to correct
* inconsistent metadata on disk (i.e. the VG write lock is held).
- * This guarantees only consistent metadata is returned unless PARTIAL_VG.
+ * This guarantees only consistent metadata is returned.
* If consistent is 0, caller must check whether consistent == 1 on return
* and take appropriate action if it isn't (e.g. abort; get write lock
* and call vg_read again).
@@ -1536,6 +1699,11 @@ static struct volume_group *_vg_read(struct cmd_context *cmd,
}
if ((correct_vg = lvmcache_get_vg(vgid, precommitted))) {
+ if (vg_missing_pv_count(correct_vg)) {
+ log_verbose("There are %d physical volumes missing.",
+ vg_missing_pv_count(correct_vg));
+ _vg_mark_partial_lvs(correct_vg);
+ }
*consistent = 1;
return correct_vg;
}
@@ -1631,7 +1799,8 @@ static struct volume_group *_vg_read(struct cmd_context *cmd,
}
}
- if (list_size(&correct_vg->pvs) != list_size(pvids)) {
+ if (list_size(&correct_vg->pvs) != list_size(pvids)
+ + vg_missing_pv_count(correct_vg)) {
log_debug("Cached VG %s had incorrect PV list",
vgname);
@@ -1640,6 +1809,8 @@ static struct volume_group *_vg_read(struct cmd_context *cmd,
else
correct_vg = NULL;
} else list_iterate_items(pvl, &correct_vg->pvs) {
+ if (pvl->pv->status & MISSING_PV)
+ continue;
if (!str_list_match_item(pvids, pvl->pv->dev->pvid)) {
log_debug("Cached VG %s had incorrect PV list",
vgname);
@@ -1722,15 +1893,6 @@ static struct volume_group *_vg_read(struct cmd_context *cmd,
if (!*consistent)
return correct_vg;
- /* Don't touch partial volume group metadata */
- /* Should be fixed manually with vgcfgbackup/restore etc. */
- if ((correct_vg->status & PARTIAL_VG)) {
- log_error("Inconsistent metadata copies found for "
- "partial volume group %s", vgname);
- *consistent = 0;
- return correct_vg;
- }
-
/* Don't touch if vgids didn't match */
if (inconsistent_vgid) {
log_error("Inconsistent metadata UUIDs found for "
@@ -1769,6 +1931,12 @@ static struct volume_group *_vg_read(struct cmd_context *cmd,
}
}
+ if (vg_missing_pv_count(correct_vg)) {
+ log_verbose("There are %d physical volumes missing.",
+ vg_missing_pv_count(correct_vg));
+ _vg_mark_partial_lvs(correct_vg);
+ }
+
if ((correct_vg->status & PVMOVE) && !pvmove_mode()) {
log_error("WARNING: Interrupted pvmove detected in "
"volume group %s", correct_vg->name);
@@ -1828,11 +1996,10 @@ static struct volume_group *_vg_read_by_vgid(struct cmd_context *cmd,
if ((vg = _vg_read(cmd, NULL, vgid,
&consistent, precommitted)) &&
!strncmp((char *)vg->id.uuid, vgid, ID_LEN)) {
+
if (!consistent) {
log_error("Volume group %s metadata is "
"inconsistent", vg->name);
- if (!partial_mode())
- return NULL;
}
return vg;
}
@@ -1860,6 +2027,7 @@ static struct volume_group *_vg_read_by_vgid(struct cmd_context *cmd,
if ((vg = _vg_read(cmd, vgname, vgid, &consistent,
precommitted)) &&
!strncmp((char *)vg->id.uuid, vgid, ID_LEN)) {
+
if (!consistent) {
log_error("Volume group %s metadata is "
"inconsistent", vgname);
@@ -1993,7 +2161,6 @@ static int _get_pvs(struct cmd_context *cmd, struct list **pvslist)
struct list *vgids;
struct volume_group *vg;
int consistent = 0;
- int old_partial;
int old_pvmove;
lvmcache_label_scan(cmd, 0);
@@ -2015,9 +2182,7 @@ static int _get_pvs(struct cmd_context *cmd, struct list **pvslist)
/* Read every VG to ensure cache consistency */
/* Orphan VG is last on list */
- old_partial = partial_mode();
old_pvmove = pvmove_mode();
- init_partial(1);
init_pvmove(1);
list_iterate_items(strl, vgids) {
vgid = strl->str;
@@ -2042,7 +2207,6 @@ static int _get_pvs(struct cmd_context *cmd, struct list **pvslist)
list_add(results, pvh);
}
init_pvmove(old_pvmove);
- init_partial(old_partial);
if (pvslist)
*pvslist = results;
diff --git a/lib/metadata/metadata.h b/lib/metadata/metadata.h
index e5538b91..6d12653b 100644
--- a/lib/metadata/metadata.h
+++ b/lib/metadata/metadata.h
@@ -61,6 +61,14 @@
//#define MIRROR_NOTSYNCED 0x00080000U /* LV */
#define ACTIVATE_EXCL 0x00100000U /* LV - internal use only */
#define PRECOMMITTED 0x00200000U /* VG - internal use only */
+//#define CONVERTING 0x00400000U /* LV */
+
+//#define MISSING_PV 0x00800000U /* PV */
+//#define PARTIAL_LV 0x01000000U /* LV - derived flag, not
+// written out in metadata*/
+
+#define POSTORDER_FLAG 0x02000000U /* Not a real flag, reserved for
+ temporary use inside vg_read. */
//#define LVM_READ 0x00000100U /* LV VG */
//#define LVM_WRITE 0x00000200U /* LV VG */
diff --git a/lib/report/report.c b/lib/report/report.c
index 70e25aec..a34d6174 100644
--- a/lib/report/report.c
+++ b/lib/report/report.c
@@ -439,7 +439,7 @@ static int _vgstatus_disp(struct dm_report *rh __attribute((unused)), struct dm_
else
repstr[2] = '-';
- if (vg->status & PARTIAL_VG)
+ if (vg_missing_pv_count(vg))
repstr[3] = 'p';
else
repstr[3] = '-';
diff --git a/man/lvm.conf.5 b/man/lvm.conf.5
index 0bb19724..47666b4e 100644
--- a/man/lvm.conf.5
+++ b/man/lvm.conf.5
@@ -300,12 +300,16 @@ in \fBlvm\fP (8).
.TP
\fBactivation\fP \(em Settings affecting device-mapper activation
.IP
-\fBmissing_stripe_filler\fP \(em When activating an incomplete
-logical volume in partial mode, this missing data is replaced
-with this device. It could perhaps be a block device that always
-returns an error when it is accessed, or one that always
-returns zeros. See \fBlvcreate\fP (8) for how to create
-such devices.
+\fBmissing_stripe_filler\fP \(em When activating an incomplete logical
+volume in partial mode, this option dictates how the missing data is
+replaced. A value of "error" will cause activation to create error
+mappings for the missing data, meaning that read access to missing
+portions of the volume will result in I/O errors. You can instead also
+use a device path, and in that case this device will be used in place of
+missing stripes. However, note that using anything other than
+"error" with mirrored or snapshotted volumes is likely to result in data
+corruption. For instructions on how to create a device that always
+returns zeros, see \fBlvcreate\fP (8).
.IP
\fBmirror_region_size\fP \(em Unit size in KB for copy operations
when mirroring.
diff --git a/man/vgreduce.8 b/man/vgreduce.8
index 8f4fe1b7..a382454d 100644
--- a/man/vgreduce.8
+++ b/man/vgreduce.8
@@ -18,11 +18,13 @@ See \fBlvm\fP for common options.
Removes all empty physical volumes if none are given on command line.
.TP
.I \-\-removemissing
-Removes all missing physical volumes from the volume group and makes
-the volume group consistent again.
+Removes all missing physical volumes from the volume group, if there are no
+logical volumes allocated on those. This resumes normal operation of the volume
+group (new logical volumes may again be created, changed and so on).
-It's a good idea to run this option with --test first to find out what it
-would remove before running it for real.
+If this is not possible (there are logical volumes referencing the missing
+physical volumes) and you cannot or do not want to remove them manually, you
+can run this option with --force to have vgreduce remove any partial LVs.
Any logical volumes and dependent snapshots that were partly on the
missing disks get removed completely. This includes those parts
diff --git a/tools/commands.h b/tools/commands.h
index fddc6a33..7121a764 100644
--- a/tools/commands.h
+++ b/tools/commands.h
@@ -850,13 +850,15 @@ xx(vgreduce,
"\t[-h|--help]\n"
"\t[--mirrorsonly]\n"
"\t[--removemissing]\n"
+ "\t[-f|--force]\n"
"\t[-t|--test]\n"
"\t[-v|--verbose]\n"
"\t[--version]" "\n"
"\tVolumeGroupName\n"
"\t[PhysicalVolumePath...]\n",
- all_ARG, autobackup_ARG, mirrorsonly_ARG, removemissing_ARG, test_ARG)
+ all_ARG, autobackup_ARG, mirrorsonly_ARG, removemissing_ARG,
+ force_ARG, test_ARG)
xx(vgremove,
"Remove volume group(s)",
diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c
index e97cf65d..c5064166 100644
--- a/tools/lvmcmdline.c
+++ b/tools/lvmcmdline.c
@@ -710,13 +710,13 @@ static int _get_settings(struct cmd_context *cmd)
cmd->current_settings.archive = arg_int_value(cmd, autobackup_ARG, cmd->current_settings.archive);
cmd->current_settings.backup = arg_int_value(cmd, autobackup_ARG, cmd->current_settings.backup);
cmd->current_settings.cache_vgmetadata = cmd->command->flags & CACHE_VGMETADATA ? 1 : 0;
+ cmd->partial_activate = 0;
if (arg_count(cmd, partial_ARG)) {
- init_partial(1);
+ cmd->partial_activate = 1;
log_print("Partial mode. Incomplete volume groups will "
"be activated read-only.");
- } else
- init_partial(0);
+ }
if (arg_count(cmd, ignorelockingfailure_ARG))
init_ignorelockingfailure(1);
@@ -826,6 +826,7 @@ static void _apply_settings(struct cmd_context *cmd)
cmd->fmt = arg_ptr_value(cmd, metadatatype_ARG,
cmd->current_settings.fmt);
+ cmd->handles_missing_pvs = 0;
}
static char *_copy_command_line(struct cmd_context *cmd, int argc, char **argv)
diff --git a/tools/lvremove.c b/tools/lvremove.c
index 703cf143..24278b25 100644
--- a/tools/lvremove.c
+++ b/tools/lvremove.c
@@ -31,6 +31,8 @@ int lvremove(struct cmd_context *cmd, int argc, char **argv)
return EINVALID_CMD_LINE;
}
+ cmd->handles_missing_pvs = 1;
+
return process_each_lv(cmd, argc, argv, LCK_VG_WRITE, NULL,
&lvremove_single);
}
diff --git a/tools/pvcreate.c b/tools/pvcreate.c
index b18609d2..b8af0e7b 100644
--- a/tools/pvcreate.c
+++ b/tools/pvcreate.c
@@ -49,7 +49,6 @@ static int pvcreate_check(struct cmd_context *cmd, const char *name,
/* FIXME Check partition type is LVM unless --force is given */
/* Is there a pv here already? */
- /* FIXME Use partial mode here? */
pv = pv_read(cmd, name, NULL, NULL, 0);
/*
@@ -272,13 +271,11 @@ static int pvcreate_validate_params(struct cmd_context *cmd,
if (arg_count(cmd, restorefile_ARG)) {
pp->restorefile = arg_str_value(cmd, restorefile_ARG, "");
/* The uuid won't already exist */
- init_partial(1);
if (!(vg = backup_read_vg(cmd, NULL, pp->restorefile))) {
log_error("Unable to read volume group from %s",
pp->restorefile);
return 0;
}
- init_partial(0);
if (!(existing_pv = find_pv_in_vg_by_uuid(vg, pp->idp))) {
log_error("Can't find uuid %s in backup file %s",
uuid, pp->restorefile);
diff --git a/tools/vgcfgbackup.c b/tools/vgcfgbackup.c
index 6418e04d..7a67e6a3 100644
--- a/tools/vgcfgbackup.c
+++ b/tools/vgcfgbackup.c
@@ -96,8 +96,7 @@ int vgcfgbackup(struct cmd_context *cmd, int argc, char **argv)
int ret;
char *last_filename = NULL;
- if (partial_mode())
- init_pvmove(1);
+ init_pvmove(1);
ret = process_each_vg(cmd, argc, argv, LCK_VG_READ, 0, &last_filename,
&vg_backup_single);
diff --git a/tools/vgreduce.c b/tools/vgreduce.c
index c0747bfc..a32aa505 100644
--- a/tools/vgreduce.c
+++ b/tools/vgreduce.c
@@ -16,7 +16,7 @@
#include "tools.h"
#include "lv_alloc.h"
-static int _remove_pv(struct volume_group *vg, struct pv_list *pvl)
+static int _remove_pv(struct volume_group *vg, struct pv_list *pvl, int silent)
{
char uuid[64] __attribute((aligned(8)));
@@ -31,8 +31,9 @@ static int _remove_pv(struct volume_group *vg, struct pv_list *pvl)
log_verbose("Removing PV with UUID %s from VG %s", uuid, vg->name);
if (pvl->pv->pe_alloc_count) {
- log_error("LVs still present on PV with UUID %s: Can't remove "
- "from VG %s", uuid, vg->name);
+ if (!silent)
+ log_error("LVs still present on PV with UUID %s: "
+ "Can't remove from VG %s", uuid, vg->name);
return 0;
}
@@ -130,11 +131,39 @@ static int _remove_lv(struct cmd_context *cmd, struct logical_volume *lv,
return 1;
}
+static int _consolidate_vg(struct cmd_context *cmd, struct volume_group *vg)
+{
+ struct pv_list *pvl;
+ struct lv_list *lvl;
+ int r = 1;
+
+ list_iterate_items(lvl, &vg->lvs)
+ if (lvl->lv->status & PARTIAL_LV) {
+ log_warn("WARNING: Partial LV %s needs to be repaired "
+ "or removed. ", lvl->lv->name);
+ r = 0;
+ }
+
+ if (!r) {
+ cmd->handles_missing_pvs = 1;
+ log_warn("WARNING: There are still partial LVs in VG %s.", vg->name);
+ log_warn("To remove them unconditionally use: vgreduce --removemissing --force.");
+ log_warn("Proceeding to remove empty missing PVs.");
+ }
+
+ list_iterate_items(pvl, &vg->pvs) {
+ if (pvl->pv->dev && !(pvl->pv->status & MISSING_PV))
+ continue;
+ if (r && !_remove_pv(vg, pvl, 0))
+ return_0;
+ }
+
+ return r;
+}
+
static int _make_vg_consistent(struct cmd_context *cmd, struct volume_group *vg)
{
- struct list *pvh, *pvht;
struct list *lvh, *lvht;
- struct pv_list *pvl;
struct lv_list *lvl, *lvl2, *lvlt;
struct logical_volume *lv;
struct physical_volume *pv;
@@ -182,20 +211,8 @@ static int _make_vg_consistent(struct cmd_context *cmd, struct volume_group *vg)
return 0;
}
- /* Remove missing PVs */
- list_iterate_safe(pvh, pvht, &vg->pvs) {
- pvl = list_item(pvh, struct pv_list);
- if (pvl->pv->dev)
- continue;
- if (!_remove_pv(vg, pvl))
- return_0;
- }
-
- /* VG is now consistent */
- vg->status &= ~PARTIAL_VG;
- vg->status |= LVM_WRITE;
-
- init_partial(0);
+ if (!_consolidate_vg(cmd, vg))
+ return_0;
/* FIXME Recovery. For now people must clean up by hand. */
@@ -208,14 +225,11 @@ static int _make_vg_consistent(struct cmd_context *cmd, struct volume_group *vg)
if (!test_mode()) {
/* Suspend lvs_changed */
- init_partial(1);
if (!suspend_lvs(cmd, &lvs_changed)) {
stack;
- init_partial(0);
vg_revert(vg);
return 0;
}
- init_partial(0);
}
if (!vg_commit(vg)) {
@@ -439,26 +453,26 @@ int vgreduce(struct cmd_context *cmd, int argc, char **argv)
char *vg_name;
int ret = 1;
int consistent = 1;
+ int fixed = 1;
+ int repairing = arg_count(cmd, removemissing_ARG);
- if (!argc && !arg_count(cmd, removemissing_ARG)) {
+ if (!argc && !repairing) {
log_error("Please give volume group name and "
"physical volume paths");
return EINVALID_CMD_LINE;
}
-
- if (!argc && arg_count(cmd, removemissing_ARG)) {
+
+ if (!argc && repairing) {
log_error("Please give volume group name");
return EINVALID_CMD_LINE;
}
- if (arg_count(cmd, mirrorsonly_ARG) &&
- !arg_count(cmd, removemissing_ARG)) {
+ if (arg_count(cmd, mirrorsonly_ARG) && !repairing) {
log_error("--mirrorsonly requires --removemissing");
return EINVALID_CMD_LINE;
}
- if (argc == 1 && !arg_count(cmd, all_ARG)
- && !arg_count(cmd, removemissing_ARG)) {
+ if (argc == 1 && !arg_count(cmd, all_ARG) && !repairing) {
log_error("Please enter physical volume paths or option -a");
return EINVALID_CMD_LINE;
}
@@ -469,7 +483,7 @@ int vgreduce(struct cmd_context *cmd, int argc, char **argv)
return EINVALID_CMD_LINE;
}
- if (argc > 1 && arg_count(cmd, removemissing_ARG)) {
+ if (argc > 1 && repairing) {
log_error("Please only specify the volume group");
return EINVALID_CMD_LINE;
}
@@ -490,8 +504,8 @@ int vgreduce(struct cmd_context *cmd, int argc, char **argv)
return ECMD_FAILED;
}
- if ((!(vg = vg_read(cmd, vg_name, NULL, &consistent)) || !consistent) &&
- !arg_count(cmd, removemissing_ARG)) {
+ if ((!(vg = vg_read(cmd, vg_name, NULL, &consistent)) || !consistent)
+ && !repairing) {
log_error("Volume group \"%s\" doesn't exist", vg_name);
unlock_vg(cmd, vg_name);
return ECMD_FAILED;
@@ -502,16 +516,15 @@ int vgreduce(struct cmd_context *cmd, int argc, char **argv)
return ECMD_FAILED;
}
- if (arg_count(cmd, removemissing_ARG)) {
- if (vg && consistent) {
+ if (repairing) {
+ if (vg && consistent && !vg_missing_pv_count(vg)) {
log_error("Volume group \"%s\" is already consistent",
vg_name);
unlock_vg(cmd, vg_name);
return ECMD_PROCESSED;
}
- init_partial(1);
- consistent = 0;
+ consistent = !arg_count(cmd, force_ARG);
if (!(vg = vg_read(cmd, vg_name, NULL, &consistent))) {
log_error("Volume group \"%s\" not found", vg_name);
unlock_vg(cmd, vg_name);
@@ -522,16 +535,17 @@ int vgreduce(struct cmd_context *cmd, int argc, char **argv)
return ECMD_FAILED;
}
if (!archive(vg)) {
- init_partial(0);
unlock_vg(cmd, vg_name);
return ECMD_FAILED;
}
- if (!_make_vg_consistent(cmd, vg)) {
- init_partial(0);
- unlock_vg(cmd, vg_name);
- return ECMD_FAILED;
- }
+ if (arg_count(cmd, force_ARG)) {
+ if (!_make_vg_consistent(cmd, vg)) {
+ unlock_vg(cmd, vg_name);
+ return ECMD_FAILED;
+ }
+ } else
+ fixed = _consolidate_vg(cmd, vg);
if (!vg_write(vg) || !vg_commit(vg)) {
log_error("Failed to write out a consistent VG for %s",
@@ -542,7 +556,9 @@ int vgreduce(struct cmd_context *cmd, int argc, char **argv)
backup(vg);
- log_print("Wrote out consistent volume group %s", vg_name);
+ if (fixed)
+ log_print("Wrote out consistent volume group %s",
+ vg_name);
} else {
if (!vg_check_status(vg, EXPORTED_VG | LVM_WRITE | RESIZEABLE_VG)) {