stable-2.02 - filters: persistent filter hides rename failure
by Zdenek Kabelac
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=e478ef2f59da65ebf47...
Commit: e478ef2f59da65ebf47ca7a1534f112669858d5a
Parent: f33ccaee45eb75c3aeac8a6647997de87ff3f207
Author: Zdenek Kabelac <zkabelac(a)redhat.com>
AuthorDate: Sat Oct 24 01:27:19 2020 +0200
Committer: Zdenek Kabelac <zkabelac(a)redhat.com>
CommitterDate: Sat Oct 24 01:31:40 2020 +0200
filters: persistent filter hides rename failure
Filters are saved on command's exit path - and as such they are
not protected by any locks.
This means 2 running commands may 'race' here and 2nd. command
may have already renamed filename for the 1st. command.
So if the rename fails here - we will only use log_sys_debug,
as the failure doesn't cause command failure result.
---
lib/filters/filter-persistent.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/lib/filters/filter-persistent.c b/lib/filters/filter-persistent.c
index 058694228..afd7eb541 100644
--- a/lib/filters/filter-persistent.c
+++ b/lib/filters/filter-persistent.c
@@ -272,8 +272,7 @@ static int _persistent_filter_dump(struct dev_filter *f, int merge_existing)
goto_out;
if (rename(tmp_file, pf->file))
- log_error("%s: rename to %s failed: %s", tmp_file, pf->file,
- strerror(errno));
+ log_sys_debug("rename", tmp_file);
r = 1;
2 years, 3 months
master - get dev size when setting pv device
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=6226512ad2d6fd30a4b...
Commit: 6226512ad2d6fd30a4b55da6a4d4c370fa7fad09
Parent: f3b723cd8d7b0ac3c7580ab6861666178bfba34f
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Wed Oct 21 16:21:50 2020 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Thu Oct 22 13:19:17 2020 -0500
get dev size when setting pv device
In some cases the dev size may not have been read yet
in set_pv_devices(). In this case get the dev size
before comparing the dev size with the pv size.
---
lib/metadata/metadata.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c
index 5639377e5..d04b8d8ba 100644
--- a/lib/metadata/metadata.c
+++ b/lib/metadata/metadata.c
@@ -3578,13 +3578,17 @@ static void _set_pv_device(struct format_instance *fid,
* full a md check in label scan
*/
if (dev && cmd && cmd->md_component_detection && !cmd->use_full_md_check) {
+ uint64_t devsize = dev->size;
+
+ if (!devsize && !dev_get_size(dev, &devsize))
+ log_debug("No size for %s when setting PV dev.", dev_name(dev));
/* PV larger than dev not common, check for md component */
- if (pv->size > dev->size)
+ else if (pv->size > devsize)
do_check = 1;
/* dev larger than PV can be common, limit check to auto mode */
- else if ((pv->size < dev->size) && !strcmp(cmd->md_component_checks, "auto"))
+ else if ((pv->size < devsize) && !strcmp(cmd->md_component_checks, "auto"))
do_check = 1;
if (do_check && dev_is_md_component(dev, NULL, 1)) {
2 years, 3 months
master - pvscan: rework to improve PVs without metadata
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=f3b723cd8d7b0ac3c75...
Commit: f3b723cd8d7b0ac3c7580ab6861666178bfba34f
Parent: a7f195b7e8f27c292761c604c1697c1d97cd5c1d
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Thu Oct 15 14:11:08 2020 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Thu Oct 22 13:14:31 2020 -0500
pvscan: rework to improve PVs without metadata
Restructure the pvscan code, and add new temporary files
that list pvids in a VG, used for processing PVs that
have no metadata.
The new temp files, in /run/lvm/pvs_lookup/<vgname>, allow a
proper pvscan --cache to be done on PVs that have no metadata.
pvscan --cache <dev> is only supposed to read <dev>, but when
<dev> has no metadata, this had not been possible. The
command had to fall back to scanning all devices to read all
VG metadata to get the list of all PVIDs needed to check for
a complete VG. Now, the temp file can be used in place of
reading metadata from all PVs on the system.
---
test/shell/hints.sh | 2 +-
test/shell/metadata-bad-text.sh | 29 +-
test/shell/pvscan-autoactivate.sh | 8 +-
tools/pvscan.c | 1312 ++++++++++++++++++++-----------------
4 files changed, 733 insertions(+), 618 deletions(-)
diff --git a/test/shell/hints.sh b/test/shell/hints.sh
index 5ac72282b..b2a53c678 100644
--- a/test/shell/hints.sh
+++ b/test/shell/hints.sh
@@ -330,7 +330,7 @@ rm $HINTS $PREV
pvs
cp $HINTS $PREV
# this next pvscan creates newhints to trigger a refresh
-pvscan --cache "$dev5"
+pvscan --cache "$dev4"
cat $NEWHINTS
# this next pvs creates new hints
pvs
diff --git a/test/shell/metadata-bad-text.sh b/test/shell/metadata-bad-text.sh
index 421706a9e..971b861ca 100644
--- a/test/shell/metadata-bad-text.sh
+++ b/test/shell/metadata-bad-text.sh
@@ -297,20 +297,13 @@ vgchange -an $vg
_clear_online_files
# pvscan of one dev with bad metadata will result
-# in the dev acting like a PV without metadata,
-# which causes pvscan to scan all devs to find the
-# VG it belongs to. In this case it finds all the
-# other devs in the VG are online and activates the
-# VG.
+# in the pvid online file being created but the
+# VG will not be known.
pvscan --cache -aay "$dev1"
ls "$RUNDIR/lvm/pvs_online/$PVID1"
-ls "$RUNDIR/lvm/pvs_online/$PVID2"
-ls "$RUNDIR/lvm/pvs_online/$PVID3"
-ls "$RUNDIR/lvm/vgs_online/$vg"
-
-lvs $vg
-check lv_field $vg/$lv1 lv_active "active"
-vgchange -an $vg
+not ls "$RUNDIR/lvm/pvs_online/$PVID2"
+not ls "$RUNDIR/lvm/pvs_online/$PVID3"
+not ls "$RUNDIR/lvm/vgs_online/$vg"
_clear_online_files
@@ -321,18 +314,6 @@ not ls "$RUNDIR/lvm/pvs_online/$PVID2"
ls "$RUNDIR/lvm/pvs_online/$PVID3"
not ls "$RUNDIR/lvm/vgs_online/$vg"
-# scan the next pv with bad metadata, causes pvscan to scan
-# and find all PVs and activate
-pvscan --cache -aay "$dev2"
-ls "$RUNDIR/lvm/pvs_online/$PVID1"
-ls "$RUNDIR/lvm/pvs_online/$PVID2"
-ls "$RUNDIR/lvm/pvs_online/$PVID3"
-ls "$RUNDIR/lvm/vgs_online/$vg"
-
-check lv_field $vg/$lv1 lv_active "active"
-vgchange -an $vg
-
-
vgck --updatemetadata $vg
pvs 2>&1 | tee out
diff --git a/test/shell/pvscan-autoactivate.sh b/test/shell/pvscan-autoactivate.sh
index d79a7e3b9..9f7fc2ee7 100644
--- a/test/shell/pvscan-autoactivate.sh
+++ b/test/shell/pvscan-autoactivate.sh
@@ -16,6 +16,7 @@ RUNDIR="/run"
test -d "$RUNDIR" || RUNDIR="/var/run"
PVS_ONLINE_DIR="$RUNDIR/lvm/pvs_online"
VGS_ONLINE_DIR="$RUNDIR/lvm/vgs_online"
+PVS_LOOKUP_DIR="$RUNDIR/lvm/pvs_lookup"
# FIXME: kills logic for running system
_clear_online_files() {
@@ -23,6 +24,7 @@ _clear_online_files() {
aux udev_wait
rm -f "$PVS_ONLINE_DIR"/*
rm -f "$VGS_ONLINE_DIR"/*
+ rm -f "$PVS_LOOKUP_DIR"/*
}
. lib/inittest
@@ -34,6 +36,7 @@ lvcreate -n $lv1 -l 4 -a n $vg1
test -d "$PVS_ONLINE_DIR" || mkdir -p "$PVS_ONLINE_DIR"
test -d "$VGS_ONLINE_DIR" || mkdir -p "$VGS_ONLINE_DIR"
+test -d "$PVS_LOOKUP_DIR" || mkdir -p "$PVS_LOOKUP_DIR"
_clear_online_files
# check pvscan with no args scans and activates all
@@ -93,12 +96,9 @@ pvscan --cache -aay "$dev1"
check lv_field $vg1/$lv1 lv_active "active"
lvchange -an $vg1
-# test case where dev without metadata is scanned first
-# which triggers scanning all, which finds both
-
_clear_online_files
pvscan --cache -aay "$dev1"
-check lv_field $vg1/$lv1 lv_active "active"
+check lv_field $vg1/$lv1 lv_active ""
pvscan --cache -aay "$dev2"
check lv_field $vg1/$lv1 lv_active "active"
lvchange -an $vg1
diff --git a/tools/pvscan.c b/tools/pvscan.c
index 4d811da55..55c708682 100644
--- a/tools/pvscan.c
+++ b/tools/pvscan.c
@@ -36,9 +36,15 @@ struct pvscan_aa_params {
unsigned int activate_errors;
};
+/*
+ * Used by _pvscan_aa_quick() which is an optimization used
+ * when one vg is being activated.
+ */
+static struct volume_group *saved_vg;
static const char *_pvs_online_dir = DEFAULT_RUN_DIR "/pvs_online";
static const char *_vgs_online_dir = DEFAULT_RUN_DIR "/vgs_online";
+static const char *_pvs_lookup_dir = DEFAULT_RUN_DIR "/pvs_lookup";
static int _pvscan_display_pv(struct cmd_context *cmd,
struct physical_volume *pv,
@@ -234,6 +240,20 @@ static int _online_pvid_file_read(char *path, int *major, int *minor, char *vgna
return 1;
}
+static void _lookup_file_remove(char *vgname)
+{
+ char path[PATH_MAX];
+
+ if (dm_snprintf(path, sizeof(path), "%s/%s", _pvs_lookup_dir, vgname) < 0) {
+ log_error("Path %s/%s is too long.", _pvs_lookup_dir, vgname);
+ return;
+ }
+
+ log_debug("Unlink pvs_lookup: %s", path);
+
+ if (unlink(path))
+ log_sys_debug("unlink", path);
+}
/*
* When a PV goes offline, remove the vg online file for that VG
@@ -295,8 +315,10 @@ static void _online_pvid_file_remove_devno(int major, int minor)
if (unlink(path))
log_sys_debug("unlink", path);
- if (file_vgname[0])
+ if (file_vgname[0]) {
_online_vg_file_remove(file_vgname);
+ _lookup_file_remove(file_vgname);
+ }
}
}
if (closedir(dir))
@@ -349,13 +371,13 @@ static int _online_pvid_file_create(struct device *dev, const char *vgname)
}
if ((len1 = dm_snprintf(buf, sizeof(buf), "%d:%d\n", major, minor)) < 0) {
- log_error("Cannot create online pv file for %d:%d.", major, minor);
+ log_error("Cannot create online file path for %s %d:%d.", dev_name(dev), major, minor);
return 0;
}
if (vgname) {
if ((len2 = dm_snprintf(buf + len1, sizeof(buf) - len1, "vg:%s\n", vgname)) < 0) {
- log_warn("Incomplete online pv file for %d:%d vg %s.", major, minor, vgname);
+ log_warn("Incomplete online file for %s %d:%d vg %s.", dev_name(dev), major, minor, vgname);
/* can still continue without vgname */
len2 = 0;
}
@@ -369,18 +391,19 @@ static int _online_pvid_file_create(struct device *dev, const char *vgname)
if (fd < 0) {
if (errno == EEXIST)
goto check_duplicate;
- log_error("Failed to open create %s: %d", path, errno);
+ log_error("Failed to create online file for %s path %s error %d", dev_name(dev), path, errno);
return 0;
}
while (len > 0) {
rv = write(fd, buf, len);
if (rv < 0) {
- log_error("Failed to write fd %d buf %s dev %s to %s: %d",
- fd, buf, dev_name(dev), path, errno);
+ /* file exists so it still works in part */
+ log_warn("Cannot write online file for %s to %s error %d",
+ dev_name(dev), path, errno);
if (close(fd))
log_sys_debug("close", path);
- return 0;
+ return 1;
}
len -= rv;
}
@@ -440,412 +463,258 @@ static int _online_pvid_file_exists(const char *pvid)
rv = stat(path, &buf);
if (!rv) {
- log_debug("Check pv online: yes");
+ log_debug("Check pv online %s: yes", pvid);
return 1;
}
- log_debug("Check pv online: no");
+ log_debug("Check pv online %s: no", pvid);
return 0;
}
-static void _online_dir_setup(void)
-{
- struct stat st;
- int rv;
-
- if (!stat(DEFAULT_RUN_DIR, &st))
- goto do_pvs;
-
- log_debug("Creating run_dir.");
- dm_prepare_selinux_context(DEFAULT_RUN_DIR, S_IFDIR);
- rv = mkdir(DEFAULT_RUN_DIR, 0755);
- dm_prepare_selinux_context(NULL, 0);
-
- if ((rv < 0) && stat(DEFAULT_RUN_DIR, &st))
- log_error("Failed to create %s %d", DEFAULT_RUN_DIR, errno);
-
-do_pvs:
- if (!stat(_pvs_online_dir, &st))
- goto do_vgs;
-
- log_debug("Creating pvs_online_dir.");
- dm_prepare_selinux_context(_pvs_online_dir, S_IFDIR);
- rv = mkdir(_pvs_online_dir, 0755);
- dm_prepare_selinux_context(NULL, 0);
-
- if ((rv < 0) && stat(_pvs_online_dir, &st))
- log_error("Failed to create %s %d", _pvs_online_dir, errno);
-
-do_vgs:
- if (!stat(_vgs_online_dir, &st))
- return;
-
- log_debug("Creating vgs_online_dir.");
- dm_prepare_selinux_context(_vgs_online_dir, S_IFDIR);
- rv = mkdir(_vgs_online_dir, 0755);
- dm_prepare_selinux_context(NULL, 0);
-
- if ((rv < 0) && stat(_vgs_online_dir, &st))
- log_error("Failed to create %s %d", _vgs_online_dir, errno);
-}
-
-static int _online_pv_found(struct cmd_context *cmd,
- struct device *dev, struct dm_list *dev_args,
- struct volume_group *vg,
- struct dm_list *found_vgnames)
+static int _write_lookup_file(struct cmd_context *cmd, struct volume_group *vg)
{
+ char path[PATH_MAX];
+ char line[ID_LEN+2];
struct pv_list *pvl;
- int pvids_not_online = 0;
- int dev_args_in_vg = 0;
-
- /*
- * Create file named for pvid to record this PV is online.
- */
+ int fd;
- if (!_online_pvid_file_create(dev, vg ? vg->name : NULL))
- return_0;
+ if (dm_snprintf(path, sizeof(path), "%s/%s", _pvs_lookup_dir, vg->name) < 0) {
+ log_error("Path %s/%s is too long.", _pvs_lookup_dir, vg->name);
+ return 0;
+ }
- if (!vg || !found_vgnames) {
- log_print("pvscan[%d] PV %s online.", getpid(), dev_name(dev));
- return 1;
+ fd = open(path, O_CREAT | O_EXCL | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
+ if (fd < 0) {
+ /* not a problem, can happen when multiple pvscans run at once */
+ log_debug("Did not create %s: %d", path, errno);
+ return 0;
}
- /*
- * Check if all the PVs for this VG are online. This is only
- * needed when autoactivating the VG which should be run only
- * when the VG is complete. If the arrival of this dev completes
- * the VG, then we want to activate the VG.
- */
+ log_debug("write_lookup_file %s", path);
dm_list_iterate_items(pvl, &vg->pvs) {
- if (!_online_pvid_file_exists((const char *)&pvl->pv->id.uuid))
- pvids_not_online++;
-
- /* Check if one of the devs on the command line is in this VG. */
- if (dev_args && dev_in_device_list(pvl->pv->dev, dev_args))
- dev_args_in_vg = 1;
- }
-
- /*
- * Return if we did not find an online file for one of the PVIDs
- * in the VG, which means the VG is not yet complete.
- */
+ memcpy(&line, &pvl->pv->id.uuid, ID_LEN);
+ line[ID_LEN] = '\n';
+ line[ID_LEN+1] = '\0';
- if (pvids_not_online) {
- log_print("pvscan[%d] PV %s online, VG %s incomplete (need %d).",
- getpid(), dev_name(dev), vg->name, pvids_not_online);
- return 1;
+ if (write(fd, &line, ID_LEN+1) < 0)
+ log_sys_debug("write", path);
}
- log_print("pvscan[%d] PV %s online, VG %s is complete.", getpid(), dev_name(dev), vg->name);
-
- /*
- * When all PVIDs from the VG are online, then add vgname to
- * found_vgnames.
- */
-
- log_debug("online dev %s completes VG %s.", dev_name(dev), vg->name);
-
- /*
- * We either want to return all complete VGs that are found on any devs
- * we are scanning, or we want to return complete VGs only when they
- * contain PVs that were specified on the command line.
- */
-
- if (!dev_args || dev_args_in_vg) {
- log_debug("online dev %s can autoactivate VG %s", dev_name(dev), vg->name);
- if (!str_list_add(cmd->mem, found_vgnames, dm_pool_strdup(cmd->mem, vg->name)))
- stack;
- }
+ if (close(fd))
+ log_sys_debug("close", path);
return 1;
}
-struct _pvscan_baton {
- struct cmd_context *cmd;
- struct volume_group *vg;
- struct format_instance *fid;
-};
-
-/*
- * If no mdas on this PV have a usable copy of the metadata,
- * then the PV behaves like a PV without metadata, which
- * causes the pvscan to scan all devs to find a copy of the
- * metadata on another dev to check if the VG is now complete
- * and can be activated.
- */
-
-static int _online_pvscan_single(struct metadata_area *mda, void *baton)
+static int _lookup_file_contains_pvid(FILE *fp, char *pvid)
{
- struct _pvscan_baton *b = baton;
- struct volume_group *vg;
- struct device *mda_dev = mda_get_device(mda);
+ char line[64];
- if (mda_is_ignored(mda))
- return 1;
- vg = mda->ops->vg_read(b->cmd, b->fid, "", mda, NULL, NULL);
- if (!vg) {
- /*
- * Many or most cases of bad metadata would be found in
- * the scan, and the mda removed from the list, so we
- * would not get here to attempt this.
- */
- log_print("pvscan[%d] metadata error in mda %d on %s.",
- getpid(), mda->mda_num, dev_name(mda_dev));
- return 1;
+ while (fgets(line, sizeof(line), fp)) {
+ if (!memcmp(pvid, line, ID_LEN))
+ return 1;
}
-
- log_debug("pvscan vg_read %s seqno %u in mda %d on %s",
- vg->name, vg->seqno, mda->mda_num, dev_name(mda_dev));
-
- if (!b->vg || vg->seqno > b->vg->seqno)
- b->vg = vg;
- else if (b->vg)
- release_vg(vg);
-
- return 1;
+ return 0;
}
-static struct volume_group *_find_saved_vg(struct dm_list *saved_vgs, const char *vgname)
+static void _lookup_file_count_pvid_files(FILE *fp, const char *vgname, int *pvs_online, int *pvs_offline)
{
- struct vg_list *vgl;
+ char line[64];
+ char pvid[ID_LEN+1];
- dm_list_iterate_items(vgl, saved_vgs) {
- if (!strcmp(vgname, vgl->vg->name))
- return vgl->vg;
- }
- return NULL;
-}
+ log_debug("checking all pvid files using lookup file for %s", vgname);
-/*
- * disable_remove is 1 when resetting the online state, which begins with
- * removing all pvid files, and then creating new pvid files for PVs that
- * are found, so we don't need to try to remove pvid files here when a PV
- * is not found on a device.
- */
+ rewind(fp);
-static int _online_pvscan_one(struct cmd_context *cmd, struct device *dev,
- struct dm_list *dev_args,
- struct dm_list *found_vgnames,
- struct dm_list *saved_vgs,
- int disable_remove,
- const char **pvid_without_metadata)
-{
- struct lvmcache_info *info;
- struct vg_list *vgl;
- struct _pvscan_baton baton;
- const struct format_type *fmt;
- /* Create a dummy instance. */
- struct format_instance_ctx fic = { .type = 0 };
- uint32_t ext_version;
- uint32_t ext_flags;
- int ret = 0;
-
- log_debug("pvscan metadata from dev %s", dev_name(dev));
-
- if (udev_dev_is_mpath_component(dev)) {
- log_print("pvscan[%d] ignore multipath component %s.", getpid(), dev_name(dev));
- return 1;
- }
+ while (fgets(line, sizeof(line), fp)) {
+ memset(pvid, 0, sizeof(pvid));
+ memcpy(pvid, line, ID_LEN);
- if (!(info = lvmcache_info_from_pvid(dev->pvid, dev, 0))) {
- log_debug("No PV info found on %s for PVID %s.", dev_name(dev), dev->pvid);
- if (!disable_remove)
- _online_pvid_file_remove_devno((int)MAJOR(dev->dev), (int)MINOR(dev->dev));
- return 1;
- }
+ if (strlen(pvid) != ID_LEN) {
+ log_debug("ignore lookup file line %s", line);
+ continue;
+ }
- if (!lvmcache_get_label(info)) {
- log_debug("No PV label found for %s.", dev_name(dev));
- if (!disable_remove)
- _online_pvid_file_remove_devno((int)MAJOR(dev->dev), (int)MINOR(dev->dev));
- return 1;
+ if (_online_pvid_file_exists((const char *)pvid))
+ (*pvs_online)++;
+ else
+ (*pvs_offline)++;
}
+}
- ext_version = lvmcache_ext_version(info);
- ext_flags = lvmcache_ext_flags(info);
+/*
+ * There is no synchronization between the one doing write_lookup_file and the
+ * other doing check_lookup_file. The pvscan doing write thinks the VG is
+ * incomplete, and the pvscan doing check may also conclude the VG is
+ * incomplete if it happens prior to the write. If neither pvscan thinks the
+ * VG is complete then neither will activate it. To solve this race, the
+ * pvscan doing write will recheck pvid online files after the write in which
+ * case it would find the pvid online file from the pvscan doing check.
+ */
- if ((ext_version >= 2) && !(ext_flags & PV_EXT_USED)) {
- log_print("pvscan[%d] PV %s not used.", getpid(), dev_name(dev));
- return 1;
- }
+/*
+ * The VG is not yet complete, more PVs need to arrive, and
+ * some of those PVs may not have metadata on them. Without
+ * metadata, pvscan for those PVs will be unable to determine
+ * if the VG is complete. We don't know if other PVs will have
+ * metadata or not.
+ *
+ * Save a temp file with a list of pvids in the vg, to be used
+ * by a later pvscan on a PV without metadata. The later
+ * pvscan will check for vg completeness using the temp file
+ * since it has no vg metadata to use.
+ *
+ * Only the first pvscan for the VG creates the temp file. If
+ * there are multiple pvscans for the same VG running at once,
+ * they all race to create the lookup file, and only the first
+ * to create the file will write it.
+ *
+ * After writing the temp file, we count pvid online files for
+ * the VG again - the VG could now be complete since multiple
+ * pvscans will run concurrently. We repeat this to cover a
+ * race where another pvscan was running _check_lookup_file
+ * during our _write_lookup_file. _write_lookup_file may not
+ * have finished before _check_lookup_file, which would cause
+ * the other pvscan to not find the pvid it's looking for, and
+ * conclude the VG is incomplete, while we also think the VG is
+ * incomplete. If we recheck online files after write_lookup,
+ * we will see the pvid online file from the other pvscan and
+ * see the VG is complete.
+ */
- fmt = lvmcache_fmt(info);
+static int _count_pvid_files_from_lookup_file(struct cmd_context *cmd, struct device *dev,
+ int *pvs_online, int *pvs_offline,
+ const char **vgname_out)
+{
+ char path[PATH_MAX];
+ FILE *fp;
+ DIR *dir;
+ struct dirent *de;
+ const char *vgname = NULL;
+ int online = 0, offline = 0;
- memset(&baton, 0, sizeof(baton));
- baton.cmd = cmd;
- baton.vg = NULL;
- baton.fid = fmt->ops->create_instance(fmt, &fic);
+ *pvs_online = 0;
+ *pvs_offline = 0;
- if (!baton.fid) {
- ret = 0;
- goto_out;
- }
+ if (!(dir = opendir(_pvs_lookup_dir)))
+ goto_bad;
- lvmcache_foreach_mda(info, _online_pvscan_single, &baton);
+ /*
+ * Read each file in pvs_lookup to find dev->pvid, and if it's
+ * found save the vgname of the file it's found in.
+ */
+ while ((de = readdir(dir))) {
+ if (de->d_name[0] == '.')
+ continue;
- if (!baton.vg) {
- log_print("pvscan[%d] PV %s has no VG metadata.", getpid(), dev_name(dev));
- if (pvid_without_metadata)
- *pvid_without_metadata = dm_pool_strdup(cmd->mem, dev->pvid);
- fmt->ops->destroy_instance(baton.fid);
- } else {
- set_pv_devices(baton.fid, baton.vg, NULL);
- }
+ memset(path, 0, sizeof(path));
+ snprintf(path, sizeof(path), "%s/%s", _pvs_lookup_dir, de->d_name);
- /* This check repeated because set_pv_devices can do new md check. */
- if (dev->flags & DEV_IS_MD_COMPONENT) {
- log_print("pvscan[%d] PV %s ignore MD component, ignore metadata.", getpid(), dev_name(dev));
- if (baton.vg)
- release_vg(baton.vg);
- else
- fmt->ops->destroy_instance(baton.fid);
- return 1;
- }
+ if (!(fp = fopen(path, "r"))) {
+ log_warn("Failed to open %s", path);
+ continue;
+ }
- if (baton.vg && vg_is_shared(baton.vg)) {
- log_print("pvscan[%d] PV %s ignore shared VG.", getpid(), dev_name(dev));
- release_vg(baton.vg);
- return 1;
- }
+ if (_lookup_file_contains_pvid(fp, dev->pvid)) {
+ vgname = dm_pool_strdup(cmd->mem, de->d_name);
+ break;
+ }
- if (baton.vg &&
- baton.vg->system_id && baton.vg->system_id[0] &&
- cmd->system_id && cmd->system_id[0] &&
- vg_is_foreign(baton.vg)) {
- log_verbose("Ignore PV %s with VG system id %s with our system id %s",
- dev_name(dev), baton.vg->system_id, cmd->system_id);
- log_print("pvscan[%d] PV %s ignore foreign VG.", getpid(), dev_name(dev));
- release_vg(baton.vg);
- return 1;
+ if (fclose(fp))
+ stack;
}
+ if (closedir(dir))
+ log_sys_debug("closedir", _pvs_lookup_dir);
- ret = _online_pv_found(cmd, dev, dev_args, baton.vg, found_vgnames);
+ if (!vgname)
+ goto_bad;
/*
- * Save vg's in case they need to be used at the end for checking PVs
- * without metadata (in _check_vg_with_pvid_complete), or activating.
+ * stat pvid online file of each pvid listed in this file
+ * the list of pvids from the file is the alternative to
+ * using vg->pvs
*/
- if (saved_vgs && baton.vg) {
- if (!_find_saved_vg(saved_vgs, baton.vg->name)) {
- if ((vgl = malloc(sizeof(struct vg_list)))) {
- vgl->vg = baton.vg;
- baton.vg = NULL;
- dm_list_add(saved_vgs, &vgl->list);
- }
- }
- }
+ _lookup_file_count_pvid_files(fp, vgname, &online, &offline);
- if (baton.vg)
- release_vg(baton.vg);
-out:
- return ret;
+ if (fclose(fp))
+ stack;
+
+ *pvs_online = online;
+ *pvs_offline = offline;
+ *vgname_out = vgname;
+ return 1;
+bad:
+ return 0;
}
-/*
- * This is to handle the case where pvscan --cache -aay (with no dev args)
- * gets to the final PV, completing the VG, but that final PV does not
- * have VG metadata. In this case, we need to use VG metadata from a
- * previously scanned PV in the same VG, which we saved in the saved_vgs
- * list. Using this saved metadata, we can find which VG this PVID
- * belongs to, and then check if that VG is now complete, and if so
- * add the VG name to the list of complete VGs to be autoactivated.
- *
- * The "pvid" arg here is the PVID of the PV that has just been scanned
- * and didn't have metadata. We look through previously scanned VG
- * metadata to find the VG this PVID belongs to, and then check that VG
- * metadata to see if all the PVs are now online.
- */
-static void _check_vg_with_pvid_complete(struct cmd_context *cmd,
- struct dm_list *found_vgnames,
- struct dm_list *saved_vgs,
- const char *pvid)
+static void _online_dir_setup(void)
{
- struct vg_list *vgl;
- struct pv_list *pvl;
- struct volume_group *vg;
- int pvids_not_online = 0;
- int found;
+ struct stat st;
+ int rv;
- dm_list_iterate_items(vgl, saved_vgs) {
- vg = vgl->vg;
- found = 0;
+ if (!stat(DEFAULT_RUN_DIR, &st))
+ goto do_pvs;
- dm_list_iterate_items(pvl, &vg->pvs) {
- if (strcmp((const char *)&pvl->pv->id.uuid, pvid))
- continue;
- found = 1;
- break;
- }
- if (!found)
- continue;
+ log_debug("Creating run_dir.");
+ dm_prepare_selinux_context(DEFAULT_RUN_DIR, S_IFDIR);
+ rv = mkdir(DEFAULT_RUN_DIR, 0755);
+ dm_prepare_selinux_context(NULL, 0);
- dm_list_iterate_items(pvl, &vg->pvs) {
- if (!_online_pvid_file_exists((const char *)&pvl->pv->id.uuid)) {
- pvids_not_online++;
- break;
- }
- }
+ if ((rv < 0) && stat(DEFAULT_RUN_DIR, &st))
+ log_error("Failed to create %s %d", DEFAULT_RUN_DIR, errno);
- if (!pvids_not_online) {
- log_debug("pvid %s makes complete VG %s", pvid, vg->name);
- if (!str_list_add(cmd->mem, found_vgnames, dm_pool_strdup(cmd->mem, vg->name)))
- stack;
- } else
- log_debug("pvid %s incomplete VG %s", pvid, vg->name);
- break;
- }
-}
+do_pvs:
+ if (!stat(_pvs_online_dir, &st))
+ goto do_vgs;
-/*
- * dev_args is the list of devices that were specified on the
- * pvscan command line.
- *
- * . When dev_args is NULL, any complete VGs that are found will
- * be returned in found_vgnames.
- *
- * . When dev_args is set, then complete VGs that that contain
- * devs in dev_args will be returned in found_vgnames.
- *
- * found_vgnames is null for 'pvscan --cache' (without -aay)
- * since the command does not need to keep track of complete
- * vgs since it does not need to activate them.
- */
+ log_debug("Creating pvs_online_dir.");
+ dm_prepare_selinux_context(_pvs_online_dir, S_IFDIR);
+ rv = mkdir(_pvs_online_dir, 0755);
+ dm_prepare_selinux_context(NULL, 0);
-static void _online_pvscan_all_devs(struct cmd_context *cmd,
- struct dm_list *found_vgnames,
- struct dm_list *saved_vgs,
- struct dm_list *dev_args)
-{
- struct dev_iter *iter;
- struct device *dev;
- const char *pvid_without_metadata;
+ if ((rv < 0) && stat(_pvs_online_dir, &st))
+ log_error("Failed to create %s %d", _pvs_online_dir, errno);
- lvmcache_label_scan(cmd);
+do_vgs:
+ if (!stat(_vgs_online_dir, &st))
+ goto do_lookup;
+
+ log_debug("Creating vgs_online_dir.");
+ dm_prepare_selinux_context(_vgs_online_dir, S_IFDIR);
+ rv = mkdir(_vgs_online_dir, 0755);
+ dm_prepare_selinux_context(NULL, 0);
+
+ if ((rv < 0) && stat(_vgs_online_dir, &st))
+ log_error("Failed to create %s %d", _vgs_online_dir, errno);
- if (!(iter = dev_iter_create(cmd->filter, 1))) {
- log_error("dev_iter creation failed");
+do_lookup:
+ if (!stat(_pvs_lookup_dir, &st))
return;
- }
- while ((dev = dev_iter_get(cmd, iter))) {
- if (sigint_caught()) {
- stack;
- break;
- }
+ log_debug("Creating pvs_lookup_dir.");
+ dm_prepare_selinux_context(_pvs_lookup_dir, S_IFDIR);
+ rv = mkdir(_pvs_lookup_dir, 0755);
+ dm_prepare_selinux_context(NULL, 0);
- pvid_without_metadata = NULL;
+ if ((rv < 0) && stat(_pvs_lookup_dir, &st))
+ log_error("Failed to create %s %d", _pvs_lookup_dir, errno);
- if (!_online_pvscan_one(cmd, dev, dev_args, found_vgnames, saved_vgs, 1, &pvid_without_metadata)) {
- stack;
- break;
- }
- /* This PV without metadata may complete a VG. */
- if (pvid_without_metadata && found_vgnames)
- _check_vg_with_pvid_complete(cmd, found_vgnames, saved_vgs, pvid_without_metadata);
- }
+}
- dev_iter_destroy(iter);
+static void _count_pvid_files(struct volume_group *vg, int *pvs_online, int *pvs_offline)
+{
+ struct pv_list *pvl;
+
+ *pvs_online = 0;
+ *pvs_offline = 0;
+
+ dm_list_iterate_items(pvl, &vg->pvs) {
+ if (_online_pvid_file_exists((const char *)&pvl->pv->id.uuid))
+ (*pvs_online)++;
+ else
+ (*pvs_offline)++;
+ }
}
static int _pvscan_aa_single(struct cmd_context *cmd, const char *vg_name,
@@ -929,7 +798,6 @@ static int _online_vg_file_create(struct cmd_context *cmd, const char *vgname)
*/
static int _get_devs_from_saved_vg(struct cmd_context *cmd, const char *vgname,
- struct dm_list *saved_vgs,
struct dm_list *devs)
{
char path[PATH_MAX];
@@ -951,8 +819,8 @@ static int _get_devs_from_saved_vg(struct cmd_context *cmd, const char *vgname,
* reading all the pvid online files, see which have a matching vg
* name, and getting the device numbers from those files.)
*/
- if (!(vg = _find_saved_vg(saved_vgs, vgname)))
- return_0;
+ if (!(vg = saved_vg))
+ goto_bad;
dm_list_iterate_items(pvl, &vg->pvs) {
pvid = (const char *)&pvl->pv->id.uuid;
@@ -969,14 +837,14 @@ static int _get_devs_from_saved_vg(struct cmd_context *cmd, const char *vgname,
if (file_vgname[0] && strcmp(vgname, file_vgname)) {
log_error("Wrong VG found for %d:%d PVID %s: %s vs %s",
file_major, file_minor, pvid, vgname, file_vgname);
- return 0;
+ goto bad;
}
devno = MKDEV(file_major, file_minor);
if (!(dev = dev_cache_get_by_devt(cmd, devno, NULL, NULL))) {
log_error("No device found for %d:%d PVID %s", file_major, file_minor, pvid);
- return 0;
+ goto bad;
}
name1 = dev_name(dev);
@@ -986,11 +854,11 @@ static int _get_devs_from_saved_vg(struct cmd_context *cmd, const char *vgname,
if (!id_write_format((const struct id *)pvid, uuidstr, sizeof(uuidstr)))
uuidstr[0] = '\0';
log_print("PVID %s read from %s last written to %s.", uuidstr, name1, name2);
- return 0;
+ goto bad;
}
if (!(devl = zalloc(sizeof(*devl))))
- return_0;
+ goto_bad;
devl->dev = dev;
dm_list_add(devs, &devl->list);
@@ -998,6 +866,13 @@ static int _get_devs_from_saved_vg(struct cmd_context *cmd, const char *vgname,
}
return 1;
+
+bad:
+ if (saved_vg) {
+ release_vg(saved_vg);
+ saved_vg = NULL;
+ }
+ return 0;
}
/*
@@ -1036,7 +911,7 @@ static int _get_devs_from_saved_vg(struct cmd_context *cmd, const char *vgname,
*/
static int _pvscan_aa_quick(struct cmd_context *cmd, struct pvscan_aa_params *pp, const char *vgname,
- struct dm_list *saved_vgs, int *no_quick)
+ int *no_quick)
{
struct dm_list devs; /* device_list */
struct volume_group *vg;
@@ -1054,7 +929,7 @@ static int _pvscan_aa_quick(struct cmd_context *cmd, struct pvscan_aa_params *pp
* The pvs_online/PVID files gives us the devnums for PVIDs.
* The dev_cache gives us struct devices from the devnums.
*/
- if (!_get_devs_from_saved_vg(cmd, vgname, saved_vgs, &devs)) {
+ if (!_get_devs_from_saved_vg(cmd, vgname, &devs)) {
log_print("pvscan[%d] VG %s not using quick activation.", getpid(), vgname);
*no_quick = 1;
return ECMD_FAILED;
@@ -1140,21 +1015,17 @@ out:
}
static int _pvscan_aa(struct cmd_context *cmd, struct pvscan_aa_params *pp,
- struct dm_list *vgnames, struct dm_list *saved_vgs)
+ int do_all, struct dm_list *vgnames)
{
struct processing_handle *handle = NULL;
struct dm_str_list *sl, *sl2;
int no_quick = 0;
int ret;
- if (dm_list_empty(vgnames)) {
- log_debug("No VGs to autoactivate.");
- return ECMD_PROCESSED;
- }
-
if (!(handle = init_processing_handle(cmd, NULL))) {
log_error("Failed to initialize processing handle.");
- return ECMD_FAILED;
+ ret = ECMD_FAILED;
+ goto out;
}
handle->custom_handle = pp;
@@ -1177,282 +1048,509 @@ static int _pvscan_aa(struct cmd_context *cmd, struct pvscan_aa_params *pp,
if (dm_list_empty(vgnames)) {
destroy_processing_handle(cmd, handle);
- return ECMD_PROCESSED;
+ ret = ECMD_PROCESSED;
+ goto out;
}
- if (dm_list_size(vgnames) == 1) {
- dm_list_iterate_items(sl, vgnames)
- ret = _pvscan_aa_quick(cmd, pp, sl->str, saved_vgs, &no_quick);
+ /*
+ * When saved_vg is set there should only be one vgname.
+ * If the optimized "quick" function finds something amiss
+ * it will set no_quick and return so that the slow version
+ * can be used.
+ */
+ if (!do_all && saved_vg && (dm_list_size(vgnames) == 1)) {
+ log_debug("autoactivate quick");
+ ret = _pvscan_aa_quick(cmd, pp, saved_vg->name, &no_quick);
}
- if ((dm_list_size(vgnames) > 1) || no_quick)
- ret = process_each_vg(cmd, 0, NULL, NULL, vgnames, READ_FOR_ACTIVATE, 0, handle, _pvscan_aa_single);
+ /*
+ * do_all indicates 'pvscan --cache' in which case
+ * pvscan_cache_all() has already done lvmcache_label_scan
+ * which does not need to be repeated by process_each_vg.
+ */
+ if (!saved_vg || (dm_list_size(vgnames) > 1) || no_quick) {
+ uint32_t read_flags = READ_FOR_ACTIVATE;
+ if (do_all)
+ read_flags |= PROCESS_SKIP_SCAN;
+ log_debug("autoactivate slow");
+ ret = process_each_vg(cmd, 0, NULL, NULL, vgnames, read_flags, 0, handle, _pvscan_aa_single);
+ }
destroy_processing_handle(cmd, handle);
-
+out:
+ if (saved_vg) {
+ release_vg(saved_vg);
+ saved_vg = NULL;
+ }
return ret;
}
-int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv)
-{
- struct pvscan_aa_params pp = { 0 };
- struct dm_list add_devs;
- struct dm_list rem_devs;
- struct dm_list vgnames;
- struct dm_list vglist;
- struct dm_list *complete_vgnames = NULL;
- struct dm_list *saved_vgs = NULL;
+struct pvscan_arg {
+ struct dm_list list;
+ const char *devname;
+ dev_t devno;
struct device *dev;
- struct device_list *devl;
- struct vg_list *vgl;
- const char *pv_name;
- const char *pvid_without_metadata = NULL;
- int32_t major = -1;
- int32_t minor = -1;
- int devno_args = 0;
- int all_devs;
+};
+
+static int _get_args(struct cmd_context *cmd, int argc, char **argv,
+ struct dm_list *pvscan_args)
+{
struct arg_value_group_list *current_group;
- dev_t devno;
- int filtered;
- int do_activate = arg_is_set(cmd, activate_ARG);
- int add_errors = 0;
- int add_single_count = 0;
- int ret = ECMD_PROCESSED;
+ struct pvscan_arg *arg;
+ const char *arg_name;
+ int major, minor;
- dm_list_init(&add_devs);
- dm_list_init(&rem_devs);
- dm_list_init(&vgnames);
- dm_list_init(&vglist);
+ /* Process position args, which can be /dev/name or major:minor */
- /*
- * When systemd/udev run pvscan --cache commands, those commands
- * should not wait on udev info since the udev info may not be
- * complete until the pvscan --cache command is done.
- */
- init_udev_sleeping(0);
+ while (argc) {
+ argc--;
+ arg_name = *argv++;
- if (do_activate) {
- complete_vgnames = &vgnames;
- saved_vgs = &vglist;
- }
+ if (arg_name[0] == '/') {
+ if (!(arg = dm_pool_zalloc(cmd->mem, sizeof(*arg))))
+ return_0;
+ arg->devname = arg_name;
+ dm_list_add(pvscan_args, &arg->list);
+ continue;
+ }
- if (arg_is_set(cmd, major_ARG) + arg_is_set(cmd, minor_ARG))
- devno_args = 1;
+ if (sscanf(arg_name, "%d:%d", &major, &minor) != 2) {
+ log_warn("WARNING: Failed to parse major:minor from %s, skipping.", arg_name);
+ continue;
+ }
- if (devno_args && (!arg_is_set(cmd, major_ARG) || !arg_is_set(cmd, minor_ARG))) {
- log_error("Both --major and --minor required to identify devices.");
- return EINVALID_CMD_LINE;
+ if (!(arg = dm_pool_zalloc(cmd->mem, sizeof(*arg))))
+ return_0;
+ arg->devno = MKDEV(major, minor);
+ dm_list_add(pvscan_args, &arg->list);
}
- if (argc || devno_args) {
- log_verbose("pvscan devices on command line.");
- cmd->pvscan_cache_single = 1;
- all_devs = 0;
- } else {
- all_devs = 1;
+ /* Process any grouped --major --minor args */
+
+ dm_list_iterate_items(current_group, &cmd->arg_value_groups) {
+ major = grouped_arg_int_value(current_group->arg_values, major_ARG, major);
+ minor = grouped_arg_int_value(current_group->arg_values, minor_ARG, minor);
+
+ if (major < 0 || minor < 0)
+ continue;
+
+ if (!(arg = dm_pool_zalloc(cmd->mem, sizeof(*arg))))
+ return_0;
+ arg->devno = MKDEV(major, minor);
+ dm_list_add(pvscan_args, &arg->list);
}
- _online_dir_setup();
+ return 1;
+}
- /* Creates a list of dev names from /dev, sysfs, etc; does not read any. */
- dev_cache_scan();
+static int _get_args_devs(struct cmd_context *cmd, struct dm_list *pvscan_args,
+ struct dm_list *pvscan_devs)
+{
+ struct pvscan_arg *arg;
+ struct device_list *devl;
- if (cmd->md_component_detection && !cmd->use_full_md_check &&
- !strcmp(cmd->md_component_checks, "auto") &&
- dev_cache_has_md_with_end_superblock(cmd->dev_types)) {
- log_debug("Enable full md component check.");
- cmd->use_full_md_check = 1;
+ /* pass NULL filter when getting devs from dev-cache, filtering is done separately */
+
+ /* in common usage, no dev will be found for a devno */
+
+ dm_list_iterate_items(arg, pvscan_args) {
+ if (arg->devname)
+ arg->dev = dev_cache_get(cmd, arg->devname, NULL);
+ else if (arg->devno)
+ arg->dev = dev_cache_get_by_devt(cmd, arg->devno, NULL, NULL);
+ else
+ return_0;
}
- /*
- * For each device command arg (from either position or --major/--minor),
- * decide if that device is being added to the system (a dev node exists
- * for it in /dev), or being removed from the system (no dev node exists
- * for it in /dev). Create entries in add_devs/rem_devs for each arg
- * accordingly.
- */
+ dm_list_iterate_items(arg, pvscan_args) {
+ if (!arg->dev)
+ continue;
- while (argc) {
- argc--;
+ if (!(devl = dm_pool_zalloc(cmd->mem, sizeof(*devl))))
+ return_0;
+ devl->dev = arg->dev;
+ dm_list_add(pvscan_devs, &devl->list);
+ }
- pv_name = *argv++;
- if (pv_name[0] == '/') {
- if (!(dev = dev_cache_get(cmd, pv_name, cmd->filter))) {
- log_debug("pvscan arg %s not found.", pv_name);
- if ((dev = dev_cache_get(cmd, pv_name, NULL))) {
- /* nothing to do for this dev name */
- log_print("pvscan[%d] device %s excluded by filter.", getpid(), dev_name(dev));
- } else {
- log_error("Physical Volume %s not found.", pv_name);
- ret = ECMD_FAILED;
- }
- } else {
- /*
- * Scan device. This dev could still be removed
- * below if it doesn't pass other filters.
- */
- log_debug("pvscan arg %s found.", pv_name);
-
- if (!(devl = dm_pool_zalloc(cmd->mem, sizeof(*devl))))
- return_0;
- devl->dev = dev;
- dm_list_add(&add_devs, &devl->list);
- }
- } else {
- if (sscanf(pv_name, "%d:%d", &major, &minor) != 2) {
- log_warn("WARNING: Failed to parse major:minor from %s, skipping.", pv_name);
- continue;
- }
- devno = MKDEV(major, minor);
-
- if (!(dev = dev_cache_get_by_devt(cmd, devno, cmd->filter, &filtered))) {
- if (filtered) {
- if ((dev = dev_cache_get_by_devt(cmd, devno, NULL, NULL)))
- log_print("pvscan[%d] device %d:%d %s excluded by filter.", getpid(), major, minor, dev_name(dev));
- else
- log_print("pvscan[%d] device %d:%d excluded by filter.", getpid(), major, minor);
- } else
- log_print("pvscan[%d] device %d:%d not found.", getpid(), major, minor);
-
- if (!(dev = dm_pool_zalloc(cmd->mem, sizeof(struct device))))
- return_0;
- if (!(devl = dm_pool_zalloc(cmd->mem, sizeof(*devl))))
- return_0;
- dev->dev = devno;
- devl->dev = dev;
- dm_list_add(&rem_devs, &devl->list);
- } else {
- /*
- * Scan device. This dev could still be removed
- * below if it doesn't pass other filters.
- */
- log_debug("pvscan arg %d:%d found.", major, minor);
-
- if (!(devl = dm_pool_zalloc(cmd->mem, sizeof(*devl))))
- return_0;
- devl->dev = dev;
- dm_list_add(&add_devs, &devl->list);
- }
+ return 1;
+}
+
+static int _online_devs(struct cmd_context *cmd, int do_all, struct dm_list *pvscan_devs,
+ int *pv_count, struct dm_list *complete_vgnames)
+{
+ struct device_list *devl, *devl2;
+ struct device *dev;
+ struct lvmcache_info *info;
+ const struct format_type *fmt;
+ struct format_instance_ctx fic = { .type = 0 };
+ struct format_instance *fid;
+ struct metadata_area *mda1, *mda2;
+ struct volume_group *vg;
+ const char *vgname;
+ uint32_t ext_version, ext_flags;
+ int do_activate = arg_is_set(cmd, activate_ARG);
+ int pvs_online;
+ int pvs_offline;
+ int pvs_unknown;
+ int vg_complete;
+ int ret = 1;
+
+ dm_list_iterate_items_safe(devl, devl2, pvscan_devs) {
+ dev = devl->dev;
+
+ log_debug("online_devs %s %s", dev_name(dev), dev->pvid);
+
+ /*
+ * This should already have been done by the filter, but make
+ * another check directly with udev in case the filter was not
+ * using udev and the native version didn't catch it.
+ */
+ if (udev_dev_is_mpath_component(dev)) {
+ log_print("pvscan[%d] ignore multipath component %s.", getpid(), dev_name(dev));
+ continue;
}
- }
- /* Process any grouped --major --minor args */
- dm_list_iterate_items(current_group, &cmd->arg_value_groups) {
- major = grouped_arg_int_value(current_group->arg_values, major_ARG, major);
- minor = grouped_arg_int_value(current_group->arg_values, minor_ARG, minor);
+ if (!(info = lvmcache_info_from_pvid(dev->pvid, dev, 0))) {
+ if (!do_all)
+ log_print("pvscan[%d] ignore %s with no lvm info.", getpid(), dev_name(dev));
+ continue;
+ }
- if (major < 0 || minor < 0)
+ ext_version = lvmcache_ext_version(info);
+ ext_flags = lvmcache_ext_flags(info);
+ if ((ext_version >= 2) && !(ext_flags & PV_EXT_USED)) {
+ log_print("pvscan[%d] PV %s not used.", getpid(), dev_name(dev));
+ (*pv_count)++;
continue;
+ }
- devno = MKDEV(major, minor);
+ fmt = lvmcache_fmt(info);
+ fid = fmt->ops->create_instance(fmt, &fic);
+ vg = NULL;
- if (!(dev = dev_cache_get_by_devt(cmd, devno, cmd->filter, &filtered))) {
- if (filtered) {
- if ((dev = dev_cache_get_by_devt(cmd, devno, NULL, NULL)))
- log_print("pvscan[%d] device %d:%d %s excluded by filter.", getpid(), major, minor, dev_name(dev));
- else
- log_print("pvscan[%d] device %d:%d excluded by filter.", getpid(), major, minor);
- } else
- log_print("pvscan[%d] device %d:%d not found.", getpid(), major, minor);
+ mda1 = lvmcache_get_dev_mda(dev, 1);
+ mda2 = lvmcache_get_dev_mda(dev, 2);
- if (!(dev = dm_pool_zalloc(cmd->mem, sizeof(struct device))))
- return_0;
- if (!(devl = dm_pool_zalloc(cmd->mem, sizeof(*devl))))
- return_0;
- dev->dev = devno;
- devl->dev = dev;
- dm_list_add(&rem_devs, &devl->list);
+ if (mda1 && !mda_is_ignored(mda1))
+ vg = mda1->ops->vg_read(cmd, fid, "", mda1, NULL, NULL);
+
+ if (!vg && mda2 && !mda_is_ignored(mda2))
+ vg = mda2->ops->vg_read(cmd, fid, "", mda2, NULL, NULL);
+
+ if (!vg) {
+ log_print("pvscan[%d] PV %s has no VG metadata.", getpid(), dev_name(dev));
+ fmt->ops->destroy_instance(fid);
+ goto online;
+ }
+
+ set_pv_devices(fid, vg, NULL);
+
+ /*
+ * Skip devs that are md components (set_pv_devices can do new
+ * md check), are shared, or foreign.
+ */
+
+ if (dev->flags & DEV_IS_MD_COMPONENT) {
+ log_print("pvscan[%d] PV %s ignore MD component, ignore metadata.", getpid(), dev_name(dev));
+ release_vg(vg);
+ continue;
+ }
+
+ if (vg_is_shared(vg)) {
+ log_print("pvscan[%d] PV %s ignore shared VG.", getpid(), dev_name(dev));
+ release_vg(vg);
+ continue;
+ }
+
+ if (vg->system_id && vg->system_id[0] &&
+ cmd->system_id && cmd->system_id[0] &&
+ vg_is_foreign(vg)) {
+ log_verbose("Ignore PV %s with VG system id %s with our system id %s",
+ dev_name(dev), vg->system_id, cmd->system_id);
+ log_print("pvscan[%d] PV %s ignore foreign VG.", getpid(), dev_name(dev));
+ release_vg(vg);
+ continue;
+ }
+
+ /*
+ * online file phase
+ * create pvs_online/<pvid>
+ * check if vg is complete: stat pvs_online/<pvid> for each vg->pvs
+ * if vg is complete, save vg name in list for activation phase
+ * if vg not complete, create pvs_lookup/<vgname> listing all pvids from vg->pvs
+ * (only if pvs_lookup/<vgname> does not yet exist)
+ * if no vg metadata, read pvs_lookup files for pvid, use that list to check if complete
+ */
+ online:
+ (*pv_count)++;
+
+ /*
+ * Create file named for pvid to record this PV is online.
+ */
+ if (!_online_pvid_file_create(dev, vg ? vg->name : NULL)) {
+ log_error("pvscan[%d] PV %s failed to create online file.", getpid(), dev_name(dev));
+ release_vg(vg);
+ ret = 0;
+ continue;
+ }
+
+ /*
+ * When not activating we don't need to know about vg completeness.
+ */
+ if (!do_activate) {
+ log_print("pvscan[%d] PV %s online.", getpid(), dev_name(dev));
+ release_vg(vg);
+ continue;
+ }
+
+ /*
+ * Check if all the PVs for this VG are online. If the arrival
+ * of this dev completes the VG, then save the vgname in
+ * complete_vgnames so it will be activated.
+ */
+ pvs_online = 0;
+ pvs_offline = 0;
+ pvs_unknown = 0;
+ vg_complete = 0;
+
+ if (vg) {
+ /*
+ * Use the VG metadata from this PV for a list of all
+ * PVIDs. Write a lookup file of PVIDs in case another
+ * pvscan needs it. After writing lookup file, recheck
+ * pvid files to resolve a possible race with another
+ * pvscan reading the lookup file that missed it.
+ */
+ log_debug("checking all pvid files from vg %s", vg->name);
+ _count_pvid_files(vg, &pvs_online, &pvs_offline);
+
+ if (pvs_offline && _write_lookup_file(cmd, vg)) {
+ log_debug("rechecking all pvid files from vg %s", vg->name);
+ _count_pvid_files(vg, &pvs_online, &pvs_offline);
+ if (!pvs_offline)
+ log_print("pvscan[%d] VG %s complete after recheck.", getpid(), vg->name);
+ }
+
+ vgname = vg->name;
} else {
- log_debug("pvscan arg %d:%d found.", major, minor);
+ /*
+ * No VG metadata on this PV, so try to use a lookup
+ * file written by a prior pvscan for a list of all
+ * PVIDs. A lookup file may not exist for this PV if
+ * it's the first to appear from the VG.
+ */
+ log_debug("checking all pvid files from lookup file");
+ if (!_count_pvid_files_from_lookup_file(cmd, dev, &pvs_online, &pvs_offline, &vgname))
+ pvs_unknown = 1;
+ }
- if (!(devl = dm_pool_zalloc(cmd->mem, sizeof(*devl))))
- return_0;
- devl->dev = dev;
- dm_list_add(&add_devs, &devl->list);
+ if (pvs_unknown) {
+ log_print("pvscan[%d] PV %s online, VG unknown.", getpid(), dev_name(dev));
+ vg_complete = 0;
+
+ } else if (pvs_offline) {
+ log_print("pvscan[%d] PV %s online, VG %s incomplete (need %d).",
+ getpid(), dev_name(dev), vgname, pvs_offline);
+ vg_complete = 0;
+
+ } else {
+ log_print("pvscan[%d] PV %s online, VG %s is complete.", getpid(), dev_name(dev), vgname);
+ if (!str_list_add(cmd->mem, complete_vgnames, dm_pool_strdup(cmd->mem, vgname)))
+ stack;
+ vg_complete = 1;
}
+
+ if (!saved_vg && vg && vg_complete && !do_all && (dm_list_size(pvscan_devs) == 1))
+ saved_vg = vg;
+ else
+ release_vg(vg);
}
+ return ret;
+}
+
+static int _pvscan_cache_all(struct cmd_context *cmd, int argc, char **argv,
+ struct dm_list *complete_vgnames)
+{
+ struct dm_list pvscan_devs;
+ struct dev_iter *iter;
+ struct device_list *devl;
+ struct device *dev;
+ int pv_count = 0;
+
+ dm_list_init(&pvscan_devs);
+
+ _online_files_remove(_pvs_online_dir);
+ _online_files_remove(_vgs_online_dir);
+ _online_files_remove(_pvs_lookup_dir);
+
/*
- * No device args means rescan/regenerate/[reactivate] everything.
- * Scan all devices when no args are given; clear all pvid
- * files on recreate pvid files for existing devices.
- * When -aay is set, any complete vg is activated
- * (even if it's already active.)
+ * pvscan --cache removes existing hints and recreates new ones.
+ * We begin by clearing hints at the start of the command.
+ * The pvscan_recreate_hints flag is used to enable the
+ * special case hint recreation in label_scan.
*/
+ cmd->pvscan_recreate_hints = 1;
+ pvscan_recreate_hints_begin(cmd);
- if (all_devs) {
- /*
- * pvscan --cache removes existing hints and recreates new ones.
- * We begin by clearing hints at the start of the command.
- * The pvscan_recreate_hints flag is used to enable the
- * special case hint recreation in label_scan.
- */
- cmd->pvscan_recreate_hints = 1;
- pvscan_recreate_hints_begin(cmd);
+ log_debug("pvscan_cache_all: label scan all");
- log_verbose("pvscan all devices for requested refresh.");
- _online_files_remove(_pvs_online_dir);
- _online_files_remove(_vgs_online_dir);
- _online_pvscan_all_devs(cmd, complete_vgnames, saved_vgs, NULL);
+ /*
+ * use lvmcache_label_scan() instead of just label_scan_devs()
+ * because label_scan() has the ability to update hints,
+ * which we want 'pvscan --cache' to do, and that uses
+ * info from lvmcache, e.g. duplicate pv info.
+ */
+ lvmcache_label_scan(cmd);
+
+ cmd->pvscan_recreate_hints = 0;
+ cmd->use_hints = 0;
+
+ log_debug("pvscan_cache_all: create list of devices");
+
+ /*
+ * The use of filter here will just reuse the existing
+ * (persistent) filter info label_scan has already set up.
+ */
+ if (!(iter = dev_iter_create(cmd->filter, 1)))
+ return_0;
- cmd->pvscan_recreate_hints = 0;
- cmd->use_hints = 0;
- goto activate;
+ while ((dev = dev_iter_get(cmd, iter))) {
+ if (!(devl = dm_pool_zalloc(cmd->mem, sizeof(*devl)))) {
+ dev_iter_destroy(iter);
+ return_0;
+ }
+ devl->dev = dev;
+ dm_list_add(&pvscan_devs, &devl->list);
}
+ dev_iter_destroy(iter);
- log_verbose("pvscan only specific devices add %d rem %d.",
- dm_list_size(&add_devs), dm_list_size(&rem_devs));
+ _online_devs(cmd, 1, &pvscan_devs, &pv_count, complete_vgnames);
+
+ return 1;
+}
+
+static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
+ struct dm_list *complete_vgnames)
+{
+ struct dm_list pvscan_args; /* struct pvscan_arg */
+ struct dm_list pvscan_devs; /* struct device_list */
+ struct pvscan_arg *arg;
+ struct device_list *devl, *devl2;
+ int pv_count = 0;
+ int ret;
+
+ dm_list_init(&pvscan_args);
+ dm_list_init(&pvscan_devs);
+
+ cmd->pvscan_cache_single = 1;
+
+ dev_cache_scan();
/*
- * Unlink online files for devices that no longer have a device node.
- * When unlinking a pvid file for dev, we don't need to scan the dev
- * (we can't since it's gone), but we know which pvid file it is
- * because the major:minor are saved in the pvid files which we can
- * read to find the correct one.
+ * Get list of args. Do not use filters.
*/
- dm_list_iterate_items(devl, &rem_devs)
- _online_pvid_file_remove_devno((int)MAJOR(devl->dev->dev), (int)MINOR(devl->dev->dev));
+ if (!_get_args(cmd, argc, argv, &pvscan_args))
+ return_0;
/*
- * Create online files for devices that exist and pass the filter.
- * When creating a pvid file for a dev, we have to scan it first
- * to know that it's ours and what its pvid is (and which vg it
- * belongs to if we want to do autoactivation.)
+ * Get list of devs for args. Do not use filters.
*/
- if (!dm_list_empty(&add_devs)) {
- label_scan_devs(cmd, cmd->filter, &add_devs);
+ if (!_get_args_devs(cmd, &pvscan_args, &pvscan_devs))
+ return_0;
- dm_list_iterate_items(devl, &add_devs) {
- dev = devl->dev;
+ /*
+ * Remove pvid online files for major/minor args for which the dev has
+ * been removed.
+ */
+ dm_list_iterate_items(arg, &pvscan_args) {
+ if (arg->dev || !arg->devno)
+ continue;
+ _online_pvid_file_remove_devno((int)MAJOR(arg->devno), (int)MINOR(arg->devno));
+ }
- if (dev->flags & DEV_FILTER_OUT_SCAN) {
- log_print("pvscan[%d] device %s excluded by filter.", getpid(), dev_name(dev));
- continue;
- }
+ /*
+ * A common pvscan removal of a single dev is done here.
+ */
+ if (dm_list_empty(&pvscan_devs))
+ return 1;
+
+ if (cmd->md_component_detection && !cmd->use_full_md_check &&
+ !strcmp(cmd->md_component_checks, "auto") &&
+ dev_cache_has_md_with_end_superblock(cmd->dev_types)) {
+ log_debug("Enable full md component check.");
+ cmd->use_full_md_check = 1;
+ }
- add_single_count++;
+ /*
+ * Apply nodata filters.
+ * bcache is not yet set up so no filters will do io.
+ */
+
+ log_debug("pvscan_cache_args: filter devs nodata");
+
+ cmd->filter_nodata_only = 1;
- if (!_online_pvscan_one(cmd, dev, NULL, complete_vgnames, saved_vgs, 0, &pvid_without_metadata))
- add_errors++;
+ dm_list_iterate_items_safe(devl, devl2, &pvscan_devs) {
+ if (!cmd->filter->passes_filter(cmd, cmd->filter, devl->dev, NULL)) {
+ log_print("pvscan[%d] %s excluded by filters: %s.", getpid(),
+ dev_name(devl->dev), dev_filtered_reason(devl->dev));
+ dm_list_del(&devl->list);
}
}
+ cmd->filter_nodata_only = 0;
+
+ /*
+ * Clear the results of nodata filters that were saved by the
+ * persistent filter so that the complete set of filters will
+ * be checked by passes_filter below.
+ */
+ dm_list_iterate_items(devl, &pvscan_devs)
+ cmd->filter->wipe(cmd, cmd->filter, devl->dev, NULL);
+
/*
- * After scanning only specific devs to add a device, there is a
- * special case that requires us to then scan all devs. That is when
- * the dev scanned has no VG metadata, and it's the final device to
- * complete the VG. In this case we want to autoactivate the VG, but
- * the scanned device does not know what VG it's in or whether that VG
- * is now complete. In this case we need to scan all devs and pick out
- * the complete VG holding this device so we can then autoactivate that
- * VG.
+ * Read header from each dev.
+ * Eliminate non-lvm devs.
+ * Apply all filters.
*/
- if (!dm_list_empty(&add_devs) && complete_vgnames && dm_list_empty(complete_vgnames) &&
- pvid_without_metadata && do_activate) {
- log_print("pvscan[%d] scan all devices for PV without metadata: %s.", getpid(), pvid_without_metadata);
- _online_pvscan_all_devs(cmd, complete_vgnames, saved_vgs, &add_devs);
+
+ log_debug("pvscan_cache_args: read and filter devs");
+
+ label_scan_setup_bcache();
+
+ dm_list_iterate_items_safe(devl, devl2, &pvscan_devs) {
+ if (!label_read_pvid(devl->dev)) {
+ /* Not an lvm device */
+ log_print("pvscan[%d] %s not an lvm device.", getpid(), dev_name(devl->dev));
+ dm_list_del(&devl->list);
+ continue;
+ }
+
+ /* Applies all filters, including those that need data from dev. */
+ if (!cmd->filter->passes_filter(cmd, cmd->filter, devl->dev, NULL)) {
+ log_print("pvscan[%d] %s excluded by filters: %s.", getpid(),
+ dev_name(devl->dev), dev_filtered_reason(devl->dev));
+ dm_list_del(&devl->list);
+ }
}
+ if (dm_list_empty(&pvscan_devs))
+ return 1;
+
+ log_debug("pvscan_cache_args: label scan devs");
+
+ /*
+ * Scan devs to populate lvmcache info, which includes the mda info that's
+ * needed to read vg metadata in the next step. The _cached variant of
+ * label_scan is used so the exsting bcache data from label_read_pvid above
+ * can be reused (although more data may need to be read depending on how
+ * much of the metadata was covered by reading the pvid.)
+ */
+ label_scan_devs_cached(cmd, NULL, &pvscan_devs);
+
+ ret = _online_devs(cmd, 0, &pvscan_devs, &pv_count, complete_vgnames);
+
/*
* When a new PV appears, the system runs pvscan --cache dev.
* This also means that existing hints are invalid, and
@@ -1460,25 +1558,61 @@ int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv)
* cases where this detects a change that the other methods
* of detecting invalid hints doesn't catch.
*/
- if (add_single_count)
+ if (pv_count)
invalidate_hints(cmd);
-activate:
+ return ret;
+}
+
+int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv)
+{
+ struct pvscan_aa_params pp = { 0 };
+ struct dm_list complete_vgnames;
+ int do_activate = arg_is_set(cmd, activate_ARG);
+ int devno_args = 0;
+ int do_all;
+ int ret;
+
+ dm_list_init(&complete_vgnames);
+
+ if (arg_is_set(cmd, major_ARG) + arg_is_set(cmd, minor_ARG))
+ devno_args = 1;
+
+ if (devno_args && (!arg_is_set(cmd, major_ARG) || !arg_is_set(cmd, minor_ARG))) {
+ log_error("Both --major and --minor required to identify devices.");
+ return EINVALID_CMD_LINE;
+ }
+
+ do_all = !argc && !devno_args;
+
+ _online_dir_setup();
+
+ if (do_all) {
+ if (!_pvscan_cache_all(cmd, argc, argv, &complete_vgnames))
+ return ECMD_FAILED;
+ } else {
+ if (!_pvscan_cache_args(cmd, argc, argv, &complete_vgnames))
+ return ECMD_FAILED;
+ }
+
+ if (!do_activate)
+ return ECMD_PROCESSED;
+
+ if (dm_list_empty(&complete_vgnames)) {
+ log_debug("No VGs to autoactivate.");
+ return ECMD_PROCESSED;
+ }
/*
- * Step 2: when the PV was recorded online, we check if all the
- * PVs for the VG are online. If so, the vgname was added to the
- * list, and we can attempt to autoactivate LVs in the VG.
+ * When the PV was recorded online, we check if all the PVs for the VG
+ * are online. If so, the vgname was added to the list, and we can
+ * attempt to autoactivate LVs in the VG.
*/
- if (do_activate)
- ret = _pvscan_aa(cmd, &pp, complete_vgnames, saved_vgs);
+ ret = _pvscan_aa(cmd, &pp, do_all, &complete_vgnames);
- if (add_errors || pp.activate_errors)
+ if (pp.activate_errors)
ret = ECMD_FAILED;
- dm_list_iterate_items(vgl, &vglist)
- release_vg(vgl->vg);
-
if (!sync_local_dev_names(cmd))
stack;
return ret;
2 years, 3 months
master - add label_scan_devs_cached
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=a7f195b7e8f27c29276...
Commit: a7f195b7e8f27c292761c604c1697c1d97cd5c1d
Parent: 677f829e540ef0f2fa2781f4facc7571dee1fb83
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Wed Oct 21 12:20:17 2020 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Wed Oct 21 16:24:16 2020 -0500
add label_scan_devs_cached
label_scan_devs without invalidating data first
for cases where the caller wants to use any
bcache data they have already read.
---
lib/label/label.c | 15 +++++++++++++++
lib/label/label.h | 1 +
2 files changed, 16 insertions(+)
diff --git a/lib/label/label.c b/lib/label/label.c
index bc8e60fc9..36eab19f3 100644
--- a/lib/label/label.c
+++ b/lib/label/label.c
@@ -1302,6 +1302,21 @@ int label_read_pvid(struct device *dev)
return 1;
}
+/*
+ * label_scan_devs without invalidating data for the devs first,
+ * when the caller wants to make use of any bcache data that
+ * they may have already read.
+ */
+int label_scan_devs_cached(struct cmd_context *cmd, struct dev_filter *f, struct dm_list *devs)
+{
+ if (!scan_bcache)
+ return 0;
+
+ _scan_list(cmd, f, devs, NULL);
+
+ return 1;
+}
+
/*
* Scan and cache lvm data from the listed devices. If a device is already
* scanned and cached, this replaces the previously cached lvm data for the
diff --git a/lib/label/label.h b/lib/label/label.h
index ddfb34af5..a98ba32e3 100644
--- a/lib/label/label.h
+++ b/lib/label/label.h
@@ -104,6 +104,7 @@ extern struct bcache *scan_bcache;
int label_scan(struct cmd_context *cmd);
int label_scan_devs(struct cmd_context *cmd, struct dev_filter *f, struct dm_list *devs);
+int label_scan_devs_cached(struct cmd_context *cmd, struct dev_filter *f, struct dm_list *devs);
int label_scan_devs_rw(struct cmd_context *cmd, struct dev_filter *f, struct dm_list *devs);
int label_scan_devs_excl(struct dm_list *devs);
int label_scan_dev(struct device *dev);
2 years, 3 months
master - add label_read_pvid
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=677f829e540ef0f2fa2...
Commit: 677f829e540ef0f2fa2781f4facc7571dee1fb83
Parent: c7311d472203614b229cf17606d14fb53df85945
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Thu Oct 15 14:05:45 2020 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Wed Oct 21 16:24:16 2020 -0500
add label_read_pvid
To read the lvm headers and set dev->pvid if the
device is a PV. Difference from label_scan_ functions
is this does not read any vg metadata or add any info
to lvmcache.
---
lib/label/label.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
lib/label/label.h | 1 +
2 files changed, 49 insertions(+)
diff --git a/lib/label/label.c b/lib/label/label.c
index a47cd1731..bc8e60fc9 100644
--- a/lib/label/label.c
+++ b/lib/label/label.c
@@ -1254,6 +1254,54 @@ int label_scan(struct cmd_context *cmd)
return 1;
}
+/*
+ * Read the header of the disk and if it's a PV
+ * save the pvid in dev->pvid.
+ */
+int label_read_pvid(struct device *dev)
+{
+ char buf[4096] __attribute__((aligned(8)));
+ struct label_header *lh;
+ struct pv_header *pvh;
+
+ memset(buf, 0, sizeof(buf));
+
+ if (!label_scan_open(dev))
+ return_0;
+
+ /*
+ * We could do:
+ * dev_read_bytes(dev, 512, LABEL_SIZE, buf);
+ * which works, but there's a bcache issue that
+ * prevents proper invalidation after that.
+ */
+ if (!dev_read_bytes(dev, 0, 4096, buf)) {
+ label_scan_invalidate(dev);
+ return 0;
+ }
+
+ lh = (struct label_header *)(buf + 512);
+ if (memcmp(lh->id, LABEL_ID, sizeof(lh->id))) {
+ /* Not an lvm deice */
+ label_scan_invalidate(dev);
+ return 0;
+ }
+
+ /*
+ * wipefs -a just clears the type field, leaving the
+ * rest of the label_header intact.
+ */
+ if (memcmp(lh->type, LVM2_LABEL, sizeof(lh->type))) {
+ /* Not an lvm deice */
+ label_scan_invalidate(dev);
+ return 0;
+ }
+
+ pvh = (struct pv_header *)(buf + 512 + 32);
+ memcpy(dev->pvid, pvh->pv_uuid, ID_LEN);
+ return 1;
+}
+
/*
* Scan and cache lvm data from the listed devices. If a device is already
* scanned and cached, this replaces the previously cached lvm data for the
diff --git a/lib/label/label.h b/lib/label/label.h
index 8778c17a8..ddfb34af5 100644
--- a/lib/label/label.h
+++ b/lib/label/label.h
@@ -117,6 +117,7 @@ int label_scan_open(struct device *dev);
int label_scan_open_excl(struct device *dev);
int label_scan_open_rw(struct device *dev);
int label_scan_reopen_rw(struct device *dev);
+int label_read_pvid(struct device *dev);
int label_scan_for_pvid(struct cmd_context *cmd, char *pvid, struct device **dev_out);
2 years, 3 months
master - lvmcache: rename label_read label_scan_dev
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=c7311d472203614b229...
Commit: c7311d472203614b229cf17606d14fb53df85945
Parent: b3cdf0d881e63f16b8a29b26bbe3439798f44184
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Thu Oct 15 12:56:36 2020 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Wed Oct 21 16:24:16 2020 -0500
lvmcache: rename label_read label_scan_dev
for consistent naming with other similar functions
---
lib/cache/lvmcache.c | 2 +-
lib/label/label.c | 2 +-
lib/label/label.h | 3 +--
tools/toollib.c | 4 ++--
4 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c
index 4b2590c4d..495748f22 100644
--- a/lib/cache/lvmcache.c
+++ b/lib/cache/lvmcache.c
@@ -1116,7 +1116,7 @@ int lvmcache_label_scan(struct cmd_context *cmd)
dm_list_iterate_items(devl, &add_cache_devs) {
log_debug_cache("Adding chosen duplicate %s", dev_name(devl->dev));
- label_read(devl->dev);
+ label_scan_dev(devl->dev);
}
dm_list_splice(&_unused_duplicates, &del_cache_devs);
diff --git a/lib/label/label.c b/lib/label/label.c
index b06aaae00..a47cd1731 100644
--- a/lib/label/label.c
+++ b/lib/label/label.c
@@ -1394,7 +1394,7 @@ void label_scan_destroy(struct cmd_context *cmd)
* device, this is not a commonly used function.
*/
-int label_read(struct device *dev)
+int label_scan_dev(struct device *dev)
{
struct dm_list one_dev;
struct device_list *devl;
diff --git a/lib/label/label.h b/lib/label/label.h
index 25913bb6d..8778c17a8 100644
--- a/lib/label/label.h
+++ b/lib/label/label.h
@@ -106,12 +106,11 @@ int label_scan(struct cmd_context *cmd);
int label_scan_devs(struct cmd_context *cmd, struct dev_filter *f, struct dm_list *devs);
int label_scan_devs_rw(struct cmd_context *cmd, struct dev_filter *f, struct dm_list *devs);
int label_scan_devs_excl(struct dm_list *devs);
+int label_scan_dev(struct device *dev);
void label_scan_invalidate(struct device *dev);
void label_scan_invalidate_lv(struct cmd_context *cmd, struct logical_volume *lv);
void label_scan_drop(struct cmd_context *cmd);
void label_scan_destroy(struct cmd_context *cmd);
-int label_read(struct device *dev);
-int label_read_sector(struct device *dev, uint64_t scan_sector);
void label_scan_confirm(struct device *dev);
int label_scan_setup_bcache(void);
int label_scan_open(struct device *dev);
diff --git a/tools/toollib.c b/tools/toollib.c
index f2372e095..0fbb0b6ec 100644
--- a/tools/toollib.c
+++ b/tools/toollib.c
@@ -1451,7 +1451,7 @@ int process_each_label(struct cmd_context *cmd, int argc, char **argv,
/*
* add info to lvmcache from the duplicate dev.
*/
- label_read(devl->dev);
+ label_scan_dev(devl->dev);
/*
* the info/label should now be found because
@@ -4892,7 +4892,7 @@ static int _pvcreate_check_used(struct cmd_context *cmd,
* If a VG is using the dev, it adds basic VG info for it to
* lvmcache.
*/
- label_read(pd->dev);
+ label_scan_dev(pd->dev);
if (!pd->dev->pvid[0]) {
log_debug("Check pvcreate arg %s no PVID found", dev_name(pd->dev));
2 years, 3 months
master - lvmcache: add lvmcache_get_dev_mda
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=b3cdf0d881e63f16b8a...
Commit: b3cdf0d881e63f16b8a29b26bbe3439798f44184
Parent: 2c9bb676048fda86867df165aa297f7078dffc4b
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Thu Oct 15 12:53:01 2020 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Wed Oct 21 16:24:16 2020 -0500
lvmcache: add lvmcache_get_dev_mda
for future patch
---
lib/cache/lvmcache.c | 15 +++++++++++++++
lib/cache/lvmcache.h | 2 ++
2 files changed, 17 insertions(+)
diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c
index ef0f4c391..4b2590c4d 100644
--- a/lib/cache/lvmcache.c
+++ b/lib/cache/lvmcache.c
@@ -271,6 +271,21 @@ void lvmcache_get_mdas(struct cmd_context *cmd,
}
}
+struct metadata_area *lvmcache_get_dev_mda(struct device *dev, int mda_num)
+{
+ struct lvmcache_info *info;
+ struct metadata_area *mda;
+
+ if (!(info = lvmcache_info_from_pvid(dev->pvid, dev, 0)))
+ return NULL;
+
+ dm_list_iterate_items(mda, &info->mdas) {
+ if (mda->mda_num == mda_num)
+ return mda;
+ }
+ return NULL;
+}
+
static void _vginfo_detach_info(struct lvmcache_info *info)
{
if (!dm_list_empty(&info->list)) {
diff --git a/lib/cache/lvmcache.h b/lib/cache/lvmcache.h
index 7a718cad8..fb4ab5562 100644
--- a/lib/cache/lvmcache.h
+++ b/lib/cache/lvmcache.h
@@ -220,4 +220,6 @@ void lvmcache_get_mdas(struct cmd_context *cmd,
const char *dev_filtered_reason(struct device *dev);
const char *devname_error_reason(const char *devname);
+struct metadata_area *lvmcache_get_dev_mda(struct device *dev, int mda_num);
+
#endif
2 years, 3 months
master - scanning: improve filtering control
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=2c9bb676048fda86867...
Commit: 2c9bb676048fda86867df165aa297f7078dffc4b
Parent: c74ccd5201d4566a77487d1aa89e72c254ea443d
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Tue Oct 20 15:10:08 2020 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Wed Oct 21 16:24:16 2020 -0500
scanning: improve filtering control
Filtering in label_scan was controlled indirectly by
the fact that bcache was not yet set up when label_scan
first ran. The result is that filters that needed data
would not run and would return -EAGAIN, which would
result in the dev flag FILTER_AFTER_SCAN being set.
After the dev header was read for checking the label,
filters would be rechecked because of FILTER_AFTER_SCAN.
All filters would be checked this time because bcache
was now set up, and the filters needing data would
largely use data already scanned for reading the label.
This design worked but is hard to adjust for future
cases where bcache is already set up.
Replace this method (based on setting up bcache, or not)
with a new cmd flag filter_nodata_only. When this flag
is set filters that need data will not run. This allows
the same label_scan behavior when bcache has been set up.
There are no expected changes in behavior.
---
lib/label/label.c | 118 ++++++++++++++++++++++++++++++++----------------------
1 file changed, 70 insertions(+), 48 deletions(-)
diff --git a/lib/label/label.c b/lib/label/label.c
index ec774dc6e..b06aaae00 100644
--- a/lib/label/label.c
+++ b/lib/label/label.c
@@ -362,34 +362,33 @@ static int _process_block(struct cmd_context *cmd, struct dev_filter *f,
uint64_t sector = 0;
int is_duplicate = 0;
int ret = 0;
- int pass;
dev->flags &= ~DEV_SCAN_FOUND_LABEL;
/*
- * The device may have signatures that exclude it from being processed.
- * If filters were applied before bcache data was available, some
- * filters may have deferred their check until the point where bcache
- * data had been read (here). They set this flag to indicate that the
- * filters should be retested now that data from the device is ready.
+ * The device may have signatures that exclude it from being processed,
+ * even if it might look like a PV. Now that the device has been read
+ * and data is available in bcache for it, recheck filters, including
+ * those that use data. The device needs to be excluded before it
+ * begins to be processed as a PV.
*/
- if (f && (dev->flags & DEV_FILTER_AFTER_SCAN)) {
- dev->flags &= ~DEV_FILTER_AFTER_SCAN;
-
- log_debug_devs("Scan filtering %s", dev_name(dev));
-
- pass = f->passes_filter(cmd, f, dev, NULL);
-
- if ((pass == -EAGAIN) || (dev->flags & DEV_FILTER_AFTER_SCAN)) {
- /* Shouldn't happen */
- dev->flags &= ~DEV_FILTER_OUT_SCAN;
- log_debug_devs("Scan filter should not be deferred %s", dev_name(dev));
- pass = 1;
- }
+ if (f) {
+ if (!f->passes_filter(cmd, f, dev, NULL)) {
+ /*
+ * If this device was previously scanned (not common)
+ * and if it passed filters at that point, lvmcache
+ * info may have been saved for it. Now the same
+ * device is being scanned again, and it may fail
+ * filters this time. If the caller did not clear
+ * lvmcache info for this dev before rescanning, do
+ * that now. It's unlikely this is actually needed.
+ */
+ if (dev->pvid[0]) {
+ log_print("Clear pvid and info for filtered dev %s", dev_name(dev));
+ lvmcache_del_dev(dev);
+ memset(dev->pvid, 0, sizeof(dev->pvid));
+ }
- if (!pass) {
- log_very_verbose("%s: Not processing filtered", dev_name(dev));
- dev->flags |= DEV_FILTER_OUT_SCAN;
*is_lvm_device = 0;
goto_out;
}
@@ -414,7 +413,12 @@ static int _process_block(struct cmd_context *cmd, struct dev_filter *f,
log_very_verbose("%s: No lvm label detected", dev_name(dev));
- lvmcache_del_dev(dev); /* FIXME: if this is needed, fix it. */
+ /* See comment above */
+ if (dev->pvid[0]) {
+ log_print("Clear pvid and info for no lvm header %s", dev_name(dev));
+ lvmcache_del_dev(dev);
+ memset(dev->pvid, 0, sizeof(dev->pvid));
+ }
*is_lvm_device = 0;
goto out;
@@ -1009,6 +1013,7 @@ int label_scan_for_pvid(struct cmd_context *cmd, char *pvid, struct device **dev
int label_scan(struct cmd_context *cmd)
{
struct dm_list all_devs;
+ struct dm_list filtered_devs;
struct dm_list scan_devs;
struct dm_list hints_list;
struct dev_iter *iter;
@@ -1021,9 +1026,15 @@ int label_scan(struct cmd_context *cmd)
log_debug_devs("Finding devices to scan");
dm_list_init(&all_devs);
+ dm_list_init(&filtered_devs);
dm_list_init(&scan_devs);
dm_list_init(&hints_list);
+ if (!scan_bcache) {
+ if (!_setup_bcache())
+ return_0;
+ }
+
/*
* dev_cache_scan() creates a list of devices on the system
* (saved in in dev-cache) which we can iterate through to
@@ -1047,29 +1058,15 @@ int label_scan(struct cmd_context *cmd)
}
/*
- * Set up the iterator that is needed to step through each device in
- * dev cache.
+ * Create a list of all devices in dev-cache (all found on the system.)
+ * Do not apply filters and do not read any (the filter arg is NULL).
+ * Invalidate bcache data for all devs (there will usually be no bcache
+ * data to invalidate.)
*/
- if (!(iter = dev_iter_create(cmd->filter, 0))) {
+ if (!(iter = dev_iter_create(NULL, 0))) {
log_error("Scanning failed to get devices.");
return 0;
}
-
- log_debug_devs("Filtering devices to scan");
-
- /*
- * Iterate through all devices in dev cache and apply filters
- * to exclude devs that we do not need to scan. Those devs
- * that pass the filters are returned by the iterator and
- * saved in a list of devs that we will proceed to scan to
- * check if they are LVM devs. IOW this loop is the
- * application of filters (those that do not require reading
- * the devs) to the list of all devices. It does that because
- * the 'cmd->filter' is used above when setting up the iterator.
- * Unfortunately, it's not obvious that this is what's happening
- * here. filters that require reading the device are not applied
- * here, but in process_block(), see DEV_FILTER_AFTER_SCAN.
- */
while ((dev = dev_iter_get(cmd, iter))) {
if (!(devl = zalloc(sizeof(*devl))))
continue;
@@ -1078,16 +1075,43 @@ int label_scan(struct cmd_context *cmd)
/*
* label_scan should not generally be called a second time,
- * so this will usually not be true.
+ * so this will usually do nothing.
*/
label_scan_invalidate(dev);
};
dev_iter_destroy(iter);
- if (!scan_bcache) {
- if (!_setup_bcache())
- return 0;
+ /*
+ * Exclude devices that fail nodata filters. (Those filters that can be
+ * checked without reading data from the device.)
+ *
+ * The result of checking nodata filters is saved by the "persistent
+ * filter", and this result needs to be cleared (wiped) so that the
+ * complete set of filters (including those that require data) can be
+ * checked in _process_block, where headers have been read.
+ */
+ log_debug_devs("Filtering devices to scan (nodata)");
+
+ cmd->filter_nodata_only = 1;
+ dm_list_iterate_items_safe(devl, devl2, &all_devs) {
+ dev = devl->dev;
+ if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, NULL)) {
+ dm_list_del(&devl->list);
+ dm_list_add(&filtered_devs, &devl->list);
+
+ if (dev->pvid[0]) {
+ log_print("Clear pvid and info for filtered dev %s", dev_name(dev));
+ lvmcache_del_dev(dev);
+ memset(dev->pvid, 0, sizeof(dev->pvid));
+ }
+ }
}
+ cmd->filter_nodata_only = 0;
+
+ dm_list_iterate_items(devl, &all_devs)
+ cmd->filter->wipe(cmd, cmd->filter, devl->dev, NULL);
+ dm_list_iterate_items(devl, &filtered_devs)
+ cmd->filter->wipe(cmd, cmd->filter, devl->dev, NULL);
/*
* In some common cases we can avoid scanning all devices
@@ -1110,8 +1134,6 @@ int label_scan(struct cmd_context *cmd)
} else
using_hints = 1;
- log_debug("Will scan %d devices skip %d", dm_list_size(&scan_devs), dm_list_size(&all_devs));
-
/*
* If the total number of devices exceeds the soft open file
* limit, then increase the soft limit to the hard/max limit
2 years, 3 months
master - filters: nodata option
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=c74ccd5201d4566a774...
Commit: c74ccd5201d4566a77487d1aa89e72c254ea443d
Parent: c601ec0d6ed0a1cf31cdf65b995e7b3f0b6f1d72
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Mon Oct 19 16:46:20 2020 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Wed Oct 21 16:24:16 2020 -0500
filters: nodata option
When filter_nodata_only is set, a filter that uses
data is skipped.
---
lib/commands/toolcontext.h | 1 +
lib/filters/filter-fwraid.c | 4 ++++
lib/filters/filter-md.c | 3 +++
lib/filters/filter-partitioned.c | 4 ++++
lib/filters/filter-signature.c | 4 ++++
5 files changed, 16 insertions(+)
diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h
index c09558a42..d20768ad0 100644
--- a/lib/commands/toolcontext.h
+++ b/lib/commands/toolcontext.h
@@ -182,6 +182,7 @@ struct cmd_context {
unsigned pvscan_recreate_hints:1; /* enable special case hint handling for pvscan --cache */
unsigned scan_lvs:1;
unsigned wipe_outdated_pvs:1;
+ unsigned filter_nodata_only:1; /* only use filters that do not require data from the dev */
/*
* Devices and filtering.
diff --git a/lib/filters/filter-fwraid.c b/lib/filters/filter-fwraid.c
index f82f87397..4ad1816e1 100644
--- a/lib/filters/filter-fwraid.c
+++ b/lib/filters/filter-fwraid.c
@@ -15,6 +15,7 @@
#include "base/memory/zalloc.h"
#include "lib/misc/lib.h"
#include "lib/filters/filter.h"
+#include "lib/commands/toolcontext.h"
#ifdef UDEV_SYNC_SUPPORT
#include <libudev.h>
@@ -69,6 +70,9 @@ static int _ignore_fwraid(struct cmd_context *cmd, struct dev_filter *f __attrib
{
int ret;
+ if (cmd->filter_nodata_only)
+ return 1;
+
dev->filtered_flags &= ~DEV_FILTERED_FWRAID;
if (!fwraid_filtering())
diff --git a/lib/filters/filter-md.c b/lib/filters/filter-md.c
index b530e407d..f72c52123 100644
--- a/lib/filters/filter-md.c
+++ b/lib/filters/filter-md.c
@@ -86,6 +86,9 @@ static int _passes_md_filter(struct cmd_context *cmd, struct dev_filter *f __att
{
int ret;
+ if (cmd->filter_nodata_only)
+ return 1;
+
dev->filtered_flags &= ~DEV_FILTERED_MD_COMPONENT;
/*
diff --git a/lib/filters/filter-partitioned.c b/lib/filters/filter-partitioned.c
index 22194f81c..c0b5554c5 100644
--- a/lib/filters/filter-partitioned.c
+++ b/lib/filters/filter-partitioned.c
@@ -16,6 +16,7 @@
#include "base/memory/zalloc.h"
#include "lib/misc/lib.h"
#include "lib/filters/filter.h"
+#include "lib/commands/toolcontext.h"
#define MSG_SKIPPING "%s: Skipping: Partition table signature found"
@@ -24,6 +25,9 @@ static int _passes_partitioned_filter(struct cmd_context *cmd, struct dev_filter
struct dev_types *dt = (struct dev_types *) f->private;
int ret;
+ if (cmd->filter_nodata_only)
+ return 1;
+
dev->filtered_flags &= ~DEV_FILTERED_PARTITIONED;
ret = dev_is_partitioned(dt, dev);
diff --git a/lib/filters/filter-signature.c b/lib/filters/filter-signature.c
index f32bb2450..eeefa4f58 100644
--- a/lib/filters/filter-signature.c
+++ b/lib/filters/filter-signature.c
@@ -16,6 +16,7 @@
#include "base/memory/zalloc.h"
#include "lib/misc/lib.h"
#include "lib/filters/filter.h"
+#include "lib/commands/toolcontext.h"
#ifdef __linux__
@@ -27,6 +28,9 @@ static int _ignore_signature(struct cmd_context *cmd, struct dev_filter *f __att
char buf[BUFSIZE];
int ret = 0;
+ if (cmd->filter_nodata_only)
+ return 1;
+
dev->filtered_flags &= ~DEV_FILTERED_SIGNATURE;
if (!scan_bcache) {
2 years, 3 months
master - filters: allow filter wipe for one device
by David Teigland
Gitweb: https://sourceware.org/git/?p=lvm2.git;a=commitdiff;h=c601ec0d6ed0a1cf31c...
Commit: c601ec0d6ed0a1cf31cdf65b995e7b3f0b6f1d72
Parent: 83d08185234748f1837436d83cbf6817180551fd
Author: David Teigland <teigland(a)redhat.com>
AuthorDate: Tue Oct 20 15:17:56 2020 -0500
Committer: David Teigland <teigland(a)redhat.com>
CommitterDate: Wed Oct 21 16:24:16 2020 -0500
filters: allow filter wipe for one device
as passes_filter already does
---
lib/device/dev-cache.h | 2 +-
lib/filters/filter-composite.c | 9 ++++++---
lib/filters/filter-persistent.c | 10 ++++++++--
3 files changed, 15 insertions(+), 6 deletions(-)
diff --git a/lib/device/dev-cache.h b/lib/device/dev-cache.h
index 533e4561d..7285bc3a7 100644
--- a/lib/device/dev-cache.h
+++ b/lib/device/dev-cache.h
@@ -28,7 +28,7 @@ struct cmd_context;
struct dev_filter {
int (*passes_filter) (struct cmd_context *cmd, struct dev_filter *f, struct device *dev, const char *use_filter_name);
void (*destroy) (struct dev_filter *f);
- void (*wipe) (struct dev_filter *f);
+ void (*wipe) (struct cmd_context *cmd, struct dev_filter *f, struct device *dev, const char *use_filter_name);
void *private;
unsigned use_count;
const char *name;
diff --git a/lib/filters/filter-composite.c b/lib/filters/filter-composite.c
index b0063f149..f4cbeefe8 100644
--- a/lib/filters/filter-composite.c
+++ b/lib/filters/filter-composite.c
@@ -60,13 +60,16 @@ static void _composite_destroy(struct dev_filter *f)
free(f);
}
-static void _wipe(struct dev_filter *f)
+static void _wipe(struct cmd_context *cmd, struct dev_filter *f, struct device *dev, const char *use_filter_name)
{
struct dev_filter **filters;
- for (filters = (struct dev_filter **) f->private; *filters; ++filters)
+ for (filters = (struct dev_filter **) f->private; *filters; ++filters) {
+ if (use_filter_name && strcmp((*filters)->name, use_filter_name))
+ continue;
if ((*filters)->wipe)
- (*filters)->wipe(*filters);
+ (*filters)->wipe(cmd, *filters, dev, use_filter_name);
+ }
}
struct dev_filter *composite_filter_create(int n, int use_dev_ext_info, struct dev_filter **filters)
diff --git a/lib/filters/filter-persistent.c b/lib/filters/filter-persistent.c
index afa32d4b9..291f3932f 100644
--- a/lib/filters/filter-persistent.c
+++ b/lib/filters/filter-persistent.c
@@ -64,11 +64,17 @@ static int _init_hash(struct pfilter *pf)
return 1;
}
-static void _persistent_filter_wipe(struct dev_filter *f)
+static void _persistent_filter_wipe(struct cmd_context *cmd, struct dev_filter *f, struct device *dev, const char *use_filter_name)
{
struct pfilter *pf = (struct pfilter *) f->private;
+ struct dm_str_list *sl;
- dm_hash_wipe(pf->devices);
+ if (!dev) {
+ dm_hash_wipe(pf->devices);
+ } else {
+ dm_list_iterate_items(sl, &dev->aliases)
+ dm_hash_remove(pf->devices, sl->str);
+ }
}
static int _lookup_p(struct cmd_context *cmd, struct dev_filter *f, struct device *dev, const char *use_filter_name)
2 years, 3 months