summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Teigland <teigland@redhat.com>2015-11-04 14:26:46 -0600
committerDavid Teigland <teigland@redhat.com>2015-11-13 15:03:07 -0600
commitadbeb5152d6bd71b69f5996131672862830dc311 (patch)
tree1b306470e23b1c2c9c435b7eb3bfbc2fd932005a
parent2e7d45630bc39f0914c2db9eaabf049c25410f9f (diff)
downloadlvm2-adbeb5152d6bd71b69f5996131672862830dc311.tar.gz
Warn about duplicate PVs when using lvmetad
A flag is set in lvmetad when duplicate PVs have been found. Commands check this flag and print a warning about duplicate PVs if it's set. When devices are scanned, the flag is set or cleared in lvmetad.
-rw-r--r--daemons/lvmetad/lvmetad-core.c17
-rw-r--r--lib/cache/lvmcache.c106
-rw-r--r--lib/cache/lvmcache.h2
-rw-r--r--lib/cache/lvmetad.c91
-rw-r--r--lib/cache/lvmetad.h6
-rw-r--r--tools/lvmcmdline.c16
6 files changed, 237 insertions, 1 deletions
diff --git a/daemons/lvmetad/lvmetad-core.c b/daemons/lvmetad/lvmetad-core.c
index 4f1fd9ecb..11761af48 100644
--- a/daemons/lvmetad/lvmetad-core.c
+++ b/daemons/lvmetad/lvmetad-core.c
@@ -258,7 +258,9 @@ struct vg_info {
uint32_t flags; /* VGFL_ */
};
-#define GLFL_INVALID 0x00000001
+#define GLFL_INVALID 0x00000001
+#define GLFL_DUPLICATES 0x00000002
+
#define VGFL_INVALID 0x00000001
typedef struct {
@@ -2357,6 +2359,7 @@ static response pv_found(lvmetad_state *s, request r)
arg_device, arg_pvid);
unlock_pvid_to_pvmeta(s);
dm_config_destroy(new_pvmeta);
+ s->flags |= GLFL_DUPLICATES;
return reply_fail("Ignore duplicate PV");
}
}
@@ -2380,6 +2383,7 @@ static response pv_found(lvmetad_state *s, request r)
new_device, old_device, arg_pvid);
unlock_pvid_to_pvmeta(s);
dm_config_destroy(new_pvmeta);
+ s->flags |= GLFL_DUPLICATES;
return reply_fail("Ignore duplicate PV");
}
@@ -2589,6 +2593,7 @@ static response vg_remove(lvmetad_state *s, request r)
static response set_global_info(lvmetad_state *s, request r)
{
const int global_invalid = daemon_request_int(r, "global_invalid", -1);
+ const int duplicates = daemon_request_int(r, "duplicates", -1);
if (global_invalid == 1)
s->flags |= GLFL_INVALID;
@@ -2596,6 +2601,12 @@ static response set_global_info(lvmetad_state *s, request r)
else if (global_invalid == 0)
s->flags &= ~GLFL_INVALID;
+ if (duplicates == 1)
+ s->flags |= GLFL_DUPLICATES;
+
+ else if (duplicates == 0)
+ s->flags &= ~GLFL_DUPLICATES;
+
return daemon_reply_simple("OK", NULL);
}
@@ -2603,6 +2614,10 @@ static response get_global_info(lvmetad_state *s, request r)
{
return daemon_reply_simple("OK", "global_invalid = " FMTd64,
(int64_t)((s->flags & GLFL_INVALID) ? 1 : 0),
+ "duplicates = " FMTd64,
+ (int64_t)((s->flags & GLFL_DUPLICATES) ? 1 : 0),
+ "token = %s",
+ s->token[0] ? s->token : "none",
NULL);
}
diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c
index acb6ae927..a56b37ac3 100644
--- a/lib/cache/lvmcache.c
+++ b/lib/cache/lvmcache.c
@@ -28,6 +28,93 @@
#include "lvmetad.h"
+/*
+ * Duplicate PV handling
+ *
+ * Duplicate PVs exist when lvm sees the same pvid on two different devices.
+ *
+ * Kinds of duplicates
+ * -------------------
+ *
+ * The two different devices could refer to the same underlying storage, e.g.
+ * multipath or a dm identity device wrapper, or they could refer to different
+ * underlying storage, e.g. dd one device to another.
+ *
+ * When multipath is running correct, lvm should not see both underlying paths
+ * so duplicates are not seen.
+ *
+ * When duplicate PVs exist on different underlying storage, the prescribed way
+ * of resolving this is to run vgimportclone on one of the devices (generally
+ * the new one or the one that's not used).
+ *
+ * When duplicate PVs exist for the same underlying storage, this should
+ * generally not be a persistent condition. If it is a persistent condition
+ * for some reason, then all but one of the duplicate paths should be rejected
+ * using the global_filter, keeping the preferred path as the one not removed
+ * by the filter.
+ *
+ * Duplicate PVs are detected by lvm in two places
+ * -----------------------------------------------
+ *
+ * 1. An lvm command performs a full device scan. Device A with pvid X is
+ * added to lvmcache, then device B with the same pvid X is added to lvmcache.
+ * When adding B, lvmcache sees that A already exists with the same pvid.
+ * lvmcache ignores device B.
+ *
+ * 2. 'pvscan --cache $dev' is run when the new device appears and generates a
+ * uevent. This command *only* reads the one dev, and sends the information
+ * from the dev (PV and VG info) to lvmetad. lvmetad sees that the pvid
+ * already exists but on a different device and ignores the new device.
+ *
+ * When not using lvmetad, only 1 is possible, but when using lvmetad, both 1
+ * and 2 occur.
+ *
+ * How lvm handles duplicate PVs
+ * -----------------------------
+ *
+ * - When a full scan sees a duplicate device in lvmcache_add(), it prints a
+ * warning, and does not add that device/PV to lvmcache. It leaves the
+ * previously seen device/PV in lvmcache. Any PV/VG info from the newly seen
+ * duplicate is ignored.
+ *
+ * - If the full scan is being done to populate the lvmetad cache, an ignored
+ * device is not sent to lvmetad.
+ *
+ * - A scan of a single dev (pvscan --cache dev) does not read all devices so
+ * it does not detect duplicates. But, sending the info from that dev can
+ * cause lvmetad to detect it's a duplicate based on existing devices lvmetad
+ * already knows about. lvmetad ignores the new device if it has the same
+ * pvid as another existing device.
+ *
+ * - When a command gets PVs from lvmetad, it does not see any duplicates
+ * because the duplicates were ignored by lvmetad. This means when a command
+ * adds devices to lvmcache from lvmetad, it will never encounter duplicates.
+ *
+ * So, both lvmcache and lvmetad ignore a device if its pvid is already known
+ * from another device. The ignored device will look like a non-PV when
+ * displayed by pvs -a or when 'pvs $dev' is run on it. In lvm commands, the
+ * PV will appear to exist only on whichever device was seen first.
+ *
+ * When not using lvmetad, all commands scan all devices and will always see
+ * and ignore duplicate devices in lvmcache, using only the first that they
+ * happen to find.
+ *
+ * When using lvmetad, duplicates are seen only by 1. the command scanning all
+ * devs to populate lvmetad, and 2. by lvmetad when a new duplicate dev is sent
+ * by pvscan --cache dev. lvmetad does not remember any duplicate devices, and
+ * subseqent commands using lvmetad are unaware of the duplicate device that
+ * was ignored.
+ *
+ * When not using lvmetad and a command is scanning all devices, warnings are
+ * printed about the duplicates when they are seen and ignored by lvmcache.
+ *
+ * When lvmetad is used and scanning all devices sees a duplicate, it sets a
+ * flag in lvmetad indicating that duplicates have been seen. Also if lvmetad
+ * is sent a duplicate PV from pvscan, it sets this flag also. When subsequent
+ * commands use lvmetad, they check for this flag and print a warning about
+ * duplicates if it's set.
+ */
+
#define CACHE_INVALID 0x00000001
#define CACHE_LOCKED 0x00000002
@@ -79,6 +166,7 @@ static int _scanning_in_progress = 0;
static int _has_scanned = 0;
static int _vgs_locked = 0;
static int _vg_global_lock_held = 0; /* Global lock held when cache wiped? */
+static int _found_duplicates = 0;
int lvmcache_init(void)
{
@@ -1544,6 +1632,23 @@ int lvmcache_update_vg(struct volume_group *vg, unsigned precommitted)
return 1;
}
+/*
+ * When scanning all devices, lvmcache detects duplicate PVs exist if it sees
+ * two different devices with the same pvid. When it sees this, it ignores the
+ * duplicate device, and sets _found_duplicates. After the scan is done, we
+ * set or clear a flag in lvmetad indicating duplicate PVs, so that subsequent
+ * commands can print warnings about thatm.
+ */
+int lvmcache_found_duplicates(void)
+{
+ return _found_duplicates;
+}
+
+void lvmcache_clear_found_duplicates(void)
+{
+ _found_duplicates = 0;
+}
+
struct lvmcache_info *lvmcache_add(struct labeller *labeller, const char *pvid,
struct device *dev,
const char *vgname, const char *vgid,
@@ -1592,6 +1697,7 @@ struct lvmcache_info *lvmcache_add(struct labeller *labeller, const char *pvid,
log_warn("Ignore duplicate PV on device %s. Already using PV from device %s. (%s)",
dev_name(dev), dev_name(existing->dev), pvid_s);
log_warn("Use the global_filter to select a different device.");
+ _found_duplicates = 1;
return NULL;
} else {
/*
diff --git a/lib/cache/lvmcache.h b/lib/cache/lvmcache.h
index b2abfa549..c232e4446 100644
--- a/lib/cache/lvmcache.h
+++ b/lib/cache/lvmcache.h
@@ -186,5 +186,7 @@ int lvmcache_contains_lock_type_sanlock(struct cmd_context *cmd);
void lvmcache_get_max_name_lengths(struct cmd_context *cmd,
unsigned *pv_max_name_len, unsigned *vg_max_name_len);
+int lvmcache_found_duplicates(void);
+void lvmcache_clear_found_duplicates(void);
#endif
diff --git a/lib/cache/lvmetad.c b/lib/cache/lvmetad.c
index 466d962dc..075fe1ad4 100644
--- a/lib/cache/lvmetad.c
+++ b/lib/cache/lvmetad.c
@@ -314,6 +314,80 @@ out:
return ret;
}
+static void _send_duplicates(struct cmd_context *cmd, int set)
+{
+ daemon_reply reply;
+
+ log_debug_lvmetad("lvmetad send duplicates %d", set);
+
+ reply = daemon_send_simple(_lvmetad, "set_global_info",
+ "token = %s", "skip",
+ "duplicates = " FMTd64, (int64_t)set,
+ NULL);
+ if (reply.error)
+ log_error("_send_duplicates error %d", reply.error);
+
+ if (strcmp(daemon_reply_str(reply, "response", ""), "OK"))
+ log_error("_send_duplicates not ok");
+
+ daemon_reply_destroy(reply);
+}
+
+/*
+ * The current command scanned devices and found a duplicate
+ * PV, so it sets the duplicates flag in lvmetad,
+ * which causes subsequent commands that use lvmetad to
+ * print a warning about duplicate PVs.
+ */
+void lvmetad_set_duplicates(struct cmd_context *cmd)
+{
+ _send_duplicates(cmd, 1);
+}
+
+/*
+ * The current command scanned devices and did not find any
+ * duplicate PVs, so it can clear the duplicates
+ * flag in lvmetad, and subsequent commands will not
+ * warn about duplicate PVs.
+ */
+void lvmetad_clear_duplicates(struct cmd_context *cmd)
+{
+ _send_duplicates(cmd, 0);
+}
+
+/*
+ * The duplicates flag can be set in lvmetad because
+ * the previous command that scanned devices found a duplicate
+ * and set the flag using lvmetad_set_duplicates(),
+ * or because a new PV was added to lvmetad by
+ * 'pvscan --cache dev', and lvmetad found that dev was a
+ * duplicate and set its duplicates flag.
+ */
+int lvmetad_check_duplicates(struct cmd_context *cmd)
+{
+ daemon_reply reply;
+ int duplicates;
+
+ reply = daemon_send_simple(_lvmetad, "get_global_info",
+ "token = %s", "skip",
+ NULL);
+
+ if (reply.error) {
+ log_error("lvmetad_check_duplicates get_global_info error %d", reply.error);
+ return 0;
+ }
+
+ if (strcmp(daemon_reply_str(reply, "response", ""), "OK")) {
+ log_error("lvmetad_check_duplicates get_global_info not ok");
+ return 0;
+ }
+
+ duplicates = daemon_reply_int(reply, "duplicates", 0);
+ daemon_reply_destroy(reply);
+
+ return duplicates;
+}
+
static int _lvmetad_pvscan_all_devs(struct cmd_context *cmd, activation_handler handler,
int ignore_obsolete);
@@ -1489,6 +1563,8 @@ static int _lvmetad_pvscan_all_devs(struct cmd_context *cmd, activation_handler
return 0;
}
+ lvmcache_clear_found_duplicates();
+
log_debug_lvmetad("Scanning all devices to update lvmetad.");
if (!(iter = dev_iter_create(cmd->lvmetad_filter, 1))) {
@@ -1531,6 +1607,21 @@ static int _lvmetad_pvscan_all_devs(struct cmd_context *cmd, activation_handler
if (!_token_update())
return 0;
+ /*
+ * We are done scanning all devices, and lvmcache set a flag if
+ * duplicate PVs were seen. If using lvmetad, and duplicates were seen
+ * during the scan, then set an "duplicates" flag in lvmetad to
+ * indicate duplicate PVs exist. Subsequent commands will check for
+ * this duplicates flag, and if it's set they will warn about duplicate
+ * PVs. When the next rescan happens, this flag will be set or cleared
+ * again dependin on whether the duplicates still exist or have been
+ * resolved.
+ */
+ if (lvmcache_found_duplicates())
+ lvmetad_set_duplicates(cmd);
+ else
+ lvmetad_clear_duplicates(cmd);
+
return r;
}
diff --git a/lib/cache/lvmetad.h b/lib/cache/lvmetad.h
index 4c4549ee9..f9b83a053 100644
--- a/lib/cache/lvmetad.h
+++ b/lib/cache/lvmetad.h
@@ -175,6 +175,9 @@ int lvmetad_pvscan_foreign_vgs(struct cmd_context *cmd, activation_handler handl
int lvmetad_vg_clear_outdated_pvs(struct volume_group *vg);
void lvmetad_validate_global_cache(struct cmd_context *cmd, int force);
int lvmetad_token_matches(struct cmd_context *cmd);
+void lvmetad_set_duplicates(struct cmd_context *cmd);
+void lvmetad_clear_duplicates(struct cmd_context *cmd);
+int lvmetad_check_duplicates(struct cmd_context *cmd);
# else /* LVMETAD_SUPPORT */
@@ -206,6 +209,9 @@ int lvmetad_token_matches(struct cmd_context *cmd);
# define lvmetad_validate_global_cache(cmd, force) do { } while (0)
# define lvmetad_token_matches(cmd) (1)
# define lvmetad_is_connected() (0)
+# define lvmetad_set_duplicates() do { } while (0)
+# define lvmetad_clear_duplicates() do { } while (0)
+# define lvmetad_check_duplicates() (0)
# endif /* LVMETAD_SUPPORT */
diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c
index f2e60b4d5..3cf7f1bae 100644
--- a/tools/lvmcmdline.c
+++ b/tools/lvmcmdline.c
@@ -1658,6 +1658,22 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv)
log_error("Failed to scan devices");
return ECMD_FAILED;
}
+ } else if (lvmetad_check_duplicates(cmd)) {
+ /*
+ * Warn if duplicate PVs exist. Because we're using lvmetad,
+ * we won't be scanning devices, so we rely on lvmetad to tell
+ * us if duplicate devices were seen by the last device scan.
+ *
+ * FIXME: maybe enhance this by listing the actual duplicate devices?
+ * The device scan that set the duplicates flag in lvmetad could
+ * also send lvmetad the names of the duplicate devices it ignored.
+ * That list could then be returned here and included in this
+ * warning. However, that list would not always be accurate,
+ * because a "full picture" scan is needed to construct a truely
+ * accurate picture of duplicate PVs.
+ */
+ log_warn("WARNING: duplicate PVs have been found.");
+ log_warn("WARNING: duplicate PVs can be resolved with filters or vgimportclone.");
}
}