[master] dd90a88 Minimal locking to prevent two backend fetches from trying to get at uncached req.body at the same time.

Poul-Henning Kamp phk at varnish-cache.org
Mon Jul 8 14:33:57 CEST 2013


commit dd90a88b1c64b5880f2c4fcd83d3a8d3973d182c
Author: Poul-Henning Kamp <phk at FreeBSD.org>
Date:   Mon Jul 8 12:33:26 2013 +0000

    Minimal locking to prevent two backend fetches from trying to
    get at uncached req.body at the same time.

diff --git a/bin/varnishd/cache/cache.h b/bin/varnishd/cache/cache.h
index b673eb1..eaaffb7 100644
--- a/bin/varnishd/cache/cache.h
+++ b/bin/varnishd/cache/cache.h
@@ -620,7 +620,7 @@ struct req {
 	enum req_step		req_step;
 	VTAILQ_ENTRY(req)	w_list;
 
-	enum req_body_state_e	req_body_status;
+	volatile enum req_body_state_e	req_body_status;
 	struct storagehead	body;
 
 	struct {
diff --git a/bin/varnishd/cache/cache_http1_fsm.c b/bin/varnishd/cache/cache_http1_fsm.c
index d4a296a..9f150ea 100644
--- a/bin/varnishd/cache/cache_http1_fsm.c
+++ b/bin/varnishd/cache/cache_http1_fsm.c
@@ -490,9 +490,27 @@ HTTP1_IterateReqBody(struct req *req, req_body_iter_f *func, void *priv)
 		return (0);
 	case REQ_BODY_PRESENT:
 		break;
+	case REQ_BODY_DONE:
+	case REQ_BODY_TAKEN:
+		VSLb(req->vsl, SLT_VCL_Error,
+		    "Uncached req.body can only be consumed once.");
+		return (-1);
 	default:
 		WRONG("Wrong req_body_status in HTTP1_IterateReqBody()");
 	}
+	Lck_Lock(&req->sp->mtx);
+	if (req->req_body_status == REQ_BODY_PRESENT) {
+		req->req_body_status = REQ_BODY_TAKEN;
+		i = 0;
+	} else
+		i = -1;
+	Lck_Unlock(&req->sp->mtx);
+	if (i) {
+		VSLb(req->vsl, SLT_VCL_Error,
+		    "Multiple attempts to access non-cached req.body");
+		return (i);
+	}
+	
 	do {
 		l = http1_iter_req_body(req, buf, sizeof buf);
 		if (l < 0) {
@@ -531,11 +549,16 @@ HTTP1_DiscardReqBody(struct req *req)
 
 	if (req->req_body_status == REQ_BODY_DONE)
 		return(0);
+	if (req->req_body_status == REQ_BODY_TAKEN)
+		return(0);
 	return(HTTP1_IterateReqBody(req, httpq_req_body_discard, NULL));
 }
 
 /*----------------------------------------------------------------------
  * Cache the req.body if it is smaller than the given size
+ *
+ * This function must be called before any backend fetches are kicked
+ * off to prevent parallelism.
  */
 
 int
diff --git a/bin/varnishd/cache/cache_vrt.c b/bin/varnishd/cache/cache_vrt.c
index 3009e90..c63be80 100644
--- a/bin/varnishd/cache/cache_vrt.c
+++ b/bin/varnishd/cache/cache_vrt.c
@@ -494,6 +494,11 @@ VRT_CacheReqBody(const struct vrt_ctx *ctx, long long maxsize)
 
 	CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
 	CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC);
+	if (ctx->method != VCL_MET_RECV) {
+		VSLb(ctx->vsl, SLT_VCL_Error,
+		    "req.body can only be cached in vcl_recv{}");
+		return (0);
+	}
 	return (HTTP1_CacheReqBody(ctx->req, maxsize));
 }
 



More information about the varnish-commit mailing list