[PATCH 10/10] Avoid race condition between segment compacting and object lookup.

Martin Blix Grydeland martin at varnish-software.com
Wed Oct 10 16:27:23 CEST 2012


---
 bin/varnishd/storage/storage_persistent.c      |    8 ++++--
 bin/varnishd/storage/storage_persistent.h      |    1 +
 bin/varnishd/storage/storage_persistent_silo.c |   36 +++++++++++++++++++++---
 3 files changed, 38 insertions(+), 7 deletions(-)

diff --git a/bin/varnishd/storage/storage_persistent.c b/bin/varnishd/storage/storage_persistent.c
index 4306e4d..a41d52a 100644
--- a/bin/varnishd/storage/storage_persistent.c
+++ b/bin/varnishd/storage/storage_persistent.c
@@ -680,6 +680,8 @@ smp_allocx(struct stevedore *st, size_t min_size, size_t max_size,
 			(*so)->ptr = 0;;
 			sg->objs = *so;
 			*idx = ++sg->p.lobjlist;
+			/* Add ref early so segment will stick around */
+			sg->nobj++;
 		}
 		(void)smp_spaceleft(sc, sg);	/* for the assert */
 	}
@@ -740,20 +742,20 @@ smp_allocobj(struct stevedore *stv, struct busyobj *bo, struct objcore **ocp,
 	oc = o->objcore;
 	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
 	oc->flags |= OC_F_LRUDONTMOVE;
+	smp_init_oc(oc, sg, objidx);
 
 	Lck_Lock(&sc->mtx);
 	sg->nfixed++;
-	sg->nobj++;
+	assert(sg->nobj > 0);	/* Our ref added by smp_allocx() */
 
 	/* We have to do this somewhere, might as well be here... */
+	so = smp_find_so(sg, objidx); /* Might have changed during unlock */
 	assert(sizeof so->hash == DIGEST_LEN);
 	memcpy(so->hash, oc->objhead->digest, DIGEST_LEN);
 	so->ttl = EXP_Grace(NULL, o);
 	so->ptr = (uint8_t*)o - sc->base;
 	so->ban = BAN_Time(oc->ban);
 
-	smp_init_oc(oc, sg, objidx);
-
 	Lck_Unlock(&sc->mtx);
 	return (o);
 }
diff --git a/bin/varnishd/storage/storage_persistent.h b/bin/varnishd/storage/storage_persistent.h
index 9e0642d..0496a95 100644
--- a/bin/varnishd/storage/storage_persistent.h
+++ b/bin/varnishd/storage/storage_persistent.h
@@ -204,6 +204,7 @@ void smp_load_seg(struct worker *, const struct smp_sc *sc, struct smp_seg *sg);
 void smp_new_seg(struct smp_sc *sc);
 void smp_close_seg(struct smp_sc *sc, struct smp_seg *sg);
 void smp_init_oc(struct objcore *oc, struct smp_seg *sg, unsigned objidx);
+struct smp_object * smp_find_so(const struct smp_seg *sg, unsigned priv2);
 void smp_sync_segs(struct smp_sc *sc);
 
 /* storage_persistent_subr.c */
diff --git a/bin/varnishd/storage/storage_persistent_silo.c b/bin/varnishd/storage/storage_persistent_silo.c
index e432302..07b6c9a 100644
--- a/bin/varnishd/storage/storage_persistent_silo.c
+++ b/bin/varnishd/storage/storage_persistent_silo.c
@@ -43,6 +43,7 @@
 #include "hash/hash_slinger.h"
 #include "vsha256.h"
 #include "vtim.h"
+#include "vmb.h"
 
 #include "persistent.h"
 #include "storage/storage_persistent.h"
@@ -267,6 +268,7 @@ smp_close_seg(struct smp_sc *sc, struct smp_seg *sg)
 	sg->flags |= SMP_SEG_SYNCSIGNS;
 
 	/* Remove the new flag and request sync of segment list */
+	VMB();			/* See comments in smp_oc_getobj() */
 	sg->flags &= ~SMP_SEG_NEW;
 	smp_sync_segs(sc);
 }
@@ -298,9 +300,11 @@ smp_check_reserve(struct smp_sc *sc)
 }
 
 /*---------------------------------------------------------------------
+ * Find the struct smp_object in the segment's object list by
+ * it's objindex (oc->priv2)
  */
 
-static struct smp_object *
+struct smp_object *
 smp_find_so(const struct smp_seg *sg, unsigned priv2)
 {
 	struct smp_object *so;
@@ -401,16 +405,33 @@ smp_oc_getobj(struct dstat *ds, struct objcore *oc)
 	struct storage *st;
 	uint64_t l;
 	int bad;
+	int has_lock;
 
+	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
 	/* Some calls are direct, but they should match anyway */
 	assert(oc->methods->getobj == smp_oc_getobj);
 
-	CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC);
 	if (ds == NULL)
 		AZ(oc->flags & OC_F_NEEDFIXUP);
 
 	CAST_OBJ_NOTNULL(sg, oc->priv, SMP_SEG_MAGIC);
+	if (sg->flags & SMP_SEG_NEW) {
+		/* Segment is new and can be closed and compacted at
+		 * any time. We need to keep a lock during access to
+		 * the objlist. */
+		Lck_Lock(&sg->sc->mtx);
+		has_lock = 1;
+	} else {
+		/* Since the NEW flag is removed after the compacting
+		 * and a memory barrier, any compacting should have
+		 * been done with the changes visible to us if we
+		 * can't see the flag. Should be safe to proceed
+		 * without locks. */
+		has_lock = 0;
+	}
 	so = smp_find_so(sg, oc->priv2);
+	AN(so);
+	AN(so->ptr);
 
 	o = (void*)(sg->sc->base + so->ptr);
 	/*
@@ -426,11 +447,17 @@ smp_oc_getobj(struct dstat *ds, struct objcore *oc)
 	 * If this flag is not set, it will not be, and the lock is not
 	 * needed to test it.
 	 */
-	if (!(oc->flags & OC_F_NEEDFIXUP))
+	if (!(oc->flags & OC_F_NEEDFIXUP)) {
+		if (has_lock)
+			Lck_Unlock(&sg->sc->mtx);
 		return (o);
+	}
 
 	AN(ds);
-	Lck_Lock(&sg->sc->mtx);
+	if (!has_lock) {
+		Lck_Lock(&sg->sc->mtx);
+		has_lock = 1;
+	}
 	/* Check again, we might have raced. */
 	if (oc->flags & OC_F_NEEDFIXUP) {
 		/* We trust caller to have a refcnt for us */
@@ -457,6 +484,7 @@ smp_oc_getobj(struct dstat *ds, struct objcore *oc)
 		ds->n_vampireobject--;
 		oc->flags &= ~OC_F_NEEDFIXUP;
 	}
+	AN(has_lock);
 	Lck_Unlock(&sg->sc->mtx);
 	EXP_Rearm(o);
 	return (o);
-- 
1.7.9.5




More information about the varnish-dev mailing list