Blame SOURCES/lvm2-2_03_03-pvscan-autoactivate-a-VG-once.patch

146ac4
From e403c3953fe8664525ca85e2ecabbe13dc7a0ae7 Mon Sep 17 00:00:00 2001
146ac4
From: David Teigland <teigland@redhat.com>
146ac4
Date: Thu, 21 Feb 2019 15:53:36 -0600
146ac4
Subject: [PATCH 3/5] pvscan: autoactivate a VG once
146ac4
146ac4
When a VG has multiple PVs, and all those PVs come online
146ac4
at the same time, concurrent pvscans for each PV will all
146ac4
create the individual pvid files, and all will often see
146ac4
the VG is now complete.  This causes each of the pvscan
146ac4
commands to think it should activate the VG, so there
146ac4
are multiple activations of the same VG.  The vg lock
146ac4
serializes them, and only the first pvscan actually does
146ac4
the activation, but there is still a lot of extra overhead
146ac4
and time used by the other pvscans that attempt to
146ac4
activate the already active VG.  This can lead to a backlog
146ac4
of pvscans and timeouts.
146ac4
146ac4
To fix this, this adds a new /run/lvm/vgs_online/ dir that
146ac4
works like the existing /run/lvm/pvs_online/ dir.  Each pvscan
146ac4
that wants to activate a VG will first try to exlusively create
146ac4
the file vgs_online/<vgname>.  Only the first pvscan will
146ac4
succeed, and that one will do the VG activation. The other
146ac4
pvscans will find the vgname file exists and will not do the
146ac4
activation step.
146ac4
146ac4
When a PV goes offline, the vgs_online file for the corresponding
146ac4
VG is removed.  This allows the VG to be autoactivated again
146ac4
when the PV comes online again.  This requires that the vgname be
146ac4
stored in the pvid files.
146ac4
---
146ac4
 test/shell/pvscan-autoactivate.sh |  39 ++++---
146ac4
 test/shell/pvscan-cache.sh        |  20 +++-
146ac4
 tools/pvscan.c                    | 214 ++++++++++++++++++++++++++++++++------
146ac4
 3 files changed, 226 insertions(+), 47 deletions(-)
146ac4
146ac4
diff --git a/test/shell/pvscan-autoactivate.sh b/test/shell/pvscan-autoactivate.sh
146ac4
index cd360e9..419fb9b 100644
146ac4
--- a/test/shell/pvscan-autoactivate.sh
146ac4
+++ b/test/shell/pvscan-autoactivate.sh
146ac4
@@ -14,14 +14,15 @@ SKIP_WITH_LVMPOLLD=1
146ac4
 
146ac4
 RUNDIR="/run"
146ac4
 test -d "$RUNDIR" || RUNDIR="/var/run"
146ac4
-ONLINEDIR="$RUNDIR/lvm/pvs_online"
146ac4
+PVS_ONLINE_DIR="$RUNDIR/lvm/pvs_online"
146ac4
+VGS_ONLINE_DIR="$RUNDIR/lvm/vgs_online"
146ac4
 
146ac4
 # FIXME: kills logic for running system
146ac4
-_clear_online() {
146ac4
+_clear_online_files() {
146ac4
 	# wait till udev is finished
146ac4
 	aux udev_wait
146ac4
-	rm -f "$ONLINEDIR"/*
146ac4
-	test -n "${1+varset}" || touch "$ONLINEDIR/foo"
146ac4
+	rm -f "$PVS_ONLINE_DIR"/*
146ac4
+	rm -f "$VGS_ONLINE_DIR"/*
146ac4
 }
146ac4
 
146ac4
 . lib/inittest
146ac4
@@ -32,8 +33,9 @@ vgcreate $vg1 "$dev1" "$dev2"
146ac4
 lvcreate -n $lv1 -l 4 -a n $vg1
146ac4
 
146ac4
 # the first pvscan scans all devs
146ac4
-test -d "$ONLINEDIR" || mkdir -p "$ONLINEDIR"
146ac4
-_clear_online nofoo
146ac4
+test -d "$PVS_ONLINE_DIR" || mkdir -p "$PVS_ONLINE_DIR"
146ac4
+test -d "$VGS_ONLINE_DIR" || mkdir -p "$VGS_ONLINE_DIR"
146ac4
+_clear_online_files
146ac4
 
146ac4
 pvscan --cache -aay
146ac4
 check lv_field $vg1/$lv1 lv_active "active"
146ac4
@@ -42,7 +44,7 @@ lvchange -an $vg1
146ac4
 # the first pvscan scans all devs even when
146ac4
 # only one device is specified
146ac4
 
146ac4
-_clear_online nofoo
146ac4
+_clear_online_files
146ac4
 
146ac4
 pvscan --cache -aay "$dev1"
146ac4
 check lv_field $vg1/$lv1 lv_active "active"
146ac4
@@ -50,7 +52,8 @@ lvchange -an $vg1
146ac4
 
146ac4
 # touch foo to disable first-pvscan case,
146ac4
 # then check pvscan with no args scans all
146ac4
-_clear_online
146ac4
+_clear_online_files
146ac4
+touch "$RUNDIR/lvm/pvs_online/foo"
146ac4
 
146ac4
 pvscan --cache -aay
146ac4
 check lv_field $vg1/$lv1 lv_active "active"
146ac4
@@ -60,7 +63,8 @@ lvchange -an $vg1
146ac4
 # then check that vg is activated only after
146ac4
 # both devs appear separately
146ac4
 
146ac4
-_clear_online
146ac4
+_clear_online_files
146ac4
+touch "$RUNDIR/lvm/pvs_online/foo"
146ac4
 
146ac4
 pvscan --cache -aay "$dev1"
146ac4
 check lv_field $vg1/$lv1 lv_active ""
146ac4
@@ -72,7 +76,8 @@ lvchange -an $vg1
146ac4
 # then check that vg is activated when both
146ac4
 # devs appear together
146ac4
 
146ac4
-_clear_online
146ac4
+_clear_online_files
146ac4
+touch "$RUNDIR/lvm/pvs_online/foo"
146ac4
 
146ac4
 pvscan --cache -aay "$dev1" "$dev2"
146ac4
 check lv_field $vg1/$lv1 lv_active "active"
146ac4
@@ -92,7 +97,8 @@ lvcreate -n $lv1 -l 4 -a n $vg1
146ac4
 # touch foo to disable first-pvscan case,
146ac4
 # test case where dev with metadata appears first
146ac4
 
146ac4
-_clear_online
146ac4
+_clear_online_files
146ac4
+touch "$RUNDIR/lvm/pvs_online/foo"
146ac4
 
146ac4
 pvscan --cache -aay "$dev2"
146ac4
 check lv_field $vg1/$lv1 lv_active ""
146ac4
@@ -104,7 +110,8 @@ lvchange -an $vg1
146ac4
 # test case where dev without metadata
146ac4
 # appears first which triggers scanning all
146ac4
 
146ac4
-_clear_online
146ac4
+_clear_online_files
146ac4
+touch "$RUNDIR/lvm/pvs_online/foo"
146ac4
 
146ac4
 pvscan --cache -aay "$dev1"
146ac4
 check lv_field $vg1/$lv1 lv_active "active"
146ac4
@@ -115,7 +122,7 @@ lvchange -an $vg1
146ac4
 # dev without metadata is scanned, but
146ac4
 # first-pvscan case scans all devs
146ac4
 
146ac4
-_clear_online nofoo
146ac4
+_clear_online_files
146ac4
 
146ac4
 pvscan --cache -aay "$dev1"
146ac4
 check lv_field $vg1/$lv1 lv_active "active"
146ac4
@@ -125,7 +132,8 @@ lvchange -an $vg1
146ac4
 # is online without the -aay option to
146ac4
 # activate until after they are online
146ac4
 
146ac4
-_clear_online
146ac4
+_clear_online_files
146ac4
+touch "$RUNDIR/lvm/pvs_online/foo"
146ac4
 
146ac4
 pvscan --cache "$dev1"
146ac4
 check lv_field $vg1/$lv1 lv_active ""
146ac4
@@ -137,7 +145,8 @@ lvchange -an $vg1
146ac4
 
146ac4
 # like previous
146ac4
 
146ac4
-_clear_online
146ac4
+_clear_online_files
146ac4
+touch "$RUNDIR/lvm/pvs_online/foo"
146ac4
 
146ac4
 pvscan --cache "$dev1"
146ac4
 check lv_field $vg1/$lv1 lv_active ""
146ac4
diff --git a/test/shell/pvscan-cache.sh b/test/shell/pvscan-cache.sh
146ac4
index c272c6c..e0576f9 100644
146ac4
--- a/test/shell/pvscan-cache.sh
146ac4
+++ b/test/shell/pvscan-cache.sh
146ac4
@@ -13,6 +13,18 @@
146ac4
 SKIP_WITH_LVMLOCKD=1
146ac4
 SKIP_WITH_LVMPOLLD=1
146ac4
 
146ac4
+RUNDIR="/run"
146ac4
+test -d "$RUNDIR" || RUNDIR="/var/run"
146ac4
+PVS_ONLINE_DIR="$RUNDIR/lvm/pvs_online"
146ac4
+VGS_ONLINE_DIR="$RUNDIR/lvm/vgs_online"
146ac4
+
146ac4
+_clear_online_files() {
146ac4
+	# wait till udev is finished
146ac4
+	aux udev_wait
146ac4
+	rm -f "$PVS_ONLINE_DIR"/*
146ac4
+	rm -f "$VGS_ONLINE_DIR"/*
146ac4
+}
146ac4
+
146ac4
 . lib/inittest
146ac4
 
146ac4
 aux prepare_pvs 2
146ac4
@@ -35,9 +47,12 @@ check lv_exists $vg1
146ac4
 check lv_field $vg1/$lv1 lv_active ""
146ac4
 
146ac4
 # Check that an LV cannot be activated by pvscan while VG is exported
146ac4
+vgchange -an $vg1
146ac4
+_clear_online_files
146ac4
 vgexport $vg1
146ac4
-not pvscan --cache -aay "$dev1"
146ac4
-not pvscan --cache -aay "$dev2"
146ac4
+pvscan --cache -aay "$dev1" || true
146ac4
+pvscan --cache -aay "$dev2" || true
146ac4
+_clear_online_files
146ac4
 vgimport $vg1
146ac4
 check lv_exists $vg1
146ac4
 check lv_field $vg1/$lv1 lv_active ""
146ac4
@@ -51,6 +66,7 @@ lvchange -an $vg1/$lv1
146ac4
 # metadata which hasn't been updated for some
146ac4
 # time and also since the MDA is marked as ignored,
146ac4
 # it should really be *ignored*!
146ac4
+_clear_online_files
146ac4
 pvchange --metadataignore y "$dev1"
146ac4
 aux disable_dev "$dev2"
146ac4
 pvscan --cache
146ac4
diff --git a/tools/pvscan.c b/tools/pvscan.c
146ac4
index df46e04..465b3e6 100644
146ac4
--- a/tools/pvscan.c
146ac4
+++ b/tools/pvscan.c
146ac4
@@ -36,6 +36,9 @@ struct pvscan_aa_params {
146ac4
 	unsigned int activate_errors;
146ac4
 };
146ac4
 
146ac4
+
146ac4
+static const char *_pvs_online_dir = DEFAULT_RUN_DIR "/pvs_online";
146ac4
+static const char *_vgs_online_dir = DEFAULT_RUN_DIR "/vgs_online";
146ac4
 static const char *_online_file = DEFAULT_RUN_DIR "/pvs_online_lock";
146ac4
 static int _online_fd = -1;
146ac4
 
146ac4
@@ -227,7 +230,51 @@ out:
146ac4
 	return ret;
146ac4
 }
146ac4
 
146ac4
-static const char *_pvs_online_dir = DEFAULT_RUN_DIR "/pvs_online";
146ac4
+static char *_vgname_in_pvid_file_buf(char *buf)
146ac4
+{
146ac4
+	char *p, *n;
146ac4
+
146ac4
+	/*
146ac4
+	 * file contains:
146ac4
+	 * <major>:<minor>\n
146ac4
+	 * vg:<vgname>\n\0
146ac4
+	 */
146ac4
+
146ac4
+	if (!(p = strchr(buf, '\n')))
146ac4
+		return NULL;
146ac4
+
146ac4
+	p++; /* skip \n */
146ac4
+
146ac4
+	if (*p && !strncmp(p, "vg:", 3)) {
146ac4
+		if ((n = strchr(p, '\n')))
146ac4
+			*n = '\0';
146ac4
+		return p + 3;
146ac4
+	}
146ac4
+	return NULL;
146ac4
+}
146ac4
+
146ac4
+#define MAX_PVID_FILE_SIZE 512
146ac4
+
146ac4
+/*
146ac4
+ * When a PV goes offline, remove the vg online file for that VG
146ac4
+ * (even if other PVs for the VG are still online).  This means
146ac4
+ * that the vg will be activated again when it becomes complete.
146ac4
+ */
146ac4
+
146ac4
+static void _online_vg_file_remove(const char *vgname)
146ac4
+{
146ac4
+	char path[PATH_MAX];
146ac4
+
146ac4
+	if (dm_snprintf(path, sizeof(path), "%s/%s", _vgs_online_dir, vgname) < 0) {
146ac4
+		log_error("Path %s/%s is too long.", _vgs_online_dir, vgname);
146ac4
+		return;
146ac4
+	}
146ac4
+
146ac4
+	log_debug("Unlink vg online: %s", path);
146ac4
+
146ac4
+	if (unlink(path))
146ac4
+		log_sys_debug("unlink", path);
146ac4
+}
146ac4
 
146ac4
 /*
146ac4
  * When a device goes offline we only know its major:minor, not its PVID.
146ac4
@@ -240,8 +287,9 @@ static const char *_pvs_online_dir = DEFAULT_RUN_DIR "/pvs_online";
146ac4
 static void _online_pvid_file_remove_devno(int major, int minor)
146ac4
 {
146ac4
 	char path[PATH_MAX];
146ac4
-	char buf[32];
146ac4
-	char buf_in[32];
146ac4
+	char buf[MAX_PVID_FILE_SIZE];
146ac4
+	char buf_in[MAX_PVID_FILE_SIZE];
146ac4
+	char *vgname = NULL;
146ac4
 	DIR *dir;
146ac4
 	struct dirent *de;
146ac4
 	int fd, rv;
146ac4
@@ -267,6 +315,8 @@ static void _online_pvid_file_remove_devno(int major, int minor)
146ac4
 			continue;
146ac4
 		}
146ac4
 
146ac4
+		memset(buf_in, 0, sizeof(buf_in));
146ac4
+
146ac4
 		rv = read(fd, buf_in, sizeof(buf_in));
146ac4
 		if (close(fd))
146ac4
 			log_sys_debug("close", path);
146ac4
@@ -276,9 +326,16 @@ static void _online_pvid_file_remove_devno(int major, int minor)
146ac4
 		}
146ac4
 
146ac4
 		if (!strncmp(buf, buf_in, strlen(buf))) {
146ac4
-			log_debug("Unlink pv online %s %s", buf, path);
146ac4
+			log_debug("Unlink pv online %s", path);
146ac4
 			if (unlink(path))
146ac4
 				log_sys_debug("unlink", path);
146ac4
+
146ac4
+			/* vgname points to an offset in buf_in */
146ac4
+			if ((vgname = _vgname_in_pvid_file_buf(buf_in)))
146ac4
+				_online_vg_file_remove(vgname);
146ac4
+			else
146ac4
+				log_debug("No vgname in pvid file");
146ac4
+
146ac4
 			break;
146ac4
 		}
146ac4
 	}
146ac4
@@ -286,13 +343,13 @@ static void _online_pvid_file_remove_devno(int major, int minor)
146ac4
 		log_sys_debug("closedir", _pvs_online_dir);
146ac4
 }
146ac4
 
146ac4
-static void _online_pvid_files_remove(void)
146ac4
+static void _online_files_remove(const char *dirpath)
146ac4
 {
146ac4
 	char path[PATH_MAX];
146ac4
 	DIR *dir;
146ac4
 	struct dirent *de;
146ac4
 
146ac4
-	if (!(dir = opendir(_pvs_online_dir)))
146ac4
+	if (!(dir = opendir(dirpath)))
146ac4
 		return;
146ac4
 
146ac4
 	while ((de = readdir(dir))) {
146ac4
@@ -300,22 +357,26 @@ static void _online_pvid_files_remove(void)
146ac4
 			continue;
146ac4
 
146ac4
 		memset(path, 0, sizeof(path));
146ac4
-		snprintf(path, sizeof(path), "%s/%s", _pvs_online_dir, de->d_name);
146ac4
+		snprintf(path, sizeof(path), "%s/%s", dirpath, de->d_name);
146ac4
 		if (unlink(path))
146ac4
 			log_sys_debug("unlink", path);
146ac4
 	}
146ac4
 	if (closedir(dir))
146ac4
-		log_sys_debug("closedir", _pvs_online_dir);
146ac4
+		log_sys_debug("closedir", dirpath);
146ac4
 }
146ac4
 
146ac4
-static int _online_pvid_file_create(struct device *dev)
146ac4
+static int _online_pvid_file_create(struct device *dev, const char *vgname)
146ac4
 {
146ac4
 	char path[PATH_MAX];
146ac4
-	char buf[32];
146ac4
+	char buf[MAX_PVID_FILE_SIZE];
146ac4
 	int major, minor;
146ac4
 	int fd;
146ac4
 	int rv;
146ac4
 	int len;
146ac4
+	int len1 = 0;
146ac4
+	int len2 = 0;
146ac4
+
146ac4
+	memset(buf, 0, sizeof(buf));
146ac4
 
146ac4
 	major = (int)MAJOR(dev->dev);
146ac4
 	minor = (int)MINOR(dev->dev);
146ac4
@@ -325,16 +386,26 @@ static int _online_pvid_file_create(struct device *dev)
146ac4
 		return 0;
146ac4
 	}
146ac4
 
146ac4
-	if ((len = dm_snprintf(buf, sizeof(buf), "%d:%d\n", major, minor)) < 0) {
146ac4
-		log_error("Device %d:%d is too long.", major, minor);
146ac4
+	if ((len1 = dm_snprintf(buf, sizeof(buf), "%d:%d\n", major, minor)) < 0) {
146ac4
+		log_error("Cannot create online pv file for %d:%d.", major, minor);
146ac4
 		return 0;
146ac4
 	}
146ac4
 
146ac4
+	if (vgname) {
146ac4
+		if ((len2 = dm_snprintf(buf + len1, sizeof(buf) - len1, "vg:%s\n", vgname)) < 0) {
146ac4
+			log_warn("Incomplete online pv file for %d:%d vg %s.", major, minor, vgname);
146ac4
+			/* can still continue without vgname */
146ac4
+			len2 = 0;
146ac4
+		}
146ac4
+	}
146ac4
+
146ac4
+	len = len1 + len2;
146ac4
+
146ac4
 	log_debug("Create pv online: %s %d:%d %s", path, major, minor, dev_name(dev));
146ac4
 
146ac4
 	fd = open(path, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
146ac4
 	if (fd < 0) {
146ac4
-		log_error("Failed to open %s: %d", path, errno);
146ac4
+		log_error("Failed to open create %s: %d", path, errno);
146ac4
 		return 0;
146ac4
 	}
146ac4
 
146ac4
@@ -377,20 +448,45 @@ static int _online_pvid_file_exists(const char *pvid)
146ac4
 	return 0;
146ac4
 }
146ac4
 
146ac4
-static void _online_pvid_dir_setup(void)
146ac4
+static void _online_dir_setup(void)
146ac4
 {
146ac4
 	struct stat st;
146ac4
 	int rv;
146ac4
 
146ac4
+	if (!stat(DEFAULT_RUN_DIR, &st))
146ac4
+		goto do_pvs;
146ac4
+
146ac4
+	log_debug("Creating run_dir.");
146ac4
+	dm_prepare_selinux_context(DEFAULT_RUN_DIR, S_IFDIR);
146ac4
+	rv = mkdir(DEFAULT_RUN_DIR, 0755);
146ac4
+	dm_prepare_selinux_context(NULL, 0);
146ac4
+
146ac4
+	if ((rv < 0) && stat(DEFAULT_RUN_DIR, &st))
146ac4
+		log_error("Failed to create %s %d", DEFAULT_RUN_DIR, errno);
146ac4
+
146ac4
+do_pvs:
146ac4
 	if (!stat(_pvs_online_dir, &st))
146ac4
-		return;
146ac4
+		goto do_vgs;
146ac4
 
146ac4
+	log_debug("Creating pvs_online_dir.");
146ac4
 	dm_prepare_selinux_context(_pvs_online_dir, S_IFDIR);
146ac4
-	rv = mkdir(_pvs_online_dir, 0777);
146ac4
+	rv = mkdir(_pvs_online_dir, 0755);
146ac4
+	dm_prepare_selinux_context(NULL, 0);
146ac4
+
146ac4
+	if ((rv < 0) && stat(_pvs_online_dir, &st))
146ac4
+		log_error("Failed to create %s %d", _pvs_online_dir, errno);
146ac4
+
146ac4
+do_vgs:
146ac4
+	if (!stat(_vgs_online_dir, &st))
146ac4
+		return;
146ac4
+
146ac4
+	log_debug("Creating vgs_online_dir.");
146ac4
+	dm_prepare_selinux_context(_vgs_online_dir, S_IFDIR);
146ac4
+	rv = mkdir(_vgs_online_dir, 0755);
146ac4
 	dm_prepare_selinux_context(NULL, 0);
146ac4
 
146ac4
-	if (rv < 0)
146ac4
-		log_debug("Failed to create %s", _pvs_online_dir);
146ac4
+	if ((rv < 0) && stat(_vgs_online_dir, &st))
146ac4
+		log_error("Failed to create %s %d", _vgs_online_dir, errno);
146ac4
 }
146ac4
 
146ac4
 static void _online_file_setup(void)
146ac4
@@ -401,8 +497,10 @@ static void _online_file_setup(void)
146ac4
 	if (!stat(_online_file, &st))
146ac4
 		return;
146ac4
 
146ac4
-	if (!(fp = fopen(_online_file, "w")))
146ac4
+	if (!(fp = fopen(_online_file, "w"))) {
146ac4
+		log_error("Failed to create %s %d", _online_file, errno);
146ac4
 		return;
146ac4
+	}
146ac4
 	if (fclose(fp))
146ac4
 		stack;
146ac4
 }
146ac4
@@ -442,11 +540,13 @@ static int _online_pv_found(struct cmd_context *cmd,
146ac4
 	 * Create file named for pvid to record this PV is online.
146ac4
 	 */
146ac4
 
146ac4
-	if (!_online_pvid_file_create(dev))
146ac4
+	if (!_online_pvid_file_create(dev, vg ? vg->name : NULL))
146ac4
 		return_0;
146ac4
 
146ac4
-	if (!vg || !found_vgnames)
146ac4
+	if (!vg || !found_vgnames) {
146ac4
+		log_print("pvscan[%d] PV %s online.", getpid(), dev_name(dev));
146ac4
 		return 1;
146ac4
+	}
146ac4
 
146ac4
 	/*
146ac4
 	 * Check if all the PVs for this VG are online.  This is only
146ac4
@@ -469,8 +569,13 @@ static int _online_pv_found(struct cmd_context *cmd,
146ac4
 	 * in the VG, which means the VG is not yet complete.
146ac4
 	 */
146ac4
 
146ac4
-	if (pvids_not_online)
146ac4
+	if (pvids_not_online) {
146ac4
+		log_print("pvscan[%d] PV %s online, VG %s incomplete (need %d).",
146ac4
+			  getpid(), dev_name(dev), vg->name, pvids_not_online);
146ac4
 		return 1;
146ac4
+	}
146ac4
+
146ac4
+	log_print("pvscan[%d] PV %s online, VG %s is complete.", getpid(), dev_name(dev), vg->name);
146ac4
 
146ac4
 	/*
146ac4
 	 * When all PVIDs from the VG are online, then add vgname to
146ac4
@@ -541,7 +646,7 @@ static int _online_pvscan_one(struct cmd_context *cmd, struct device *dev,
146ac4
 	log_debug("pvscan metadata from dev %s", dev_name(dev));
146ac4
 
146ac4
 	if (udev_dev_is_mpath_component(dev)) {
146ac4
-		log_debug("Ignore multipath component for pvscan.");
146ac4
+		log_print("pvscan[%d] ignore multipath component %s.", getpid(), dev_name(dev));
146ac4
 		return 1;
146ac4
 	}
146ac4
 
146ac4
@@ -649,10 +754,37 @@ static int _pvscan_aa_single(struct cmd_context *cmd, const char *vg_name,
146ac4
 	return ECMD_PROCESSED;
146ac4
 }
146ac4
 
146ac4
+static int _online_vg_file_create(struct cmd_context *cmd, const char *vgname)
146ac4
+{
146ac4
+	char path[PATH_MAX];
146ac4
+	int fd;
146ac4
+
146ac4
+	if (dm_snprintf(path, sizeof(path), "%s/%s", _vgs_online_dir, vgname) < 0) {
146ac4
+		log_error("Path %s/%s is too long.", _vgs_online_dir, vgname);
146ac4
+		return 0;
146ac4
+	}
146ac4
+
146ac4
+	log_debug("Create vg online: %s", path);
146ac4
+
146ac4
+	fd = open(path, O_CREAT | O_EXCL | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
146ac4
+	if (fd < 0) {
146ac4
+		log_debug("Failed to create %s: %d", path, errno);
146ac4
+		return 0;
146ac4
+	}
146ac4
+
146ac4
+	/* We don't care about syncing, these files are not even persistent. */
146ac4
+
146ac4
+	if (close(fd))
146ac4
+		log_sys_debug("close", path);
146ac4
+
146ac4
+	return 1;
146ac4
+}
146ac4
+
146ac4
 static int _pvscan_aa(struct cmd_context *cmd, struct pvscan_aa_params *pp,
146ac4
 		      struct dm_list *vgnames)
146ac4
 {
146ac4
 	struct processing_handle *handle = NULL;
146ac4
+	struct dm_str_list *sl, *sl2;
146ac4
 	int ret;
146ac4
 
146ac4
 	if (dm_list_empty(vgnames)) {
146ac4
@@ -667,6 +799,27 @@ static int _pvscan_aa(struct cmd_context *cmd, struct pvscan_aa_params *pp,
146ac4
 
146ac4
 	handle->custom_handle = pp;
146ac4
 
146ac4
+	/*
146ac4
+	 * For each complete vg that can be autoactivated, see if this
146ac4
+	 * particular pvscan command should activate the vg.  There can be
146ac4
+	 * multiple concurrent pvscans for the same completed vg (when all the
146ac4
+	 * PVs for the VG appear at once), and we want only one of the pvscans
146ac4
+	 * to run the activation.  The first to create the file will do it.
146ac4
+	 */
146ac4
+	dm_list_iterate_items_safe(sl, sl2, vgnames) {
146ac4
+		if (!_online_vg_file_create(cmd, sl->str)) {
146ac4
+			log_print("pvscan[%d] VG %s skip autoactivation.", getpid(), sl->str);
146ac4
+			str_list_del(vgnames, sl->str);
146ac4
+			continue;
146ac4
+		}
146ac4
+		log_print("pvscan[%d] VG %s run autoactivation.", getpid(), sl->str);
146ac4
+	}
146ac4
+
146ac4
+	if (dm_list_empty(vgnames)) {
146ac4
+		destroy_processing_handle(cmd, handle);
146ac4
+		return ECMD_PROCESSED;
146ac4
+	}
146ac4
+
146ac4
 	ret = process_each_vg(cmd, 0, NULL, NULL, vgnames, READ_FOR_UPDATE, 0, handle, _pvscan_aa_single);
146ac4
 
146ac4
 	destroy_processing_handle(cmd, handle);
146ac4
@@ -707,7 +860,7 @@ int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv)
146ac4
 		return EINVALID_CMD_LINE;
146ac4
 	}
146ac4
 
146ac4
-	_online_pvid_dir_setup();
146ac4
+	_online_dir_setup();
146ac4
 	_online_file_setup();
146ac4
 	
146ac4
 	if (!lock_vol(cmd, VG_GLOBAL, LCK_VG_READ, NULL)) {
146ac4
@@ -721,8 +874,8 @@ int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv)
146ac4
 	if (!argc && !devno_args) {
146ac4
 		_lock_online(LOCK_EX, 0);
146ac4
 		log_verbose("pvscan all devices for requested refresh.");
146ac4
-		_online_pvid_files_remove();
146ac4
-		/* identify complete vgs, and only activate those vgs */
146ac4
+		_online_files_remove(_pvs_online_dir);
146ac4
+		_online_files_remove(_vgs_online_dir);
146ac4
 		_online_pvscan_all_devs(cmd, complete_vgnames, NULL);
146ac4
 		_unlock_online();
146ac4
 		goto activate;
146ac4
@@ -750,16 +903,17 @@ int pvscan_cache_cmd(struct cmd_context *cmd, int argc, char **argv)
146ac4
 	 * In the non-init case, a VG with two PVs, where both PVs appear at once
146ac4
 	 * two parallel pvscans for each PV create the pvid files for each PV in
146ac4
 	 * parallel, then both pvscans see the vg has completed, and both pvscans
146ac4
-	 * activate the VG in parallel.  The activations should be serialized by
146ac4
-	 * the VG lock.
146ac4
+	 * activate the VG in parallel.  The first pvscan to create the vgname
146ac4
+	 * file in vgs_online will do the activation, any others will skip it.
146ac4
 	 */
146ac4
 
146ac4
 	_lock_online(LOCK_EX, 0);
146ac4
 
146ac4
 	if (_online_pvid_files_missing()) {
146ac4
 		log_verbose("pvscan all devices to initialize available PVs.");
146ac4
-		_online_pvid_files_remove();
146ac4
-		/* identify complete vgs, and only activate those vgs */
146ac4
+		_online_files_remove(_pvs_online_dir);
146ac4
+		_online_files_remove(_vgs_online_dir);
146ac4
+		cmd->pvscan_cache_single = 1;
146ac4
 		_online_pvscan_all_devs(cmd, complete_vgnames, NULL);
146ac4
 		_unlock_online();
146ac4
 		goto activate;
146ac4
-- 
146ac4
1.8.3.1
146ac4