diff options
author | David Teigland <teigland@redhat.com> | 2015-11-04 14:26:46 -0600 |
---|---|---|
committer | David Teigland <teigland@redhat.com> | 2015-11-13 15:03:07 -0600 |
commit | adbeb5152d6bd71b69f5996131672862830dc311 (patch) | |
tree | 1b306470e23b1c2c9c435b7eb3bfbc2fd932005a | |
parent | 2e7d45630bc39f0914c2db9eaabf049c25410f9f (diff) | |
download | lvm2-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.c | 17 | ||||
-rw-r--r-- | lib/cache/lvmcache.c | 106 | ||||
-rw-r--r-- | lib/cache/lvmcache.h | 2 | ||||
-rw-r--r-- | lib/cache/lvmetad.c | 91 | ||||
-rw-r--r-- | lib/cache/lvmetad.h | 6 | ||||
-rw-r--r-- | tools/lvmcmdline.c | 16 |
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."); } } |