[master] 74328de This MegaCommit replaces the VSM implementation.

Poul-Henning Kamp phk at FreeBSD.org
Mon Aug 28 23:24:07 CEST 2017


commit 74328de8eeb2703591fe59190f73fcabf057adbe
Author: Poul-Henning Kamp <phk at FreeBSD.org>
Date:   Mon Aug 28 21:23:17 2017 +0000

    This MegaCommit replaces the VSM implementation.
    
    Instead of having a single mmap(2)'ed file inside which we allocate
    the necessary chunks, put each chunk in its own file and use two index
    files (MGR/CHILD) to keep track of them.
    
    Major benefits:
    
    * You don't need to guess how much VSM space you need at startup.
    
    * VSM chunk Identifier (and therefore VSC counter names) are unlimited length.
    
    Minor benefits:
    
    * VSM API has simpler and explainable semantics now.
    
    * The bogo-you-didnt-guess-right-malloc-VSM allocations are gone.
    
    * -t argument moves up/down to VSM level and works the same through-out.
    
    * VSM/VUT utils now stay attached even if the MGT process restarts.
    
    Some relatively minor adjustments are still to come before the VSM API is frozen
    and the .map file finalized for the release.

diff --git a/bin/varnishadm/varnishadm.c b/bin/varnishadm/varnishadm.c
index 43cab6f..aa3bf6d 100644
--- a/bin/varnishadm/varnishadm.c
+++ b/bin/varnishadm/varnishadm.c
@@ -389,44 +389,32 @@ usage(int status)
 }
 
 static int
-n_arg_sock(const char *n_arg)
+n_arg_sock(const char *n_arg, const char *t_arg)
 {
 	char *T_arg = NULL, *T_start = NULL;
 	char *S_arg = NULL;
 	struct vsm *vsm;
 	char *p;
 	int sock;
-	struct vsm_fantom vt;
 
 	vsm = VSM_New();
 	AN(vsm);
-	if (VSM_n_Arg(vsm, n_arg) < 0) {
-		fprintf(stderr, "%s\n", VSM_Error(vsm));
-		VSM_Destroy(&vsm);
-		return (-1);
-	}
-	if (VSM_Start(vsm, 0, -1)) {
+	if (VSM_Arg(vsm, 'n', n_arg) < 0 ||
+	    VSM_Arg(vsm, 't', t_arg) < 0 ||
+	    VSM_Attach(vsm, STDERR_FILENO) < 0) {
 		fprintf(stderr, "%s\n", VSM_Error(vsm));
 		VSM_Destroy(&vsm);
 		return (-1);
 	}
 
-	if (!VSM_Get(vsm, &vt, "Arg", "-T")) {
-		fprintf(stderr, "No -T arg in shared memory\n");
-		VSM_Destroy(&vsm);
+	T_start = T_arg = VSM_Dup(vsm, "Arg", "-T");
+	S_arg = VSM_Dup(vsm, "Arg", "-S");
+	VSM_Destroy(&vsm);
+
+	if (T_arg == NULL) {
+		fprintf(stderr, "No -T in shared memory\n");
 		return (-1);
 	}
-	AZ(VSM_Map(vsm, &vt));
-	AN(vt.b);
-	T_start = T_arg = strdup(vt.b);
-
-	if (VSM_Get(vsm, &vt, "Arg", "-S")) {
-		AZ(VSM_Map(vsm, &vt));
-		AN(vt.b);
-		S_arg = strdup(vt.b);
-	}
-
-	VSM_Destroy(&vsm);
 
 	sock = -1;
 	while (*T_arg) {
@@ -449,6 +437,7 @@ main(int argc, char * const *argv)
 	const char *T_arg = NULL;
 	const char *S_arg = NULL;
 	const char *n_arg = NULL;
+	const char *t_arg = NULL;
 	int opt, sock;
 
 	/*
@@ -474,9 +463,7 @@ main(int argc, char * const *argv)
 			T_arg = optarg;
 			break;
 		case 't':
-			timeout = VNUM(optarg);
-			if (isnan(timeout))
-				usage(1);
+			t_arg = optarg;
 			break;
 		default:
 			usage(1);
@@ -486,27 +473,23 @@ main(int argc, char * const *argv)
 	argc -= optind;
 	argv += optind;
 
-	if (n_arg != NULL) {
-		if (T_arg != NULL || S_arg != NULL)
+	if (T_arg != NULL) {
+		if (n_arg != NULL)
 			usage(1);
-		sock = n_arg_sock(n_arg);
-	} else if (T_arg == NULL) {
-		sock = n_arg_sock("");
-	} else {
-		assert(T_arg != NULL);
 		sock = cli_sock(T_arg, S_arg);
+	} else {
+		if (S_arg != NULL)
+			usage(1);
+		sock = n_arg_sock(n_arg, t_arg);
 	}
 	if (sock < 0)
 		exit(2);
 
 	if (argc > 0)
 		do_args(sock, argc, argv);
-	else {
-		if (isatty(0)) {
-			interactive(sock);
-		} else {
-			pass(sock);
-		}
-	}
+	else if (isatty(0))
+		interactive(sock);
+	else
+		pass(sock);
 	exit(0);
 }
diff --git a/bin/varnishd/Makefile.am b/bin/varnishd/Makefile.am
index 8248966..c6228b8 100644
--- a/bin/varnishd/Makefile.am
+++ b/bin/varnishd/Makefile.am
@@ -53,7 +53,6 @@ varnishd_SOURCES = \
 	cache/cache_wrk.c \
 	cache/cache_ws.c \
 	common/common_vsc.c \
-	common/common_vsm.c \
 	hash/hash_classic.c \
 	hash/hash_critbit.c \
 	hash/mgt_hash.c \
diff --git a/bin/varnishd/cache/cache.h b/bin/varnishd/cache/cache.h
index 87338d8..7645198 100644
--- a/bin/varnishd/cache/cache.h
+++ b/bin/varnishd/cache/cache.h
@@ -970,9 +970,6 @@ void SES_Set_String_Attr(struct sess *sp, enum sess_attr a, const char *src);
 const char *SES_Get_String_Attr(const struct sess *sp, enum sess_attr a);
 
 /* cache_shmlog.c */
-void *VSM_Alloc(unsigned size, const char *class, const char *type,
-    const char *ident);
-void VSM_Free(void *ptr);
 #ifdef VSL_ENDMARKER
 void VSLv(enum VSL_tag_e tag, uint32_t vxid, const char *fmt, va_list va);
 void VSL(enum VSL_tag_e tag, uint32_t vxid, const char *fmt, ...)
diff --git a/bin/varnishd/cache/cache_backend.h b/bin/varnishd/cache/cache_backend.h
index da5ffdc..15cd260 100644
--- a/bin/varnishd/cache/cache_backend.h
+++ b/bin/varnishd/cache/cache_backend.h
@@ -105,6 +105,7 @@ struct vbc {
 void VBE_fill_director(struct backend *be);
 
 /* cache_backend_cfg.c */
+void VBE_SetHappy(const struct backend *, uint64_t);
 unsigned VBE_Healthy(const struct backend *b, double *changed);
 #ifdef VCL_MET_MAX
 void VBE_Event(struct backend *, enum vcl_event_e);
diff --git a/bin/varnishd/cache/cache_backend_cfg.c b/bin/varnishd/cache/cache_backend_cfg.c
index 976b647..1ec9ef5 100644
--- a/bin/varnishd/cache/cache_backend_cfg.c
+++ b/bin/varnishd/cache/cache_backend_cfg.c
@@ -165,6 +165,16 @@ VRT_delete_backend(VRT_CTX, struct director **dp)
 	// this is why we don't bust the director's magic number.
 }
 
+void
+VBE_SetHappy(const struct backend *be, uint64_t happy)
+{
+
+		Lck_Lock(&backends_mtx);
+		if (be->vsc != NULL)
+			be->vsc->happy = happy;
+		Lck_Unlock(&backends_mtx);
+}
+
 /*---------------------------------------------------------------------
  * These are for cross-calls with cache_vcl.c only.
  */
@@ -176,8 +186,10 @@ VBE_Event(struct backend *be, enum vcl_event_e ev)
 	CHECK_OBJ_NOTNULL(be, BACKEND_MAGIC);
 
 	if (ev == VCL_EVENT_WARM) {
+		Lck_Lock(&backends_mtx);
 		be->vsc = VSC_vbe_New(be->display_name);
 		AN(be->vsc);
+		Lck_Unlock(&backends_mtx);
 	}
 
 	if (be->probe != NULL && ev == VCL_EVENT_WARM)
@@ -186,8 +198,11 @@ VBE_Event(struct backend *be, enum vcl_event_e ev)
 	if (be->probe != NULL && ev == VCL_EVENT_COLD)
 		VBP_Control(be, 0);
 
-	if (ev == VCL_EVENT_COLD)
+	if (ev == VCL_EVENT_COLD) {
+		Lck_Lock(&backends_mtx);
 		VSC_vbe_Destroy(&be->vsc);
+		Lck_Unlock(&backends_mtx);
+	}
 }
 
 void
diff --git a/bin/varnishd/cache/cache_backend_probe.c b/bin/varnishd/cache/cache_backend_probe.c
index af3336d..75a49a5 100644
--- a/bin/varnishd/cache/cache_backend_probe.c
+++ b/bin/varnishd/cache/cache_backend_probe.c
@@ -53,8 +53,6 @@
 #include "cache_director.h"
 #include "cache_backend.h"
 
-#include "VSC_vbe.h"
-
 /* Default averaging rate, we want something pretty responsive */
 #define AVG_RATE			4
 
@@ -167,6 +165,7 @@ vbp_update_backend(struct vbp_target *vt)
 		bits[i++] = (vt->n & 1) ? c : '-';
 #include "tbl/backend_poll.h"
 		bits[i] = '\0';
+		assert(i < sizeof bits);
 
 		if (vt->good >= vt->threshold) {
 			if (vt->backend->healthy)
@@ -188,8 +187,7 @@ vbp_update_backend(struct vbp_target *vt)
 		    vt->backend->display_name, logmsg, bits,
 		    vt->good, vt->threshold, vt->window,
 		    vt->last, vt->avg, vt->resp_buf);
-		if (vt->backend != NULL && vt->backend->vsc != NULL)
-			vt->backend->vsc->happy = vt->happy;
+		VBE_SetHappy(vt->backend, vt->happy);
 	}
 	Lck_Unlock(&vbp_mtx);
 }
diff --git a/bin/varnishd/cache/cache_shmlog.c b/bin/varnishd/cache/cache_shmlog.c
index 986f685..1c24dc2 100644
--- a/bin/varnishd/cache/cache_shmlog.c
+++ b/bin/varnishd/cache/cache_shmlog.c
@@ -42,11 +42,11 @@
 #include "vgz.h"
 #include "vsl_priv.h"
 #include "vmb.h"
-#include "vtim.h"
+#include "vsmw.h"
 
 /* These cannot be struct lock, which depends on vsm/vsl working */
 static pthread_mutex_t vsl_mtx;
-static pthread_mutex_t vsm_mtx = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t vsm_mtx;
 
 static struct VSL_head		*vsl_head;
 static const uint32_t		*vsl_end;
@@ -471,20 +471,16 @@ VSL_End(struct vsl_log *vsl)
 	vsl->wid = 0;
 }
 
-/*--------------------------------------------------------------------*/
+static void
+vsm_vsc_lock(void)
+{
+	AZ(pthread_mutex_lock(&vsm_mtx));
+}
 
-static void *
-vsm_cleaner(void *priv)
+static void
+vsm_vsc_unlock(void)
 {
-	(void)priv;
-	THR_SetName("vsm_cleaner");
-	while (1) {
-		AZ(pthread_mutex_lock(&vsm_mtx));
-		CVSM_cleaner(heritage.vsm, (void*)VSC_C_main);
-		AZ(pthread_mutex_unlock(&vsm_mtx));
-		VTIM_sleep(1.1);
-	}
-	NEEDLESS(return NULL);
+	AZ(pthread_mutex_unlock(&vsm_mtx));
 }
 
 /*--------------------------------------------------------------------*/
@@ -493,14 +489,21 @@ void
 VSM_Init(void)
 {
 	int i;
-	pthread_t tp;
 
 	assert(UINT_MAX % VSL_SEGMENTS == VSL_SEGMENTS - 1);
 
 	AZ(pthread_mutex_init(&vsl_mtx, NULL));
 	AZ(pthread_mutex_init(&vsm_mtx, NULL));
 
-	vsl_head = VSM_Alloc(cache_param->vsl_space, VSL_CLASS, "", "");
+	vsc_lock = vsm_vsc_lock;
+	vsc_unlock = vsm_vsc_unlock;
+
+	VSC_C_main = VSC_main_New("");
+	AN(VSC_C_main);
+
+	AN(proc_vsmw);
+	vsl_head = VSMW_Allocf(proc_vsmw, VSL_CLASS,
+	    cache_param->vsl_space, VSL_CLASS);
 	AN(vsl_head);
 	vsl_segsize = ((cache_param->vsl_space - sizeof *vsl_head) /
 	    sizeof *vsl_end) / VSL_SEGMENTS;
@@ -520,32 +523,4 @@ VSM_Init(void)
 		vsl_head->offset[i] = -1;
 	VWMB();
 	memcpy(vsl_head->marker, VSL_HEAD_MARKER, sizeof vsl_head->marker);
-
-	VSC_C_main = VSC_main_New("");
-	AN(VSC_C_main);
-
-	AZ(pthread_create(&tp, NULL, vsm_cleaner, NULL));
-}
-
-/*--------------------------------------------------------------------*/
-
-void *
-VSM_Alloc(unsigned size, const char *class, const char *type,
-    const char *ident)
-{
-	volatile void *p;
-
-	AZ(pthread_mutex_lock(&vsm_mtx));
-	p = CVSM_alloc(heritage.vsm, size, class, type, ident);
-	AZ(pthread_mutex_unlock(&vsm_mtx));
-	return (TRUST_ME(p));
-}
-
-void
-VSM_Free(void *ptr)
-{
-
-	AZ(pthread_mutex_lock(&vsm_mtx));
-	CVSM_free(heritage.vsm, ptr);
-	AZ(pthread_mutex_unlock(&vsm_mtx));
 }
diff --git a/bin/varnishd/common/common_vsc.c b/bin/varnishd/common/common_vsc.c
index 00a1ab0..eb40edf 100644
--- a/bin/varnishd/common/common_vsc.c
+++ b/bin/varnishd/common/common_vsc.c
@@ -38,15 +38,12 @@
 #include "common/common_vsm.h"
 
 #include "vend.h"
+#include "vsmw.h"
 #include "vgz.h"
 #include "vmb.h"
 #include "vapi/vsc_int.h"
 
-/*--------------------------------------------------------------------*/
-
-void *VSM_Alloc(unsigned size, const char *class, const char *type,
-    const char *ident);
-void VSM_Free(void *ptr);
+struct vsmw *proc_vsmw;
 
 /*--------------------------------------------------------------------*/
 
@@ -54,6 +51,7 @@ struct vsc_segs {
 	unsigned		magic;
 #define VSC_SEGS_MAGIC		0x9b355991
 
+	const char		*nm;
 	VTAILQ_ENTRY(vsc_segs)	list;
 	void			*seg;
 	void			*ptr;
@@ -62,25 +60,30 @@ struct vsc_segs {
 static VTAILQ_HEAD(,vsc_segs)	vsc_seglist =
     VTAILQ_HEAD_INITIALIZER(vsc_seglist);
 
+vsc_callback_f *vsc_lock;
+vsc_callback_f *vsc_unlock;
+
 void *
-VSC_Alloc(const char *nm, size_t sd,
-    size_t sj, const unsigned char *zj, size_t szj,
-    const char *fmt, va_list va)
+VSC_Alloc(const char *nm, size_t sd, size_t sj, const unsigned char *zj,
+    size_t szj, const char *fmt, va_list va)
 {
 	char *p;
 	z_stream vz;
 	struct vsc_segs *vsg;
+	char buf[1024];
 
-	(void)nm;
-	(void)fmt;
-	(void)va;
+	if (vsc_lock != NULL)
+		vsc_lock();
 
+	if (*fmt == '\0')
+		bprintf(buf, "%s", nm);
+	else
+		bprintf(buf, "%s.%s", nm, fmt);
 
-	p = VSM_Alloc(8 + sd + sj, VSC_CLASS, nm, fmt);
+	AN(proc_vsmw);
+	p = VSMW_Allocv(proc_vsmw, VSC_CLASS, 8 + sd + sj, buf, va);
 	AN(p);
 
-	memset(p, 0, sd);
-
 	memset(&vz, 0, sizeof vz);
 	assert(Z_OK == inflateInit2(&vz, 31));
 	vz.next_in = TRUST_ME(zj);
@@ -96,6 +99,9 @@ VSC_Alloc(const char *nm, size_t sd,
 	VTAILQ_INSERT_TAIL(&vsc_seglist, vsg, list);
 	VWMB();
 	vbe64enc(p, sd);
+	vsg->nm = nm;
+	if (vsc_unlock != NULL)
+		vsc_unlock();
 	return (p + 8);
 }
 
@@ -104,14 +110,19 @@ VSC_Destroy(const char *nm, const void *p)
 {
 	struct vsc_segs *vsg;
 
-	(void)nm;
+	if (vsc_lock != NULL)
+		vsc_lock();
+
+	AN(proc_vsmw);
 	VTAILQ_FOREACH(vsg, &vsc_seglist, list) {
 		if (vsg->ptr != p)
 			continue;
-		VSM_Free(vsg->seg);
+		assert(vsg->nm == nm);
+		VSMW_Free(proc_vsmw, &vsg->seg);
 		VTAILQ_REMOVE(&vsc_seglist, vsg, list);
 		FREE_OBJ(vsg);
-		return;
+		break;
 	}
-	WRONG("Freeing unknown VSC");
+	if (vsc_unlock != NULL)
+		vsc_unlock();
 }
diff --git a/bin/varnishd/common/common_vsm.c b/bin/varnishd/common/common_vsm.c
deleted file mode 100644
index f900b26..0000000
--- a/bin/varnishd/common/common_vsm.c
+++ /dev/null
@@ -1,395 +0,0 @@
-/*-
- * Copyright (c) 2010-2011 Varnish Software AS
- * All rights reserved.
- *
- * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * VSM stuff common to manager and child.
- *
- * Please see comments in <vsm_priv.h> for details of protocols and
- * data consistency.
- *
- */
-
-#include "config.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "common.h"
-#include "common/com_params.h"
-#include "common/common_vsm.h"
-
-#include "vsm_priv.h"
-#include "vmb.h"
-#include "vtim.h"
-
-#include "VSC_main.h"
-
-extern volatile struct params * cache_param;
-
-/*--------------------------------------------------------------------*/
-
-struct vsm_range {
-	unsigned			magic;
-#define VSM_RANGE_MAGIC			0x8d30f14
-	VTAILQ_ENTRY(vsm_range)		list;
-	ssize_t				off;
-	ssize_t				len;
-	double				cool;
-	struct VSM_chunk		*chunk;
-	void				*ptr;
-};
-
-struct vsm_sc {
-	unsigned			magic;
-#define VSM_SC_MAGIC			0x8b83270d
-	char				*b;
-	ssize_t				len;
-	struct VSM_head			*head;
-	double				t0;
-	VTAILQ_HEAD(,vsm_range)		r_used;
-	VTAILQ_HEAD(,vsm_range)		r_cooling;
-	VTAILQ_HEAD(,vsm_range)		r_free;
-	VTAILQ_HEAD(,vsm_range)		r_bogus;
-	uint64_t			g_free;
-	uint64_t			g_used;
-	uint64_t			g_cooling;
-	uint64_t			g_overflow;
-	uint64_t			c_overflow;
-};
-
-/*--------------------------------------------------------------------
- * The free list is sorted by size, which means that collapsing ranges
- * on free becomes a multi-pass operation.
- */
-
-static void
-vsm_common_insert_free(struct vsm_sc *sc, struct vsm_range *vr)
-{
-	struct vsm_range *vr2;
-
-	CHECK_OBJ_NOTNULL(sc, VSM_SC_MAGIC);
-	CHECK_OBJ_NOTNULL(vr, VSM_RANGE_MAGIC);
-
-	/* First try to see if we can collapse anything */
-	VTAILQ_FOREACH(vr2, &sc->r_free, list) {
-		if (vr2->off == vr->off + vr->len) {
-			vr2->off = vr->off;
-			vr2->len += vr->len;
-			FREE_OBJ(vr);
-			VTAILQ_REMOVE(&sc->r_free, vr2, list);
-			vsm_common_insert_free(sc, vr2);
-			return;
-		}
-		if (vr->off == vr2->off + vr2->len) {
-			vr2->len += vr->len;
-			FREE_OBJ(vr);
-			VTAILQ_REMOVE(&sc->r_free, vr2, list);
-			vsm_common_insert_free(sc, vr2);
-			return;
-		}
-	}
-	/* Insert in size order */
-	VTAILQ_FOREACH(vr2, &sc->r_free, list) {
-		if (vr2->len > vr->len) {
-			VTAILQ_INSERT_BEFORE(vr2, vr, list);
-			return;
-		}
-	}
-	/* At tail, if everything in the list is smaller */
-	VTAILQ_INSERT_TAIL(&sc->r_free, vr, list);
-}
-
-/*--------------------------------------------------------------------
- * Initialize a new VSM segment
- */
-
-struct vsm_sc *
-CVSM_new(void *p, ssize_t l)
-{
-	struct vsm_sc *sc;
-	struct vsm_range *vr;
-
-	assert(PAOK(sizeof(struct VSM_chunk)));
-	assert(PAOK(p));
-	ALLOC_OBJ(sc, VSM_SC_MAGIC);
-	AN(sc);
-	VTAILQ_INIT(&sc->r_used);
-	VTAILQ_INIT(&sc->r_cooling);
-	VTAILQ_INIT(&sc->r_free);
-	VTAILQ_INIT(&sc->r_bogus);
-	sc->b = p;
-	sc->len = l;
-	sc->t0 = VTIM_mono();
-
-	sc->head = (void *)sc->b;
-	/* This should not be necessary, but just in case...*/
-	memset(sc->head, 0, sizeof *sc->head);
-	memcpy(sc->head->marker, VSM_HEAD_MARKER, sizeof sc->head->marker);
-	sc->head->hdrsize = sizeof *sc->head;
-	sc->head->shm_size = l;
-	sc->head->alloc_seq = random() | 1;
-	VWMB();
-
-	ALLOC_OBJ(vr, VSM_RANGE_MAGIC);
-	AN(vr);
-	vr->off = RUP2(sizeof(*sc->head), 16);
-	vr->len = RDN2(l - vr->off, 16);
-	VTAILQ_INSERT_TAIL(&sc->r_free, vr, list);
-	sc->g_free = vr->len;
-	return (sc);
-}
-
-/*--------------------------------------------------------------------
- * Move from cooling list to free list
- */
-
-void
-CVSM_cleaner(struct vsm_sc *sc, struct VSC_main *stats)
-{
-	double now = VTIM_real();
-	struct vsm_range *vr, *vr2;
-
-	CHECK_OBJ_NOTNULL(sc, VSM_SC_MAGIC);
-
-	/* Move cooled off stuff to free list */
-	VTAILQ_FOREACH_SAFE(vr, &sc->r_cooling, list, vr2) {
-		if (vr->cool > now)
-			break;
-		VTAILQ_REMOVE(&sc->r_cooling, vr, list);
-		sc->g_cooling -= vr->len;
-		sc->g_free += vr->len;
-		vsm_common_insert_free(sc, vr);
-	}
-	stats->vsm_free = sc->g_free;
-	stats->vsm_used = sc->g_used;
-	stats->vsm_cooling = sc->g_cooling;
-	stats->vsm_overflow = sc->g_overflow;
-	stats->vsm_overflowed = sc->c_overflow;
-}
-
-/*--------------------------------------------------------------------
- * Allocate a chunk from VSM
- */
-
-void *
-CVSM_alloc(struct vsm_sc *sc, ssize_t size,
-    const char *class, const char *type, const char *ident)
-{
-	struct vsm_range *vr, *vr2, *vr3;
-	unsigned l1, l2;
-
-	CHECK_OBJ_NOTNULL(sc, VSM_SC_MAGIC);
-	AN(size);
-
-	/* XXX: silent truncation instead of assert ? */
-	AN(class);
-	assert(strlen(class) < sizeof(vr->chunk->class));
-	AN(type);
-	assert(strlen(type) < sizeof(vr->chunk->type));
-	AN(ident);
-	assert(strlen(ident) < sizeof(vr->chunk->ident));
-
-	l1 = RUP2(size + sizeof(struct VSM_chunk), 16);
-	l2 = RUP2(size + 2 * sizeof(struct VSM_chunk), 16);
-
-	/* Find space in free-list */
-	VTAILQ_FOREACH_SAFE(vr, &sc->r_free, list, vr2) {
-		if (vr->len < l1)
-			continue;
-		if (vr->len <= l2) {
-			VTAILQ_REMOVE(&sc->r_free, vr, list);
-		} else {
-			ALLOC_OBJ(vr3, VSM_RANGE_MAGIC);
-			AN(vr3);
-			vr3->off = vr->off;
-			vr3->len = l1;
-			vr->off += l1;
-			vr->len -= l1;
-			VTAILQ_REMOVE(&sc->r_free, vr, list);
-			vsm_common_insert_free(sc, vr);
-			vr = vr3;
-		}
-		break;
-	}
-
-	if (vr == NULL) {
-		/*
-		 * No space in VSM, return malloc'd space
-		 * This space will not be visible via the VSM
-		 */
-		ALLOC_OBJ(vr, VSM_RANGE_MAGIC);
-		AN(vr);
-		vr->ptr = calloc(size, 1);
-		AN(vr->ptr);
-		vr->len = size;
-		VTAILQ_INSERT_TAIL(&sc->r_bogus, vr, list);
-		sc->g_overflow += vr->len;
-		sc->c_overflow += vr->len;
-		return (vr->ptr);
-	}
-
-	sc->g_free -= vr->len;
-	sc->g_used += vr->len;
-
-	/* Zero the entire allocation, to avoid garbage confusing readers */
-	memset(sc->b + vr->off, 0, vr->len);
-
-	vr->chunk = (void *)(sc->b + vr->off);
-	vr->ptr = (vr->chunk + 1);
-
-	memcpy(vr->chunk->marker, VSM_CHUNK_MARKER, sizeof vr->chunk->marker);
-	vr->chunk->len = vr->len;
-	strcpy(vr->chunk->class, class);
-	strcpy(vr->chunk->type, type);
-	strcpy(vr->chunk->ident, ident);
-	VWMB();
-
-	vr3 = VTAILQ_FIRST(&sc->r_used);
-	VTAILQ_INSERT_HEAD(&sc->r_used, vr, list);
-
-	if (vr3 != NULL) {
-		AZ(vr3->chunk->next);
-		vr3->chunk->next = vr->off;
-	} else {
-		sc->head->first = vr->off;
-	}
-	sc->head->alloc_seq += 2;
-	VWMB();
-	return (vr->ptr);
-}
-
-/*--------------------------------------------------------------------
- * Free a chunk
- */
-
-void
-CVSM_free(struct vsm_sc *sc, void *ptr)
-{
-	struct vsm_range *vr, *vr2;
-
-	CHECK_OBJ_NOTNULL(sc, VSM_SC_MAGIC);
-	AN(ptr);
-
-	/* Look in used list, move to cooling list */
-	VTAILQ_FOREACH(vr, &sc->r_used, list) {
-		if (vr->ptr != ptr)
-			continue;
-
-		sc->g_used -= vr->len;
-		sc->g_cooling += vr->len;
-
-		vr2 = VTAILQ_NEXT(vr, list);
-		VTAILQ_REMOVE(&sc->r_used, vr, list);
-		VTAILQ_INSERT_TAIL(&sc->r_cooling, vr, list);
-		vr->cool = VTIM_real() + cache_param->vsm_free_cooldown;
-		if (vr2 != NULL)
-			vr2->chunk->next = vr->chunk->next;
-		else
-			sc->head->first = vr->chunk->next;
-		VWMB();
-		vr->chunk->len = 0;
-		sc->head->alloc_seq += 2;
-		VWMB();
-		return;
-	}
-
-	/* Look in bogus list, free */
-	VTAILQ_FOREACH(vr, &sc->r_bogus, list) {
-		if (vr->ptr != ptr)
-			continue;
-
-		sc->g_overflow -= vr->len;
-
-		VTAILQ_REMOVE(&sc->r_bogus, vr, list);
-		FREE_OBJ(vr);
-		free(ptr);
-		return;
-	}
-	/* Panic */
-	assert(ptr == NULL);
-}
-
-/*--------------------------------------------------------------------
- * Delete a VSM segment
- */
-
-void
-CVSM_delete(struct vsm_sc **scp)
-{
-	struct vsm_range *vr, *vr2;
-	struct vsm_sc *sc;
-
-	TAKE_OBJ_NOTNULL(sc, scp, VSM_SC_MAGIC);
-	VTAILQ_FOREACH_SAFE(vr, &sc->r_free, list, vr2)
-		FREE_OBJ(vr);
-	VTAILQ_FOREACH_SAFE(vr, &sc->r_used, list, vr2)
-		FREE_OBJ(vr);
-	VTAILQ_FOREACH_SAFE(vr, &sc->r_cooling, list, vr2)
-		FREE_OBJ(vr);
-	VTAILQ_FOREACH_SAFE(vr, &sc->r_bogus, list, vr2) {
-		free(vr->ptr);
-		FREE_OBJ(vr);
-	}
-
-	/* Mark VSM as abandoned */
-	sc->head->alloc_seq = 0;
-
-	VWMB();
-	FREE_OBJ(sc);
-}
-
-/*--------------------------------------------------------------------
- * Copy all chunks in one VSM segment to another VSM segment
- */
-
-void
-CVSM_copy(struct vsm_sc *to, const struct vsm_sc *from)
-{
-	struct vsm_range *vr;
-	void *p;
-
-	CHECK_OBJ_NOTNULL(to, VSM_SC_MAGIC);
-	CHECK_OBJ_NOTNULL(from, VSM_SC_MAGIC);
-	VTAILQ_FOREACH(vr, &from->r_used, list) {
-		p = CVSM_alloc(to, vr->chunk->len,
-		    vr->chunk->class, vr->chunk->type, vr->chunk->ident);
-		AN(p);
-		memcpy(p, vr->chunk + 1, vr->chunk->len);
-	}
-}
-
-/*--------------------------------------------------------------------
- * Update age
- */
-
-void
-CVSM_ageupdate(const struct vsm_sc *sc)
-{
-
-	CHECK_OBJ_NOTNULL(sc, VSM_SC_MAGIC);
-	sc->head->age = (uint64_t)(VTIM_mono() - sc->t0);
-}
diff --git a/bin/varnishd/common/common_vsm.h b/bin/varnishd/common/common_vsm.h
index 45e6dd8..f675d07 100644
--- a/bin/varnishd/common/common_vsm.h
+++ b/bin/varnishd/common/common_vsm.h
@@ -33,17 +33,14 @@
 #endif
 #define COMMON_COMMON_VSM_H
 
-/* common_vsm.c */
-struct vsm_sc;
-struct VSC_main;
-struct vsm_sc *CVSM_new(void *ptr, ssize_t len);
-void *CVSM_alloc(struct vsm_sc *sc, ssize_t size,
-    const char *class, const char *type, const char *ident);
-void CVSM_free(struct vsm_sc *sc, void *ptr);
-void CVSM_delete(struct vsm_sc **sc);
-void CVSM_copy(struct vsm_sc *to, const struct vsm_sc *from);
-void CVSM_cleaner(struct vsm_sc *sc, struct VSC_main *stats);
-void CVSM_ageupdate(const struct vsm_sc *sc);
+struct vsmw;
+
+extern struct vsmw *proc_vsmw;
+
+typedef void vsc_callback_f(void);
+
+extern vsc_callback_f *vsc_lock;
+extern vsc_callback_f *vsc_unlock;
 
 void *VSC_Alloc(const char *, size_t, size_t, const unsigned char *, size_t,
     const char *, va_list);
diff --git a/bin/varnishd/common/heritage.h b/bin/varnishd/common/heritage.h
index 528bc1e..95f6c3e 100644
--- a/bin/varnishd/common/heritage.h
+++ b/bin/varnishd/common/heritage.h
@@ -29,7 +29,7 @@
  * This file contains the heritage passed when mgt forks cache
  */
 
-struct vsm_sc;
+struct vsmw;
 struct suckaddr;
 struct listen_sock;
 struct transport;
@@ -67,14 +67,14 @@ struct heritage {
 	/* File descriptor for stdout/stderr */
 	int				std_fd;
 
+	int				vsm_fd;
+
 	/* Sockets from which to accept connections */
 	struct listen_sock_head		socks;
 
 	/* Hash method */
 	const struct hash_slinger	*hash;
 
-	struct vsm_sc			*vsm;
-
 	struct params			*param;
 
 	const char			*identity;
diff --git a/bin/varnishd/main.vsc b/bin/varnishd/main.vsc
index dd53b9e..726ee9e 100644
--- a/bin/varnishd/main.vsc
+++ b/bin/varnishd/main.vsc
@@ -725,53 +725,4 @@
 	from a backend. They are done to verify the gzip stream while it's
 	inserted in storage.
 
-.. varnish_vsc:: vsm_free
-	:type:	gauge
-	:format:	bytes
-	:level:	diag
-	:oneliner:	Free VSM space
-
-	Number of bytes free in the shared memory used to communicate with
-	tools like varnishstat, varnishlog etc.
-
-.. varnish_vsc:: vsm_used
-	:type:	gauge
-	:format:	bytes
-	:level:	diag
-	:oneliner:	Used VSM space
-
-	Number of bytes used in the shared memory used to communicate with
-	tools like varnishstat, varnishlog etc.
-
-.. varnish_vsc:: vsm_cooling
-	:type:	gauge
-	:format:	bytes
-	:level:	debug
-	:oneliner:	Cooling VSM space
-
-	Number of bytes which will soon (max 1 minute) be freed in the
-	shared memory used to communicate with tools like varnishstat,
-	varnishlog etc.
-
-.. varnish_vsc:: vsm_overflow
-	:type:	gauge
-	:format:	bytes
-	:level:	diag
-	:oneliner:	Overflow VSM space
-
-	Number of bytes which does not fit in the shared memory used to
-	communicate with tools like varnishstat, varnishlog etc. If this
-	counter is not zero, consider increasing the runtime variable
-	vsm_space.
-
-.. varnish_vsc:: vsm_overflowed
-	:level:	diag
-	:format:	bytes
-	:oneliner:	Overflowed VSM space
-
-	Total number of bytes which did not fit in the shared memory used
-	to communicate with tools like varnishstat, varnishlog etc. If this
-	counter is not zero, consider increasing the runtime variable
-	vsm_space.
-
 .. varnish_vsc_end::	main
diff --git a/bin/varnishd/mgt/mgt.h b/bin/varnishd/mgt/mgt.h
index 0a4efbd..6549d78 100644
--- a/bin/varnishd/mgt/mgt.h
+++ b/bin/varnishd/mgt/mgt.h
@@ -133,7 +133,6 @@ extern const struct jail_tech jail_tech_solaris;
 
 /* mgt_main.c */
 extern struct VSC_mgt	*VSC_C_mgt;
-extern struct VSC_mgt	static_VSC_C_mgt;
 struct choice {
 	const char      *name;
 	const void	*ptr;
@@ -165,10 +164,14 @@ extern struct params mgt_param;
 /* mgt_shmem.c */
 void mgt_SHM_Init(void);
 void mgt_SHM_static_alloc(const void *, ssize_t size,
-    const char *class, const char *type, const char *ident);
+    const char *class, const char *ident);
 void mgt_SHM_Create(void);
 void mgt_SHM_Destroy(int keep);
-void mgt_SHM_Size_Adjust(void);
+
+extern struct vsmw *mgt_vsmw;
+extern struct vsmw *child_vsmw;
+void mgt_SHM_ChildNew(void);
+void mgt_SHM_ChildDestroy(void);
 
 /* mgt_param_tcp.c */
 void MCF_TcpParams(void);
diff --git a/bin/varnishd/mgt/mgt_child.c b/bin/varnishd/mgt/mgt_child.c
index 47bd905..91d6c5e 100644
--- a/bin/varnishd/mgt/mgt_child.c
+++ b/bin/varnishd/mgt/mgt_child.c
@@ -44,6 +44,7 @@
 
 #include "mgt/mgt.h"
 #include "common/heritage.h"
+#include "common/common_vsm.h"
 
 #include "vbm.h"
 #include "vcli_serve.h"
@@ -51,6 +52,7 @@
 #include "vfil.h"
 #include "vlu.h"
 #include "vtim.h"
+#include "vsmw.h"
 
 static pid_t		child_pid = -1;
 
@@ -139,7 +141,7 @@ mch_cli_panic_clear(struct cli *cli, const char * const *av, void *priv)
 		VCLI_Out(cli, "Unknown parameter \"%s\".", av[2]);
 		return;
 	} else if (av[2] != NULL) {
-		VSC_C_mgt->child_panic = static_VSC_C_mgt.child_panic = 0;
+		VSC_C_mgt->child_panic = 0;
 		if (child_panic == NULL)
 			return;
 	}
@@ -312,17 +314,19 @@ mgt_launch_child(struct cli *cli)
 	heritage.std_fd = cp[1];
 	child_output = cp[0];
 
-	AN(heritage.vsm);
-	mgt_SHM_Size_Adjust();
-	AN(heritage.vsm);
+	mgt_SHM_ChildNew();
+
 	AN(heritage.param);
+	AN(heritage.panic_str);
 	if ((pid = fork()) < 0) {
-		/* XXX */
 		perror("Could not fork child");
-		exit(1);
+		exit(1);		// XXX Harsh ?
 	}
 	if (pid == 0) {
 
+		proc_vsmw = VSMW_New(heritage.vsm_fd, 0640, "_.index");
+		AN(proc_vsmw);
+
 		/* Redirect stdin/out/err */
 		VFIL_null_fd(STDIN_FILENO);
 		assert(dup2(heritage.std_fd, STDOUT_FILENO) == STDOUT_FILENO);
@@ -381,7 +385,7 @@ mgt_launch_child(struct cli *cli)
 	}
 	assert(pid > 1);
 	MGT_Complain(C_DEBUG, "Child (%jd) Started", (intmax_t)pid);
-	VSC_C_mgt->child_start = ++static_VSC_C_mgt.child_start;
+	VSC_C_mgt->child_start++;
 
 	/* Close stuff the child got */
 	closefd(&heritage.std_fd);
@@ -512,20 +516,20 @@ mgt_reap_child(void)
 		VSB_printf(vsb, " status=%d", WEXITSTATUS(status));
 		exit_status |= 0x20;
 		if (WEXITSTATUS(status) == 1)
-			VSC_C_mgt->child_exit = ++static_VSC_C_mgt.child_exit;
+			VSC_C_mgt->child_exit++;
 		else
-			VSC_C_mgt->child_stop = ++static_VSC_C_mgt.child_stop;
+			VSC_C_mgt->child_stop++;
 	}
 	if (WIFSIGNALED(status)) {
 		VSB_printf(vsb, " signal=%d", WTERMSIG(status));
 		exit_status |= 0x40;
-		VSC_C_mgt->child_died = ++static_VSC_C_mgt.child_died;
+		VSC_C_mgt->child_died++;
 	}
 #ifdef WCOREDUMP
 	if (WCOREDUMP(status)) {
 		VSB_printf(vsb, " (core dumped)");
 		exit_status |= 0x80;
-		VSC_C_mgt->child_dump = ++static_VSC_C_mgt.child_dump;
+		VSC_C_mgt->child_dump++;
 	}
 #endif
 	AZ(VSB_finish(vsb));
@@ -535,12 +539,10 @@ mgt_reap_child(void)
 	/* Dispose of shared memory but evacuate panic messages first */
 	if (heritage.panic_str[0] != '\0') {
 		mgt_panic_record(r);
-		mgt_SHM_Destroy(1);
-		VSC_C_mgt->child_panic = ++static_VSC_C_mgt.child_panic;
-	} else {
-		mgt_SHM_Destroy(MGT_DO_DEBUG(DBG_VSM_KEEP));
+		VSC_C_mgt->child_panic++;
 	}
-	mgt_SHM_Create();
+
+	mgt_SHM_ChildDestroy();
 
 	if (child_state == CH_RUNNING)
 		child_state = CH_DIED;
diff --git a/bin/varnishd/mgt/mgt_cli.c b/bin/varnishd/mgt/mgt_cli.c
index 3694263..2d6481c 100644
--- a/bin/varnishd/mgt/mgt_cli.c
+++ b/bin/varnishd/mgt/mgt_cli.c
@@ -503,7 +503,7 @@ mgt_cli_secret(const char *S_arg)
 	char buf[BUFSIZ];
 
 	/* Save in shmem */
-	mgt_SHM_static_alloc(S_arg, strlen(S_arg) + 1L, "Arg", "", "-S");
+	mgt_SHM_static_alloc(S_arg, strlen(S_arg) + 1L, "Arg", "-S");
 
 	VJ_master(JAIL_MASTER_FILE);
 	fd = open(S_arg, O_RDONLY);
@@ -573,7 +573,7 @@ mgt_cli_telnet(const char *T_arg)
 	if (VSB_len(vsb) == 0)
 		ARGV_ERR("-T %s could not be listened on.\n", T_arg);
 	/* Save in shmem */
-	mgt_SHM_static_alloc(VSB_data(vsb), VSB_len(vsb) + 1, "Arg", "", "-T");
+	mgt_SHM_static_alloc(VSB_data(vsb), VSB_len(vsb) + 1, "Arg", "-T");
 	VSB_destroy(&vsb);
 }
 
diff --git a/bin/varnishd/mgt/mgt_main.c b/bin/varnishd/mgt/mgt_main.c
index 0f50910..abd4445 100644
--- a/bin/varnishd/mgt/mgt_main.c
+++ b/bin/varnishd/mgt/mgt_main.c
@@ -43,7 +43,6 @@
 
 #include "mgt/mgt.h"
 #include "common/heritage.h"
-#include "common/common_vsm.h"
 
 #include "hash/hash_slinger.h"
 #include "vav.h"
@@ -65,7 +64,6 @@ pid_t			mgt_pid;
 struct vev_base		*mgt_evb;
 int			exit_status = 0;
 struct vsb		*vident;
-struct VSC_mgt		static_VSC_C_mgt;
 struct VSC_mgt		*VSC_C_mgt;
 static int		I_fd = -1;
 static char		Cn_arg[] = "/tmp/varnishd_C_XXXXXXX";
@@ -418,10 +416,7 @@ mgt_uptime(const struct vev *e, int what)
 	AN(VSC_C_mgt);
 	if (mgt_uptime_t0 == 0)
 		mgt_uptime_t0 = VTIM_real();
-	VSC_C_mgt->uptime = static_VSC_C_mgt.uptime =
-	    (uint64_t)(VTIM_real() - mgt_uptime_t0);
-	if (heritage.vsm != NULL)
-		CVSM_ageupdate(heritage.vsm);
+	VSC_C_mgt->uptime = (uint64_t)(VTIM_real() - mgt_uptime_t0);
 	return (0);
 }
 
@@ -458,7 +453,6 @@ mgt_f_read(const char *fn)
 	if (VFIL_searchpath(vcl_path, NULL, &f, fn, &fnp) || f == NULL) {
 		ARGV_ERR("Cannot read -f file '%s' (%s)\n",
 		    fnp != NULL ? fnp : fn, strerror(errno));
-		free(fnp);
 	}
 	free(fa->farg);
 	fa->farg = fnp;
@@ -597,10 +591,6 @@ main(int argc, char * const *argv)
 		mgt_pid = getpid();
 	}
 
-	/* Set up the mgt counters */
-	memset(&static_VSC_C_mgt, 0, sizeof static_VSC_C_mgt);
-	VSC_C_mgt = &static_VSC_C_mgt;
-
 	VRND_SeedAll();
 
 	vident = mgt_BuildVident();
@@ -848,7 +838,8 @@ main(int argc, char * const *argv)
 
 	mgt_SHM_Init();
 
-	mgt_SHM_static_alloc(i_arg, strlen(i_arg) + 1L, "Arg", "-i", "-i");
+	mgt_SHM_static_alloc(i_arg, strlen(i_arg) + 1L, "Arg", "-i");
+	VSC_C_mgt = VSC_mgt_New("");
 
 	if (M_arg != NULL)
 		mgt_cli_master(M_arg);
@@ -872,8 +863,6 @@ main(int argc, char * const *argv)
 	if (strcmp(S_arg, "none"))
 		mgt_cli_secret(S_arg);
 
-	mgt_SHM_Create();
-
 	memset(&sac, 0, sizeof sac);
 	sac.sa_handler = SIG_IGN;
 	sac.sa_flags = SA_RESTART;
diff --git a/bin/varnishd/mgt/mgt_shmem.c b/bin/varnishd/mgt/mgt_shmem.c
index 7c05ff7..f0832b1 100644
--- a/bin/varnishd/mgt/mgt_shmem.c
+++ b/bin/varnishd/mgt/mgt_shmem.c
@@ -30,7 +30,7 @@
 
 #include "config.h"
 
-#include <sys/mman.h>
+#include <sys/stat.h>
 
 #include <errno.h>
 #include <fcntl.h>
@@ -42,239 +42,93 @@
 #include "common/heritage.h"
 #include "common/common_vsm.h"
 
-#include "vfl.h"
 #include "vsm_priv.h"
-#include "vfil.h"
+#include "vsmw.h"
 
-#ifndef MAP_HASSEMAPHORE
-#define MAP_HASSEMAPHORE 0 /* XXX Linux */
-#endif
-
-#ifndef MAP_NOSYNC
-#define MAP_NOSYNC 0 /* XXX Linux */
-#endif
-
-#define PAN_CLASS "Panic"
-
-static void *mgt_vsm_p;
-static ssize_t mgt_vsm_l;
+struct vsmw	*mgt_vsmw;
+struct vsmw	*child_vsmw;
 
 /*--------------------------------------------------------------------
- * Use a bogo-VSM to hold master-copies of the VSM chunks the master
- * publishes, such as -S & -T arguments.
  */
 
-static struct vsm_sc *static_vsm;
-static char static_vsm_buf[1024];
-
 void
 mgt_SHM_static_alloc(const void *ptr, ssize_t size,
-    const char *class, const char *type, const char *ident)
+    const char *class, const char *ident)
 {
 	void *p;
 
-	p = CVSM_alloc(static_vsm, size, class, type, ident);
+	p = VSMW_Allocf(mgt_vsmw, class, size, "%s", ident);
 	AN(p);
 	memcpy(p, ptr, size);
-	if (heritage.vsm != NULL) {
-		p = CVSM_alloc(heritage.vsm, size, class, type, ident);
-		AN(p);
-		memcpy(p, ptr, size);
-	}
 }
 
 /*--------------------------------------------------------------------
- * Build a zeroed file
+ * Exit handler that clears the owning pid from the SHMLOG
  */
 
-static int
-vsm_zerofile(const char *fn, ssize_t size)
+static void
+mgt_shm_atexit(void)
 {
-	int fd;
-	int flags;
 
-	fd = VFL_Open(fn, O_RDWR | O_CREAT | O_EXCL | O_NONBLOCK, 0640);
-	if (fd < 0) {
-		MGT_Complain(C_ERR, "Could not create %s: %s",
-		    fn, strerror(errno));
-		return (-1);
-	}
-	VJ_fix_vsm_file(fd);
-	flags = fcntl(fd, F_GETFL);
-	assert(flags != -1);
-	flags &= ~O_NONBLOCK;
-	AZ(fcntl(fd, F_SETFL, flags));
-	if (VFIL_allocate(fd, (off_t)size, 1)) {
-		MGT_Complain(C_ERR, "File allocation error %s: %s",
-		    fn, strerror(errno));
-		return (-1);
+	/* Do not let VCC kill our VSM */
+	if (getpid() != mgt_pid)
+		return;
+	VSMW_Destroy(&mgt_vsmw);
+	if (!MGT_DO_DEBUG(DBG_VTC_MODE)) {
+		AZ(system("rm -rf " VSM_MGT_DIRNAME));
+		AZ(system("rm -rf " VSM_CHILD_DIRNAME));
 	}
-	return (fd);
 }
 
 /*--------------------------------------------------------------------
- * Create a VSM instance
+ * Initialize VSM subsystem
  */
 
-static size_t
-mgt_shm_size(void)
+void
+mgt_SHM_Init(void)
 {
-	size_t size, ps;
 
-	size = mgt_param.vsl_space + mgt_param.vsm_space;
-	ps = getpagesize();
-	size = RUP2(size, ps);
-	return (size);
-}
+	// XXX: VJ/mode/owner/group
+	AZ(system("rm -rf " VSM_MGT_DIRNAME));
+	AZ(mkdir(VSM_MGT_DIRNAME, 0755));
+	mgt_vsmw = VSMW_New(open(VSM_MGT_DIRNAME, O_RDONLY), 0640, "_.index");
+	AN(mgt_vsmw);
 
-static void
-mgt_shm_cleanup(void)
-{
-	char fnbuf[64];
+	proc_vsmw = mgt_vsmw;
 
-	bprintf(fnbuf, "%s.%jd", VSM_FILENAME, (intmax_t)getpid());
-	VJ_master(JAIL_MASTER_FILE);
-	(void)unlink(fnbuf);
-	VJ_master(JAIL_MASTER_LOW);
+	/* Setup atexit handler */
+	AZ(atexit(mgt_shm_atexit));
 }
 
 void
-mgt_SHM_Create(void)
+mgt_SHM_ChildNew(void)
 {
-	size_t size;
-	void *p;
-	char fnbuf[64];
-	int vsm_fd;
-
-	AZ(heritage.vsm);
-	size = mgt_shm_size();
-
-	bprintf(fnbuf, "%s.%jd", VSM_FILENAME, (intmax_t)getpid());
-
-	VJ_master(JAIL_MASTER_FILE);
-	vsm_fd = vsm_zerofile(fnbuf, size);
-	VJ_master(JAIL_MASTER_LOW);
-	if (vsm_fd < 0) {
-		mgt_shm_cleanup();
-		exit(1);
-	}
-
-	p = (void *)mmap(NULL, size,
-	    PROT_READ|PROT_WRITE,
-	    MAP_HASSEMAPHORE | MAP_NOSYNC | MAP_SHARED,
-	    vsm_fd, 0);
-
-	closefd(&vsm_fd);
 
-	if (p == MAP_FAILED) {
-		MGT_Complain(C_ERR, "Mmap error %s: %s",
-		    fnbuf, strerror(errno));
-		mgt_shm_cleanup();
-		exit(1);
-	}
-
-	mgt_vsm_p = p;
-	mgt_vsm_l = size;
-
-	/* This may or may not work */
-	(void)mlock(p, size);
+	AZ(system("rm -rf " VSM_CHILD_DIRNAME));
+	AZ(mkdir(VSM_CHILD_DIRNAME, 0755));
 
-	heritage.vsm = CVSM_new(p, size);
+	heritage.vsm_fd = open(VSM_CHILD_DIRNAME, O_RDONLY);
+	assert(heritage.vsm_fd >= 0);
+	MCH_Fd_Inherit(heritage.vsm_fd, "VSMW");
 
-	CVSM_copy(heritage.vsm, static_vsm);
-
-	heritage.param = CVSM_alloc(heritage.vsm,
-	    sizeof *heritage.param, VSM_CLASS_PARAM, "", "");
+	heritage.param = VSMW_Allocf(mgt_vsmw, VSM_CLASS_PARAM,
+	    sizeof *heritage.param, "");
 	AN(heritage.param);
 	*heritage.param = mgt_param;
 
 	heritage.panic_str_len = 64 * 1024;
-	heritage.panic_str = CVSM_alloc(heritage.vsm,
-	    heritage.panic_str_len, PAN_CLASS, "", "");
+	heritage.panic_str = VSMW_Allocf(mgt_vsmw, "Panic",
+	    heritage.panic_str_len, "");
 	AN(heritage.panic_str);
-
-	/* Copy management counters to shm and update pointer */
-	VSC_C_mgt = VSC_mgt_New("");
-	AN(VSC_C_mgt);
-	*VSC_C_mgt = static_VSC_C_mgt;
-
-#ifdef __OpenBSD__
-	/* Commit changes, for OS's without coherent VM/buf */
-	AZ(msync(p, getpagesize(), MS_SYNC));
-#endif
-	VJ_master(JAIL_MASTER_FILE);
-	if (rename(fnbuf, VSM_FILENAME)) {
-		MGT_Complain(C_ERR, "Rename failed %s -> %s: %s",
-		    fnbuf, VSM_FILENAME, strerror(errno));
-		(void)unlink(fnbuf);
-		exit(1);
-	}
-	VJ_master(JAIL_MASTER_LOW);
 }
 
-/*--------------------------------------------------------------------
- * Destroy a VSM instance
- */
-
 void
-mgt_SHM_Destroy(int keep)
+mgt_SHM_ChildDestroy(void)
 {
 
-	/* Point mgt counters back at static version */
-	VSC_C_mgt = &static_VSC_C_mgt;
-
-	AN(heritage.vsm);
-	if (keep)
-		(void)rename(VSM_FILENAME, VSM_FILENAME ".keep");
+	closefd(&heritage.vsm_fd);
+	if (!MGT_DO_DEBUG(DBG_VTC_MODE))
+		AZ(system("rm -rf " VSM_CHILD_DIRNAME));
 	heritage.panic_str = NULL;
-	heritage.panic_str_len = 0;
 	heritage.param = NULL;
-	CVSM_delete(&heritage.vsm);
-	AZ(munmap(mgt_vsm_p, mgt_vsm_l));
-	mgt_vsm_p = NULL;
-	mgt_vsm_l = 0;
-}
-
-/*--------------------------------------------------------------------
- * Destroy and recreate VSM if its size should change
- */
-
-void
-mgt_SHM_Size_Adjust(void)
-{
-
-	AN(heritage.vsm);
-	if (mgt_vsm_l == mgt_shm_size())
-		return;
-	mgt_SHM_Destroy(0);
-	mgt_SHM_Create();
-}
-
-/*--------------------------------------------------------------------
- * Exit handler that clears the owning pid from the SHMLOG
- */
-
-static void
-mgt_shm_atexit(void)
-{
-
-	/* Do not let VCC kill our VSM */
-	if (getpid() != mgt_pid)
-		return;
-	if (heritage.vsm != NULL)
-		CVSM_delete(&heritage.vsm);
-}
-
-/*--------------------------------------------------------------------
- * Initialize VSM subsystem
- */
-
-void
-mgt_SHM_Init(void)
-{
-	/* Create our static VSM instance */
-	static_vsm = CVSM_new(static_vsm_buf, sizeof static_vsm_buf);
-
-	/* Setup atexit handler */
-	AZ(atexit(mgt_shm_atexit));
 }
diff --git a/bin/varnishhist/varnishhist.c b/bin/varnishhist/varnishhist.c
index bc38a2c..b33b55e 100644
--- a/bin/varnishhist/varnishhist.c
+++ b/bin/varnishhist/varnishhist.c
@@ -76,6 +76,7 @@ static double timebend = 0, t0;
 static double vsl_t0 = 0, vsl_to, vsl_ts = 0;
 static pthread_cond_t timebend_cv;
 static double log_ten;
+static char *ident;
 
 static const int scales[] = {
 	1,
@@ -153,7 +154,7 @@ update(void)
 	if (end_of_file)
 		mvprintw(0, 0, "%*s", COLS - 1, "EOF");
 	else
-		mvprintw(0, 0, "%*s", COLS - 1, VSM_Name(VUT.vsm));
+		mvprintw(0, 0, "%*s", COLS - 1, ident);
 
 	/* count our flock */
 	for (i = 0; i < n; ++i)
@@ -605,6 +606,7 @@ main(int argc, char **argv)
 	log_ten = log(10.0);
 
 	VUT_Setup();
+	ident = VSM_Dup(VUT.vsm, "Arg", "-i");
 	if (pthread_create(&thr, NULL, do_curses, NULL) != 0)
 		VUT_Error(1, "pthread_create(): %s", strerror(errno));
 	VUT.dispatch_f = accumulate;
diff --git a/bin/varnishstat/varnishstat.c b/bin/varnishstat/varnishstat.c
index 1cb5f94..c9270cf 100644
--- a/bin/varnishstat/varnishstat.c
+++ b/bin/varnishstat/varnishstat.c
@@ -253,7 +253,6 @@ int
 main(int argc, char * const *argv)
 {
 	struct vsm *vd;
-	double t_arg = 5.0;
 	int once = 0, xml = 0, json = 0, f_list = 0, curses = 0;
 	signed char opt;
 	int i;
@@ -273,17 +272,6 @@ main(int argc, char * const *argv)
 		case 'l':
 			f_list = 1;
 			break;
-		case 't':
-			if (!strcasecmp(optarg, "off"))
-				t_arg = -1.;
-			else {
-				t_arg = VNUM(optarg);
-				if (isnan(t_arg))
-					VUT_Error(1, "-t: Syntax error");
-				if (t_arg < 0.)
-					VUT_Error(1, "-t: Range error");
-			}
-			break;
 		case 'V':
 			VCS_Message("varnishstat");
 			exit(0);
@@ -308,7 +296,7 @@ main(int argc, char * const *argv)
 	if (!(xml || json || once || f_list))
 		curses = 1;
 
-	if (VSM_Start(vd, t_arg, STDERR_FILENO))
+	if (VSM_Attach(vd, STDERR_FILENO))
 		VUT_Error(1, "%s", VSM_Error(vd));
 
 	if (curses)
diff --git a/bin/varnishstat/varnishstat_curses.c b/bin/varnishstat/varnishstat_curses.c
index 791ce69..dbf6036 100644
--- a/bin/varnishstat/varnishstat_curses.c
+++ b/bin/varnishstat/varnishstat_curses.c
@@ -1043,6 +1043,8 @@ handle_keypress(int ch)
 	redraw = 1;
 }
 
+#include <syslog.h>
+
 void
 do_curses(struct vsm *vd, double delay)
 {
@@ -1070,14 +1072,13 @@ do_curses(struct vsm *vd, double delay)
 
 	init_hitrate();
 	while (keep_running) {
-		if (VSM_Abandoned(vd)) {
+		if (VSM_Status(vd) | (VSM_MGT_CHANGED|VSM_WRK_CHANGED)) {
 			init_hitrate();
 			delete_pt_list();
-			VSM_Close(vd);
-			VSM_Open(vd);
-		}
-		if (VSM_valid != VSM_StillValid(vd, &f_iter))
 			build_pt_list(vd, &f_iter);
+			rebuild = 1;
+			redraw = 1;
+		}
 
 		now = VTIM_mono();
 		if (now - t_sample > interval)
@@ -1107,6 +1108,6 @@ do_curses(struct vsm *vd, double delay)
 			break;
 		}
 	}
-	VSM_Close(vd);
+	VSM_Destroy(&vd);
 	AZ(endwin());
 }
diff --git a/bin/varnishtest/tests/b00000.vtc b/bin/varnishtest/tests/b00000.vtc
index 7937df8..388c9b8 100644
--- a/bin/varnishtest/tests/b00000.vtc
+++ b/bin/varnishtest/tests/b00000.vtc
@@ -14,10 +14,12 @@ varnish v1 -vcl+backend {
 varnish v1 -cliok "param.set debug +workspace"
 varnish v1 -cliok "param.set debug +witness"
 
-varnish v1 -expect n_object == 0
-varnish v1 -expect sess_conn == 0
-varnish v1 -expect client_req == 0
-varnish v1 -expect cache_miss == 0
+varnish v1 -vsc *
+
+varnish v1 -expect MAIN.n_object == 0
+varnish v1 -expect MAIN.sess_conn == 0
+varnish v1 -expect MAIN.client_req == 0
+varnish v1 -expect MAIN.cache_miss == 0
 
 client c1 {
 	txreq -url "/"
diff --git a/bin/varnishtest/tests/c00083.vtc b/bin/varnishtest/tests/c00083.vtc
deleted file mode 100644
index 9b80321..0000000
--- a/bin/varnishtest/tests/c00083.vtc
+++ /dev/null
@@ -1,30 +0,0 @@
-varnishtest "VSM overflow allocation"
-
-server s1 {
-	rxreq
-	txresp
-} -start
-
-varnish v1 -arg "-pvcc_allow_inline_c=true" -vcl+backend {
-
-	C{
-	void *VSM_Alloc(unsigned size, const char *class, const char *type,
-	    const char *ident);
-	void VSM_Free(void *ptr);
-	}C
-
-	sub vcl_recv {
-		C{
-		void *p;
-		p = VSM_Alloc(3 * 1024 * 1024, "TEST", "TYPE", "0");
-		VSM_Free(p);
-		}C
-	}
-
-} -start
-
-client c1 {
-	txreq
-	rxresp
-} -run
-
diff --git a/bin/varnishtest/tests/m00029.vtc b/bin/varnishtest/tests/m00029.vtc
index c92b839..023fee0 100644
--- a/bin/varnishtest/tests/m00029.vtc
+++ b/bin/varnishtest/tests/m00029.vtc
@@ -10,7 +10,7 @@ varnish v1 -vcl+backend {
 
 	sub vcl_deliver {
 		set resp.http.existsA = std.file_exists("/non/existent");
-		set resp.http.existsB = std.file_exists("${tmpdir}/v1/_.vsm");
+		set resp.http.existsB = std.file_exists("${tmpdir}/v1/_.vsm_mgt/_.index");
 	}
 } -start
 
diff --git a/bin/varnishtest/tests/r01984.vtc b/bin/varnishtest/tests/r01984.vtc
deleted file mode 100644
index b8b4a12..0000000
--- a/bin/varnishtest/tests/r01984.vtc
+++ /dev/null
@@ -1,18 +0,0 @@
-varnishtest "g_cooldown counter test"
-
-server s1 {} -start
-varnish v1 -vcl+backend {} -start
-varnish v1 -cliok "param.set vsm_free_cooldown 10.0"
-
-# Load and use a new VCL, freeze the first
-varnish v1 -vcl+backend {}
-varnish v1 -cliok "vcl.state vcl1 cold"
-
-# the VSM_common_cleaner runs every 1.1 seconds.
-# Wait for it to update vsm_cooling
-delay 2
-varnish v1 -expect vsm_cooling > 0
-
-# Wait long enough for all the cooling vsm memory to be transfered to the free list:
-delay 12
-varnish v1 -expect vsm_cooling == 0
diff --git a/bin/varnishtest/tests/r02270.vtc b/bin/varnishtest/tests/r02270.vtc
index 8547ca5..2959e8d 100644
--- a/bin/varnishtest/tests/r02270.vtc
+++ b/bin/varnishtest/tests/r02270.vtc
@@ -16,4 +16,6 @@ varnish v1 -expect VBE.vcl2.s1.happy >= 0
 
 delay 5
 
+varnish v1 -vsc VBE.*
+
 varnish v1 -expect !VBE.vcl2.s1.happy
diff --git a/bin/varnishtest/tests/u00005.vtc b/bin/varnishtest/tests/u00005.vtc
index 5d91958..52242be 100644
--- a/bin/varnishtest/tests/u00005.vtc
+++ b/bin/varnishtest/tests/u00005.vtc
@@ -37,9 +37,11 @@ shell -expect "Copyright (c) 2006 Verdens Gang AS" \
 	"varnishstat -V"
 shell -err -expect "Usage: varnishstat <options>" \
 	"varnishstat extra"
-shell -err -expect "-t: Range error" \
+shell -err -expect "-t: Invalid argument: -1" \
 	"varnishstat -t -1"
-shell -err -expect "-t: Syntax error" \
+shell -err -expect "-t: Invalid argument: Nan" \
+	"varnishstat -t Nan"
+shell -err -expect "-t: Invalid argument: foo" \
 	"varnishstat -t foo"
 shell -err -expect "Could not get hold of varnishd" \
 	"varnishstat -n /nonexistent -t 1"
diff --git a/bin/varnishtest/tests/u00006.vtc b/bin/varnishtest/tests/u00006.vtc
index a36eff2..e87e4da 100644
--- a/bin/varnishtest/tests/u00006.vtc
+++ b/bin/varnishtest/tests/u00006.vtc
@@ -41,10 +41,12 @@ shell -err -expect {-I: "Resp" is ambiguous} \
 	"varnishlog -I Resp:bar"
 shell -err -expect {-I: Regex error at position 4 (missing ))} \
 	{varnishlog -I "(foo"}
-shell -err -expect "-t: Range error" \
+shell -err -expect "-t: Invalid argument" \
 	"varnishlog -t -1"
-shell -err -expect "-t: Syntax error" \
+shell -err -expect "-t: Invalid argument" \
 	"varnishlog -t foo"
+shell -err -expect "-t: Invalid argument" \
+	"varnishlog -t NaN"
 shell -err -expect {-x: Syntax error in "**"} \
 	{varnishlog -x "**"}
 shell -err -expect {-X: Syntax error in "**"} \
diff --git a/bin/varnishtest/vtc_logexp.c b/bin/varnishtest/vtc_logexp.c
index 1c5a696..7f7d054 100644
--- a/bin/varnishtest/vtc_logexp.c
+++ b/bin/varnishtest/vtc_logexp.c
@@ -227,10 +227,12 @@ logexp_new(const char *name, const char *varg)
 	VSB_destroy(&vsb);
 	if (n_arg == NULL)
 		vtc_fatal(le->vl, "-v argument problems");
-	if (VSM_n_Arg(le->vsm, VSB_data(n_arg)) <= 0)
+	if (VSM_Arg(le->vsm, 'n', VSB_data(n_arg)) <= 0)
 		vtc_fatal(le->vl, "-v argument error: %s",
 		    VSM_Error(le->vsm));
 	VSB_destroy(&n_arg);
+	if (VSM_Attach(le->vsm, -1))
+		vtc_fatal(le->vl, "VSM_Attach: %s", VSM_Error(le->vsm));
 	return (le);
 }
 
@@ -378,7 +380,6 @@ logexp_close(struct logexp *le)
 	if (le->vslq)
 		VSLQ_Delete(&le->vslq);
 	AZ(le->vslq);
-	VSM_Close(le->vsm);
 }
 
 static void
@@ -390,9 +391,8 @@ logexp_start(struct logexp *le)
 	AN(le->vsl);
 	AZ(le->vslq);
 
-	if (VSM_Start(le->vsm, 0, -1))
-		vtc_fatal(le->vl, "VSM_Start: %s", VSM_Error(le->vsm));
 	AN(le->vsl);
+	(void)VSM_Status(le->vsm);
 	c = VSL_CursorVSM(le->vsl, le->vsm,
 	    (le->d_arg ? 0 : VSL_COPT_TAIL) | VSL_COPT_BATCH);
 	if (c == NULL)
diff --git a/bin/varnishtest/vtc_varnish.c b/bin/varnishtest/vtc_varnish.c
index 4590754..3b5014c 100644
--- a/bin/varnishtest/vtc_varnish.c
+++ b/bin/varnishtest/vtc_varnish.c
@@ -76,10 +76,8 @@ struct varnish {
 	char			*jail;
 	char			*proto;
 
-	struct vsm		*vd;		/* vsc use */
-	struct vsm		*vdl;		/* log use */
+	struct vsm		*vd;
 	int			has_a_arg;
-	struct vsm_fantom	vf;
 
 	unsigned		vsl_tag_count[256];
 
@@ -212,15 +210,7 @@ varnishlog_thread(void *priv)
 
 	vsl = VSL_New();
 	AN(vsl);
-	vsm = v->vdl;
-#if 0
-	//vsm = VSM_New();
-	AN(vsm);
-	(void)VSM_n_Arg(vsm, v->workdir);
-
-	if (VSM_Start(vsm, vtc_maxdur, 2))
-		vtc_fatal(v->vl, "vsm|%s", VSM_Error(vsm));
-#endif
+	vsm = v->vd;
 
 	c = NULL;
 	opt = 0;
@@ -229,16 +219,10 @@ varnishlog_thread(void *priv)
 			if (vtc_error)
 				break;
 			VTIM_sleep(0.1);
-			if (VSM_Open(vsm)) {
-				vtc_log(v->vl, 3, "vsm|%s",
-				    VSM_Error(vsm));
-				VSM_ResetError(vsm);
-				continue;
-			}
+			(void)VSM_Status(vsm);
 			c = VSL_CursorVSM(vsl, vsm, opt);
 			if (c == NULL) {
-				vtc_log(v->vl, 3, "vsl|%s",
-				    VSL_Error(vsl));
+				vtc_log(v->vl, 3, "vsl|%s", VSL_Error(vsl));
 				VSL_ResetError(vsl);
 				continue;
 			}
@@ -271,12 +255,17 @@ varnishlog_thread(void *priv)
 		if (i == 0) {
 			/* Nothing to do but wait */
 			v->vsl_idle++;
-			VTIM_sleep(0.1);
+			if (!(VSM_Status(vsm) & VSM_WRK_RUNNING)) {
+				/* Abandoned - try reconnect */
+				VSL_DeleteCursor(c);
+				c = NULL;
+			} else {
+				VTIM_sleep(0.1);
+			}
 		} else if (i == -2) {
 			/* Abandoned - try reconnect */
 			VSL_DeleteCursor(c);
 			c = NULL;
-			VSM_Close(vsm);
 		} else
 			break;
 	}
@@ -284,7 +273,6 @@ varnishlog_thread(void *priv)
 	if (c)
 		VSL_DeleteCursor(c);
 	VSL_Delete(vsl);
-	VSM_Destroy(&v->vdl);
 
 	return (NULL);
 }
@@ -532,13 +520,9 @@ varnish_launch(struct varnish *v)
 	free(r);
 
 	v->vd = VSM_New();
-	(void)VSM_n_Arg(v->vd, v->workdir);
-	AZ(VSM_Start(v->vd, vtc_maxdur, -1));
-	assert(VSM_Get(v->vd, &v->vf, "Arg", "-i") > 0);
+	(void)VSM_Arg(v->vd, 'n', v->workdir);
+	AZ(VSM_Attach(v->vd, -1));
 
-	v->vdl = VSM_New();
-	(void)VSM_n_Arg(v->vdl, v->workdir);
-	AZ(VSM_Start(v->vdl, vtc_maxdur, -1));
 	AZ(pthread_create(&v->tp_vsl, NULL, varnishlog_thread, v));
 }
 
@@ -848,19 +832,14 @@ do_stat_dump_cb(void *priv, const struct VSC_point * const pt)
 }
 
 static void
-varnish_vsc(struct varnish *v, const char *arg)
+varnish_vsc(const struct varnish *v, const char *arg)
 {
 	struct dump_priv dp;
 
 	memset(&dp, 0, sizeof dp);
 	dp.v = v;
 	dp.arg = arg;
-	if (VSM_StillValid(v->vd, &v->vf) != VSM_valid) {
-		VSM_Close(v->vd);
-		if (VSM_Open(v->vd) < 0)
-			vtc_fatal(v->vl, "Could not open VSM (%s)",
-			    VSM_Error(v->vd));
-	}
+	(void)VSM_Status(v->vd);
 
 	(void)VSC_Iter(v->vd, NULL, do_stat_dump_cb, &dp);
 }
@@ -876,7 +855,7 @@ struct stat_priv {
 };
 
 static int
-do_stat_cb(void *priv, const struct VSC_point * const pt)
+do_expect_cb(void *priv, const struct VSC_point * const pt)
 {
 	struct stat_priv *sp = priv;
 
@@ -896,7 +875,7 @@ do_stat_cb(void *priv, const struct VSC_point * const pt)
  */
 
 static void
-varnish_expect(struct varnish *v, char * const *av)
+varnish_expect(const struct varnish *v, char * const *av)
 {
 	uint64_t ref;
 	int good;
@@ -925,15 +904,10 @@ varnish_expect(struct varnish *v, char * const *av)
 	sp.v = v;
 	ref = 0;
 	good = 0;
-	for (i = 0; i < 10; i++, (void)usleep(100000)) {
-		if (VSM_StillValid(v->vd, &v->vf) != VSM_valid) {
-			VSM_Close(v->vd);
-			good = VSM_Open(v->vd);
-		}
-		if (good < 0)
-			continue;
+	for (i = 0; i < 50; i++, (void)usleep(100000)) {
+		(void)VSM_Status(v->vd);
 
-		good = VSC_Iter(v->vd, NULL, do_stat_cb, &sp);
+		good = VSC_Iter(v->vd, NULL, do_expect_cb, &sp);
 		if (!good) {
 			good = -2;
 			continue;
diff --git a/bin/varnishtop/varnishtop.c b/bin/varnishtop/varnishtop.c
index ca4df6d..1d7ebe0 100644
--- a/bin/varnishtop/varnishtop.c
+++ b/bin/varnishtop/varnishtop.c
@@ -75,6 +75,7 @@ static unsigned ntop;
 static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
 static int f_flag = 0;
 static unsigned maxfieldlen = 0;
+static char *ident;
 
 static volatile sig_atomic_t quit = 0;
 
@@ -201,7 +202,7 @@ update(int p)
 	if (n < p)
 		n++;
 	AC(erase());
-	q = VSM_Name(VUT.vsm);
+	q = ident;
 	len = COLS - strlen(q);
 	if (end_of_file)
 		AC(mvprintw(0, len - (1 + 6), "%s (EOF)", q));
@@ -360,6 +361,7 @@ main(int argc, char **argv)
 		usage(1);
 
 	VUT_Setup();
+	ident = VSM_Dup(VUT.vsm, "Arg", "-i");
 	if (!once) {
 		if (pthread_create(&thr, NULL, do_curses, NULL) != 0) {
 			fprintf(stderr, "pthread_create(): %s\n",
diff --git a/include/vapi/vsc.h b/include/vapi/vsc.h
index 022ccfc..7a87f70 100644
--- a/include/vapi/vsc.h
+++ b/include/vapi/vsc.h
@@ -44,7 +44,7 @@ struct vsm_fantom;
  * VSC level access functions
  */
 
-int VSC_Arg(struct vsm *vd, int arg, const char *opt);
+int VSC_Arg(struct vsm *vd, char arg, const char *opt);
 	/*
 	 * Handle standard stat-presenter arguments
 	 * Return:
diff --git a/include/vapi/vsm.h b/include/vapi/vsm.h
index 6a22c70..0ccbd44 100644
--- a/include/vapi/vsm.h
+++ b/include/vapi/vsm.h
@@ -36,7 +36,6 @@
 #ifndef VAPI_VSM_H_INCLUDED
 #define VAPI_VSM_H_INCLUDED
 
-struct VSM_chunk;
 struct vsm;
 
 /*
@@ -44,16 +43,15 @@ struct vsm;
  */
 
 struct vsm_fantom {
+	uintptr_t		priv;		/* VSM private */
 	struct VSM_chunk	*chunk;
 	void			*b;		/* first byte of payload */
 	void			*e;		/* first byte past payload */
-	uintptr_t		priv;		/* VSM private */
 	char			*class;
-	char			*type;
 	char			*ident;
 };
 
-#define VSM_FANTOM_NULL { 0, 0, 0, 0, 0, 0, 0 }
+#define VSM_FANTOM_NULL { 0, 0, 0, 0, 0, 0 }
 
 /*---------------------------------------------------------------------
  * VSM level access functions
@@ -62,7 +60,7 @@ struct vsm_fantom {
 struct vsm *VSM_New(void);
 	/*
 	 * Allocate and initialize a VSL_data handle structure.
-	 * This is the first thing you will have to do, always.
+	 * This is the first thing you will always have to do.
 	 * You can have multiple active vsm handles at the same time
 	 * referencing the same or different shared memory files.
 	 * Returns:
@@ -73,78 +71,70 @@ struct vsm *VSM_New(void);
 void VSM_Destroy(struct vsm **vd);
 	/*
 	 * Close and deallocate all storage and mappings.
-	 * (including any VSC and VSL "sub-classes")
+	 * (including any VSC and VSL "sub-classes" XXX?)
 	 */
 
 const char *VSM_Error(const struct vsm *vd);
 	/*
-	 * Return the latest error message.
+	 * Return the first recorded error message.
 	 */
 
 void VSM_ResetError(struct vsm *vd);
 	/*
-	 * Reset any error message.
+	 * Reset recorded error message.
 	 */
 
 #define VSM_n_USAGE	"[-n varnish_name]"
+#define VSM_t_USAGE	"[-t <seconds|off>]"
 
-int VSM_n_Arg(struct vsm *, const char *n_arg);
+int VSM_Arg(struct vsm *, char flag, const char *t_arg);
 	/*
-	 * Configure which varnishd instance to access.
-	 * Uses hostname if n_arg is NULL or "".
+	 * Handle all VSM specific command line arguments.
 	 *
 	 * Returns:
 	 *	 1 on success
 	 *	 <0 on failure, VSM_Error() returns diagnostic string
-	 */
-
-int VSM_Start(struct vsm *, double patience, int progress_fd);
-
-const char *VSM_Name(struct vsm *);
-	/*
-	 * Return the instance name (-i argument to varnishd)
-	 */
-
-int VSM_Open(struct vsm *);
-	/*
-	 * Attempt to open and map the VSM file.
 	 *
-	 * Returns:
-	 *	0 on success, or the VSM log was already open
-	 *	<0 on failure, VSM_Error() returns diagnostic string
-	 */
-
-int VSM_IsOpen(const struct vsm *vd);
-	/*
-	 * Check if the VSM is open.
+	 * 't' Configure patience during startup
 	 *
-	 * Returns:
-	 *       1: Is open
-	 *       0: Is closed
+	 *	If t_arg is "off", VSM_Attach() will wait forever.
+	 *	Otherwise t_arg is the number of seconds to be patient
+	 *	while the varnishd manager process gets started.
+	 *
+	 *	The default is five seconds.
+	 *
+	 * 'n' Configure varnishd instance to access
+	 *
+	 *	The default is the hostname.
 	 */
 
-int VSM_Abandoned(struct vsm *vd);
+int VSM_Attach(struct vsm *, int progress);
 	/*
-	 * Find out if the VSM file has been abandoned or closed and should
-	 * be reopened.  This function calls stat(2) and should only be
-	 * used when lack of activity or invalidation of fantoms indicate
-	 * abandonment.
+	 * Attach to the master process VSM segment, according to
+	 * the 't' argument.  If `progress_fd` is non-negative, a
+	 * period ('.') will be output for each second waited, and if
+	 * any periods were output, a NL ('\n') is outout before the
+	 * function returns.
 	 *
 	 * Returns:
-	 *	0  No reopen needed.
-	 *	1  VSM abandoned.
+	 *	0	Attached
+	 *	-1	Not Attached.
 	 */
 
-void VSM_Close(struct vsm *vd);
+#define VSM_MGT_RUNNING		(1U<<1)
+#define VSM_MGT_CHANGED		(1U<<2)
+#define VSM_MGT_RESTARTED	(1U<<3)
+#define VSM_WRK_RUNNING		(1U<<9)
+#define VSM_WRK_CHANGED		(1U<<10)
+#define VSM_WRK_RESTARTED	(1U<<11)
+
+unsigned VSM_Status(struct vsm *);
 	/*
-	 * Close and unmap shared memory, if open. Any reference to
-	 * previously returned memory areas will cause segmentation
-	 * fault. This includes any VSC counter areas or any VSL SHM
-	 * record references.
+	 * Returns a bitmap of the current status and changes in it
+	 * since the previous call to VSM_Status
 	 */
 
-
-void VSM__iter0(struct vsm *, struct vsm_fantom *vf);
+void VSM__iter0(const struct vsm *, struct vsm_fantom *vf);
 int VSM__itern(struct vsm *, struct vsm_fantom *vf);
 
 #define VSM_FOREACH(vf, vd) \
@@ -164,37 +154,20 @@ struct vsm_valid {
 
 extern const struct vsm_valid VSM_invalid[];
 extern const struct vsm_valid VSM_valid[];
-extern const struct vsm_valid VSM_similar[];
 
-const struct vsm_valid *VSM_StillValid(struct vsm *, struct vsm_fantom *vf);
+const struct vsm_valid *VSM_StillValid(const struct vsm *, const struct vsm_fantom *vf);
 	/*
 	 * Check the validity of a previously looked up vsm_fantom.
 	 *
 	 * VSM_invalid means that the SHM chunk this fantom points to does
-	 * not exist in the log file any longer. Using the fantom's
-	 * pointer gives undefined results. Further checking with
-	 * VSM_Abandoned() may be a good idea.
-	 *
-	 * VSM_valid means that the SHM structure has not changed since
-	 * the fantom was looked up or since the last call to
-	 * VSM_StillValid().
+	 * not exist in the log file any longer.
 	 *
-	 * VSM_similar means that the SHM structure has changed, but there
-	 * is still a valid chunk present with the same the same type and
-	 * identifier. The fantom's pointers and dimensions haven't
-	 * changed. The next call to VSM_StillValid() on this fantom will
-	 * return VSM_valid.
-	 *
-	 * Applications using the fantom to monitor a single chunk can
-	 * treat VSM_similar as equal to VSM_valid.  Applications using a
-	 * fantom to monitor the SHM file for new or removed chunks,
-	 * should reiterate over the chunks on VSM_similar as the
-	 * structure has changed.
+	 * VSM_valid means that the SHM chunk this fantom points to is still
+	 * good.
 	 *
 	 * Return:
 	 *   VSM_invalid: fantom is not valid any more.
 	 *   VSM_valid:   fantom is still the same.
-	 *   VSM_similar: a fantom with same dimensions exist in same position.
 	 */
 
 int VSM_Get(struct vsm *, struct vsm_fantom *vf,
@@ -205,4 +178,13 @@ int VSM_Get(struct vsm *, struct vsm_fantom *vf,
 	 * class is mandatory, ident optional.
 	 */
 
+char *VSM_Dup(struct vsm*, const char *class, const char *ident);
+	/*
+	 * Returns a malloc'ed copy of the fanton.
+	 *
+	 * Return:
+	 *   NULL = Failure
+	 *   !NULL = malloc'ed pointer
+	 */
+
 #endif /* VAPI_VSM_H_INCLUDED */
diff --git a/include/vsm_priv.h b/include/vsm_priv.h
index 20f4f90..00619fc 100644
--- a/include/vsm_priv.h
+++ b/include/vsm_priv.h
@@ -26,98 +26,14 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * Define the layout of the shared memory log segment.
- *
- * NB: THIS IS NOT A PUBLIC API TO VARNISH!
- *
- * There is a lot of diplomacy and protocol involved with the VSM segment
- * since there is no way to (and no desire to!) lock between the readers
- * and the writer.
- *
- * In particular we want the readers to seamlessly jump from one VSM instance
- * to another when the child restarts.
- *
- * The VSM segment life-cycle is:
- *
- *	Manager creates VSM file under temp name
- *
- *	Temp VSM file is initialized such that VSM_head is consistent
- *	with a non-zero alloc_seq
- *
- *	Manager renames Temp VSM file to correct filename as atomic
- *	operation.
- *
- *	When manager abandons VSM file, alloc_seq is set to zero, which
- *	never happens in any other circumstances.
- *
- *	If a manager is started and finds and old abandoned VSM segment
- *	it will zero the alloc_seq in it, before replacing the file.
- *
- * Subscribers will have to monitor three things to make sure they look at
- * the right thing: The alloc_seq field, the age counter and the dev+inode
- * of the path-name.  The former check is by far the cheaper, the second
- * can be used to check that varnishd is still alive and the last check
- * should only be employed when lack of activity in the VSM segment raises
- * suspicion that something has happened.
- *
- * The allocations ("chunks") in the VSM forms a linked list, starting with
- * VSM_head->first, with the first/next fields being byte offsets relative
- * to the start of the VSM segment.
- *
- * The last chunk on the list, has next == 0.
- *
- * New chunks are appended to the list, no matter where in the VSM
- * they happen to be allocated.
- *
- * Chunk allocation sequence is:
- *	Find free space
- *	Zero payload
- *	Init Chunk header
- *	Write memory barrier
- *	update hdr->first or $last->next pointer
- *	hdr->alloc_seq changes
- *	Write memory barrier
- *
- * Chunk contents should be designed so that zero bytes are not mistaken
- * for valid contents.
- *
- * Chunk deallocation sequence is:
- *	update hdr->first or $prev->next pointer
- *	Write memory barrier
- *	this->len = 0
- *	hdr->alloc_seq changes
- *	Write memory barrier
- *
- * The space occupied by the chunk is put on a cooling list and is not
- * recycled for at least a minute.
- *
  */
 
 #ifndef VSM_PRIV_H_INCLUDED
 #define VSM_PRIV_H_INCLUDED
 
-#define VSM_FILENAME		"_.vsm"
-#define VSM_MARKER_LEN	8
-#define VSM_IDENT_LEN	128
+#define VSM_MGT_DIRNAME		"_.vsm_mgt"
+#define VSM_CHILD_DIRNAME	"_.vsm_child"
 
-struct VSM_chunk {
-#define VSM_CHUNK_MARKER	"VSMCHUNK"
-	char			marker[VSM_MARKER_LEN];
-	ssize_t			len;		/* Incl VSM_chunk */
-	ssize_t			next;		/* Offset in shmem */
-	char			class[VSM_MARKER_LEN];
-	char			type[VSM_MARKER_LEN];
-	char			ident[VSM_IDENT_LEN];
-};
-
-struct VSM_head {
-#define VSM_HEAD_MARKER		"VSMHEAD0"	/* Incr. as version# */
-	char			marker[VSM_MARKER_LEN];
-	ssize_t			hdrsize;
-	ssize_t			shm_size;
-	ssize_t			first;		/* Offset, first chunk */
-	unsigned		alloc_seq;
-	uint64_t		age;
-};
+#define VSM_MARKER_LEN	8
 
 #endif /* VSM_PRIV_H_INCLUDED */
diff --git a/include/vut.h b/include/vut.h
index 981524e..346d7bd 100644
--- a/include/vut.h
+++ b/include/vut.h
@@ -45,7 +45,7 @@ struct VUT {
 	char		*P_arg;
 	char		*q_arg;
 	char		*r_arg;
-	double		t_arg;
+	char		*t_arg;
 
 	/* State */
 	struct VSL_data	*vsl;
diff --git a/lib/libvarnish/vfil.c b/lib/libvarnish/vfil.c
index 55da29f..6df5af6 100644
--- a/lib/libvarnish/vfil.c
+++ b/lib/libvarnish/vfil.c
@@ -223,14 +223,17 @@ VFIL_fsinfo(int fd, unsigned *pbs, uintmax_t *psize, uintmax_t *pspace)
  *
  * Returns 0 on success, -1 on failure with errno set.
  */
+
 int
 VFIL_allocate(int fd, off_t size, int insist)
 {
 	struct stat st;
 	uintmax_t fsspace;
 	size_t l;
-	ssize_t l2;
-	char buf[64 * 1024];
+	ssize_t l2, l3;
+	char *buf;
+	ssize_t bufsiz;
+	int retval = 0;
 
 	if (ftruncate(fd, size))
 		return (-1);
@@ -268,18 +271,26 @@ VFIL_allocate(int fd, off_t size, int insist)
 
 	/* Write size zero bytes to make sure the entire file is allocated
 	   in the file system */
-	memset(buf, 0, sizeof buf);
+	if (size > 65536)
+		bufsiz = 64 * 1024;
+	else
+		bufsiz = size;
+	buf = calloc(bufsiz, 1);
+	AN(buf);
 	assert(lseek(fd, 0, SEEK_SET) == 0);
 	for (l = 0; l < size; l += l2) {
-		l2 = sizeof buf;
+		l2 = bufsiz;
 		if (l + l2 > size)
 			l2 = size - l;
-		l2 = write(fd, buf, l2);
-		if (l2 < 0)
-			return (-1);
+		l3 = write(fd, buf, l2);
+		if (l3 != l2) {
+			retval = -1;
+			break;
+		}
 	}
 	assert(lseek(fd, 0, SEEK_SET) == 0);
-	return (0);
+	free(buf);
+	return (retval);
 }
 
 struct vfil_dir {
diff --git a/lib/libvarnish/vin.c b/lib/libvarnish/vin.c
index f2a3119..5a3d948 100644
--- a/lib/libvarnish/vin.c
+++ b/lib/libvarnish/vin.c
@@ -38,7 +38,6 @@
 #include <string.h>
 #include <unistd.h>
 
-#include "vsm_priv.h"
 #include "vas.h"	// XXX Flexelint "not used" - but req'ed for assert()
 #include "vdef.h"
 #include "vin.h"
@@ -75,12 +74,6 @@ VIN_n_Arg(const char *n_arg, char **dir)
 		bprintf(dn, "%s/%s", VARNISH_STATE_DIR, nm);
 	}
 
-	/* Definitive length check */
-	if (strlen(dn) + 1 + strlen(VSM_FILENAME) >= sizeof dn) {
-		errno = ENAMETOOLONG;
-		return (-1);
-	}
-
 	strcat(dn, "/");
 
 	if (dir != NULL) {
diff --git a/lib/libvarnish/vsmw.c b/lib/libvarnish/vsmw.c
index d57557e..3d1c095 100644
--- a/lib/libvarnish/vsmw.c
+++ b/lib/libvarnish/vsmw.c
@@ -85,11 +85,22 @@ struct vsmw {
 	char				*idx;
 	VTAILQ_HEAD(, vsmwseg)		segs;
 	struct vsb			*vsb;
+	pid_t				pid;
+	time_t				birth;
 };
 
 /*--------------------------------------------------------------------*/
 
 static void
+vsmw_idx_head(const struct vsmw *vsmw, int fd)
+{
+	char buf[64];
+
+	bprintf(buf, "# %jd %jd\n", (intmax_t)vsmw->pid, (intmax_t)vsmw->birth);
+	assert(write(fd, buf, strlen(buf)) == strlen(buf));
+}
+
+static void
 vsmw_write_index(const struct vsmw *vsmw, int fd, const struct vsmwseg *seg)
 {
 	ssize_t s;
@@ -116,7 +127,9 @@ vsmw_mkent(const struct vsmw *vsmw, const char *pfx)
 
 	while (1) {
 		VSB_clear(vsmw->vsb);
-		VSB_printf(vsmw->vsb, "%s.%lx", pfx, VRND_RandomTestable());
+		VSB_printf(vsmw->vsb, "_.%s", pfx);
+		VSB_printf(vsmw->vsb, ".%08lx", VRND_RandomTestable());
+		VSB_printf(vsmw->vsb, "%08lx", VRND_RandomTestable());
 		AZ(VSB_finish(vsmw->vsb));
 		fd = openat(vsmw->vdirfd, VSB_data(vsmw->vsb), O_RDONLY);
 		if (fd < 0 && errno == ENOENT)
@@ -215,12 +228,13 @@ vsmw_delseg(struct vsmw *vsmw, struct vsmwseg *seg, int fixidx)
 		vsmw_mkent(vsmw, vsmw->idx);
 		REPLACE(t, VSB_data(vsmw->vsb));
 		AN(t);
-		fd = open(t, O_WRONLY|O_CREAT|O_EXCL, vsmw->mode);
+		fd = openat(vsmw->vdirfd, t, O_WRONLY|O_CREAT|O_EXCL, vsmw->mode);
 		assert(fd >= 0);
+		vsmw_idx_head(vsmw, fd);
 		VTAILQ_FOREACH(seg, &vsmw->segs, list)
 			vsmw_write_index(vsmw, fd, seg);
 		AZ(close(fd));
-		AZ(rename(t, vsmw->idx));
+		AZ(renameat(vsmw->vdirfd, t, vsmw->vdirfd, vsmw->idx));
 		REPLACE(t, NULL);
 	}
 }
@@ -256,19 +270,24 @@ VSMW_New(int vdirfd, int mode, const char *idxname)
 	assert(vdirfd > 0);
 	assert(mode > 0);
 	AN(idxname);
+
 	ALLOC_OBJ(vsmw, VSMW_MAGIC);
 	AN(vsmw);
+
 	VTAILQ_INIT(&vsmw->segs);
 	vsmw->vsb = VSB_new_auto();
 	AN(vsmw->vsb);
 	REPLACE(vsmw->idx, idxname);
 	vsmw->mode = mode;
 	vsmw->vdirfd = vdirfd;
+	vsmw->pid = getpid();
+	vsmw->birth = time(NULL);
 
 	(void)unlinkat(vdirfd, vsmw->idx, 0);
 	fd = openat(vdirfd,
 	    vsmw->idx, O_APPEND | O_WRONLY | O_CREAT, vsmw->mode);
 	assert(fd >= 0);
+	vsmw_idx_head(vsmw, fd);
 	AZ(close(fd));
 
 	return (vsmw);
diff --git a/lib/libvarnishapi/Makefile.am b/lib/libvarnishapi/Makefile.am
index 54f1884..fbd4014 100644
--- a/lib/libvarnishapi/Makefile.am
+++ b/lib/libvarnishapi/Makefile.am
@@ -23,6 +23,7 @@ libvarnishapi_la_SOURCES = \
 	../../include/vcs_version.h \
 	../libvarnish/version.c \
 	../libvarnish/vcli_proto.c \
+	../libvarnish/vfil.c \
 	../libvarnish/vfl.c \
 	../libvarnish/vin.c \
 	../libvarnish/vmb.c \
diff --git a/lib/libvarnishapi/libvarnishapi.map b/lib/libvarnishapi/libvarnishapi.map
index c860e7d..6a7cab2 100644
--- a/lib/libvarnishapi/libvarnishapi.map
+++ b/lib/libvarnishapi/libvarnishapi.map
@@ -188,4 +188,9 @@ LIBVARNISHAPI_1.7 {
 	VSM_Unmap;
 	VSC_Destroy_Point;
 	VSC_Clone_Point;
+	VSM_Refresh;
+	VSM_Attach;
+	VSM_Status;
+	VSM_Arg;
+	VSM_Dup;
 } LIBVARNISHAPI_1.0;
diff --git a/lib/libvarnishapi/vsc.c b/lib/libvarnishapi/vsc.c
index c4245b7..399c054 100644
--- a/lib/libvarnishapi/vsc.c
+++ b/lib/libvarnishapi/vsc.c
@@ -193,12 +193,14 @@ vsc_f_arg(struct vsm *vd, const char *opt)
 /*--------------------------------------------------------------------*/
 
 int
-VSC_Arg(struct vsm *vd, int arg, const char *opt)
+VSC_Arg(struct vsm *vd, char arg, const char *opt)
 {
 
 	switch (arg) {
 	case 'f': return (vsc_f_arg(vd, opt));
-	case 'n': return (VSM_n_Arg(vd, opt));
+	case 'n':
+	case 't':
+		return (VSM_Arg(vd, arg, opt));
 	default:
 		return (0);
 	}
@@ -241,10 +243,7 @@ vsc_iter_elem(struct vsm *vd, const struct vsm_fantom *fantom,
 	assert(vt->type == VJSN_STRING);
 
 	VSB_clear(vsb);
-	VSB_printf(vsb, "%s", fantom->type);
-	if (*fantom->ident)
-		VSB_printf(vsb, ".%s", fantom->ident);
-	VSB_printf(vsb, ".%s", vt->value);
+	VSB_printf(vsb, "%s.%s", fantom->ident, vt->value);
 	AZ(VSB_finish(vsb));
 
 	if (vsc_filter(vd, VSB_data(vsb)))
@@ -314,6 +313,7 @@ vsc_iter_fantom(struct vsm *vd, const struct vsm_fantom *fantom,
 	struct vjsn_val *vv, *vve;
 
 	p = (char*)fantom->b + 8 + vbe64dec(fantom->b);
+	assert (p < (char*)fantom->e);
 	vj = vjsn_parse(p, &e);
 	if (e != NULL) {
 		fprintf(stderr, "%s\n", p);
@@ -360,7 +360,8 @@ VSC_Iter(struct vsm *vd, struct vsm_fantom *fantom, VSC_iter_f *func,
 		if (fantom != NULL)
 			*fantom = ifantom;
 		i = vsc_iter_fantom(vd, &ifantom, vsb, func, priv);
-		AZ(VSM_Unmap(vd, &ifantom));
+		if (fantom == NULL)
+			AZ(VSM_Unmap(vd, &ifantom));
 		if (i)
 			break;
 	}
diff --git a/lib/libvarnishapi/vsl_cursor.c b/lib/libvarnishapi/vsl_cursor.c
index c161abb..0424922 100644
--- a/lib/libvarnishapi/vsl_cursor.c
+++ b/lib/libvarnishapi/vsl_cursor.c
@@ -77,6 +77,7 @@ vslc_vsm_delete(const struct VSL_cursor *cursor)
 	struct vslc_vsm *c;
 
 	CAST_OBJ_NOTNULL(c, cursor->priv_data, VSLC_VSM_MAGIC);
+	AZ(VSM_Unmap(c->vsm, &c->vf));
 	assert(&c->cursor == cursor);
 	FREE_OBJ(c);
 }
@@ -143,8 +144,7 @@ vslc_vsm_next(const struct VSL_cursor *cursor)
 		}
 
 		if (t == VSL_ENDMARKER) {
-			if (VSM_invalid == VSM_StillValid(c->vsm, &c->vf) ||
-			    VSM_Abandoned(c->vsm))
+			if (VSM_StillValid(c->vsm, &c->vf) != VSM_valid)
 				return (-2); /* VSL abandoned */
 			if (c->options & VSL_COPT_TAILSTOP)
 				return (-1); /* EOF */
@@ -252,16 +252,22 @@ VSL_CursorVSM(struct VSL_data *vsl, struct vsm *vsm, unsigned options)
 		    "No VSL chunk found (child not started ?)");
 		return (NULL);
 	}
-	AZ(VSM_Map(vsm, &vf));
+	if (VSM_Map(vsm, &vf)) {
+		(void)vsl_diag(vsl,
+		    "VSM_Map(): %s", VSM_Error(vsm));
+		return (NULL);
+	}
 	AN(vf.b);
 
 	head = vf.b;
 	if (memcmp(head->marker, VSL_HEAD_MARKER, sizeof head->marker)) {
+		AZ(VSM_Unmap(vsm, &vf));
 		(void)vsl_diag(vsl, "Not a VSL chunk");
 		return (NULL);
 	}
 	ALLOC_OBJ(c, VSLC_VSM_MAGIC);
 	if (c == NULL) {
+		AZ(VSM_Unmap(vsm, &vf));
 		(void)vsl_diag(vsl, "Out of memory");
 		return (NULL);
 	}
diff --git a/lib/libvarnishapi/vsm.c b/lib/libvarnishapi/vsm.c
index 9a41974..4c0e9ed 100644
--- a/lib/libvarnishapi/vsm.c
+++ b/lib/libvarnishapi/vsm.c
@@ -35,8 +35,11 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <float.h>
+#include <math.h>
 #include <stdarg.h>
 #include <stdint.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -45,85 +48,76 @@
 #include "vas.h"
 #include "miniobj.h"
 
+#include "vav.h"
 #include "vin.h"
 #include "vsb.h"
 #include "vsm_priv.h"
 #include "vsc_priv.h"
+#include "vqueue.h"
 #include "vtim.h"
 
 #include "vapi/vsm.h"
 
-
 #ifndef MAP_HASSEMAPHORE
 #define MAP_HASSEMAPHORE 0 /* XXX Linux */
 #endif
 
 const struct vsm_valid VSM_invalid[1] = {{"invalid"}};
 const struct vsm_valid VSM_valid[1] = {{"valid"}};
-const struct vsm_valid VSM_similar[1] = {{"similar"}};
 
 /*--------------------------------------------------------------------*/
 
-struct vsc;
+struct vsm_set;
 
-struct vsm {
+struct vsm_seg {
 	unsigned		magic;
-#define VSM_MAGIC		0x6e3bd69b
-
-	struct vsb		*diag;
-
-	char			*dname;
-	char			*iname;
-
-	struct stat		fstat;
-
-	int			vsm_fd;
-	struct VSM_head		*head;
-	char			*b;
-	char			*e;
-
-	uint64_t		age_ok;
-	double			t_ok;
-
-	int			started;
-
-	struct vsc		*vsc;
+#define VSM_SEG_MAGIC		0xeb6c6dfd
+	VTAILQ_ENTRY(vsm_seg)	list;
+	int			markscan;
+	int			stale;
+	struct vsm_set		*set;
+	char			**av;
+	int			refs;
+	void			*b;
+	void			*e;
+	size_t			sz;
+	uintptr_t		serial;
 };
 
-/*--------------------------------------------------------------------*/
-
-struct vsm *
-VSM_New(void)
-{
-	struct vsm *vd;
+struct vsm_set {
+	unsigned		magic;
+#define VSM_SET_MAGIC		0xdee401b8
+	const char		*dname;
+	VTAILQ_HEAD(,vsm_seg)	segs;
+	VTAILQ_HEAD(,vsm_seg)	stale;
 
-	ALLOC_OBJ(vd, VSM_MAGIC);
-	if (vd == NULL)
-		return (vd);
+	int			dfd;
+	struct stat		dst;
 
-	vd->vsm_fd = -1;
+	int			fd;
+	struct stat		fst;
 
-	CHECK_OBJ_NOTNULL(vd, VSM_MAGIC);
-	return (vd);
-}
+	uintmax_t		id1, id2;
+};
 
-/*--------------------------------------------------------------------*/
+struct vsm {
+	unsigned		magic;
+#define VSM_MAGIC		0x6e3bd69b
 
-void
-VSM_SetVSC(struct vsm *vd, struct vsc *vsc)
-{
-	CHECK_OBJ_NOTNULL(vd, VSM_MAGIC);
+	struct vsb		*diag;
+	uintptr_t		serial;
 
-	vd->vsc = vsc;
-}
+	int			dfd;
+	struct stat		dst;
+	char			*dname;
 
-struct vsc *
-VSM_GetVSC(const struct vsm *vd)
-{
-	CHECK_OBJ_NOTNULL(vd, VSM_MAGIC);
+	struct vsc		*vsc;
+	struct vsm_set		*mgt;
+	struct vsm_set		*child;
 
-	return (vd->vsc);
-}
+	int			attached;
+	double			patience;
+};
 
 /*--------------------------------------------------------------------*/
 
@@ -145,344 +139,490 @@ vsm_diag(struct vsm *vd, const char *fmt, ...)
 	AZ(VSB_finish(vd->diag));
 	return (-1);
 }
+
 /*--------------------------------------------------------------------*/
 
-const char *
-VSM_Error(const struct vsm *vd)
+static struct vsm_set *
+vsm_newset(const char *dirname)
 {
+	struct vsm_set *vs;
+
+	ALLOC_OBJ(vs, VSM_SET_MAGIC);
+	AN(vs);
+	VTAILQ_INIT(&vs->segs);
+	VTAILQ_INIT(&vs->stale);
+	vs->dname = dirname;
+	vs->dfd = vs->fd = -1;
+	return (vs);
+}
 
-	CHECK_OBJ_NOTNULL(vd, VSM_MAGIC);
-
-	if (vd->diag == NULL)
-		return (NULL);
-	else
-		return (VSB_data(vd->diag));
+static void
+vsm_delset(struct vsm_set **p)
+{
+	struct vsm_set *vs;
+
+	AN(p);
+	vs = *p;
+	*p = NULL;
+	if (vs->fd >= 0)
+		closefd(&vs->fd);
+	if (vs->dfd >= 0)
+		closefd(&vs->dfd);
+	// XXX: delete segments
+	FREE_OBJ(vs);
 }
 
 /*--------------------------------------------------------------------*/
 
-void
-VSM_ResetError(struct vsm *vd)
+struct vsm *
+VSM_New(void)
 {
+	struct vsm *vd;
 
-	CHECK_OBJ_NOTNULL(vd, VSM_MAGIC);
+	ALLOC_OBJ(vd, VSM_MAGIC);
+	AN(vd);
 
-	if (vd->diag == NULL)
-		return;
-	VSB_destroy(&vd->diag);
+	vd->mgt = vsm_newset(VSM_MGT_DIRNAME);
+	vd->child = vsm_newset(VSM_CHILD_DIRNAME);
+	vd->dfd = -1;
+	vd->patience = 5;
+	return (vd);
 }
 
 /*--------------------------------------------------------------------*/
 
 int
-VSM_n_Arg(struct vsm *vd, const char *arg)
+VSM_Arg(struct vsm *vd, char flag, const char *arg)
 {
-	char *dname = NULL;
-	struct vsb *vsb;
+	char *p = NULL;
 
 	CHECK_OBJ_NOTNULL(vd, VSM_MAGIC);
-	AZ(vd->started);
-
-	if (vd->head)
-		return (vsm_diag(vd, "VSM_n_Arg: Already open"));
-	if (VIN_n_Arg(arg, &dname))
-		return (vsm_diag(vd, "Invalid instance name: %s",
-		    strerror(errno)));
-	AN(dname);
-	vsb = VSB_new_auto();
-	AN(vsb);
-	VSB_printf(vsb, "%s%s", dname, VSM_FILENAME);
-	AZ(VSB_finish(vsb));
 
-	REPLACE(vd->dname, dname);
-	REPLACE(vd->iname, VSB_data(vsb));
+	if (arg == NULL)
+		return (1);
+	switch (flag) {
+	case 't':
+		if (!strcasecmp(arg, "off")) {
+			vd->patience = -1;
+		} else {
+			vd->patience = strtod(arg, &p);
+			if ((p != NULL && *p != '\0') ||
+			    !isfinite(vd->patience) || vd->patience < 0)
+				return (vsm_diag(vd,
+				    "-t: Invalid argument: %s", arg));
+		}
+		break;
+	case 'n':
+		if (VIN_n_Arg(arg, &p))
+			return (vsm_diag(vd, "Invalid instance name: %s",
+			    strerror(errno)));
+		AN(p);
+		REPLACE(vd->dname, p);
+		free(p);
+		break;
+	default:
+		return (vsm_diag(vd, "Unknown VSM_Arg('%c')", flag));
+	}
+	return (1);
+}
 
-	VSB_destroy(&vsb);
-	free(dname);
+/*--------------------------------------------------------------------*/
 
-	return (1);
+void
+VSM_Destroy(struct vsm **vdp)
+{
+	struct vsm *vd;
+
+	AN(vdp);
+	vd = *vdp;
+	*vdp = NULL;
+
+	CHECK_OBJ_NOTNULL(vd, VSM_MAGIC);
+
+	if (vd->vsc != NULL)
+		VSC_Delete(vd->vsc);
+	VSM_ResetError(vd);
+	free(vd->dname);
+	vsm_delset(&vd->mgt);
+	vsm_delset(&vd->child);
+	if (vd->dfd >= 0)
+		closefd(&vd->dfd);
+	FREE_OBJ(vd);
 }
 
 /*--------------------------------------------------------------------*/
 
-int
-VSM_Start(struct vsm *vd, double patience, int progress)
+void
+VSM_SetVSC(struct vsm *vd, struct vsc *vsc)
 {
-	double t0;
-	int i, n = 0;
+	CHECK_OBJ_NOTNULL(vd, VSM_MAGIC);
+
+	vd->vsc = vsc;
+}
 
+struct vsc *
+VSM_GetVSC(const struct vsm *vd)
+{
 	CHECK_OBJ_NOTNULL(vd, VSM_MAGIC);
-	t0 = VTIM_mono();
-	while (1) {
-		VSM_ResetError(vd);
-		i = VSM_Open(vd);
-		AN(vd->dname);
-		if (patience <= 0. || i == 0) {
-			if (progress >= 0 && n > 4)
-				(void)write(progress, "\n", 1);
-			vd->started = 1;
-			return (i);
-		}
-		if (t0 + patience < VTIM_mono()) {
-			if (progress >= 0 && n > 4)
-				(void)write(progress, "\n", 1);
-			return (vsm_diag(vd,
-			    "Could not get hold of varnishd, is it running?"));
-		}
-		if (progress >= 0 && !(++n % 4))
-			(void)write(progress, ".", 1);
-		VTIM_sleep(.25);
-	}
+
+	return (vd->vsc);
 }
 
 /*--------------------------------------------------------------------*/
 
 const char *
-VSM_Name(struct vsm *vd)
+VSM_Error(const struct vsm *vd)
 {
-	struct vsm_fantom vt;
 
 	CHECK_OBJ_NOTNULL(vd, VSM_MAGIC);
-	if (VSM_Get(vd, &vt, "Arg", "-i"))
-		return (vt.b);
-	return ("");
+
+	if (vd->diag == NULL)
+		return ("No VSM error");
+	else
+		return (VSB_data(vd->diag));
 }
 
 /*--------------------------------------------------------------------*/
 
 void
-VSM_Destroy(struct vsm **vdp)
+VSM_ResetError(struct vsm *vd)
 {
-	struct vsm *vd;
-
-	AN(vdp);
-	vd = *vdp;
-	*vdp = NULL;
 
 	CHECK_OBJ_NOTNULL(vd, VSM_MAGIC);
 
-	VSM_Close(vd);
-	if (vd->vsc != NULL)
-		VSC_Delete(vd->vsc);
-	VSM_ResetError(vd);
-	free(vd->dname);
-	free(vd->iname);
-	FREE_OBJ(vd);
+	if (vd->diag == NULL)
+		return;
+	VSB_destroy(&vd->diag);
 }
 
 /*--------------------------------------------------------------------
- * The VSM open function
- *
- * Return:
- *	0 = success
- *	<0 = failure
- *
  */
 
-/*--------------------------------------------------------------------*/
+#define VSM_NUKE_ALL	(1U << 16)
 
-static void *ppp;
+static int
+vsm_cmp_av(char * const *a1, char * const *a2)
+{
 
-int
-VSM_Open(struct vsm *vd)
+	while (1) {
+		if (*a1 == NULL && *a2 == NULL)
+			return (0);
+		if (*a1 == NULL || *a2 == NULL)
+			return (1);
+		if (strcmp(*a1, *a2))
+			return (1);
+		a1++;
+		a2++;
+	}
+}
+
+static unsigned
+vsm_refresh_set2(struct vsm *vd, struct vsm_set *vs, struct vsb *vsb)
 {
-	int i;
-	struct VSM_head slh;
-	void *v;
+	unsigned retval = 0;
+	struct stat st;
+	char buf[BUFSIZ];
+	ssize_t sz;
+	int i, ac;
+	char *p, *e;
+	uintmax_t id1, id2;
+	char **av;
+	struct vsm_seg *vg, *vg2;
+
 
 	CHECK_OBJ_NOTNULL(vd, VSM_MAGIC);
+	CHECK_OBJ_NOTNULL(vs, VSM_SET_MAGIC);
+	if (vs->dfd >= 0) {
+		if (fstatat(vd->dfd, vs->dname, &st, AT_SYMLINK_NOFOLLOW)) {
+			closefd(&vs->dfd);
+			vs->id1 = vs->id2 = 0;
+			return (VSM_MGT_RESTARTED|VSM_NUKE_ALL);
+		}
+		if (st.st_ino != vs->dst.st_ino ||
+		    st.st_dev != vs->dst.st_dev ||
+		    st.st_mode != vs->dst.st_mode) {
+			closefd(&vs->dfd);
+			vs->id1 = vs->id2 = 0;
+		}
+	}
 
-	if (vd->head != NULL)
-		/* Already open */
-		return (0);
+	if (vs->dfd < 0) {
+		if (vs->fd >= 0)
+			closefd(&vs->fd);
+		vs->dfd = openat(vd->dfd, vs->dname, O_RDONLY);
+		retval |= VSM_MGT_RESTARTED;
+		if (vs->dfd < 0) {
+			vs->id1 = vs->id2 = 0;
+			return (retval|VSM_NUKE_ALL);
+		}
+		AZ(fstat(vs->dfd, &vs->dst));
+	}
 
-	if (vd->dname == NULL) {
-		/* Use default (hostname) */
-		i = VSM_n_Arg(vd, "");
-		if (i < 0)
-			return (i);
-		AN(vd->dname);
+	if (vs->fd >= 0 && (
+	    fstatat(vs->dfd, "_.index", &st, AT_SYMLINK_NOFOLLOW) ||
+	    st.st_ino != vs->fst.st_ino ||
+	    st.st_dev != vs->fst.st_dev ||
+	    st.st_mode != vs->fst.st_mode ||
+	    st.st_size != vs->fst.st_size ||
+	    st.st_nlink < 1 ||
+	    memcmp(&st.st_mtim, &vs->fst.st_mtim, sizeof st.st_mtim))) {
+		closefd(&vs->fd);
 	}
 
-	vd->vsm_fd = open(vd->iname, O_RDONLY);
-	if (vd->vsm_fd < 0)
-		return (vsm_diag(vd, "Cannot open %s: %s",
-		    vd->iname, strerror(errno)));
 
-	AZ(fstat(vd->vsm_fd, &vd->fstat));
-	if (!S_ISREG(vd->fstat.st_mode)) {
-		closefd(&vd->vsm_fd);
-		return (vsm_diag(vd, "%s is not a regular file", vd->iname));
-	}
+	if (vs->fd >= 0)
+		return (retval|VSM_MGT_RUNNING);
 
-	i = read(vd->vsm_fd, &slh, sizeof slh);
-	if (i != sizeof slh) {
-		closefd(&vd->vsm_fd);
-		return (vsm_diag(vd, "Cannot read %s: %s",
-		    vd->iname, strerror(errno)));
-	}
+	retval |= VSM_MGT_CHANGED;
+	vs->fd = openat(vs->dfd, "_.index", O_RDONLY);
+	if (vs->fd < 0)
+		return (retval|VSM_NUKE_ALL);
 
-	if (memcmp(slh.marker, VSM_HEAD_MARKER, sizeof slh.marker)) {
-		closefd(&vd->vsm_fd);
-		return (vsm_diag(vd, "Not a VSM file %s", vd->iname));
-	}
+	AZ(fstat(vs->fd, &vs->fst));
 
-	if (slh.alloc_seq == 0) {
-		closefd(&vd->vsm_fd);
-		return (vsm_diag(vd,
-		    "Abandoned VSM file (Varnish not running?) %s",
-		    vd->iname));
-	}
+	VSB_clear(vsb);
+	do {
+		sz = read(vs->fd, buf, sizeof buf);
+		if (sz > 0)
+			VSB_bcat(vsb, buf, sz);
+	} while (sz > 0);
+	AZ(VSB_finish(vsb));
+
+	vs->fst.st_size = VSB_len(vsb);
+
+	if (VSB_len(vsb) == 0)
+		return (retval|VSM_NUKE_ALL);
 
-	v = mmap(ppp, slh.shm_size,
-	    PROT_READ, MAP_SHARED|MAP_HASSEMAPHORE, vd->vsm_fd, 0);
-	if (v == MAP_FAILED)
-		v = mmap(NULL, slh.shm_size,
-		    PROT_READ, MAP_SHARED|MAP_HASSEMAPHORE, vd->vsm_fd, 0);
-	if (v == MAP_FAILED) {
-		closefd(&vd->vsm_fd);
-		return (vsm_diag(vd, "Cannot mmap %s: %s",
-		    vd->iname, strerror(errno)));
-	}
 	/*
-	 * Force failure of client depends on remapping at same address.
+	 * Examine the ident line
+	 * XXX: for now ignore that one of the ID's is a pid which could
+	 * XXX: be kill(pid,0)'ed for more rapid abandonment detection.
 	 */
-	ppp = (char*)v + getpagesize() * 1000;
+	i = sscanf(VSB_data(vsb), "# %ju %ju\n%n", &id1, &id2, &ac);
+	if (i != 2) {
+		retval |= VSM_MGT_RESTARTED;
+		return (retval|VSM_NUKE_ALL);
+	}
+	if (id1 != vs->id1 || id2 != vs->id2) {
+		retval |= VSM_MGT_RESTARTED;
+		vs->id1 = id1;
+		vs->id2 = id2;
+	}
+	p = VSB_data(vsb) + ac;
+	retval |= VSM_MGT_RUNNING;
+
+	VTAILQ_FOREACH(vg, &vs->segs, list)
+		vg->markscan = 0;
+
+	vg = VTAILQ_FIRST(&vs->segs);
+	while (p != NULL && *p != '\0') {
+		e = strchr(p, '\n');
+		if (e == NULL)
+			break;
+		*e = '\0';
+		av = VAV_Parse(p, &ac, 0);
+		p = e + 1;
+
+		if (av[0] != NULL || ac < 4 || ac > 5) {
+			(void)(vsm_diag(vd, "VSM_Open: bad index (%d/%s)",
+			    ac, av[0]));
+			VAV_Free(av);
+			break;
+		}
+		while (vg != NULL && !vsm_cmp_av(vg->av, av))
+			vg = VTAILQ_NEXT(vg, list);
+
+		if (vg != NULL) {
+			VAV_Free(av);
+			vg->markscan = 1;
+			vg = VTAILQ_NEXT(vg, list);
+		} else {
+			ALLOC_OBJ(vg2, VSM_SEG_MAGIC);
+			AN(vg2);
+			vg2->av = av;
+			vg2->set = vs;
+			vg2->markscan = 1;
+			vg2->serial = ++vd->serial;
+			VTAILQ_INSERT_TAIL(&vs->segs, vg2, list);
+		}
 
-	vd->head = v;
-	vd->b = v;
-	vd->e = vd->b + slh.shm_size;
-	vd->age_ok = vd->head->age;
-	vd->t_ok = VTIM_mono();
 
-	return (0);
+	}
+	return (retval);
 }
 
-/*--------------------------------------------------------------------*/
-
-int
-VSM_IsOpen(const struct vsm *vd)
+static unsigned
+vsm_refresh_set(struct vsm *vd, struct vsm_set *vs, struct vsb *vsb)
 {
-
-	CHECK_OBJ_NOTNULL(vd, VSM_MAGIC);
-	return (vd->head != NULL);
+	unsigned retval;
+	struct vsm_seg *vg, *vg2;
+
+	retval = vsm_refresh_set2(vd, vs, vsb);
+	if (retval & VSM_NUKE_ALL)
+		retval |= VSM_MGT_CHANGED;
+	VTAILQ_FOREACH_SAFE(vg, &vs->segs, list, vg2) {
+		if (!vg->markscan || (retval & VSM_NUKE_ALL)) {
+			VTAILQ_REMOVE(&vs->segs, vg, list);
+			if (vg->refs) {
+				vg->stale = 1;
+				VTAILQ_INSERT_TAIL(&vs->stale, vg, list);
+			} else {
+				// XXX: munmap
+				VAV_Free(vg->av);
+				FREE_OBJ(vg);
+			}
+		}
+	}
+	return (retval & ~VSM_NUKE_ALL);
 }
 
 /*--------------------------------------------------------------------*/
 
-void
-VSM_Close(struct vsm *vd)
+unsigned
+VSM_Status(struct vsm *vd)
 {
+	unsigned retval = 0, u;
+	struct stat st;
+	struct vsb *vsb;
 
 	CHECK_OBJ_NOTNULL(vd, VSM_MAGIC);
 
-	if (vd->head == NULL)
-		return;
+	/* See if the -n workdir changed */
+	if (vd->dfd >= 0) {
+		AZ(fstat(vd->dfd, &st));
+		if (st.st_ino != vd->dst.st_ino ||
+		    st.st_dev != vd->dst.st_dev ||
+		    st.st_mode != vd->dst.st_mode ||
+		    st.st_nlink == 0) {
+			closefd(&vd->dfd);
+			retval |= VSM_MGT_CHANGED;
+			retval |= VSM_WRK_CHANGED;
+		}
+	}
 
-	assert(vd->vsm_fd >= 0);
-	AZ(munmap((void*)vd->b, vd->e - vd->b));
-	vd->b = NULL;
-	vd->e = NULL;
-	vd->head = NULL;
-	closefd(&vd->vsm_fd);
+	/* Open workdir */
+	if (vd->dfd < 0) {
+		vd->dfd = open(vd->dname, O_RDONLY);
+		if (vd->dfd < 0)
+			(void)vsm_diag(vd, "VSM_Open: Cannot open workdir");
+		else
+			AZ(fstat(vd->dfd, &vd->dst));
+	}
+
+	vsb = VSB_new_auto();
+	AN(vsb);
+
+	u = vsm_refresh_set(vd, vd->mgt, vsb);
+	retval |= u;
+	if (u & VSM_MGT_RUNNING)
+		retval |= vsm_refresh_set(vd, vd->child, vsb) << 8;
+	VSB_destroy(&vsb);
+	return (retval);
 }
 
 /*--------------------------------------------------------------------*/
 
 int
-VSM_Abandoned(struct vsm *vd)
+VSM_Attach(struct vsm *vd, int progress)
 {
-	struct stat st;
-	double now;
+	double t0;
+	unsigned u;
+	int n = 0;
 
 	CHECK_OBJ_NOTNULL(vd, VSM_MAGIC);
 
-	if (vd->head == NULL)
-		/* Not open */
-		return (1);
-	if (!vd->head->alloc_seq)
-		/* Flag of abandonment set by mgt */
-		return (1);
-	if (vd->head->age < vd->age_ok)
-		/* Age going backwards */
-		return (1);
-	now = VTIM_mono();
-	if (vd->head->age == vd->age_ok && now - vd->t_ok > 2.) {
-		/* No age change for 2 seconds, stat the file */
-		if (stat(vd->iname, &st))
-			return (1);
-		if (st.st_dev != vd->fstat.st_dev)
-			return (1);
-		if (st.st_ino != vd->fstat.st_ino)
-			return (1);
-		vd->t_ok = now;
-	} else if (vd->head->age > vd->age_ok) {
-		/* It is aging, update timestamps */
-		vd->t_ok = now;
-		vd->age_ok = vd->head->age;
+	if (vd->patience < 0)
+		t0 = DBL_MAX;
+	else
+		t0 = VTIM_mono() + vd->patience;
+
+	AZ(vd->attached);
+	while (1) {
+		u = VSM_Status(vd);
+		VSM_ResetError(vd);
+		if (u & VSM_MGT_RUNNING) {
+			if (progress >= 0 && n > 4)
+				(void)write(progress, "\n", 1);
+			vd->attached = 1;
+			return (0);
+		}
+		if (t0 < VTIM_mono()) {
+			if (progress >= 0 && n > 4)
+				(void)write(progress, "\n", 1);
+			return (vsm_diag(vd,
+			    "Could not get hold of varnishd, is it running?"));
+		}
+		if (progress >= 0 && !(++n % 4))
+			(void)write(progress, ".", 1);
+		VTIM_sleep(.25);
 	}
-	return (0);
+}
+
+/*--------------------------------------------------------------------*/
+
+static struct vsm_seg *
+vsm_findseg(const struct vsm *vd, const struct vsm_fantom *vf)
+{
+	struct vsm_set *vs;
+	struct vsm_seg *vg;
+	uintptr_t x;
+
+	x = vf->priv;
+	vs = vd->mgt;
+	VTAILQ_FOREACH(vg, &vs->segs, list)
+		if (vg->serial == x)
+			return (vg);
+	VTAILQ_FOREACH(vg, &vs->stale, list)
+		if (vg->serial == x)
+			return (vg);
+	vs = vd->child;
+	VTAILQ_FOREACH(vg, &vs->segs, list)
+		if (vg->serial == x)
+			return (vg);
+	VTAILQ_FOREACH(vg, &vs->stale, list)
+		if (vg->serial == x)
+			return (vg);
+	return (NULL);
 }
 
 /*--------------------------------------------------------------------*/
 
 void
-VSM__iter0(struct vsm *vd, struct vsm_fantom *vf)
+VSM__iter0(const struct vsm *vd, struct vsm_fantom *vf)
 {
 
 	CHECK_OBJ_NOTNULL(vd, VSM_MAGIC);
 	AN(vf);
 
+	AN(vd->attached);
 	memset(vf, 0, sizeof *vf);
 }
 
 int
 VSM__itern(struct vsm *vd, struct vsm_fantom *vf)
 {
-	struct VSM_chunk *c = NULL;
+	struct vsm_seg *vg, *vg2;
 
 	CHECK_OBJ_NOTNULL(vd, VSM_MAGIC);
+	AN(vd->attached);
 	AN(vf);
 
-	if (!vd->head)
-		return (0);	/* Not open */
-	if (vd->head->alloc_seq == 0)
-		return (0);	/* abandoned VSM */
-	else if (vf->chunk != NULL) {
-		/* get next chunk */
-		if (vf->priv != vd->head->alloc_seq)
-			return (0); /* changes during iteration */
-		if (vf->chunk->len == 0)
-			return (0); /* free'd during iteration */
-		if (vf->chunk->next == 0)
-			return (0); /* last */
-		c = (struct VSM_chunk *)(void*)(vd->b + vf->chunk->next);
-		assert(c != vf->chunk);
-	} else if (vd->head->first == 0) {
-		return (0);	/* empty vsm */
+	if (vf->priv == 0) {
+		vg2 = VTAILQ_FIRST(&vd->mgt->segs);
 	} else {
-		/* get first chunk */
-		AZ(vf->chunk);
-		c = (struct VSM_chunk *)(void*)(vd->b + vd->head->first);
+		vg = vsm_findseg(vd, vf);
+		if (vg == NULL)
+			return (vsm_diag(vd, "VSM_FOREACH: inconsistency"));
+		vg2 = VTAILQ_NEXT(vg, list);
+		if (vg2 == NULL && vg->set == vd->mgt)
+			vg2 = VTAILQ_FIRST(&vd->child->segs);
 	}
-	AN(c);
-	if (memcmp(c->marker, VSM_CHUNK_MARKER, sizeof c->marker))
-		return (0);	/* XXX - assert? */
-
-	vf->chunk = c;
-	vf->priv = vd->head->alloc_seq;
-	vf->b = (void*)(vf->chunk + 1);
-	vf->e = (char*)vf->b + vf->chunk->len;
-
-	AZ(vf->chunk->class[sizeof vf->chunk->class - 1]);
-	REPLACE(vf->class, vf->chunk->class);
-
-	AZ(vf->chunk->type[sizeof vf->chunk->type - 1]);
-	REPLACE(vf->type, vf->chunk->type);
-
-	AZ(vf->chunk->ident[sizeof vf->chunk->ident - 1]);
-	REPLACE(vf->ident, vf->chunk->ident);
-
+	if (vg2 == NULL)
+		return (0);
+	memset(vf, 0, sizeof *vf);
+	vf->priv = vg2->serial;
+	vf->class = vg2->av[3];
+	vf->ident = vg2->av[4];
 	return (1);
 }
 
@@ -491,10 +631,66 @@ VSM__itern(struct vsm *vd, struct vsm_fantom *vf)
 int
 VSM_Map(struct vsm *vd, struct vsm_fantom *vf)
 {
+	struct vsm_seg *vg;
+	size_t sz, ps, len;
+	struct vsb *vsb;
+	int fd;
 
 	CHECK_OBJ_NOTNULL(vd, VSM_MAGIC);
+	AN(vd->attached);
 	AN(vf);
-	AN(vf->b);
+	vg = vsm_findseg(vd, vf);
+	if (vg == NULL)
+		return (vsm_diag(vd, "VSM_Map: bad fantom"));
+
+	assert(vg->serial == vf->priv);
+	assert(vg->av[3] == vf->class);
+	assert(vg->av[4] == vf->ident);
+
+	if (vg->b != NULL) {
+		assert(vg->refs > 0);
+		AN(vg->e);
+		vf->b = vg->b;
+		vf->e = vg->e;
+		vg->refs++;
+		return (0);
+	}
+
+	sz = strtoul(vg->av[2], NULL, 10);
+	assert(sz > 0);
+	ps = getpagesize();
+	len = RUP2(sz, ps);
+
+	vsb = VSB_new_auto();
+	AN(vsb);
+	VSB_printf(vsb, "%s/%s/%s", vd->dname, vg->set->dname, vg->av[1]);
+	AZ(VSB_finish(vsb));
+
+	fd = open(VSB_data(vsb), O_RDONLY);	// XXX: openat
+	if (fd < 0) {
+		VSB_destroy(&vsb);
+		return (vsm_diag(vd, "Could not open segment"));
+	}
+
+	vg->b = (void*)mmap(NULL, len,
+	    PROT_READ,
+	    MAP_HASSEMAPHORE | MAP_NOSYNC | MAP_SHARED,
+	    fd, 0);
+
+	VSB_destroy(&vsb);
+
+
+	closefd(&fd);
+	if (vg->b == MAP_FAILED)
+		return (vsm_diag(vd, "Could not mmap segment"));
+	vg->e = (char *)vg->b + sz;
+	vg->sz = len;
+
+	vf->b = vg->b;
+	vf->e = vg->e;
+
+	vg->refs++;
+
 	return (0);
 }
 
@@ -503,51 +699,61 @@ VSM_Map(struct vsm *vd, struct vsm_fantom *vf)
 int
 VSM_Unmap(struct vsm *vd, struct vsm_fantom *vf)
 {
+	struct vsm_seg *vg;
+	size_t sz, ps, len;
 
 	CHECK_OBJ_NOTNULL(vd, VSM_MAGIC);
+	AN(vd->attached);
 	AN(vf);
 	AN(vf->b);
+	vg = vsm_findseg(vd, vf);
+	if (vg == NULL)
+		return (vsm_diag(vd, "VSM_Map: bad fantom"));
+	assert(vg->refs > 0);
+	vg->refs--;
+	vf->b = NULL;
+	vf->e = NULL;
+	if (vg->refs > 0)
+		return(0);
+	if (vg->stale) {
+		VTAILQ_REMOVE(&vg->set->stale, vg, list);
+		VAV_Free(vg->av);
+		FREE_OBJ(vg);
+	} else {
+		sz = strtoul(vg->av[2], NULL, 10);
+		assert(sz > 0);
+		ps = getpagesize();
+		len = RUP2(sz, ps);
+		AZ(munmap(vg->b, len));
+		vg->b = vg->e = NULL;
+	}
 	return (0);
 }
 
 /*--------------------------------------------------------------------*/
 
 const struct vsm_valid *
-VSM_StillValid(struct vsm *vd, struct vsm_fantom *vf)
+VSM_StillValid(const struct vsm *vd, const struct vsm_fantom *vf)
 {
-	struct vsm_fantom f2;
+	struct vsm_seg *vg;
 
 	CHECK_OBJ_NOTNULL(vd, VSM_MAGIC);
 	AN(vf);
-	if (!vd->head)
-		return (VSM_invalid);
-	if (!vd->head->alloc_seq)
+	vg = vsm_findseg(vd, vf);
+	if (vg == NULL || vg->stale)
 		return (VSM_invalid);
-	if (vf->chunk == NULL)
-		return (VSM_invalid);
-	if (vf->priv == vd->head->alloc_seq)
-		return (VSM_valid);
-	VSM_FOREACH(&f2, vd) {
-		if (f2.chunk != vf->chunk || f2.b != vf->b || f2.e != vf->e)
-			continue;
-		if (strcmp(f2.class, vf->class))
-			continue;
-		if (strcmp(f2.type, vf->type))
-			continue;
-		if (strcmp(f2.ident, vf->ident))
-			continue;
-		vf->priv = vd->head->alloc_seq;
-		return (VSM_similar);
-	}
-	return (VSM_invalid);
+	return (VSM_valid);
 }
 
+/*--------------------------------------------------------------------*/
+
 int
 VSM_Get(struct vsm *vd, struct vsm_fantom *vf,
     const char *class, const char *ident)
 {
 
 	CHECK_OBJ_NOTNULL(vd, VSM_MAGIC);
+	AN(vd->attached);
 	VSM_FOREACH(vf, vd) {
 		if (strcmp(vf->class, class))
 			continue;
@@ -558,3 +764,30 @@ VSM_Get(struct vsm *vd, struct vsm_fantom *vf,
 	memset(vf, 0, sizeof *vf);
 	return (0);
 }
+
+/*--------------------------------------------------------------------*/
+
+char *
+VSM_Dup(struct vsm *vd, const char *class, const char *ident)
+{
+	struct vsm_fantom vf;
+	char *p = NULL;
+
+	CHECK_OBJ_NOTNULL(vd, VSM_MAGIC);
+	AN(vd->attached);
+	VSM_FOREACH(&vf, vd) {
+		if (strcmp(vf.class, class))
+			continue;
+		if (ident != NULL && strcmp(vf.ident, ident))
+			continue;
+		AZ(VSM_Map(vd, &vf));
+		AN(vf.b);
+		AN(vf.e);
+		p = malloc((char*)vf.e - (char*)vf.b);
+		AN(p);
+		memcpy(p, vf.b, (char*)vf.e - (char*)vf.b);
+		AZ(VSM_Unmap(vd, &vf));
+		break;
+	}
+	return (p);
+}
diff --git a/lib/libvarnishapi/vut.c b/lib/libvarnishapi/vut.c
index 68ded26..c4f0f99 100644
--- a/lib/libvarnishapi/vut.c
+++ b/lib/libvarnishapi/vut.c
@@ -51,7 +51,6 @@
 #include "vas.h"
 #include "miniobj.h"
 #include "vcs.h"
-#include "vnum.h"
 
 #include "vut.h"
 
@@ -180,16 +179,7 @@ VUT_Arg(int opt, const char *arg)
 		return (1);
 	case 't':
 		/* VSM connect timeout */
-		AN(arg);
-		if (!strcasecmp("off", arg))
-			VUT.t_arg = -1.;
-		else {
-			VUT.t_arg = VNUM(arg);
-			if (isnan(VUT.t_arg))
-				VUT_Error(1, "-t: Syntax error");
-			if (VUT.t_arg < 0.)
-				VUT_Error(1, "-t: Range error");
-		}
+		REPLACE(VUT.t_arg, arg);
 		return (1);
 	case 'V':
 		/* Print version number and exit */
@@ -225,7 +215,6 @@ VUT_Init(const char *progname, int argc, char * const *argv,
 	VUT.vsl = VSL_New();
 	AN(VUT.vsl);
 	VUT.k_arg = -1;
-	VUT.t_arg = 5.;
 }
 
 void
@@ -253,23 +242,20 @@ VUT_Setup(void)
 		c = VSL_CursorFile(VUT.vsl, VUT.r_arg, 0);
 		if (c == NULL)
 			VUT_Error(1, "%s", VSL_Error(VUT.vsl));
+		VSLQ_SetCursor(VUT.vslq, &c);
+		AZ(c);
 	} else {
 		VUT.vsm = VSM_New();
 		AN(VUT.vsm);
-		if (VUT.n_arg && VSM_n_Arg(VUT.vsm, VUT.n_arg) <= 0)
+		if (VUT.n_arg && VSM_Arg(VUT.vsm, 'n', VUT.n_arg) <= 0)
+			VUT_Error(1, "%s", VSM_Error(VUT.vsm));
+		if (VUT.t_arg && VSM_Arg(VUT.vsm, 't', VUT.t_arg) <= 0)
 			VUT_Error(1, "%s", VSM_Error(VUT.vsm));
-		if (VSM_Start(VUT.vsm, VUT.t_arg, STDERR_FILENO))
+		if (VSM_Attach(VUT.vsm, STDERR_FILENO))
 			VUT_Error(1, "VSM: %s", VSM_Error(VUT.vsm));
-		c = VSL_CursorVSM(VUT.vsl, VUT.vsm,
-		    (VUT.d_opt ? VSL_COPT_TAILSTOP : VSL_COPT_TAIL)
-		    | VSL_COPT_BATCH);
-		if (c == 0)
-			VUT_Error(1, "VSL: %s", VSL_Error(VUT.vsl));
+		// Cursor is handled in VUT_Main()
 	}
 
-	VSLQ_SetCursor(VUT.vslq, &c);
-	AZ(c);
-
 	/* Signal handlers */
 	(void)signal(SIGHUP, vut_sighup);
 	(void)signal(SIGINT, vut_sigint);
@@ -322,6 +308,7 @@ VUT_Main(void)
 {
 	struct VSL_cursor *c;
 	int i = -1;
+	int hascursor = -1;
 
 	AN(VUT.vslq);
 
@@ -340,25 +327,32 @@ VUT_Main(void)
 			(void)VSLQ_Flush(VUT.vslq, vut_dispatch, NULL);
 		}
 
-		if (VUT.vsm != NULL && !VSM_IsOpen(VUT.vsm)) {
+		// We must repeatedly call VSM_Status() when !hascursor
+		// to make VSM discover our segment.
+		if (VUT.vsm != NULL &&
+		    (VSM_Status(VUT.vsm) & VSM_WRK_RESTARTED)) {
+			if (hascursor < 1) {
+				fprintf(stderr, "Log abandonned\n");
+				VSLQ_SetCursor(VUT.vslq, NULL);
+				hascursor = 0;
+			}
+		}
+		if (VUT.vsm != NULL && hascursor < 1) {
 			/* Reconnect VSM */
 			AZ(VUT.r_arg);
 			VTIM_sleep(0.1);
-			if (VSM_Open(VUT.vsm)) {
-				VSM_ResetError(VUT.vsm);
-				continue;
-			}
 			c = VSL_CursorVSM(VUT.vsl, VUT.vsm,
 			    (VUT.d_opt ? VSL_COPT_TAILSTOP : VSL_COPT_TAIL)
 			    | VSL_COPT_BATCH);
 			if (c == NULL) {
 				VSL_ResetError(VUT.vsl);
-				VSM_Close(VUT.vsm);
 				continue;
 			}
+			if (hascursor >= 0)
+				fprintf(stderr, "Log reacquired\n");
+			hascursor = 1;
 			VSLQ_SetCursor(VUT.vslq, &c);
 			AZ(c);
-			fprintf(stderr, "Log reacquired\n");
 		}
 
 		i = VSLQ_Dispatch(VUT.vslq, vut_dispatch, NULL);
@@ -386,14 +380,14 @@ VUT_Main(void)
 
 		(void)VSLQ_Flush(VUT.vslq, vut_dispatch, NULL);
 
-		if (i == -2)
+		if (i == -2) {
 			/* Abandoned */
 			fprintf(stderr, "Log abandoned\n");
-		else if (i < -2)
+			VSLQ_SetCursor(VUT.vslq, NULL);
+			hascursor = 0;
+		} else if (i < -2)
 			/* Overrun */
 			fprintf(stderr, "Log overrun\n");
-
-		VSM_Close(VUT.vsm);
 	}
 
 	return (i);



More information about the varnish-commit mailing list