1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-22 17:35:59 +03:00

Improve the way VGs with PVs missing are handled so manual intervention

is required in fewer circumstances.  (mornfall)
This commit is contained in:
Alasdair Kergon 2008-09-19 06:42:00 +00:00
parent 09a2dff8de
commit 95d43e17b3
31 changed files with 430 additions and 180 deletions

View File

@ -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.

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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,

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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))

View File

@ -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);

View File

@ -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))

View File

@ -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;
}

View File

@ -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",

View File

@ -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;
}

View File

@ -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.
*/

View File

@ -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;

View File

@ -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 */

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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;

View File

@ -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 */

View File

@ -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] = '-';

View File

@ -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.

View File

@ -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

View File

@ -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)",

View File

@ -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)

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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)) {