diff options
Diffstat (limited to 'lib/cache/lvmcache.c')
-rw-r--r-- | lib/cache/lvmcache.c | 246 |
1 files changed, 202 insertions, 44 deletions
diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c index c5f78c864..5ef40a732 100644 --- a/lib/cache/lvmcache.c +++ b/lib/cache/lvmcache.c @@ -1529,6 +1529,60 @@ void lvmcache_replace_dev(struct cmd_context *cmd, struct physical_volume *pv, pv->dev = dev; } +/* + * We can see multiple different devices with the + * same pvid, i.e. duplicates. + * + * There may be different reasons for seeing two + * devices with the same pvid: + * - multipath showing two paths to the same thing + * - one device copied to another, e.g. with dd, + * also referred to as cloned devices. + * - a "subsystem" taking a device and creating + * another device of its own that represents the + * underlying device it is using, e.g. using dm + * to create an identity mapping of a PV. + * + * Given duplicate devices, we have to choose one + * of them to be the "preferred" dev, i.e. the one + * that will be referenced in lvmcache, by pv->dev. + * We can keep the existing dev, that's currently + * used in lvmcache, or we can replace the existing + * dev with the new duplicate. + * + * Regardless of which device is preferred, we need + * to print messages explaining which devices were + * found so that a user can sort out for themselves + * what has happened if the preferred device is not + * the one they are interested in. + * + * If a user wants to use the non-preferred device, + * they will need to filter out the device that + * lvm is preferring. + * + * The dev_subsystem calls check if the major number + * of the dev is part of a subsystem like DM/MD/DRBD. + * A dev that's part of a subsystem is preferred over a + * 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: there may be other reasons to prefer one + * device over another: + * + * . are there other use/open counts we could check + * beyond the holders? + * + * . check if either is bad/usable and prefer + * the good one? + * + * . prefer the one with smaller minor number? + * Might avoid disturbing things due to a new + * transient duplicate? + */ + struct lvmcache_info *lvmcache_add(struct labeller *labeller, const char *pvid, struct device *dev, const char *vgname, const char *vgid, @@ -1576,54 +1630,158 @@ struct lvmcache_info *lvmcache_add(struct labeller *labeller, const char *pvid, lvmcache_del_bas(info); } else { if (existing->dev != dev) { - /* Is the existing entry a duplicate pvid e.g. md ? */ - if (dev_subsystem_part_major(dt, existing->dev) && - !dev_subsystem_part_major(dt, dev)) { - log_very_verbose("Ignoring duplicate PV %s on " - "%s - using %s %s", - pvid, dev_name(dev), - dev_subsystem_name(dt, existing->dev), - dev_name(existing->dev)); - return NULL; - } else if (dm_is_dm_major(MAJOR(existing->dev->dev)) && - !dm_is_dm_major(MAJOR(dev->dev))) { - log_very_verbose("Ignoring duplicate PV %s on " - "%s - using dm %s", - pvid, dev_name(dev), - dev_name(existing->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; + + /* + * Here are different devices with the same pvid: + * duplicates. See comment above. + */ + + /* + * 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.) + */ + _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. + */ + 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 (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 (!dev_subsystem_part_major(dt, existing->dev) && - dev_subsystem_part_major(dt, dev)) - log_very_verbose("Duplicate PV %s on %s - " - "using %s %s", pvid, - dev_name(existing->dev), - dev_subsystem_name(dt, existing->dev), - dev_name(dev)); - else if (!dm_is_dm_major(MAJOR(existing->dev->dev)) && - dm_is_dm_major(MAJOR(dev->dev))) - log_very_verbose("Duplicate PV %s on %s - " - "using dm %s", pvid, - dev_name(existing->dev), - dev_name(dev)); - /* FIXME If both dm, check dependencies */ - //else if (dm_is_dm_major(MAJOR(existing->dev->dev)) && - //dm_is_dm_major(MAJOR(dev->dev))) - // - else if (!strcmp(pvid_s, existing->dev->pvid)) { - log_error("Found duplicate PV %s: using %s not %s", - pvid_s, - dev_name(existing->dev), - dev_name(dev)); - strncpy(dev->pvid, pvid_s, sizeof(dev->pvid)); - _found_duplicate_pvs = 1; + + } 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. */ + 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) { + /* + * Both existing and new are dm devices. + * Use new, replace old. + * (because this is how it's worked previously.) + */ + 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 more recent, 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: is one often better than another? + * Transient duplicates might be handled + * better by staying with the old? + * Use new, replace old. (for now) + */ + 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 more recent, replacing %s", + dev_name(dev), + dev_name(existing->dev)); } + } else { + /* + * The new dev is the same as the existing dev. + * + * Why can't we just return NULL here if the device + * already exists? + */ + log_verbose("Found same device %s with same pvid %s", + dev_name(existing->dev), pvid_s); } - if (strcmp(pvid_s, existing->dev->pvid)) - log_debug_cache("Updating pvid cache to %s (%s) from %s (%s)", - pvid_s, dev_name(dev), - existing->dev->pvid, dev_name(existing->dev)); - /* Switch over to new preferred device */ + + /* + * When could this ever happen? + */ + if (strcmp(pvid_s, existing->dev->pvid)) { + log_warn("Replacing dev %s pvid %s with dev %s pvid %s", + dev_name(existing->dev), existing->dev->pvid, + dev_name(dev), pvid_s); + } + + /* + * Switch over to new preferred device. + */ existing->dev = dev; info = existing; /* Has labeller changed? */ |