master - lvmcache: process duplicate PVs directly
by David Teigland
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=d3d13e134af156...
Commit: d3d13e134af15611c3f12107aa1627b12110a974
Parent: 8b7a78c728be3b9af698dae9344d01752c4cf615
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Thu Feb 11 12:37:36 2016 -0600
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Fri May 6 09:00:00 2016 -0500
lvmcache: process duplicate PVs directly
Previously, duplicate PVs were processed as a side effect
of processing the "chosen" PV in lvmcache. The duplicate
PV would be hacked into lvmcache temporarily in place of
the chosen PV.
In the old way, we had to always process the "chosen" PV
device, even if a duplicate of it was named on the command
line. This meant we were processing a different device than
was asked for. This could be worked around by naming
multiple duplicate devs on the command line in which case
they were swapped in and out of lvmcache for processing.
Now, the duplicate devs are processed directly in their
own processing loop. This means we can remove the old
hacks related to processing dups as a side effect of
processing the chosen device. We can now simply process
the device that was named on the command line.
When the same PVID exists on two or more devices, one device
is preferred and used in the VG, and the others are duplicates
and are not used in the VG. The preferred device exists in
lvmcache as usual. The duplicates exist in a specical list
of unused duplicate devices.
The duplicate devs have the "d" attribute and the "duplicate"
reporting field displays "duplicate" for them.
'pvs' warns about duplicates, but the formal output only
includes the single preferred PV.
'pvs -a' has the same warnings, and the duplicate devs are
included in the output.
'pvs <path>' has the same warnings, and displays the named
device, whether it is preferred or a duplicate.
---
lib/cache/lvmcache.c | 41 +++++-----
lib/cache/lvmcache.h | 6 +-
tools/commands.h | 4 +-
tools/pvchange.c | 17 ++++
tools/toollib.c | 226 ++++++++++++++++++++++++++++----------------------
tools/tools.h | 2 +
6 files changed, 172 insertions(+), 124 deletions(-)
diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c
index 14ce68b..ba87312 100644
--- a/lib/cache/lvmcache.c
+++ b/lib/cache/lvmcache.c
@@ -444,6 +444,21 @@ int lvmcache_found_duplicate_pvs(void)
return _found_duplicate_pvs;
}
+int lvmcache_get_unused_duplicate_devs(struct cmd_context *cmd, struct dm_list *head)
+{
+ struct device_list *devl, *devl2;
+
+ dm_list_iterate_items(devl, &_unused_duplicate_devs) {
+ if (!(devl2 = dm_pool_alloc(cmd->mem, sizeof(*devl2)))) {
+ log_error("device_list element allocation failed");
+ return 0;
+ }
+ devl2->dev = devl->dev;
+ dm_list_add(head, &devl2->list);
+ }
+ return 1;
+}
+
static void _vginfo_attach_info(struct lvmcache_vginfo *vginfo,
struct lvmcache_info *info)
{
@@ -683,6 +698,11 @@ struct lvmcache_info *lvmcache_info_from_pvid(const char *pvid, int valid_only)
return info;
}
+const struct format_type *lvmcache_fmt_from_info(struct lvmcache_info *info)
+{
+ return info->fmt;
+}
+
const char *lvmcache_vgname_from_info(struct lvmcache_info *info)
{
if (info->vginfo)
@@ -1868,27 +1888,6 @@ int lvmcache_update_vg(struct volume_group *vg, unsigned precommitted)
}
/*
- * Replace pv->dev with dev so that dev will appear for reporting.
- */
-
-void lvmcache_replace_dev(struct cmd_context *cmd, struct physical_volume *pv,
- struct device *dev)
-{
- struct lvmcache_info *info;
- char pvid_s[ID_LEN + 1] __attribute__((aligned(8)));
-
- strncpy(pvid_s, (char *) &pv->id, sizeof(pvid_s) - 1);
- pvid_s[sizeof(pvid_s) - 1] = '\0';
-
- if (!(info = lvmcache_info_from_pvid(pvid_s, 0)))
- return;
-
- info->dev = dev;
- info->label->dev = dev;
- pv->dev = dev;
-}
-
-/*
* We can see multiple different devices with the
* same pvid, i.e. duplicates.
*
diff --git a/lib/cache/lvmcache.h b/lib/cache/lvmcache.h
index c964aec..9b3c67b 100644
--- a/lib/cache/lvmcache.h
+++ b/lib/cache/lvmcache.h
@@ -111,6 +111,7 @@ const char *lvmcache_pvid_from_devname(struct cmd_context *cmd,
const char *devname);
char *lvmcache_vgname_from_pvid(struct cmd_context *cmd, const char *pvid);
const char *lvmcache_vgname_from_info(struct lvmcache_info *info);
+const struct format_type *lvmcache_fmt_from_info(struct lvmcache_info *info);
int lvmcache_vgs_locked(void);
int lvmcache_vgname_is_locked(const char *vgname);
@@ -193,11 +194,10 @@ unsigned lvmcache_mda_count(struct lvmcache_info *info);
int lvmcache_vgid_is_cached(const char *vgid);
uint64_t lvmcache_smallest_mda_size(struct lvmcache_info *info);
-void lvmcache_replace_dev(struct cmd_context *cmd, struct physical_volume *pv,
- struct device *dev);
-
int lvmcache_found_duplicate_pvs(void);
+int lvmcache_get_unused_duplicate_devs(struct cmd_context *cmd, struct dm_list *head);
+
int vg_has_duplicate_pvs(struct volume_group *vg);
int lvmcache_contains_lock_type_sanlock(struct cmd_context *cmd);
diff --git a/tools/commands.h b/tools/commands.h
index f49e57f..7b32835 100644
--- a/tools/commands.h
+++ b/tools/commands.h
@@ -824,7 +824,7 @@ xx(pvdata,
xx(pvdisplay,
"Display various attributes of physical volume(s)",
- CACHE_VGMETADATA | PERMITTED_READ_ONLY | ENABLE_ALL_DEVS | LOCKD_VG_SH,
+ CACHE_VGMETADATA | PERMITTED_READ_ONLY | ENABLE_ALL_DEVS | ENABLE_DUPLICATE_DEVS | LOCKD_VG_SH,
"pvdisplay\n"
"\t[-c|--colon]\n"
"\t[--commandprofile ProfileName]\n"
@@ -933,7 +933,7 @@ xx(pvremove,
xx(pvs,
"Display information about physical volumes",
- CACHE_VGMETADATA | PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT | ENABLE_ALL_DEVS | LOCKD_VG_SH,
+ CACHE_VGMETADATA | PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT | ENABLE_ALL_DEVS | ENABLE_DUPLICATE_DEVS | LOCKD_VG_SH,
"pvs\n"
"\t[-a|--all]\n"
"\t[--aligned]\n"
diff --git a/tools/pvchange.c b/tools/pvchange.c
index e103ebe..e100b80 100644
--- a/tools/pvchange.c
+++ b/tools/pvchange.c
@@ -40,6 +40,23 @@ static int _pvchange_single(struct cmd_context *cmd, struct volume_group *vg,
goto bad;
}
+ /*
+ * The primary location of this check is in vg_write(), but it needs
+ * to be copied here to prevent the pv_write() which is called before
+ * the vg_write().
+ */
+ if (vg && lvmcache_found_duplicate_pvs() && vg_has_duplicate_pvs(vg)) {
+ if (!find_config_tree_bool(vg->cmd, devices_allow_changes_with_duplicate_pvs_CFG, NULL)) {
+ log_error("Cannot update volume group %s with duplicate PV devices.",
+ vg->name);
+ goto bad;
+ }
+ if (arg_count(cmd, uuid_ARG)) {
+ log_error("Resolve duplicate PV UUIDs with vgimportclone (or filters).");
+ goto bad;
+ }
+ }
+
/* If in a VG, must change using volume group. */
if (!is_orphan(pv)) {
if (tagargs && !(vg->fid->fmt->features & FMT_TAGS)) {
diff --git a/tools/toollib.c b/tools/toollib.c
index 9917a67..429ab91 100644
--- a/tools/toollib.c
+++ b/tools/toollib.c
@@ -2928,18 +2928,6 @@ static struct device_id_list *_device_list_find_dev(struct dm_list *devices, str
return NULL;
}
-static struct device_id_list *_device_list_find_pvid(struct dm_list *devices, struct physical_volume *pv)
-{
- struct device_id_list *dil;
-
- dm_list_iterate_items(dil, devices) {
- if (id_equal((struct id *) dil->pvid, &pv->id))
- return dil;
- }
-
- return NULL;
-}
-
static int _device_list_copy(struct cmd_context *cmd, struct dm_list *src, struct dm_list *dst)
{
struct device_id_list *dil;
@@ -3030,6 +3018,94 @@ static int _process_device_list(struct cmd_context *cmd, struct dm_list *all_dev
return ECMD_PROCESSED;
}
+static int _process_duplicate_pvs(struct cmd_context *cmd,
+ struct dm_list *all_devices,
+ struct dm_list *arg_devices,
+ int process_all_devices,
+ struct processing_handle *handle,
+ process_single_pv_fn_t process_single_pv)
+{
+ struct physical_volume pv_dummy;
+ struct physical_volume *pv;
+ struct device_id_list *dil;
+ struct device_list *devl;
+ struct dm_list unused_duplicate_devs;
+ struct lvmcache_info *info;
+ struct volume_group *vg = NULL;
+ const char *vgname = NULL;
+ const char *vgid = NULL;
+ int ret_max = ECMD_PROCESSED;
+ int ret = 0;
+
+ dm_list_init(&unused_duplicate_devs);
+
+ if (!lvmcache_get_unused_duplicate_devs(cmd, &unused_duplicate_devs))
+ return_ECMD_FAILED;
+
+ dm_list_iterate_items(devl, &unused_duplicate_devs) {
+ /* Duplicates are displayed if -a is used or the dev is named as an arg. */
+
+ _device_list_remove(all_devices, devl->dev);
+
+ if (!process_all_devices && dm_list_empty(arg_devices))
+ continue;
+
+ if ((dil = _device_list_find_dev(arg_devices, devl->dev)))
+ _device_list_remove(arg_devices, devl->dev);
+
+ if (!process_all_devices && !dil)
+ continue;
+
+ if (!(cmd->command->flags & ENABLE_DUPLICATE_DEVS))
+ continue;
+
+ /*
+ * Use the cached VG from the preferred device for the PV,
+ * the vg is only used to display the VG name.
+ *
+ * This VG from lvmcache was not read from the duplicate
+ * dev being processed here, but from the preferred dev
+ * in lvmcache.
+ *
+ * When a duplicate PV is displayed, the reporting fields
+ * that come from the VG metadata are not shown, because
+ * the dev is not a part of the VG, the dev for the
+ * preferred PV is (also the VG metadata in lvmcache is
+ * not from the duplicate dev, but from the preferred dev).
+ */
+
+ log_very_verbose("Processing duplicate device %s.", dev_name(devl->dev));
+
+ info = lvmcache_info_from_pvid(devl->dev->pvid, 0);
+ if (info)
+ vgname = lvmcache_vgname_from_info(info);
+ if (vgname)
+ vgid = lvmcache_vgid_from_vgname(cmd, vgname);
+ if (vgid)
+ vg = lvmcache_get_vg(cmd, vgname, vgid, 0);
+
+ memset(&pv_dummy, 0, sizeof(pv_dummy));
+ dm_list_init(&pv_dummy.tags);
+ dm_list_init(&pv_dummy.segments);
+ pv_dummy.dev = devl->dev;
+ pv_dummy.fmt = lvmcache_fmt_from_info(info);
+ pv = &pv_dummy;
+
+ ret = process_single_pv(cmd, vg, pv, handle);
+
+ if (vg)
+ release_vg(vg);
+
+ if (ret > ret_max)
+ ret_max = ret;
+
+ if (sigint_caught())
+ return_ECMD_FAILED;
+ }
+
+ return ECMD_PROCESSED;
+}
+
static int _process_pvs_in_vg(struct cmd_context *cmd,
struct volume_group *vg,
struct dm_list *all_devices,
@@ -3045,7 +3121,6 @@ static int _process_pvs_in_vg(struct cmd_context *cmd,
struct physical_volume *pv;
struct pv_list *pvl;
struct device_id_list *dil;
- struct device *dev_orig;
const char *pv_name;
int selected;
int process_pv;
@@ -3082,14 +3157,6 @@ static int _process_pvs_in_vg(struct cmd_context *cmd,
_device_list_remove(arg_devices, dil->dev);
}
- /* Select the PV if the device arg has the same pvid. */
-
- if (!process_pv && !dm_list_empty(arg_devices) &&
- (dil = _device_list_find_pvid(arg_devices, pv))) {
- process_pv = 1;
- _device_list_remove(arg_devices, dil->dev);
- }
-
if (!process_pv && !dm_list_empty(arg_tags) &&
str_list_match_list(arg_tags, &pv->tags, NULL))
process_pv = 1;
@@ -3121,83 +3188,6 @@ static int _process_pvs_in_vg(struct cmd_context *cmd,
if (ret > ret_max)
ret_max = ret;
}
-
- /*
- * We have processed the PV on device pv->dev. Now
- * deal with any duplicates of this PV on other
- * devices.
- */
-
- /*
- * This is a very rare and obscure case where multiple
- * duplicate devices are specified on the command line
- * referring to this PV. In this case we want to
- * process this PV once for each specified device.
- */
- if (!skip && !dm_list_empty(arg_devices)) {
- while ((dil = _device_list_find_pvid(arg_devices, pv))) {
- _device_list_remove(arg_devices, dil->dev);
-
- /*
- * Replace pv->dev with this dil->dev
- * in lvmcache so the duplicate dev
- * info will be reported. FIXME: it
- * would be nicer to override pv->dev
- * without munging lvmcache content.
- */
- dev_orig = pv->dev;
- lvmcache_replace_dev(cmd, pv, dil->dev);
-
- log_very_verbose("Processing PV %s device %s in VG %s.",
- pv_name, dev_name(dil->dev), vg->name);
-
- ret = process_single_pv(cmd, vg, pv, handle);
- if (ret != ECMD_PROCESSED)
- stack;
- if (ret > ret_max)
- ret_max = ret;
-
- /* Put the cache state back as it was. */
- lvmcache_replace_dev(cmd, pv, dev_orig);
- }
- }
-
- /*
- * This is another rare and obscure case where multiple
- * duplicate devices are being displayed by pvs -a, and
- * we want each of them to be displayed in the context
- * of this VG, so that this VG name appears next to it.
- */
- if (process_all_devices && lvmcache_found_duplicate_pvs()) {
- while ((dil = _device_list_find_pvid(all_devices, pv))) {
- _device_list_remove(all_devices, dil->dev);
-
- dev_orig = pv->dev;
- lvmcache_replace_dev(cmd, pv, dil->dev);
-
- ret = process_single_pv(cmd, vg, pv, handle);
- if (ret != ECMD_PROCESSED)
- stack;
- if (ret > ret_max)
- ret_max = ret;
-
- lvmcache_replace_dev(cmd, pv, dev_orig);
- }
- }
-
- /*
- * Remove any duplicates of the processed device from
- * the list of all devices. If they were left in the
- * list of all devices, they would be considered
- * "missed" at the end.
- */
- if (process_all_pvs && lvmcache_found_duplicate_pvs()) {
- while ((dil = _device_list_find_pvid(all_devices, pv))) {
- log_very_verbose("Skip duplicate device %s of processed device %s",
- dev_name(dil->dev), dev_name(pv->dev));
- _device_list_remove(all_devices, dil->dev);
- }
- }
}
/*
@@ -3410,6 +3400,46 @@ int process_each_pv(struct cmd_context *cmd,
ret_max = ret;
/*
+ * Process the list of unused duplicate devs so they can be shown by
+ * report/display commands. These are the devices that were not chosen
+ * to be used in lvmcache because another device with the same PVID was
+ * preferred. The unused duplicate devs are not seen by
+ * _process_pvs_in_vgs, which only sees the preferred device for the
+ * PVID.
+ *
+ * The main purpose in reporting/displaying the unused duplicate PVs
+ * here is so that they do not appear to be unused/free devices or
+ * orphans.
+ *
+ * We do not allow modifying the unused duplicate PVs. To modify a
+ * non-preferred duplicate PV, e.g. pvchange -u, a filter needs to be
+ * used with the command to exclude the other devices with the same
+ * PVID. This results in the command seeing only the one device with
+ * the PVID and allows it to be changed. (If the duplicates actually
+ * represent the same underlying storage, these precautions are
+ * unnecessary, but lvm can't tell when the duplicates are different
+ * paths to the same storage or different underlying storage.)
+ *
+ * Even the preferred duplicate PV in lvmcache is limitted from being
+ * modified (by allow_changes_with_duplicate_pvs setting), because lvm
+ * cannot be sure that the preferred duplicate device is the correct one,
+ * e.g. if a VG has two PVs, and both PVs are cloned, lvm might prefer
+ * one of the original PVs and one of the cloned PVs, pairing them
+ * together as the VG. Any changes on the VG or PVs in that state would
+ * end up changing one of the original PVs and one of the cloned PVs.
+ *
+ * vgimportclone of the two cloned PVs changes their PV UUIDs and gives
+ * them a new VG name.
+ */
+
+ ret = _process_duplicate_pvs(cmd, &all_devices, &arg_devices, process_all_devices,
+ handle, process_single_pv);
+ if (ret != ECMD_PROCESSED)
+ stack;
+ if (ret > ret_max)
+ ret_max = ret;
+
+ /*
* If the orphans lock was held, there shouldn't be missed devices. If
* there were, we cannot clear the cache while holding the orphans lock
* anyway.
diff --git a/tools/tools.h b/tools/tools.h
index 7b1bda3..cfb79de 100644
--- a/tools/tools.h
+++ b/tools/tools.h
@@ -112,6 +112,8 @@ struct arg_value_group_list {
#define MUST_USE_ALL_ARGS 0x00000100
/* Command wants to control the device scan for lvmetad itself. */
#define NO_LVMETAD_AUTOSCAN 0x00000200
+/* Command should process unused duplicate devices. */
+#define ENABLE_DUPLICATE_DEVS 0x00000400
/* a register of the lvm commands */
struct command {
6 years, 10 months
master - lvmcache: improve duplicate PV handling
by David Teigland
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=8b7a78c728be3b...
Commit: 8b7a78c728be3b9af698dae9344d01752c4cf615
Parent: 67da017fd2502138d0cc469f3aea320440353156
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Tue Feb 9 13:06:27 2016 -0600
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Fri May 6 09:00:00 2016 -0500
lvmcache: improve duplicate PV handling
Wait to compare and choose alternate duplicate devices until
after all devices are scanned. During scanning, the first
duplicate dev is kept in lvmcache, and others are kept in a
new list (_found_duplicate_devs).
After all devices are scanned, compare all the duplicates
available for a given PVID and decide which is best.
If the dev used in lvmcache is changed, drop the old dev
from lvmcache entirely and rescan the replacement dev.
Previously the VG metadata from the old dev was kept in
lvmcache and only the dev was replaced.
A new config setting devices/allow_changes_with_duplicate_pvs
can be set to 0 which disallows modifying a VG or activating
LVs in it when the VG contains PVs with duplicate devices.
Set to 1 is the old behavior which allowed the VG to be
changed.
The logic for which of two devs is preferred has changed.
The primary goal is to choose a device that is currently
in use if the other isn't, e.g. by an active LV.
. prefer dev with fs mounted if the other doesn't, else
. prefer dev that is dm if the other isn't, else
. prefer dev in subsystem if the other isn't
If neither device is preferred by these rules, then don't
change devices in lvmcache, leaving the one that was found
first.
The previous logic for preferring a device was:
. prefer dev in subsystem if the other isn't, else
. prefer dev without holders if the other has holders, else
. prefer dev that is dm if the other isn't
---
lib/cache/lvmcache.c | 557 +++++++++++++++++++++++++-----------------
lib/cache/lvmcache.h | 2 +
lib/config/config_settings.h | 12 +
lib/config/defaults.h | 1 +
lib/metadata/metadata.c | 17 ++
tools/toollib.c | 9 +
6 files changed, 376 insertions(+), 222 deletions(-)
diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c
index 4e89371..14ce68b 100644
--- a/lib/cache/lvmcache.c
+++ b/lib/cache/lvmcache.c
@@ -79,6 +79,8 @@ static struct dm_hash_table *_vgid_hash = NULL;
static struct dm_hash_table *_vgname_hash = NULL;
static struct dm_hash_table *_lock_hash = NULL;
static DM_LIST_INIT(_vginfos);
+static DM_LIST_INIT(_found_duplicate_devs);
+static DM_LIST_INIT(_unused_duplicate_devs);
static int _scanning_in_progress = 0;
static int _has_scanned = 0;
static int _vgs_locked = 0;
@@ -428,6 +430,14 @@ int lvmcache_vgs_locked(void)
* When lvmcache sees a duplicate PV, this is set.
* process_each_pv() can avoid searching for duplicates
* by checking this and seeing that no duplicate PVs exist.
+ *
+ *
+ * found_duplicate_pvs tells the process_each_pv code
+ * to search the devices list for duplicates, so that
+ * devices can be processed together with their
+ * duplicates (while processing the VG, rather than
+ * reporting pv->dev under the VG, and its duplicate
+ * outside the VG context.)
*/
int lvmcache_found_duplicate_pvs(void)
{
@@ -733,8 +743,194 @@ void lvmcache_force_next_label_scan(void)
_force_label_scan = 1;
}
+/*
+ * Check if any PVs in vg->pvs have the same PVID as any
+ * entries in _unused_duplicate_devices.
+ */
+
+int vg_has_duplicate_pvs(struct volume_group *vg)
+{
+ struct pv_list *pvl;
+ struct device_list *devl;
+
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ dm_list_iterate_items(devl, &_unused_duplicate_devs) {
+ if (id_equal(&pvl->pv->id, (const struct id *)devl->dev->pvid))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Compare _found_duplicate_devs entries with the corresponding duplicate dev
+ * in lvmcache. There may be multiple duplicates in _found_duplicate_devs for
+ * a given pvid. If a dev from _found_duplicate_devs is preferred over the dev
+ * in lvmcache, then drop the dev in lvmcache and rescan the preferred dev to
+ * add it to lvmcache.
+ *
+ * _found_duplicate_devs: duplicate devs found during initial scan.
+ * These are compared to lvmcache devs to see if any are preferred.
+ *
+ * _unused_duplicate_devs: duplicate devs not chosen to be used.
+ * These are _found_duplicate_devs entries that were not chosen,
+ * or unpreferred lvmcache devs that were dropped.
+ *
+ * del_cache_devs: devices to drop from lvmcache
+ * add_cache_devs: devices to scan to add to lvmcache
+ */
+
+static void _choose_preferred_devs(struct cmd_context *cmd,
+ struct dm_list *del_cache_devs,
+ struct dm_list *add_cache_devs)
+{
+ char uuid[64] __attribute__((aligned(8)));
+ struct dm_list altdevs;
+ struct dev_types *dt = cmd->dev_types;
+ struct device_list *devl, *devl_safe, *alt, *del;
+ struct lvmcache_info *info;
+ struct device *dev1, *dev2;
+ uint32_t dev1_major, dev1_minor, dev2_major, dev2_minor;
+ int in_subsys1, in_subsys2;
+ int is_dm1, is_dm2;
+ int has_fs1, has_fs2;
+ int change;
+
+ /*
+ * Create a list of all alternate devs for the same pvid: altdevs.
+ */
+next:
+ dm_list_init(&altdevs);
+ alt = NULL;
+
+ dm_list_iterate_items_safe(devl, devl_safe, &_found_duplicate_devs) {
+ if (!alt) {
+ dm_list_move(&altdevs, &devl->list);
+ alt = devl;
+ } else {
+ if (!strcmp(alt->dev->pvid, devl->dev->pvid))
+ dm_list_move(&altdevs, &devl->list);
+ }
+ }
+
+ if (!alt)
+ return;
+
+ /*
+ * Find the device for the pvid that's currently in lvmcache.
+ */
+
+ if (!(info = lvmcache_info_from_pvid(alt->dev->pvid, 0))) {
+ /* This shouldn't happen */
+ log_warn("WARNING: PV %s on duplicate device %s not found in cache.",
+ alt->dev->pvid, dev_name(alt->dev));
+ goto next;
+ }
+
+ /*
+ * Compare devices for the given pvid to find one that's preferred.
+ * "dev1" is the currently preferred device, starting with the device
+ * currently in lvmcache.
+ */
+
+ dev1 = info->dev;
+
+ dm_list_iterate_items(devl, &altdevs) {
+ dev2 = devl->dev;
+
+ if (dev1 == dev2) {
+ /* This shouldn't happen */
+ log_warn("Same duplicate device repeated %s", dev_name(dev1));
+ continue;
+ }
+
+ dev1_major = MAJOR(dev1->dev);
+ dev1_minor = MINOR(dev1->dev);
+ dev2_major = MAJOR(dev2->dev);
+ dev2_minor = MINOR(dev2->dev);
+
+ in_subsys1 = dev_subsystem_part_major(dt, dev1);
+ in_subsys2 = dev_subsystem_part_major(dt, dev2);
+
+ is_dm1 = dm_is_dm_major(dev1_major);
+ is_dm2 = dm_is_dm_major(dev2_major);
+
+ has_fs1 = dm_device_has_mounted_fs(dev1_major, dev1_minor);
+ has_fs2 = dm_device_has_mounted_fs(dev2_major, dev2_minor);
+
+ log_debug_cache("PV %s compare duplicates %s and %s",
+ devl->dev->pvid, dev_name(dev1), dev_name(dev2));
+
+ log_debug_cache("dup dev1 %s subsys %d dm %d fs %d",
+ dev_name(dev1), in_subsys1, is_dm1, has_fs1);
+ log_debug_cache("dup dev2 %s subsys %d dm %d fs %d",
+ dev_name(dev2), in_subsys2, is_dm2, has_fs2);
+
+ change = 0;
+
+ if (has_fs1 && !has_fs2) {
+ /* keep 1 */
+ } else if (has_fs2 && !has_fs1) {
+ /* change to 2 */
+ change = 1;
+ } else if (is_dm1 && !is_dm2) {
+ /* keep 1 */
+ } else if (is_dm2 && !is_dm1) {
+ /* change to 2 */
+ change = 1;
+ } else if (in_subsys1 && !in_subsys2) {
+ /* keep 1 */
+ } else if (in_subsys2 && !in_subsys1) {
+ /* change to 2 */
+ change = 1;
+ }
+
+ if (change) {
+ dev1 = dev2;
+ alt = devl;
+ }
+ }
+
+ id_write_format((const struct id *)info->dev->pvid, uuid, sizeof(uuid));
+
+ if (dev1 != info->dev) {
+ log_warn("PV %s is using preferred device %s, changed from %s",
+ uuid, dev_name(dev1), dev_name(info->dev));
+ /*
+ * Move the preferred device from altdevs to add_cache_devs.
+ * Create a del_cache_devs entry for the current lvmcache
+ * device to drop.
+ */
+
+ dm_list_move(add_cache_devs, &alt->list);
+
+ if ((del = dm_pool_alloc(cmd->mem, sizeof(*del)))) {
+ del->dev = info->dev;
+ dm_list_add(del_cache_devs, &del->list);
+ }
+ } else {
+ log_warn("PV %s is using preferred device %s",
+ uuid, dev_name(info->dev));
+ }
+
+ /*
+ * alt devs not chosen are moved to _unused_duplicate_devs.
+ * del_cache_devs being dropped are moved to _unused_duplicate_devs
+ * after being dropped. So, _unused_duplicate_devs represents all
+ * duplicates not being used in lvmcache.
+ */
+
+ dm_list_splice(&_unused_duplicate_devs, &altdevs);
+
+ goto next;
+}
+
int lvmcache_label_scan(struct cmd_context *cmd)
{
+ struct dm_list del_cache_devs;
+ struct dm_list add_cache_devs;
+ struct lvmcache_info *info;
+ struct device_list *devl;
struct label *label;
struct dev_iter *iter;
struct device *dev;
@@ -781,6 +977,44 @@ int lvmcache_label_scan(struct cmd_context *cmd)
log_very_verbose("Scanned %d device labels", dev_count);
+ /*
+ * _choose_preferred_devs() returns:
+ *
+ * . del_cache_devs: a list of devs currently in lvmcache that should
+ * be removed from lvmcache because they will be replaced with
+ * alternative devs for the same PV.
+ *
+ * . add_cache_devs: a list of devs that are preferred over devs in
+ * lvmcache for the same PV. These devices should be rescanned to
+ * populate lvmcache from them.
+ *
+ * First remove lvmcache info for the devs to be dropped, then rescan
+ * the devs that are preferred to add them to lvmcache.
+ *
+ * Keep a complete list of all devs that are unused by moving the
+ * del_cache_devs onto _unused_duplicate_devs.
+ */
+
+ if (!dm_list_empty(&_found_duplicate_devs)) {
+ dm_list_init(&del_cache_devs);
+ dm_list_init(&add_cache_devs);
+
+ _choose_preferred_devs(cmd, &del_cache_devs, &add_cache_devs);
+
+ dm_list_iterate_items(devl, &del_cache_devs) {
+ log_debug_cache("Drop duplicate device %s in lvmcache", dev_name(devl->dev));
+ if ((info = lvmcache_info_from_pvid(devl->dev->pvid, 0)))
+ lvmcache_del(info);
+ }
+
+ dm_list_iterate_items(devl, &add_cache_devs) {
+ log_debug_cache("Rescan preferred device %s for lvmcache", dev_name(devl->dev));
+ (void) label_read(devl->dev, &label, UINT64_C(0));
+ }
+
+ dm_list_splice(&_unused_duplicate_devs, &del_cache_devs);
+ }
+
_has_scanned = 1;
/* Perform any format-specific scanning e.g. text files */
@@ -1185,26 +1419,6 @@ void lvmcache_del(struct lvmcache_info *info)
return;
}
-static int _lvmcache_update_pvid(struct lvmcache_info *info, const char *pvid)
-{
- /*
- * Nothing to do if already stored with same pvid.
- */
-
- if (((dm_hash_lookup(_pvid_hash, pvid)) == info) &&
- !strcmp(info->dev->pvid, pvid))
- return 1;
- if (*info->dev->pvid)
- dm_hash_remove(_pvid_hash, info->dev->pvid);
- strncpy(info->dev->pvid, pvid, sizeof(info->dev->pvid));
- if (!dm_hash_insert(_pvid_hash, pvid, info)) {
- log_error("_lvmcache_update: pvid insertion failed: %s", pvid);
- return 0;
- }
-
- return 1;
-}
-
/*
* vginfo must be info->vginfo unless info is NULL (orphans)
*/
@@ -1711,13 +1925,6 @@ void lvmcache_replace_dev(struct cmd_context *cmd, struct physical_volume *pv,
* duplicate of that dev that is not part of a
* subsystem.
*
- * The has_holders calls check if the device is being
- * used by another, and prefers one that's being used.
- *
- * FIXME: why do we prefer a device without holders
- * over a device with holders? We should understand
- * the reason for that choice.
- *
* FIXME: there may be other reasons to prefer one
* device over another:
*
@@ -1732,73 +1939,74 @@ void lvmcache_replace_dev(struct cmd_context *cmd, struct physical_volume *pv,
* transient duplicate?
*/
-struct lvmcache_info *lvmcache_add(struct labeller *labeller, const char *pvid,
- struct device *dev,
- const char *vgname, const char *vgid,
- uint32_t vgstatus)
+static struct lvmcache_info * _create_info(struct labeller *labeller, struct device *dev)
{
- const struct format_type *fmt = labeller->fmt;
- struct dev_types *dt = fmt->cmd->dev_types;
+ struct lvmcache_info *info;
struct label *label;
- struct lvmcache_info *existing, *info;
- char pvid_s[ID_LEN + 1] __attribute__((aligned(8)));
- struct lvmcache_vgsummary vgsummary = {
- .vgname = vgname,
- .vgstatus = vgstatus,
- };
-
- /* N.B. vgid is not NUL-terminated when called from _text_pv_write */
- if (vgid)
- strncpy((char *)&vgsummary.vgid, vgid, sizeof(vgsummary.vgid));
- if (!_vgname_hash && !lvmcache_init()) {
- log_error("Internal cache initialisation failed");
+ if (!(label = label_create(labeller)))
+ return_NULL;
+ if (!(info = dm_zalloc(sizeof(*info)))) {
+ log_error("lvmcache_info allocation failed");
+ label_destroy(label);
return NULL;
}
+ info->dev = dev;
+ info->fmt = labeller->fmt;
+
+ label->info = info;
+ info->label = label;
+
+ dm_list_init(&info->list);
+ lvmcache_del_mdas(info);
+ lvmcache_del_das(info);
+ lvmcache_del_bas(info);
+
+ return info;
+}
+
+struct lvmcache_info *lvmcache_add(struct labeller *labeller,
+ const char *pvid, struct device *dev,
+ const char *vgname, const char *vgid, uint32_t vgstatus)
+{
+ char pvid_s[ID_LEN + 1] __attribute__((aligned(8)));
+ char uuid[64] __attribute__((aligned(8)));
+ struct lvmcache_vgsummary vgsummary = { 0 };
+ const struct format_type *fmt = labeller->fmt;
+ struct lvmcache_info *info;
+ struct lvmcache_info *info_lookup;
+ struct device_list *devl;
+ int created = 0;
+
strncpy(pvid_s, pvid, sizeof(pvid_s) - 1);
pvid_s[sizeof(pvid_s) - 1] = '\0';
+ id_write_format((const struct id *)&pvid_s, uuid, sizeof(uuid));
- if (!(existing = lvmcache_info_from_pvid(pvid_s, 0)) &&
- !(existing = lvmcache_info_from_pvid(dev->pvid, 0))) {
- if (!(label = label_create(labeller)))
- return_NULL;
- if (!(info = dm_zalloc(sizeof(*info)))) {
- log_error("lvmcache_info allocation failed");
- label_destroy(label);
- return NULL;
- }
+ /*
+ * Find existing info struct in _pvid_hash or create a new one.
+ */
- label->info = info;
- info->label = label;
- dm_list_init(&info->list);
- info->dev = dev;
+ info = lvmcache_info_from_pvid(pvid_s, 0);
- lvmcache_del_mdas(info);
- lvmcache_del_das(info);
- lvmcache_del_bas(info);
- } else {
- if (existing->dev != dev) {
- int old_in_subsystem = 0;
- int new_in_subsystem = 0;
- int old_is_dm = 0;
- int new_is_dm = 0;
- int old_has_holders = 0;
- int new_has_holders = 0;
+ if (!info)
+ info = lvmcache_info_from_pvid(dev->pvid, 0);
- /*
- * Here are different devices with the same pvid:
- * duplicates. See comment above.
- */
+ if (!info) {
+ info = _create_info(labeller, dev);
+ created = 1;
+ }
- /*
- * This flag tells the process_each_pv code to search
- * the devices list for duplicates, so that devices
- * can be processed together with their duplicates
- * (while processing the VG, rather than reporting
- * pv->dev under the VG, and its duplicate outside
- * the VG context.)
- */
+ if (!info)
+ return_NULL;
+
+ /*
+ * If an existing info struct was found, check if any values are new.
+ */
+ if (!created) {
+ if (info->dev != dev) {
+ log_warn("WARNING: PV %s on %s was already found on %s.",
+ uuid, dev_name(dev), dev_name(info->dev));
if (!_found_duplicate_pvs && lvmetad_used()) {
log_warn("WARNING: Disabling lvmetad cache which does not support duplicate PVs.");
@@ -1806,174 +2014,75 @@ struct lvmcache_info *lvmcache_add(struct labeller *labeller, const char *pvid,
}
_found_duplicate_pvs = 1;
- /*
- * The new dev may not have pvid set.
- * The process_each_pv code needs to have the pvid
- * set in each device to detect that the devices
- * are duplicates.
- */
strncpy(dev->pvid, pvid_s, sizeof(dev->pvid));
/*
- * Now decide if we are going to ignore the new
- * device, or replace the existing/old device in
- * lvmcache with the new one.
+ * Keep the existing PV/dev in lvmcache, and save the
+ * new duplicate in the list of duplicates. After
+ * scanning is complete, compare the duplicate devs
+ * with those in lvmcache to check if one of the
+ * duplicates is preferred and if so switch lvmcache to
+ * use it.
*/
- old_in_subsystem = dev_subsystem_part_major(dt, existing->dev);
- new_in_subsystem = dev_subsystem_part_major(dt, dev);
-
- old_is_dm = dm_is_dm_major(MAJOR(existing->dev->dev));
- new_is_dm = dm_is_dm_major(MAJOR(dev->dev));
-
- old_has_holders = dm_device_has_holders(MAJOR(existing->dev->dev), MINOR(existing->dev->dev));
- new_has_holders = dm_device_has_holders(MAJOR(dev->dev), MINOR(dev->dev));
-
- if (old_has_holders && new_has_holders) {
- /*
- * This is not a selection of old or new, but
- * just a warning to be aware of.
- */
- log_warn("WARNING: duplicate PV %s is being used from both devices %s and %s",
- pvid_s,
- dev_name(existing->dev),
- dev_name(dev));
- }
- if (existing->vginfo->preferred_duplicates) {
- /*
- * The preferred duplicate devs have already
- * been chosen during a previous populating of
- * lvmcache, so just use the existing preferences.
- */
- log_warn("Found duplicate PV %s: using existing dev %s",
- pvid_s,
- dev_name(existing->dev));
- return NULL;
- }
+ if (!(devl = dm_pool_alloc(fmt->cmd->mem, sizeof(*devl))))
+ return_NULL;
+ devl->dev = dev;
- if (old_in_subsystem && !new_in_subsystem) {
- /* Use old, ignore new. */
- log_warn("Found duplicate PV %s: using %s not %s",
- pvid_s,
- dev_name(existing->dev),
- dev_name(dev));
- log_warn("Using duplicate PV %s from subsystem %s, ignoring %s",
- dev_name(existing->dev),
- dev_subsystem_name(dt, existing->dev),
- dev_name(dev));
- return NULL;
-
- } else if (!old_in_subsystem && new_in_subsystem) {
- /* Use new, replace old. */
- log_warn("Found duplicate PV %s: using %s not %s",
- pvid_s,
- dev_name(dev),
- dev_name(existing->dev));
- log_warn("Using duplicate PV %s from subsystem %s, replacing %s",
- dev_name(dev),
- dev_subsystem_name(dt, dev),
- dev_name(existing->dev));
-
- } else if (old_has_holders && !new_has_holders) {
- /* Use new, replace old. */
- /* FIXME: why choose the one without olders? */
- log_warn("Found duplicate PV %s: using %s not %s",
- pvid_s,
- dev_name(dev),
- dev_name(existing->dev));
- log_warn("Using duplicate PV %s without holders, replacing %s",
- dev_name(dev),
- dev_name(existing->dev));
-
- } else if (!old_has_holders && new_has_holders) {
- /* Use old, ignore new. */
- log_warn("Found duplicate PV %s: using %s not %s",
- pvid_s,
- dev_name(existing->dev),
- dev_name(dev));
- log_warn("Using duplicate PV %s without holders, ignoring %s",
- dev_name(existing->dev),
- dev_name(dev));
- return NULL;
-
- } else if (old_is_dm && new_is_dm) {
- /* Use new, replace old. */
- /* FIXME: why choose the new instead of the old? */
- log_warn("Found duplicate PV %s: using %s not %s",
- pvid_s,
- dev_name(dev),
- dev_name(existing->dev));
- log_warn("Using duplicate PV %s which is last seen, replacing %s",
- dev_name(dev),
- dev_name(existing->dev));
-
- } else if (!strcmp(pvid_s, existing->dev->pvid)) {
- /* No criteria to use for preferring old or new. */
- /* FIXME: why choose the new instead of the old? */
- /* FIXME: a transient duplicate would be a reason
- * to select the old instead of the new. */
- log_warn("Found duplicate PV %s: using %s not %s",
- pvid_s,
- dev_name(dev),
- dev_name(existing->dev));
- log_warn("Using duplicate PV %s which is last seen, replacing %s",
- dev_name(dev),
- dev_name(existing->dev));
- }
- } else {
- /*
- * The new dev is the same as the existing dev.
- *
- * FIXME: Why can't we just return NULL here if the
- * device already exists? Things don't seem to work
- * if we do that for some reason.
- */
- log_debug_cache("Found same device %s with same pvid %s.",
- dev_name(existing->dev), pvid_s);
+ dm_list_add(&_found_duplicate_devs, &devl->list);
+ return NULL;
}
- /*
- * This happens when running pvcreate on an existing PV.
- */
- if (strcmp(pvid_s, existing->dev->pvid)) {
- log_verbose("Replacing dev %s pvid %s with dev %s pvid %s",
- dev_name(existing->dev), existing->dev->pvid,
- dev_name(dev), pvid_s);
+ if (info->dev->pvid[0] && pvid[0] && strcmp(pvid_s, info->dev->pvid)) {
+ /* This happens when running pvcreate on an existing PV. */
+ log_verbose("Changing pvid on dev %s from %s to %s",
+ dev_name(info->dev), info->dev->pvid, pvid_s);
}
- /*
- * Switch over to new preferred device.
- */
- existing->dev = dev;
- info = existing;
- /* Has labeller changed? */
if (info->label->labeller != labeller) {
+ log_verbose("Changing labeller on dev %s from %s to %s",
+ dev_name(info->dev),
+ info->label->labeller->fmt->name,
+ labeller->fmt->name);
label_destroy(info->label);
if (!(info->label = label_create(labeller)))
- /* FIXME leaves info without label! */
return_NULL;
info->label->info = info;
}
- label = info->label;
}
- info->fmt = labeller->fmt;
info->status |= CACHE_INVALID;
- if (!_lvmcache_update_pvid(info, pvid_s)) {
- if (!existing) {
- dm_free(info);
- label_destroy(label);
- }
+ /*
+ * Add or update the _pvid_hash mapping, pvid to info.
+ */
+
+ info_lookup = dm_hash_lookup(_pvid_hash, pvid_s);
+ if ((info_lookup == info) && !strcmp(info->dev->pvid, pvid_s))
+ goto update_vginfo;
+
+ if (info->dev->pvid[0])
+ dm_hash_remove(_pvid_hash, info->dev->pvid);
+
+ strncpy(info->dev->pvid, pvid_s, sizeof(info->dev->pvid));
+
+ if (!dm_hash_insert(_pvid_hash, pvid_s, info)) {
+ log_error("Adding pvid to hash failed %s", pvid_s);
return NULL;
}
+update_vginfo:
+ vgsummary.vgstatus = vgstatus;
+ vgsummary.vgname = vgname;
+ if (vgid)
+ strncpy((char *)&vgsummary.vgid, vgid, sizeof(vgsummary.vgid));
+
if (!lvmcache_update_vgname_and_id(info, &vgsummary)) {
- if (!existing) {
+ if (created) {
dm_hash_remove(_pvid_hash, pvid_s);
strcpy(info->dev->pvid, "");
+ dm_free(info->label);
dm_free(info);
- label_destroy(label);
}
return NULL;
}
@@ -2058,6 +2167,10 @@ void lvmcache_destroy(struct cmd_context *cmd, int retain_orphans, int reset)
if (retain_orphans)
if (!init_lvmcache_orphans(cmd))
stack;
+
+ dm_list_init(&_found_duplicate_devs);
+ dm_list_init(&_unused_duplicate_devs);
+ _found_duplicate_pvs = 0;
}
int lvmcache_pvid_is_locked(const char *pvid) {
diff --git a/lib/cache/lvmcache.h b/lib/cache/lvmcache.h
index f62607f..c964aec 100644
--- a/lib/cache/lvmcache.h
+++ b/lib/cache/lvmcache.h
@@ -198,6 +198,8 @@ void lvmcache_replace_dev(struct cmd_context *cmd, struct physical_volume *pv,
int lvmcache_found_duplicate_pvs(void);
+int vg_has_duplicate_pvs(struct volume_group *vg);
+
int lvmcache_contains_lock_type_sanlock(struct cmd_context *cmd);
void lvmcache_get_max_name_lengths(struct cmd_context *cmd,
diff --git a/lib/config/config_settings.h b/lib/config/config_settings.h
index 5d5ac48..0955d0a 100644
--- a/lib/config/config_settings.h
+++ b/lib/config/config_settings.h
@@ -395,6 +395,18 @@ cfg(devices_issue_discards_CFG, "issue_discards", devices_CFG_SECTION, 0, CFG_TY
"generally do. If enabled, discards will only be issued if both the\n"
"storage and kernel provide support.\n")
+cfg(devices_allow_changes_with_duplicate_pvs_CFG, "allow_changes_with_duplicate_pvs", devices_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_ALLOW_CHANGES_WITH_DUPLICATE_PVS, vsn(2, 2, 153), NULL, 0, NULL,
+ "Allow VG modification while a PV appears on multiple devices.\n"
+ "When a PV appears on multiple devices, LVM attempts to choose the\n"
+ "best device to use for the PV. If the devices represent the same\n"
+ "underlying storage, the choice has minimal consequence. If the\n"
+ "devices represent different underlying storage, the wrong choice\n"
+ "can result in data loss if the VG is modified. Disabling this\n"
+ "setting is the safest option because it prevents modifying a VG\n"
+ "or activating LVs in it while a PV appears on multiple devices.\n"
+ "Enabling this setting allows the VG to be used as usual even with\n"
+ "uncertain devices.\n")
+
cfg_array(allocation_cling_tag_list_CFG, "cling_tag_list", allocation_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(2, 2, 77), NULL, 0, NULL,
"Advise LVM which PVs to use when searching for new space.\n"
"When searching for free space to extend an LV, the 'cling' allocation\n"
diff --git a/lib/config/defaults.h b/lib/config/defaults.h
index 5e10ce0..07ca5de 100644
--- a/lib/config/defaults.h
+++ b/lib/config/defaults.h
@@ -45,6 +45,7 @@
#define DEFAULT_DATA_ALIGNMENT_DETECTION 1
#define DEFAULT_ISSUE_DISCARDS 0
#define DEFAULT_PV_MIN_SIZE_KB 2048
+#define DEFAULT_ALLOW_CHANGES_WITH_DUPLICATE_PVS 0
#define DEFAULT_LOCKING_LIB "liblvm2clusterlock.so"
#define DEFAULT_ERROR_WHEN_FULL 0
diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c
index c7d3fcf..dbe443e 100644
--- a/lib/metadata/metadata.c
+++ b/lib/metadata/metadata.c
@@ -3434,6 +3434,13 @@ int vg_write(struct volume_group *vg)
return 0;
}
+ if (lvmcache_found_duplicate_pvs() && vg_has_duplicate_pvs(vg) &&
+ !find_config_tree_bool(vg->cmd, devices_allow_changes_with_duplicate_pvs_CFG, NULL)) {
+ log_error("Cannot update volume group %s with duplicate PV devices.",
+ vg->name);
+ return 0;
+ }
+
if (vg_has_unknown_segments(vg) && !vg->cmd->handles_unknown_segments) {
log_error("Cannot update volume group %s with unknown segments in it!",
vg->name);
@@ -3918,6 +3925,11 @@ static int _repair_inconsistent_vg(struct volume_group *vg)
{
unsigned saved_handles_missing_pvs = vg->cmd->handles_missing_pvs;
+ if (lvmcache_found_duplicate_pvs()) {
+ log_debug_metadata("Skip metadata repair with duplicates.");
+ return 0;
+ }
+
/* Cannot write foreign VGs, the owner will repair it. */
if (_is_foreign_vg(vg)) {
log_verbose("Skip metadata repair for foreign VG.");
@@ -3952,6 +3964,11 @@ static int _wipe_outdated_pvs(struct cmd_context *cmd, struct volume_group *vg,
struct pv_list *pvl, *pvl2;
char uuid[64] __attribute__((aligned(8)));
+ if (lvmcache_found_duplicate_pvs()) {
+ log_debug_metadata("Skip wiping outdated PVs with duplicates.");
+ return 0;
+ }
+
/*
* Cannot write foreign VGs, the owner will repair it.
* Also, if another host is updating its VG, we may read
diff --git a/tools/toollib.c b/tools/toollib.c
index f1fe5b1..9917a67 100644
--- a/tools/toollib.c
+++ b/tools/toollib.c
@@ -1070,6 +1070,15 @@ int lv_change_activate(struct cmd_context *cmd, struct logical_volume *lv,
}
}
+ if (is_change_activating(activate) &&
+ lvmcache_found_duplicate_pvs() &&
+ vg_has_duplicate_pvs(lv->vg) &&
+ !find_config_tree_bool(cmd, devices_allow_changes_with_duplicate_pvs_CFG, NULL)) {
+ log_error("Cannot activate LVs in VG %s while PVs appear on duplicate devices.",
+ lv->vg->name);
+ return 0;
+ }
+
if (!lv_active_change(cmd, lv, activate, 0))
return_0;
6 years, 10 months
master - lvmetad: remove client side altdev code
by David Teigland
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=67da017fd25021...
Commit: 67da017fd2502138d0cc469f3aea320440353156
Parent: 3d2fbfe243550b7801165f393d27e35044c1a56d
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Fri Jan 29 15:36:55 2016 -0600
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Fri May 6 09:00:00 2016 -0500
lvmetad: remove client side altdev code
This is no longer used since lvmetad no longer
keeps track of alternate devices for duplicate PVs,
but is simply disabled when duplicates appear.
---
lib/cache/lvmcache.c | 42 ------------------------------------------
lib/cache/lvmcache.h | 2 --
lib/cache/lvmetad.c | 43 ++-----------------------------------------
3 files changed, 2 insertions(+), 85 deletions(-)
diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c
index 8ac113f..4e89371 100644
--- a/lib/cache/lvmcache.c
+++ b/lib/cache/lvmcache.c
@@ -72,7 +72,6 @@ struct lvmcache_vginfo {
unsigned vg_use_count; /* Counter of vg reusage */
unsigned precommitted; /* Is vgmetadata live or precommitted? */
unsigned cached_vg_invalidated; /* Signal to regenerate cached_vg */
- unsigned preferred_duplicates; /* preferred duplicate pvs have been set */
};
static struct dm_hash_table *_pvid_hash = NULL;
@@ -122,47 +121,6 @@ int lvmcache_init(void)
return 1;
}
-/*
- * Once PV info has been populated in lvmcache and
- * lvmcache has chosen preferred duplicate devices,
- * set this flag so that lvmcache will not try to
- * compare and choose preferred duplicate devices
- * again (which may result in different preferred
- * devices.) PV info can be populated in lvmcache
- * multiple times, each time causing lvmcache to
- * compare the duplicate devices, so we need to
- * record that the comparison/preferences have
- * already been done, so the preferrences from the
- * first time through are not changed.
- *
- * This is something of a hack to work around the
- * fact that the code isn't really designed to
- * handle duplicate PVs, and the fact that lvmetad
- * has its own way of picking a preferred duplicate
- * and lvmcache has another way based on having
- * more information than lvmetad does.
- *
- * If we come up with a better overall method to
- * handle duplicate PVs, then this can probably be
- * removed.
- *
- * FIXME: if we want to make lvmetad work with clvmd,
- * then this may need to be changed to set
- * preferred_duplicates back to 0.
- */
-
-void lvmcache_set_preferred_duplicates(const char *vgid)
-{
- struct lvmcache_vginfo *vginfo;
-
- if (!(vginfo = lvmcache_vginfo_from_vgid(vgid))) {
- stack;
- return;
- }
-
- vginfo->preferred_duplicates = 1;
-}
-
void lvmcache_seed_infos_from_lvmetad(struct cmd_context *cmd)
{
if (!lvmetad_used() || _has_scanned)
diff --git a/lib/cache/lvmcache.h b/lib/cache/lvmcache.h
index 0ebbe07..f62607f 100644
--- a/lib/cache/lvmcache.h
+++ b/lib/cache/lvmcache.h
@@ -198,8 +198,6 @@ void lvmcache_replace_dev(struct cmd_context *cmd, struct physical_volume *pv,
int lvmcache_found_duplicate_pvs(void);
-void lvmcache_set_preferred_duplicates(const char *vgid);
-
int lvmcache_contains_lock_type_sanlock(struct cmd_context *cmd);
void lvmcache_get_max_name_lengths(struct cmd_context *cmd,
diff --git a/lib/cache/lvmetad.c b/lib/cache/lvmetad.c
index 6c7bddd..86ccd59 100644
--- a/lib/cache/lvmetad.c
+++ b/lib/cache/lvmetad.c
@@ -665,17 +665,14 @@ static int _pv_populate_lvmcache(struct cmd_context *cmd,
struct dm_config_node *cn,
struct format_type *fmt, dev_t fallback)
{
- struct device *dev, *dev_alternate, *dev_alternate_cache = NULL;
- struct label *label;
+ struct device *dev;
struct id pvid, vgid;
char mda_id[32];
char da_id[32];
int i = 0;
struct dm_config_node *mda, *da;
- struct dm_config_node *alt_devices = dm_config_find_node(cn->child, "devices_alternate");
- struct dm_config_value *alt_device = NULL;
uint64_t offset, size;
- struct lvmcache_info *info, *info_alternate;
+ struct lvmcache_info *info;
const char *pvid_txt = dm_config_find_str(cn->child, "id", NULL),
*vgid_txt = dm_config_find_str(cn->child, "vgid", NULL),
*vgname = dm_config_find_str(cn->child, "vgname", NULL),
@@ -760,42 +757,6 @@ static int _pv_populate_lvmcache(struct cmd_context *cmd,
++i;
} while (da);
- if (alt_devices)
- alt_device = alt_devices->v;
-
- while (alt_device) {
- dev_alternate = dev_cache_get_by_devt(alt_device->v.i, cmd->filter);
-
- log_verbose("PV on device %s (%d:%d %d) is also on device %s (%d:%d %d) %s",
- dev_name(dev),
- (int)MAJOR(devt), (int)MINOR(devt), (int)devt,
- dev_alternate ? dev_name(dev_alternate) : "unknown",
- (int)MAJOR(alt_device->v.i), (int)MINOR(alt_device->v.i), (int)alt_device->v.i,
- pvid_txt);
-
- if (dev_alternate) {
- if ((info_alternate = lvmcache_add(fmt->labeller, (const char *)&pvid, dev_alternate,
- vgname, (const char *)&vgid, 0))) {
- dev_alternate_cache = dev_alternate;
- info = info_alternate;
- lvmcache_get_label(info)->dev = dev_alternate;
- }
- }
- alt_device = alt_device->next;
- }
-
- /*
- * Update lvmcache with the info about the alternate device by
- * reading its label, which should update lvmcache.
- */
- if (dev_alternate_cache) {
- if (!label_read(dev_alternate_cache, &label, 0)) {
- log_warn("No PV label found on duplicate device %s.", dev_name(dev_alternate_cache));
- }
- }
-
- lvmcache_set_preferred_duplicates((const char *)&vgid);
-
lvmcache_set_ext_flags(info, ext_flags);
lvmcache_set_ext_version(info, ext_version);
6 years, 10 months
master - lvmetad: set disabled flag when duplicate PVs are seen
by David Teigland
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=3d2fbfe243550b...
Commit: 3d2fbfe243550b7801165f393d27e35044c1a56d
Parent: 9539ee809827a870c88d02cfb2d0dc1e3eaac0d0
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Fri Jan 29 13:31:54 2016 -0600
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Fri May 6 08:59:59 2016 -0500
lvmetad: set disabled flag when duplicate PVs are seen
When duplicate PVs are detected, set the disabled
flag so that commands will disable use of lvmetad.
This duplicate detection is done by lvmetad itself
when it's told about a single new PV with a PVID
that matches an existing PV on another device.
(This is different from the case where the command
is scanning all devices and detects the duplicate.)
Remove the "altdev" logic that attempted to keep
track of multiple devices for a single PV. It
is no longer used since lvmetad is disabled in
this case.
---
daemons/lvmetad/lvmetad-core.c | 183 +++++++---------------------------------
1 files changed, 31 insertions(+), 152 deletions(-)
diff --git a/daemons/lvmetad/lvmetad-core.c b/daemons/lvmetad/lvmetad-core.c
index 45641a6..188c9b9 100644
--- a/daemons/lvmetad/lvmetad-core.c
+++ b/daemons/lvmetad/lvmetad-core.c
@@ -1869,46 +1869,12 @@ out:
return retval;
}
-static dev_t device_remove(lvmetad_state *s, struct dm_config_tree *pvmeta, dev_t device)
-{
- struct dm_config_node *pvmeta_tmp;
- struct dm_config_value *v = NULL;
- dev_t alt_device = 0, prim_device = 0;
-
- if ((pvmeta_tmp = dm_config_find_node(pvmeta->root, "pvmeta/devices_alternate")))
- v = pvmeta_tmp->v;
-
- prim_device = dm_config_find_int64(pvmeta->root, "pvmeta/device", 0);
-
- /* it is the primary device */
- if (device > 0 && device == prim_device && pvmeta_tmp && pvmeta_tmp->v)
- {
- alt_device = pvmeta_tmp->v->v.i;
- pvmeta_tmp->v = pvmeta_tmp->v->next;
- pvmeta_tmp = dm_config_find_node(pvmeta->root, "pvmeta/device");
- pvmeta_tmp->v->v.i = alt_device;
- } else if (device != prim_device)
- alt_device = prim_device;
-
- /* it is an alternate device */
- if (device > 0 && v && v->v.i == device)
- pvmeta_tmp->v = v->next;
- else while (device > 0 && pvmeta_tmp && v) {
- if (v->next && v->next->v.i == device)
- v->next = v->next->next;
- v = v->next;
- }
-
- return alt_device;
-}
-
static response pv_gone(lvmetad_state *s, request r)
{
const char *arg_pvid = NULL;
char *old_pvid = NULL;
const char *pvid;
int64_t device;
- int64_t alt_device = 0;
struct dm_config_tree *pvmeta;
char *vgid;
@@ -1938,17 +1904,7 @@ static response pv_gone(lvmetad_state *s, request r)
vgid = dm_hash_lookup(s->pvid_to_vgid, pvid);
dm_hash_remove_binary(s->device_to_pvid, &device, sizeof(device));
-
- alt_device = device_remove(s, pvmeta, device);
-
- if (!alt_device) {
- /* The PV was not a duplicate, so remove it. */
- dm_hash_remove(s->pvid_to_pvmeta, pvid);
- } else {
- /* The PV remains on another device. */
- DEBUGLOG(s, "pv_gone %s device %" PRIu64 " has alt_device %" PRIu64,
- pvid, device, alt_device);
- }
+ dm_hash_remove(s->pvid_to_pvmeta, pvid);
unlock_pvid_to_pvmeta(s);
@@ -1970,18 +1926,11 @@ static response pv_gone(lvmetad_state *s, request r)
vgid = NULL;
}
- if (!alt_device) {
- dm_config_destroy(pvmeta);
- if (old_pvid)
- dm_free(old_pvid);
- }
+ dm_config_destroy(pvmeta);
+ if (old_pvid)
+ dm_free(old_pvid);
- if (alt_device) {
- return daemon_reply_simple("OK",
- "device = %"PRId64, alt_device,
- NULL);
- } else
- return daemon_reply_simple("OK", NULL );
+ return daemon_reply_simple("OK", NULL );
}
static response pv_clear_all(lvmetad_state *s, request r)
@@ -2100,10 +2049,7 @@ static response pv_found(lvmetad_state *s, request r)
struct dm_config_tree *new_pvmeta = NULL;
struct dm_config_tree *prev_pvmeta_on_dev = NULL;
struct dm_config_tree *vgmeta = NULL;
- struct dm_config_node *altdev = NULL;
- struct dm_config_value *altdev_v = NULL;
const char *arg_pvid = NULL;
- const char *arg_pvid_dup = NULL;
const char *arg_pvid_lookup = NULL;
const char *new_pvid = NULL;
const char *new_pvid_dup = NULL;
@@ -2281,40 +2227,14 @@ static response pv_found(lvmetad_state *s, request r)
} else if (new_pvid && !new_device) {
/*
* New PV on old device (existing device reused for new PV).
- * The previous PV on arg_device may or may not still exist,
- * this is determined by device_remove() which checks the
- * pvmeta for the previous PV (prev_pvid_on_dev and
- * prev_pvmeta_on_dev) to see if arg_device was the only
- * device for the PV, or if arg_device was a duplicate for
- * the PV. If arg_device was previously a duplicate, then
- * the prev PV should be kept, but with arg_device removed.
- * If arg_device was the only device for the prev PV, then
- * the prev PV should be removed entirely.
+ * The previous PV on arg_device is replaced by the new one.
*
- * In either case, don't free prev_pvid or prev_vgid
- * strings because they are used at the end to check
- * the VG metadata.
+ * Don't free prev_pvid or prev_vgid strings because they are
+ * used at the end to check the VG metadata.
*/
changed |= 1;
- if (prev_pvmeta_on_dev &&
- !device_remove(s, prev_pvmeta_on_dev, arg_device)) {
- /*
- * The prev PV has no remaining device after
- * removing arg_device, so arg_device was not a
- * duplicate; remove the prev PV entirely.
- *
- * (hash_remove in pvid_to_vgid is done at the
- * end after the VG metadata is checked)
- *
- * FIXME: we could check if the new pvid is a new
- * duplicate of another existing PV. This can happen:
- * start with two different pvs A and B,
- * dd if=A of=B, pvscan --cache B. This detects that
- * B is removed, but doesn't detect that A is now a
- * duplicate. ('pvscan --cache' does detect the
- * dup because all pvs are scanned.)
- */
+ if (prev_pvmeta_on_dev) {
DEBUGLOG(s, "pv_found new pvid device_to_pvid %" PRIu64 " to %s removes prev pvid %s",
arg_device, new_pvid, prev_pvid_on_dev);
@@ -2324,23 +2244,24 @@ static response pv_found(lvmetad_state *s, request r)
/* removes arg_device/prev_pvid_on_dev mapping */
dm_hash_remove_binary(s->device_to_pvid, &arg_device, sizeof(arg_device));
- } else {
+
/*
- * The prev PV existing on a remaining alternate
- * device after removing arg_device, so arg_device
- * was a duplicate; keep the prev PV.
- *
- * FIXME: if the duplicate devices were path aliases
- * to the same underlying device, then keeping the
- * prev PV for the remaining alt device isn't nice.
+ * The new PV replacing the prev PV was copied from
+ * another existing PV, creating a duplicate PV which
+ * we ignore.
*/
- DEBUGLOG(s, "pv_found new pvid device_to_pvid %" PRIu64 " to %s keeping prev pvid %s",
- arg_device, new_pvid, prev_pvid_on_dev);
-
- /* removes arg_device/prev_pvid_on_dev mapping */
- dm_hash_remove_binary(s->device_to_pvid, &arg_device, sizeof(arg_device));
+ if (dm_hash_lookup(s->pvid_to_pvmeta, new_pvid)) {
+ DEBUGLOG(s, "pv_found ignore duplicate device %" PRIu64 " of existing PV for pvid %s",
+ arg_device, arg_pvid);
+ unlock_pvid_to_pvmeta(s);
+ dm_config_destroy(new_pvmeta);
+ s->flags |= GLFL_DISABLE;
+ s->flags |= GLFL_DISABLE_REASON_DUPLICATES;
+ return reply_fail("Ignore duplicate PV");
+ }
}
+
if (!(new_pvid_dup = dm_strdup(new_pvid)))
goto nomem;
@@ -2353,57 +2274,15 @@ static response pv_found(lvmetad_state *s, request r)
} else if (new_device && !new_pvid) {
/*
* Old PV on new device (duplicate)
- * . add new_device/arg_pvid mapping
- * . leave existing old_device/arg_pvid mapping
- * . add new_pvmeta, replacing old_pvmeta
- * . modify new_pvmeta, adding an alternate device entry for old_device
+ * Ignore it.
*/
- changed |= 1;
-
- DEBUGLOG(s, "pv_found new device device_to_pvid %" PRIu64 " to %s also %" PRIu64 " to %s",
- new_device, arg_pvid, old_device, arg_pvid);
-
- if (!(arg_pvid_dup = dm_strdup(arg_pvid)))
- goto nomem;
-
- if (!dm_hash_insert_binary(s->device_to_pvid, &new_device, sizeof(new_device), (char *)arg_pvid_dup))
- goto nomem;
-
- if (!dm_hash_insert(s->pvid_to_pvmeta, arg_pvid, new_pvmeta))
- goto nomem;
-
- /* Copy existing altdev info, or create new, and add it to new pvmeta. */
- if ((altdev = dm_config_find_node(old_pvmeta->root, "pvmeta/devices_alternate"))) {
- if (!(altdev = dm_config_clone_node(new_pvmeta, altdev, 0)))
- goto nomem;
- chain_node(altdev, new_pvmeta->root, 0);
- } else {
- if (!(altdev = make_config_node(new_pvmeta, "devices_alternate", new_pvmeta->root, 0)))
- goto nomem;
- }
-
- /* Add an altdev entry for old_device. */
- altdev_v = altdev->v;
- while (1) {
- if (altdev_v && altdev_v->v.i == old_device)
- break;
- if (altdev_v)
- altdev_v = altdev_v->next;
- if (!altdev_v) {
- if (!(altdev_v = dm_config_create_value(new_pvmeta)))
- goto nomem;
- altdev_v->next = altdev->v;
- altdev->v = altdev_v;
- altdev->v->v.i = old_device;
- break;
- }
- };
- altdev_v = altdev->v;
- while (altdev_v) {
- if (altdev_v->next && altdev_v->next->v.i == new_device)
- altdev_v->next = altdev_v->next->next;
- altdev_v = altdev_v->next;
- }
+ DEBUGLOG(s, "pv_found ignore duplicate device %" PRIu64 " of existing device %" PRIu64 " for pvid %s",
+ new_device, old_device, arg_pvid);
+ unlock_pvid_to_pvmeta(s);
+ dm_config_destroy(new_pvmeta);
+ s->flags |= GLFL_DISABLE;
+ s->flags |= GLFL_DISABLE_REASON_DUPLICATES;
+ return reply_fail("Ignore duplicate PV");
}
unlock_pvid_to_pvmeta(s);
6 years, 10 months
master - lvmetad: set disabled flag in lvmetad if duplicate PVs are found
by David Teigland
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=9539ee809827a8...
Commit: 9539ee809827a870c88d02cfb2d0dc1e3eaac0d0
Parent: a1dbd54885844a934e17f69691d2371a15453033
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Fri Jan 29 12:25:20 2016 -0600
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Fri May 6 08:59:59 2016 -0500
lvmetad: set disabled flag in lvmetad if duplicate PVs are found
When devices are being scanned, if duplicate PVs are seen,
tell lvmetad to set its disabled flag because of duplicate PVs.
---
lib/cache/lvmcache.c | 6 ++
lib/cache/lvmetad.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 144 insertions(+), 0 deletions(-)
diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c
index e2c86c4..8ac113f 100644
--- a/lib/cache/lvmcache.c
+++ b/lib/cache/lvmcache.c
@@ -27,6 +27,7 @@
#include "config.h"
#include "lvmetad.h"
+#include "lvmetad-client.h"
#define CACHE_INVALID 0x00000001
#define CACHE_LOCKED 0x00000002
@@ -1840,6 +1841,11 @@ struct lvmcache_info *lvmcache_add(struct labeller *labeller, const char *pvid,
* pv->dev under the VG, and its duplicate outside
* the VG context.)
*/
+
+ if (!_found_duplicate_pvs && lvmetad_used()) {
+ log_warn("WARNING: Disabling lvmetad cache which does not support duplicate PVs.");
+ lvmetad_set_disabled(fmt->cmd, LVMETAD_DISABLE_REASON_DUPLICATES);
+ }
_found_duplicate_pvs = 1;
/*
diff --git a/lib/cache/lvmetad.c b/lib/cache/lvmetad.c
index 8b63696..6c7bddd 100644
--- a/lib/cache/lvmetad.c
+++ b/lib/cache/lvmetad.c
@@ -2268,6 +2268,144 @@ int lvmetad_vg_is_foreign(struct cmd_context *cmd, const char *vgname, const cha
return ret;
}
+/*
+ * lvmetad has a disabled state in which it continues running,
+ * and returns the "disabled" flag in a get_global_info query.
+ *
+ * Case 1
+ * ------
+ * When "normal" commands start, (those not specifically
+ * intended to rescan devs) they begin by checking lvmetad's
+ * token and global info:
+ *
+ * - If the token doesn't match (should be uncommon), the
+ * command first rescans devices to repopulate lvmetad with
+ * the global_filter it is using. After rescanning, the
+ * lvmetad disabled state is set or cleared depending on
+ * what the scan saw.
+ *
+ * An unmatching token occurs when:
+ * . lvmetad was just started and has not been populated yet.
+ * . The global_filter has been changed in lvm.conf since the
+ * last command was run.
+ * . The global_filter is overriden on the command line.
+ * (There's little point in using lvmetad if global_filter
+ * is often changed/overridden.)
+ *
+ * - If the token does match (common case), the command and
+ * lvmetad are using the same global_filter and the command
+ * does not rescan devs to repopulate lvmetad, or change the
+ * lvmetad disabled state.
+ *
+ * - After the token check/sync, the command checks if the
+ * disabled flag is set in lvmetad. If it is, the command will
+ * not use the lvmetad cache and will revert to scanning, i.e.
+ * it runs the same as if use_lvmetad=0.
+ *
+ * So, "normal" commands try to use the lvmetad cache to avoid
+ * scanning devices. In the uncommon case when the token doesn't
+ * match, these commands will first rescan devs to repopulate the
+ * lvmetad cache, and then attempt to use the lvmetad cache.
+ * In the uncommon case where lvmetad is disabled (by a previous
+ * command), the common commands do not rescan devs to repopulate
+ * lvmetad, but revert the equivalent of use_lvmetad=0, reading
+ * from disk instead of the cache.
+ * The combination of those two uncommon cases means that a command
+ * could begin by rescanning devs because of a token mismatch, then
+ * disable lvmetad as a result of that scan, and continue without
+ * using lvmetad.
+ *
+ * Case 2
+ * ------
+ * Commands that are meant to scan devices to repopulate the
+ * lvmetad cache, e.g. pvscan --cache, will always rescan
+ * devices and then set/clear the disabled state according to
+ * what they found when scanning. The global_filter is always
+ * used when choosing which devices to scan to populate lvmetad.
+ * The command-specific filter is never used when choosing
+ * which devices to scan for repopulating the lvmetad cache.
+ *
+ * During a scan repopulating the lvmetad cache, a command looks
+ * for PVs with lvm1 metadata, or duplicate PVs (two devices with
+ * the same PVID). If either of those are found during the scan,
+ * the command sets the disabled state in lvmetad. If none are
+ * found, the command clears the disabled state in lvmetad.
+ * (Other problems scanning may also cause the command to set the
+ * disabled state.)
+ *
+ * Case 3
+ * ------
+ * The special command 'pvscan --cache <dev>' is meant to only
+ * scan the specified device and send info from the dev to
+ * lvmetad. This single-dev pvscan will not detect duplicate PVs
+ * since it only sees the one device. If lvmetad already knows
+ * about the same PV on another device, then lvmetad will be the
+ * first to discover that a duplicate PV exists. In this case,
+ * lvmetad sets the disabled state for itself.
+ *
+ * Duplicates
+ * ----------
+ * The most common reasons for duplicate PVs to exist are:
+ *
+ * 1. Multipath. When multipath is running, it creates a new
+ * mpath device for the underlying "duplicate" devs. lvm has
+ * built in, automatic filtering that will hide the duplicate
+ * devs of the underlying mpath dev, so the duplicates will
+ * be skipping during scanning (multipath_component_detection).
+ *
+ * If multipath_component_detection=0, or if multipathd is not
+ * running, or multipath is not set up to handle a particular
+ * set of devs, then lvm will see the multipath paths as
+ * duplicates. lvm will choose one of them to use, consider
+ * the other a duplicate, and disable lvmetad. multipathd
+ * should be configured and running to resolve these duplicates,
+ * and multipath_component_detection enabled.
+ *
+ * 2. Cloning by copying. One device is copied over another, e.g.
+ * with dd. This is a more concerning case because using the
+ * wrong device could lead to corruption. LVM will attempt to
+ * choose the best device as the PV, but it may not always
+ * be the right one. In this case, lvmetad is disabled.
+ * vgimportclone should be used on the new copy to resolve the
+ * duplicates.
+ *
+ * 3. Cloning by hardware. A LUN is cloned/snapshotted on
+ * a hardware device. The description here is the same as
+ * cloning by copying.
+ *
+ * 4. Creating LVM snapshots of LVs being used as PVs.
+ * If pvcreate is run on an LV, and lvcreate is used to
+ * create a snapshot of that LV, then the two LVs will
+ * appear to be duplicate PVs.
+ *
+ * Filtering duplicates
+ * --------------------
+ *
+ * If all but one copy of a PV is added to the global_filter,
+ * then duplicates will not be seen when scanning to populate
+ * the lvmetad cache. Neither common commands nor scanning
+ * commands will see the duplicates, and lvmetad will not be
+ * disabled.
+ *
+ * If the global_filter is *not* used to hide duplicates,
+ * then lvmetad will be disabled when they are scanned, but
+ * common commands can use the command filter to hide the
+ * duplicates and work with a selected instance of the PV.
+ * The command will not use lvmetad in this case, but will
+ * not see duplicate PVs itself because its command filter
+ * is more restrictive than the global_filter and has hidden
+ * the duplicates.
+ */
+
+/*
+ * FIXME: if we fail to disable lvmetad, then other commands could
+ * potentially use incorrect cache data from lvmetad. Should we
+ * do something more severe if the disable messages fails, like
+ * sending SIGKILL to the lvmetad pid?
+ *
+ * FIXME: log something in syslog any time we disable lvmetad?
+ * At a minimum if we fail to disable lvmetad.
+ */
void lvmetad_set_disabled(struct cmd_context *cmd, const char *reason)
{
daemon_reply reply;
6 years, 10 months
master - pvscan: fix errors for single dev scan while lvmetad is disabled
by David Teigland
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=a1dbd54885844a...
Commit: a1dbd54885844a934e17f69691d2371a15453033
Parent: 08aeea6a126d71843a2c133b15565d8efb664ae2
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Wed Apr 27 15:06:43 2016 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Fri May 6 08:59:59 2016 -0500
pvscan: fix errors for single dev scan while lvmetad is disabled
While lvmetad was disabled, 'pvscan --cache dev' would produce
confusing error messages.
---
tools/pvscan.c | 13 +++++++++++++
1 files changed, 13 insertions(+), 0 deletions(-)
diff --git a/tools/pvscan.c b/tools/pvscan.c
index d93773a..50753fd 100644
--- a/tools/pvscan.c
+++ b/tools/pvscan.c
@@ -257,6 +257,7 @@ static int _pvscan_lvmetad(struct cmd_context *cmd, int argc, char **argv)
int ret = ECMD_PROCESSED;
struct device *dev;
const char *pv_name;
+ const char *reason = NULL;
int32_t major = -1;
int32_t minor = -1;
int devno_args = 0;
@@ -309,6 +310,18 @@ static int _pvscan_lvmetad(struct cmd_context *cmd, int argc, char **argv)
}
/*
+ * When lvmetad is disabled, all devices need to be rescanned,
+ * i.e. the !argc case above, pvscan --cache.
+ */
+ if (lvmetad_used() && lvmetad_is_disabled(cmd, &reason)) {
+ log_warn("WARNING: Not using lvmetad because %s.", reason);
+ log_warn("WARNING: Rescan all devices to update lvmetad cache (pvscan --cache).");
+ log_error("Failed to update cache.");
+ ret = ECMD_FAILED;
+ goto out;
+ }
+
+ /*
* FIXME: when specific devs are named, we generally don't
* want to scan any other devs, but if lvmetad is not yet
* populated, the first 'pvscan --cache dev' does need to
6 years, 10 months
master - tests: do not use EPOCH for get wrapper
by Zdenek Kabelac
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=08aeea6a126d71...
Commit: 08aeea6a126d71843a2c133b15565d8efb664ae2
Parent: 07c1694ff59fd760a602fb1425c5c99bc0bd33ca
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Tue May 3 21:22:34 2016 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Thu May 5 23:55:20 2016 +0200
tests: do not use EPOCH for get wrapper
Avoid poluting filesystem with debug.log_DEBUG for get wrapper
---
test/lib/get.sh | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/test/lib/get.sh b/test/lib/get.sh
index 74ee7e8..f650417 100644
--- a/test/lib/get.sh
+++ b/test/lib/get.sh
@@ -103,4 +103,5 @@ first_extent_sector() {
#set -x
unset LVM_VALGRIND
+unset LVM_LOG_FILE_EPOCH
"$@"
6 years, 10 months
master - tests: update aux raid support
by Zdenek Kabelac
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=07c1694ff59fd7...
Commit: 07c1694ff59fd760a602fb1425c5c99bc0bd33ca
Parent: 7bb39af03630beb91acd457b9d949f7ecfa0e75e
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Thu May 5 21:00:42 2016 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Thu May 5 23:55:20 2016 +0200
tests: update aux raid support
For raid1 use chunksize as bitmap-chunk specification.
Always enforce usage of bitmap - getting comparable outcome
as lvm2 raid support uses.
Add udev_wait after stopping md array - as in fact leg-device
are still in use by target even command has finished.
(mdadm --stop causes WATCH rule wakeup, and
ioctl(STOP_ARRAY) returns IMHO to early - it should finish
and fsync work on leg devices first).
---
test/lib/aux.sh | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)
diff --git a/test/lib/aux.sh b/test/lib/aux.sh
index 08187b5..3bb469b 100644
--- a/test/lib/aux.sh
+++ b/test/lib/aux.sh
@@ -659,6 +659,8 @@ prepare_md_dev() {
rm -f debug.log strace.log MD_DEV MD_DEV_PV MD_DEVICES
+ coption="--chunk"
+ test "$level" = "1" && coption="--bitmap-chunk"
# Have MD use a non-standard name to avoid colliding with an existing MD device
# - mdadm >= 3.0 requires that non-standard device names be in /dev/md/
# - newer mdadm _completely_ defers to udev to create the associated device node
@@ -668,7 +670,7 @@ prepare_md_dev() {
mddev=/dev/md/md_lvm_test0 || \
mddev=/dev/md_lvm_test0
- mdadm --create --metadata=1.0 "$mddev" --auto=md --level $level --chunk $rchunk --raid-devices=$rdevs "${@:4}" || {
+ mdadm --create --metadata=1.0 "$mddev" --auto=md --level $level --bitmap=internal $coption=$rchunk --raid-devices=$rdevs "${@:4}" || {
# Some older 'mdadm' version managed to open and close devices internaly
# and reporting non-exclusive access on such device
# let's just skip the test if this happens.
@@ -707,6 +709,7 @@ cleanup_md_dev() {
mdadm --stop "$dev" || true
test "$DM_DEV_DIR" != "/dev" && rm -f "$DM_DEV_DIR/$(basename $dev)"
notify_lvmetad $(< MD_DEV_PV)
+ udev_wait # wait till events are process, not zeroing to early
for dev in $(< MD_DEVICES); do
mdadm --zero-superblock "$dev" || true
notify_lvmetad "$dev"
6 years, 10 months
master - tests: check default policy is used when unspecified
by Zdenek Kabelac
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=7bb39af03630be...
Commit: 7bb39af03630beb91acd457b9d949f7ecfa0e75e
Parent: 95f0e25601155755baf4ea1649cd241949120185
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Thu May 5 21:35:19 2016 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Thu May 5 23:55:20 2016 +0200
tests: check default policy is used when unspecified
If the policy is not given with policy settings we assume
the default one is going to be used - it's users responsibility
to pair them properly.
---
test/shell/lvcreate-cache.sh | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/test/shell/lvcreate-cache.sh b/test/shell/lvcreate-cache.sh
index 8f35f93..fbea5b8 100644
--- a/test/shell/lvcreate-cache.sh
+++ b/test/shell/lvcreate-cache.sh
@@ -229,7 +229,7 @@ fail lvcreate -H -L10 --chunksize 16M $vg/cpool4
lvremove -f $vg
lvcreate --type cache-pool -L10 $vg/cpool
-lvcreate --type cache -l 1 --cachepool $vg/cpool -n corigin $vg --cachepolicy mq --cachesettings migration_threshold=233
+lvcreate --type cache -l 1 --cachepool $vg/cpool -n corigin $vg --cachesettings migration_threshold=233
dmsetup status | grep $vg
dmsetup status | grep $vg-corigin | grep 'migration_threshold 233'
lvchange -an $vg
6 years, 10 months
master - tests: check for thin tools
by Zdenek Kabelac
Gitweb: http://git.fedorahosted.org/git/?p=lvm2.git;a=commitdiff;h=95f0e256011557...
Commit: 95f0e25601155755baf4ea1649cd241949120185
Parent: 673d4f745322d6d9ca274697148267114058beec
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Thu May 5 23:14:38 2016 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Thu May 5 23:55:20 2016 +0200
tests: check for thin tools
Test needs repair utils to be available.
---
test/shell/lvconvert-repair-thin-raid.sh | 9 +++++++++
1 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/test/shell/lvconvert-repair-thin-raid.sh b/test/shell/lvconvert-repair-thin-raid.sh
index a895e08..7afceef 100644
--- a/test/shell/lvconvert-repair-thin-raid.sh
+++ b/test/shell/lvconvert-repair-thin-raid.sh
@@ -21,6 +21,15 @@ aux have_thin 1 0 0 || skip
aux have_raid 1 4 0 || skip
#
+# To continue this test - we need real tools available
+# When they are not present mark test as skipped, but still
+# let proceed initial part which should work even without tools
+#
+aux have_tool_at_least "$LVM_TEST_THIN_CHECK_CMD" 0 3 1 || skip
+aux have_tool_at_least "$LVM_TEST_THIN_DUMP_CMD" 0 3 1 || skip
+aux have_tool_at_least "$LVM_TEST_THIN_REPAIR_CMD" 0 3 1 || skip
+
+#
# Main
#
6 years, 10 months