[lvm-devel] [PATCH 1/3] Share VG multiple times

Zdenek Kabelac zkabelac at redhat.com
Fri Jul 8 15:52:23 UTC 2011


Saves CPU by skipping creation of the same volume_group structure.

Structure vginfo is extended vg_cache and counters.

It tracks the number of reuses of every cached VG.
For lv_suspend we occasionaly need to reference the same VG more then
once - it's the reason for reference counter.

Caching is not used when VG is opened RW recover mode.

As there is far more case when sharing is possible - use rather specific
code to drop VG from lvmcache in this case - such VG is then unlocked.
and it is fully modifiable.

Signed-off-by: Zdenek Kabelac <zkabelac at redhat.com>
---
 lib/cache/lvmcache.c    |   67 ++++++++++++++++++++++++++++++++++++++++++++++-
 lib/cache/lvmcache.h    |    5 +++
 lib/metadata/metadata.c |   26 +++++++++++++++++-
 lib/metadata/vg.h       |    2 +
 4 files changed, 97 insertions(+), 3 deletions(-)

diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c
index 0e92fa3..275087f 100644
--- a/lib/cache/lvmcache.c
+++ b/lib/cache/lvmcache.c
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
- * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
  *
  * This file is part of LVM2.
  *
@@ -89,6 +89,12 @@ static void _free_cached_vgmetadata(struct lvmcache_vginfo *vginfo)
 		vginfo->cft = NULL;
 	}
 
+	/* Release cached VG, and check there is at most 1 user */
+	/* Note: 1 reference is allowed during precommit lvmcache update */
+	free_vg(vginfo->vg_cache);
+	if (vginfo->vg_cache && !lvmcache_release_vg(vginfo->vg_cache))
+		log_error(INTERNAL_ERROR "Cache releases referenced VG.");
+
 	log_debug("Metadata cache: VG %s wiped.", vginfo->vgname);
 }
 
@@ -662,6 +668,17 @@ struct volume_group *lvmcache_get_vg(const char *vgid, unsigned precommitted)
 	    (!precommitted && vginfo->precommitted && !critical_section()))
 		return NULL;
 
+	/* Use already cached VG when available */
+	if (vginfo->vg_cache)  {
+		if (vginfo->vg_ref_count > 2)
+			/* For lv_suspend we need lv and lv_precommit */
+			log_error(INTERNAL_ERROR "More then one shared VG.");
+		vg = vginfo->vg_cache;
+		vginfo->vg_ref_count++;
+		vginfo->vg_use_count++;
+		goto out;
+	}
+
 	fic.type = FMT_INSTANCE_VG | FMT_INSTANCE_MDAS | FMT_INSTANCE_AUX_MDAS;
 	fic.context.vg_ref.vg_name = vginfo->vgname;
 	fic.context.vg_ref.vg_id = vgid;
@@ -678,6 +695,13 @@ struct volume_group *lvmcache_get_vg(const char *vgid, unsigned precommitted)
 	if (!(vg = import_vg_from_config_tree(vginfo->cft, fid)))
 		goto_bad;
 
+	/* Put VG into lvmcache */
+	vg->vginfo = vginfo;
+	vginfo->vg_cache = vg;
+	vginfo->vg_ref_count = 2; /* Keep 1 reference for lvmcache */
+	vginfo->vg_use_count = 1;
+
+out:
 	log_debug("Using cached %smetadata for VG %s.",
 		  vginfo->precommitted ? "pre-committed" : "", vginfo->vgname);
 
@@ -689,6 +713,47 @@ bad:
 	return NULL;
 }
 
+/**
+ * Release a referenced VG from lvmcache.
+ */
+int lvmcache_release_vg(struct volume_group *vg)
+{
+	if (!vg->vginfo)
+		return 1;
+
+	if (--vg->vginfo->vg_ref_count)
+		return 0; /* Still referenced */
+
+	log_debug("Released VG:%p %s from cache (used:%d).",
+		  vg, vg->name, vg->vginfo->vg_use_count);
+
+	vg->vginfo->vg_cache = NULL;
+	vg->vginfo = NULL;
+
+	return 1;
+}
+
+/**
+ * Drop VG from lvmcache.
+ *
+ * This is used for 'write' operations.
+ * Note: VG must not be shared in this moment.
+ */
+int lvmcache_drop_vg(struct volume_group *vg)
+{
+	if (!vg->vginfo)
+		return 1; /* Not cached */
+
+	free_vg(vg);
+
+	if (!lvmcache_release_vg(vg)) {
+		log_error(INTERNAL_ERROR "Dropping referenced VG.");
+		return 0;
+	}
+
+	return 1;
+}
+
 struct dm_list *lvmcache_get_vgids(struct cmd_context *cmd,
 				   int include_internal)
 {
diff --git a/lib/cache/lvmcache.h b/lib/cache/lvmcache.h
index 9aafff5..f5c1131 100644
--- a/lib/cache/lvmcache.h
+++ b/lib/cache/lvmcache.h
@@ -50,6 +50,9 @@ struct lvmcache_vginfo {
 	char *vgmetadata;	/* Copy of VG metadata as format_text string */
 	struct config_tree *cft; /* Config tree created from vgmetadata */
 				/* Lifetime is directly tied to vgmetadata */
+	struct volume_group *vg_cache; /* Cached VG */
+	unsigned vg_ref_count; /* Cached VG ref counter */
+	unsigned vg_use_count; /* Cached VG usage */
 	unsigned precommitted;	/* Is vgmetadata live or precommitted? */
 };
 
@@ -122,6 +125,8 @@ struct dm_list *lvmcache_get_pvids(struct cmd_context *cmd, const char *vgname,
 
 /* Returns cached volume group metadata. */
 struct volume_group *lvmcache_get_vg(const char *vgid, unsigned precommitted);
+int lvmcache_release_vg(struct volume_group *vg);
+int lvmcache_drop_vg(struct volume_group *vg);
 void lvmcache_drop_metadata(const char *vgname, int drop_precommitted);
 void lvmcache_commit_metadata(const char *vgname);
 
diff --git a/lib/metadata/metadata.c b/lib/metadata/metadata.c
index 842885f..d7fb6c3 100644
--- a/lib/metadata/metadata.c
+++ b/lib/metadata/metadata.c
@@ -866,6 +866,12 @@ static struct volume_group *_vg_make_handle(struct cmd_context *cmd,
 					    struct volume_group *vg,
 					    uint32_t failure)
 {
+	if (vg && vg->vginfo && (failure != SUCCESS)) {
+		/* Avoid overwrite of cached VG */
+		free_vg(vg);
+		vg = NULL;
+	}
+
 	if (!vg && !(vg = alloc_vg("vg_make_handle", cmd, NULL)))
 		return_NULL;
 
@@ -2865,8 +2871,13 @@ static struct volume_group *_vg_read(struct cmd_context *cmd,
 	    (use_precommitted || !*consistent || !(correct_vg->status & INCONSISTENT_VG))) {
 		if (!(correct_vg->status & INCONSISTENT_VG))
 			*consistent = 1;
-		else	/* Inconsistent but we can't repair it */
+		else {
+			/* Inconsistent VG is being modified, no caching */
+			if (!lvmcache_drop_vg(correct_vg))
+				return_NULL;
+			/* Inconsistent but we can't repair it */
 			correct_vg->status &= ~INCONSISTENT_VG;
+		}
 
 		return correct_vg;
 	} else {
@@ -3312,6 +3323,11 @@ void free_vg(struct volume_group *vg)
 	if (!vg)
 		return;
 
+	log_debug("Releasing VG %s (vg:%p, info:%p).", vg->name, vg, vg->vginfo);
+
+	if (!lvmcache_release_vg(vg))
+		return;    /* Still referenced, do not destroy mempool */
+
 	vg_set_fid(vg, NULL);
 
 	if (vg->cmd && vg->vgmem == vg->cmd->mem) {
@@ -3850,7 +3866,13 @@ static struct volume_group *_recover_vg(struct cmd_context *cmd,
 		return_NULL;
 	}
 
-	return (struct volume_group *)vg;
+	/* Test if the VG is not accidentally from lvmcache */
+	if (vg->vginfo) {
+		log_error(INTERNAL_ERROR "Recovered VG is in the cache.");
+		return NULL;
+	}
+
+	return vg;
 }
 
 /*
diff --git a/lib/metadata/vg.h b/lib/metadata/vg.h
index bebe6cf..2cda69b 100644
--- a/lib/metadata/vg.h
+++ b/lib/metadata/vg.h
@@ -20,6 +20,7 @@ struct dm_pool;
 struct format_instance;
 struct dm_list;
 struct id;
+struct lvmcache_vginfo;
 
 typedef enum {
 	ALLOC_INVALID,
@@ -41,6 +42,7 @@ struct volume_group {
 	struct cmd_context *cmd;
 	struct dm_pool *vgmem;
 	struct format_instance *fid;
+	struct lvmcache_vginfo *vginfo;	/* Backward reference to lvmcache */
 	struct dm_list *cmd_vgs;/* List of wanted/locked and opened VGs */
 	uint32_t cmd_missing_vgs;/* Flag marks missing VG */
 	uint32_t seqno;		/* Metadata sequence number */
-- 
1.7.6




More information about the lvm-devel mailing list