Blob Blame History Raw
 lib/cache/lvmcache.c          |  24 +++++++++
 lib/cache/lvmcache.h          |   4 ++
 lib/device/bcache-utils.c     |  15 ++++++
 lib/device/bcache.h           |   1 +
 lib/format_text/format-text.c |  16 +++++-
 lib/format_text/layout.h      |   2 +-
 lib/format_text/text_label.c  |   2 +-
 lib/label/label.c             |   5 ++
 lib/label/label.h             |   1 +
 lib/metadata/metadata.c       | 117 +++++++++++++++++++++++++++++++++++++++++-
 lib/metadata/metadata.h       |   4 +-
 11 files changed, 186 insertions(+), 5 deletions(-)

diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c
index b6a02b0..5b8dce8 100644
--- a/lib/cache/lvmcache.c
+++ b/lib/cache/lvmcache.c
@@ -3068,3 +3068,27 @@ uint64_t lvmcache_max_metadata_size(void)
 	return _max_metadata_size;
 }
 
+void lvmcache_get_mdas(struct cmd_context *cmd,
+		       const char *vgname, const char *vgid,
+                       struct dm_list *mda_list)
+{
+	struct lvmcache_vginfo *vginfo;
+	struct lvmcache_info *info;
+	struct mda_list *mdal;
+	struct metadata_area *mda, *mda2;
+
+	if (!(vginfo = lvmcache_vginfo_from_vgname(vgname, vgid))) {
+		log_error(INTERNAL_ERROR "lvmcache_get_mdas no vginfo %s", vgname);
+		return;
+	}
+
+	dm_list_iterate_items(info, &vginfo->infos) {
+		dm_list_iterate_items_safe(mda, mda2, &info->mdas) {
+			if (!(mdal = dm_zalloc(sizeof(*mdal))))
+				continue;
+			mdal->mda = mda;
+			dm_list_add(mda_list, &mdal->list);
+		}
+	}
+}
+
diff --git a/lib/cache/lvmcache.h b/lib/cache/lvmcache.h
index f436785..541e8be 100644
--- a/lib/cache/lvmcache.h
+++ b/lib/cache/lvmcache.h
@@ -228,4 +228,8 @@ void lvmcache_drop_saved_vgid(const char *vgid);
 uint64_t lvmcache_max_metadata_size(void);
 void lvmcache_save_metadata_size(uint64_t val);
 
+void lvmcache_get_mdas(struct cmd_context *cmd,
+                       const char *vgname, const char *vgid,
+                       struct dm_list *mda_list);
+
 #endif
diff --git a/lib/device/bcache-utils.c b/lib/device/bcache-utils.c
index a533a66..2f0b01d 100644
--- a/lib/device/bcache-utils.c
+++ b/lib/device/bcache-utils.c
@@ -79,6 +79,21 @@ bool bcache_read_bytes(struct bcache *cache, int fd, uint64_t start, size_t len,
 	return true;
 }
 
+bool bcache_invalidate_bytes(struct bcache *cache, int fd, uint64_t start, size_t len)
+{
+	block_address bb, be;
+	bool result = true;
+
+	byte_range_to_block_range(cache, start, len, &bb, &be);
+
+	for (; bb != be; bb++) {
+		if (!bcache_invalidate(cache, fd, bb))
+			result = false;
+	}
+
+	return result;
+}
+
 //----------------------------------------------------------------
 
 // Writing bytes and zeroing bytes are very similar, so we factor out
diff --git a/lib/device/bcache.h b/lib/device/bcache.h
index f9067f7..3e7a168 100644
--- a/lib/device/bcache.h
+++ b/lib/device/bcache.h
@@ -163,6 +163,7 @@ bool bcache_read_bytes(struct bcache *cache, int fd, uint64_t start, size_t len,
 bool bcache_write_bytes(struct bcache *cache, int fd, uint64_t start, size_t len, void *data);
 bool bcache_zero_bytes(struct bcache *cache, int fd, uint64_t start, size_t len);
 bool bcache_set_bytes(struct bcache *cache, int fd, uint64_t start, size_t len, uint8_t val);
+bool bcache_invalidate_bytes(struct bcache *cache, int fd, uint64_t start, size_t len);
 
 void bcache_set_last_byte(struct bcache *cache, int fd, uint64_t offset, int sector_size);
 void bcache_unset_last_byte(struct bcache *cache, int fd);
diff --git a/lib/format_text/format-text.c b/lib/format_text/format-text.c
index 6f5d739..75d7fcd 100644
--- a/lib/format_text/format-text.c
+++ b/lib/format_text/format-text.c
@@ -1199,6 +1199,7 @@ static int _scan_file(const struct format_type *fmt, const char *vgname)
 }
 
 int read_metadata_location_summary(const struct format_type *fmt,
+		    struct metadata_area *mda,
 		    struct mda_header *mdah, int primary_mda, struct device_area *dev_area,
 		    struct lvmcache_vgsummary *vgsummary, uint64_t *mda_free_sectors)
 {
@@ -1251,6 +1252,19 @@ int read_metadata_location_summary(const struct format_type *fmt,
 		return 0;
 	}
 
+	/*
+	 * This function is used to read the vg summary during label scan.
+	 * Save the text start location and checksum during scan.  After the VG
+	 * lock is acquired in vg_read, we can reread the mda_header, and
+	 * compare rlocn->offset,checksum to what was saved during scan.  If
+	 * unchanged, it means that the metadata was not changed between scan
+	 * and the read.
+	 */
+	if (mda) {
+		mda->scan_text_offset = rlocn->offset;
+		mda->scan_text_checksum = rlocn->checksum;
+	}
+
 	/* We found a VG - now check the metadata */
 	if (rlocn->offset + rlocn->size > mdah->size)
 		wrap = (uint32_t) ((rlocn->offset + rlocn->size) - mdah->size);
@@ -1374,7 +1388,7 @@ static int _scan_raw(const struct format_type *fmt, const char *vgname __attribu
 			continue;
 		}
 
-		if (read_metadata_location_summary(fmt, mdah, 0, &rl->dev_area, &vgsummary, NULL)) {
+		if (read_metadata_location_summary(fmt, NULL, mdah, 0, &rl->dev_area, &vgsummary, NULL)) {
 			vg = _vg_read_raw_area(&fid, vgsummary.vgname, &rl->dev_area, NULL, NULL, 0, 0);
 			if (vg) {
 				lvmcache_update_vg(vg, 0);
diff --git a/lib/format_text/layout.h b/lib/format_text/layout.h
index 2671bbf..4601952 100644
--- a/lib/format_text/layout.h
+++ b/lib/format_text/layout.h
@@ -104,7 +104,7 @@ struct mda_context {
 #define MDA_SIZE_MIN (8 * (unsigned) lvm_getpagesize())
 #define MDA_ORIGINAL_ALIGNMENT 512	/* Original alignment used for start of VG metadata content */
 
-int read_metadata_location_summary(const struct format_type *fmt, struct mda_header *mdah, int primary_mda, 
+int read_metadata_location_summary(const struct format_type *fmt, struct metadata_area *mda, struct mda_header *mdah, int primary_mda, 
 		    struct device_area *dev_area, struct lvmcache_vgsummary *vgsummary,
 		    uint64_t *mda_free_sectors);
 
diff --git a/lib/format_text/text_label.c b/lib/format_text/text_label.c
index 7d10e06..fc8294e 100644
--- a/lib/format_text/text_label.c
+++ b/lib/format_text/text_label.c
@@ -345,7 +345,7 @@ static int _read_mda_header_and_metadata(struct metadata_area *mda, void *baton)
 		return 1;
 	}
 
-	if (!read_metadata_location_summary(fmt, mdah, mda_is_primary(mda), &mdac->area,
+	if (!read_metadata_location_summary(fmt, mda, mdah, mda_is_primary(mda), &mdac->area,
 					    &vgsummary, &mdac->free_sectors)) {
 		if (vgsummary.zero_offset)
 			return 1;
diff --git a/lib/label/label.c b/lib/label/label.c
index 70b7934..8a4b662 100644
--- a/lib/label/label.c
+++ b/lib/label/label.c
@@ -1418,6 +1418,11 @@ bool dev_write_bytes(struct device *dev, uint64_t start, size_t len, void *data)
 	return true;
 }
 
+bool dev_invalidate_bytes(struct device *dev, uint64_t start, size_t len)
+{
+	return bcache_invalidate_bytes(scan_bcache, dev->bcache_fd, start, len);
+}
+
 bool dev_write_zeros(struct device *dev, uint64_t start, size_t len)
 {
 	if (test_mode())
diff --git a/lib/label/label.h b/lib/label/label.h
index 42c9946..ea29c84 100644
--- a/lib/label/label.h
+++ b/lib/label/label.h
@@ -128,6 +128,7 @@ bool dev_read_bytes(struct device *dev, uint64_t start, size_t len, void *data);
 bool dev_write_bytes(struct device *dev, uint64_t start, size_t len, void *data);
 bool dev_write_zeros(struct device *dev, uint64_t start, size_t len);
 bool dev_set_bytes(struct device *dev, uint64_t start, size_t len, uint8_t val);
+bool dev_invalidate_bytes(struct device *dev, uint64_t start, size_t len);
 void dev_set_last_byte(struct device *dev, uint64_t offset);
 void dev_unset_last_byte(struct device *dev);
 
diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c
index d448fd9..39f10fe 100644
--- a/lib/metadata/metadata.c
+++ b/lib/metadata/metadata.c
@@ -33,6 +33,8 @@
 #include "lvmlockd.h"
 #include "time.h"
 #include "lvmnotify.h"
+#include "format_text/format-text.h"
+#include "format_text/layout.h"
 
 #include <math.h>
 #include <sys/param.h>
@@ -3760,6 +3762,118 @@ out:
 	return r;
 }
 
+/*
+ * Reread an mda_header.  If the text offset is the same as was seen and saved
+ * by label scan, it means the metadata is unchanged and we do not need to
+ * reread metadata.
+ *
+ * This is used to ensure that the metadata seen during scan still matches
+ * what's on disk.  If the scan data still matches what's on disk we don't
+ * need to reread the metadata from disk.  When we read the metadata from
+ * bcache it may come from the cache or from disk again if the cache has
+ * dropped it.
+ */
+
+static bool _scan_text_mismatch(struct cmd_context *cmd, const char *vgname, const char *vgid)
+{
+	struct dm_list mda_list;
+	struct mda_list *mdal, *safe;
+	struct metadata_area *mda;
+	struct mda_context *mdac;
+	struct device_area *area;
+	struct mda_header *mdah;
+	struct raw_locn *rlocn;
+	struct device *dev;
+	bool ret = true;
+
+	/*
+	 * if cmd->can_use_one_scan, check one mda_header is unchanged,
+	 * else check that all mda_headers are unchanged.
+	 */
+
+	dm_list_init(&mda_list);
+
+	lvmcache_get_mdas(cmd, vgname, vgid, &mda_list);
+
+	dm_list_iterate_items(mdal, &mda_list) {
+		mda = mdal->mda;
+
+		if (!mda->scan_text_offset)
+			continue;
+
+		if (!mda_is_primary(mda))
+			continue;
+
+		if (!(dev = mda_get_device(mda))) {
+			log_debug("rescan for text mismatch - no mda dev");
+			goto out;
+		}
+
+		mdac = mda->metadata_locn;
+		area = &mdac->area;
+
+		/*
+		 * Invalidate mda_header in bcache so it will be reread from disk.
+		 */
+		if (!dev_invalidate_bytes(dev, 4096, 512)) {
+			log_debug("rescan for text mismatch - cannot invalidate");
+			goto out;
+		}
+
+		if (!(mdah = raw_read_mda_header(cmd->fmt, area, 1))) {
+			log_debug("rescan for text mismatch - no mda header");
+			goto out;
+		}
+
+		rlocn = mdah->raw_locns;
+
+		if (rlocn->checksum != mda->scan_text_checksum) {
+			log_debug("rescan for text checksum mismatch on %s - now %x prev %x offset now %llu prev %llu",
+				  dev_name(dev),
+				  rlocn->checksum, mda->scan_text_checksum,
+				  (unsigned long long)rlocn->offset,
+				  (unsigned long long)mda->scan_text_offset);
+		} else if (rlocn->offset != mda->scan_text_offset) {
+			log_debug("rescan for text offset mismatch on %s - now %llu prev %llu checksum %x",
+				  dev_name(dev),
+				  (unsigned long long)rlocn->offset,
+				  (unsigned long long)mda->scan_text_offset,
+				  rlocn->checksum);
+		} else {
+			/* the common case where fields match and no rescan needed */
+			ret = false;
+		}
+
+		dm_pool_free(cmd->mem, mdah);
+
+		/* For can_use_one_scan commands, return result from checking one mda. */
+		if (cmd->can_use_one_scan)
+			goto out;
+
+		/* For other commands, return mismatch immediately. */
+		if (ret)
+			goto_out;
+	}
+
+	if (ret) {
+		/* shouldn't happen */
+		log_debug("rescan for text mismatch - no mdas");
+		goto out;
+	}
+out:
+	if (!ret)
+		log_debug("rescan skipped - unchanged offset %llu checksum %x",
+			  (unsigned long long)mda->scan_text_offset,
+			  mda->scan_text_checksum);
+
+	dm_list_iterate_items_safe(mdal, safe, &mda_list) {
+		dm_list_del(&mdal->list);
+		free(mdal);
+	}
+
+	return ret;
+}
+
 /* Caller sets consistent to 1 if it's safe for vg_read_internal to correct
  * inconsistent metadata on disk (i.e. the VG write lock is held).
  * This guarantees only consistent metadata is returned.
@@ -3904,7 +4018,8 @@ static struct volume_group *_vg_read(struct cmd_context *cmd,
 	 * lock is taken prior to the label scan, and still held here,
 	 * we can also skip the rescan in that case.
 	 */
-	if (!cmd->can_use_one_scan || lvmcache_scan_mismatch(cmd, vgname, vgid)) {
+	if (!cmd->can_use_one_scan ||
+	    lvmcache_scan_mismatch(cmd, vgname, vgid) || _scan_text_mismatch(cmd, vgname, vgid)) {
 		/* the skip rescan special case is for clvmd vg_read_by_vgid */
 		/* FIXME: this is not a warn flag, pass this differently */
 		if (warn_flags & SKIP_RESCAN)
diff --git a/lib/metadata/metadata.h b/lib/metadata/metadata.h
index f8083e5..96bbb56 100644
--- a/lib/metadata/metadata.h
+++ b/lib/metadata/metadata.h
@@ -173,6 +173,8 @@ struct metadata_area {
 	struct metadata_area_ops *ops;
 	void *metadata_locn;
 	uint32_t status;
+	uint64_t scan_text_offset; /* rlocn->offset seen during scan */
+	uint32_t scan_text_checksum; /* rlocn->checksum seen during scan */
 };
 struct metadata_area *mda_copy(struct dm_pool *mem,
 			       struct metadata_area *mda);
@@ -234,7 +236,7 @@ struct name_list {
 
 struct mda_list {
 	struct dm_list list;
-	struct device_area mda;
+	struct metadata_area *mda;
 };
 
 struct peg_list {