[master] c3693c9bc vcl: Allocation-free vcl.discard

Nils Goroll nils.goroll at uplex.de
Tue Jan 12 15:34:07 UTC 2021


commit c3693c9bcf1581aadc63cf6e7bf3c66f48f9f6be
Author: Dridi Boukelmoune <dridi.boukelmoune at gmail.com>
Date:   Tue Dec 8 11:41:28 2020 +0100

    vcl: Allocation-free vcl.discard
    
    This replaces the previous mapping of VCLs to discard with a mark and
    sweep operation where a different head is used to track matching VCLs.
    
    To discard VCLs in topological order, we can simply discard dependencies
    recursively before discarding a given VCL.

diff --git a/bin/varnishd/mgt/mgt_vcl.c b/bin/varnishd/mgt/mgt_vcl.c
index 5b8a57115..84ac7fada 100644
--- a/bin/varnishd/mgt/mgt_vcl.c
+++ b/bin/varnishd/mgt/mgt_vcl.c
@@ -657,14 +657,17 @@ mcf_vcl_use(struct cli *cli, const char * const *av, void *priv)
 }
 
 static void
-mgt_vcl_discard(struct cli *cli, struct vclprog *vp)
+mgt_vcl_discard(struct cli *cli, struct vclproghead *vh, struct vclprog *vp)
 {
 	char *p = NULL;
 	unsigned status;
 
 	AN(vp);
+	AN(vp->discard);
 	assert(vp != active_vcl);
-	assert(VTAILQ_EMPTY(&vp->dto));
+
+	while (!VTAILQ_EMPTY(&vp->dto))
+		mgt_vcl_discard(cli, vh, VTAILQ_FIRST(&vp->dto)->from);
 
 	if (mcf_is_label(vp)) {
 		AN(vp->warm);
@@ -678,9 +681,39 @@ mgt_vcl_discard(struct cli *cli, struct vclprog *vp)
 			assert(status == CLIS_OK || status == CLIS_COMMS);
 		free(p);
 	}
+	VTAILQ_REMOVE(vh, vp, discard_list);
 	mgt_vcl_del(vp);
 }
 
+static int
+mgt_vcl_discard_mark(struct cli *cli, struct vclproghead *vh, const char *name)
+{
+	struct vclprog *vp;
+	unsigned marked = 0;
+
+	VTAILQ_FOREACH(vp, &vclhead, list) {
+		if (strcmp(name, vp->name))
+			continue;
+		if (vp == active_vcl) {
+			VCLI_SetResult(cli, CLIS_CANT);
+			VCLI_Out(cli, "Cannot discard active VCL program %s\n",
+			    vp->name);
+			return (-1);
+		}
+		if (!vp->discard)
+			VTAILQ_INSERT_TAIL(vh, vp, discard_list);
+		vp->discard = 1;
+		marked++;
+	}
+
+	if (marked == 0) {
+		VCLI_SetResult(cli, CLIS_PARAM);
+		VCLI_Out(cli, "No VCL named %s known\n", name);
+	}
+
+	return (marked);
+}
+
 static void
 mgt_vcl_discard_depfail(struct cli *cli, struct vclprog *vp)
 {
@@ -710,113 +743,55 @@ mgt_vcl_discard_depfail(struct cli *cli, struct vclprog *vp)
 	}
 }
 
-static struct vclprog **
-mgt_vcl_discard_depcheck(struct cli *cli, const char * const *names,
-    unsigned *lp)
+static int
+mgt_vcl_discard_depcheck(struct cli *cli, struct vclproghead *vh)
 {
-	struct vclprog **res, *vp;
+	struct vclprog *vp;
 	struct vcldep *vd;
-	const char * const *s;
-	unsigned i, j, l;
-
-	l = 0;
-	s = names;
-	while (*s != NULL) {
-		l++;
-		s++;
-	}
-	AN(l);
-
-	res = calloc(l, sizeof *res);
-	AN(res);
-
-	/* NB: Build a list of VCL programs and labels to discard. A null
-	 * pointer in the array is a VCL program that no longer needs to be
-	 * discarded.
-	 */
-	for (i = 0; i < l; i++) {
-		vp = mcf_find_vcl(cli, names[i]);
-		if (vp == NULL)
-			break;
-		if (vp == active_vcl) {
-			VCLI_SetResult(cli, CLIS_CANT);
-			VCLI_Out(cli, "Cannot discard active VCL program %s\n",
-			    vp->name);
-			break;
-		}
-		for (j = 0; j < i; j++)
-			if (vp == res[j])
-				break;
-		if (j < i)
-			continue; /* skip duplicates */
-		//lint -e{661}
-		res[i] = vp;
-	}
 
-	if (i < l) {
-		free(res);
-		return (NULL);
+	VTAILQ_FOREACH(vp, vh, list) {
+		VTAILQ_FOREACH(vd, &vp->dto, lto)
+			if (!vd->from->discard) {
+				mgt_vcl_discard_depfail(cli, vp);
+				return (-1);
+			}
 	}
 
-	/* NB: Check that all direct dependent VCL programs and labels belong
-	 * to the list of VCLs to be discarded. This mechanically ensures that
-	 * indirect dependent VCLs also belong.
-	 */
-	for (i = 0; i < l; i++) {
-		if (res[i] == NULL || VTAILQ_EMPTY(&res[i]->dto))
-			continue;
-		VTAILQ_FOREACH(vd, &res[i]->dto, lto) {
-			for (j = 0; j < l; j++)
-				if (res[j] == vd->from)
-					break;
-			if (j == l)
-				break;
-		}
-		if (vd != NULL)
-			break;
-	}
+	return (0);
+}
 
-	if (i < l) {
-		mgt_vcl_discard_depfail(cli, res[i]);
-		free(res);
-		return (NULL);
-	}
+static void
+mgt_vcl_discard_clear(struct vclproghead *vh)
+{
+	struct vclprog *vp, *vp2;
 
-	*lp = l;
-	return (res);
+	VTAILQ_FOREACH_SAFE(vp, vh, discard_list, vp2) {
+		AN(vp->discard);
+		vp->discard = 0;
+		VTAILQ_REMOVE(vh, vp, discard_list);
+	}
 }
 
 static void v_matchproto_(cli_func_t)
 mcf_vcl_discard(struct cli *cli, const char * const *av, void *priv)
 {
-	struct vclprog **vp;
-	unsigned i, l, done;
+	struct vclproghead vh[1];
 
 	(void)priv;
-	vp = mgt_vcl_discard_depcheck(cli, av + 2, &l);
-	if (vp == NULL)
-		return;
 
-	/* NB: discard VCLs in topological order. At this point it
-	 * can only succeed, but the loop ensures that it eventually
-	 * completes even if the dependency check is ever broken.
-	 */
-	AN(l);
-	do {
-		done = 0;
-		for (i = 0; i < l; i++) {
-			if (vp[i] == NULL || vp[i]->nto > 0)
-				continue;
-			mgt_vcl_discard(cli, vp[i]);
-			vp[i] = NULL;
-			done++;
+	VTAILQ_INIT(vh);
+	for (av += 2; *av != NULL; av++) {
+		if (mgt_vcl_discard_mark(cli, vh, *av) <= 0) {
+			mgt_vcl_discard_clear(vh);
+			break;
 		}
-	} while (done > 0);
+	}
+
+	if (mgt_vcl_discard_depcheck(cli, vh) != 0)
+		mgt_vcl_discard_clear(vh);
 
-	for (i = 0; i < l; i++)
-		if (vp[i] != NULL)
-			WRONG(vp[i]->name);
-	free(vp);
+	while (!VTAILQ_EMPTY(vh))
+		mgt_vcl_discard(cli, vh, VTAILQ_FIRST(vh));
 }
 
 static void v_matchproto_(cli_func_t)
diff --git a/bin/varnishd/mgt/mgt_vcl.h b/bin/varnishd/mgt/mgt_vcl.h
index 6f3b7d4bf..b82b19413 100644
--- a/bin/varnishd/mgt/mgt_vcl.h
+++ b/bin/varnishd/mgt/mgt_vcl.h
@@ -68,6 +68,8 @@ struct vclprog {
 	int			nto;
 	int			loaded;
 	VTAILQ_HEAD(, vmoddep)	vmods;
+	unsigned		discard;
+	VTAILQ_ENTRY(vclprog)	discard_list;
 };
 
 struct vmodfile {


More information about the varnish-commit mailing list