Blob Blame History Raw
commit 923b95504e189fb3b9353a66a3c3a9e147a46e39
Author: Peter Rajnoha <prajnoha@redhat.com>
Date:   Wed Mar 5 16:48:17 2014 +0100

    lvmetad_fixes
---
 WHATS_NEW                       |  3 +++
 daemons/lvmetad/lvmetad-core.c  | 20 +++++++++++++++++---
 test/shell/lvmetad-ambiguous.sh | 34 ++++++++++++++++++++++++++++++++++
 3 files changed, 54 insertions(+), 3 deletions(-)

diff --git a/WHATS_NEW b/WHATS_NEW
index 224e351..3ee9585 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,8 @@
 Version 2.02.106 - 
 ====================================
+  Fix cache consistency in lvmetad when PV moves around.
+  Fix memleak when lvmetad discovers PV to appear on another device.
+  Fix invalid memory read in lvmetad that could cause a deadlock.
   Fix calculation of maximum size of COW device for snapshot (2.02.99).
   Do not allow stripe size to be bigger then extent size for lvresize.
   Zero snapshot COW header when creating read-only snapshot.
diff --git a/daemons/lvmetad/lvmetad-core.c b/daemons/lvmetad/lvmetad-core.c
index e6e222f..f35db89 100644
--- a/daemons/lvmetad/lvmetad-core.c
+++ b/daemons/lvmetad/lvmetad-core.c
@@ -861,7 +861,7 @@ static response pv_found(lvmetad_state *s, request r)
 	const char *vgid = daemon_request_str(r, "metadata/id", NULL);
 	const char *vgid_old = NULL;
 	struct dm_config_node *pvmeta = dm_config_find_node(r.cft->root, "pvmeta");
-	uint64_t device;
+	uint64_t device, device_old_pvid = 0;
 	struct dm_config_tree *cft, *pvmeta_old_dev = NULL, *pvmeta_old_pvid = NULL;
 	char *old;
 	char *pvid_dup;
@@ -883,9 +883,12 @@ static response pv_found(lvmetad_state *s, request r)
 		dm_hash_remove(s->pvid_to_pvmeta, old);
 		vgid_old = dm_hash_lookup(s->pvid_to_vgid, old);
 	}
-	pvmeta_old_pvid = dm_hash_lookup(s->pvid_to_pvmeta, pvid);
 
-	DEBUGLOG(s, "pv_found %s, vgid = %s, device = %" PRIu64 ", old = %s", pvid, vgid, device, old);
+	if ((pvmeta_old_pvid = dm_hash_lookup(s->pvid_to_pvmeta, pvid)))
+		dm_config_get_uint64(pvmeta_old_pvid->root, "pvmeta/device", &device_old_pvid);
+
+	DEBUGLOG(s, "pv_found %s, vgid = %s, device = %" PRIu64 " (previously %" PRIu64 "), old = %s",
+		 pvid, vgid, device, device_old_pvid, old);
 
 	dm_free(old);
 
@@ -903,6 +906,12 @@ static response pv_found(lvmetad_state *s, request r)
 		return reply_fail("out of memory");
 	}
 
+	if (pvmeta_old_pvid && device != device_old_pvid) {
+		DEBUGLOG(s, "pv %s no longer on device %" PRIu64, pvid, device_old_pvid);
+		dm_free(dm_hash_lookup_binary(s->device_to_pvid, &device_old_pvid, sizeof(device_old_pvid)));
+		dm_hash_remove_binary(s->device_to_pvid, &device_old_pvid, sizeof(device_old_pvid));
+	}
+
 	if (!dm_hash_insert(s->pvid_to_pvmeta, pvid, cft) ||
 	    !dm_hash_insert_binary(s->device_to_pvid, &device, sizeof(device), (void*)pvid_dup)) {
 		dm_hash_remove(s->pvid_to_pvmeta, pvid);
@@ -911,6 +920,7 @@ static response pv_found(lvmetad_state *s, request r)
 		dm_free(pvid_dup);
 		return reply_fail("out of memory");
 	}
+
 	if (pvmeta_old_pvid)
 		dm_config_destroy(pvmeta_old_pvid);
 	if (pvmeta_old_dev && pvmeta_old_dev != pvmeta_old_pvid)
@@ -949,9 +959,13 @@ static response pv_found(lvmetad_state *s, request r)
 	}
 
 	if (vgid_old && (!vgid || strcmp(vgid, vgid_old))) {
+		/* make a copy, because vg_remove_if_missing will deallocate the
+		 * storage behind vgid_old */
+		vgid_old = dm_strdup(vgid_old);
 		lock_vg(s, vgid_old);
 		vg_remove_if_missing(s, vgid_old, 1);
 		unlock_vg(s, vgid_old);
+		dm_free((char*)vgid_old);
 	}
 
 	return daemon_reply_simple("OK",
diff --git a/test/shell/lvmetad-ambiguous.sh b/test/shell/lvmetad-ambiguous.sh
new file mode 100644
index 0000000..455aa5d
--- /dev/null
+++ b/test/shell/lvmetad-ambiguous.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+. lib/test
+
+test -e LOCAL_LVMETAD || skip
+
+aux prepare_pvs 2
+
+# flip the devices around
+aux init_udev_transaction
+dmsetup remove -f "$dev1"
+dmsetup remove -f "$dev2"
+dmsetup create -u TEST-${PREFIX}pv2 ${PREFIX}pv2 ${PREFIX}pv2.table
+dmsetup create -u TEST-${PREFIX}pv1 ${PREFIX}pv1 ${PREFIX}pv1.table
+aux finish_udev_transaction
+
+# re-scan them
+pvscan --cache $dev1
+pvscan --cache $dev2
+
+# expect both to be there
+pvs | tee pvs.txt
+grep $dev1 pvs.txt
+grep $dev2 pvs.txt
+