Commit 573c98a7 authored by Poul-Henning Kamp's avatar Poul-Henning Kamp

Make the number of HTTP headers a parameter, and allocate only as

many as we need for the objects.

This passes the tests, but performance tests have not been performed
and there may be alignment issues on platforms with strict alignment.

If this works out, the benefits will include:

A) no need to recompile if you need more than 64 HTTP headers.

B) You do not waste a kilobyte per object, on space for headers you do not have.

Reports very welcome!



git-svn-id: http://www.varnish-cache.org/svn/trunk/varnish-cache@4456 d4fa192b-c00b-0410-8231-f00ffab90ce4
parent 3a61b691
......@@ -71,16 +71,8 @@ enum {
HTTP_HDR_RESPONSE,
/* HTTP header lines */
HTTP_HDR_FIRST,
HTTP_HDR_MAX = HTTP_HDR_MAX_VAL
};
/* Note: intentionally not IOV_MAX unless it has to be */
#if (IOV_MAX < (HTTP_HDR_MAX_VAL * 2))
# define MAX_IOVS IOV_MAX
#else
# define MAX_IOVS (HTTP_HDR_MAX_VAL * 2)
#endif
struct cli;
struct vsb;
struct sess;
......@@ -154,11 +146,12 @@ struct http {
int status;
double protover;
txt hd[HTTP_HDR_MAX];
unsigned char hdf[HTTP_HDR_MAX];
unsigned shd; /* Size of hd space */
txt *hd;
unsigned char *hdf;
#define HDF_FILTER (1 << 0) /* Filtered by Connection */
#define HDF_COPY (1 << 1) /* Copy this field */
unsigned nhd;
unsigned nhd; /* Next free hd */
};
/*--------------------------------------------------------------------
......@@ -214,8 +207,9 @@ struct worker {
int *wfd;
unsigned werr; /* valid after WRK_Flush() */
struct iovec iov[MAX_IOVS];
int niov;
struct iovec *iov;
unsigned siov;
unsigned niov;
ssize_t liov;
struct VCL_conf *vcl;
......@@ -227,7 +221,7 @@ struct worker {
struct http_conn htc[1];
struct ws ws[1];
struct http http[3];
struct http *http[3];
struct http *bereq;
struct http *beresp1;
struct http *beresp;
......@@ -341,7 +335,7 @@ struct object {
double last_modified;
double last_lru;
struct http http[1];
struct http *http;
VTAILQ_HEAD(, storage) store;
......@@ -513,8 +507,10 @@ int FetchReqBody(struct sess *sp);
void Fetch_Init(void);
/* cache_http.c */
unsigned HTTP_estimate(unsigned nhttp);
struct http *HTTP_create(void *p, unsigned nhttp);
const char *http_StatusMessage(unsigned);
unsigned http_EstimateWS(const struct http *fm, unsigned how);
unsigned http_EstimateWS(const struct http *fm, unsigned how, unsigned *nhd);
void HTTP_Init(void);
void http_ClrHeader(struct http *to);
unsigned http_Write(struct worker *w, const struct http *hp, int resp);
......
......@@ -184,7 +184,7 @@ cnt_deliver(struct sess *sp)
sp->obj->last_lru = sp->t_resp; /* XXX: locking ? */
sp->obj->last_use = sp->t_resp; /* XXX: locking ? */
}
sp->wrk->resp = &sp->wrk->http[2];
sp->wrk->resp = sp->wrk->http[2];
http_Setup(sp->wrk->resp, sp->wrk->ws);
RES_BuildHttp(sp);
VCL_deliver_method(sp);
......@@ -336,7 +336,7 @@ cnt_error(struct sess *sp)
if (sp->obj == NULL) {
HSH_Prealloc(sp);
sp->wrk->cacheable = 0;
sp->obj = STV_NewObject(sp, 0, 0);
sp->obj = STV_NewObject(sp, 0, 0, params->http_headers);
sp->obj->xid = sp->xid;
sp->obj->entered = sp->t_req;
} else {
......@@ -421,7 +421,7 @@ cnt_fetch(struct sess *sp)
int i;
struct http *hp, *hp2;
char *b;
unsigned handling, l;
unsigned handling, l, nhttp;
int varyl = 0;
struct vsb *vary = NULL;
......@@ -432,7 +432,7 @@ cnt_fetch(struct sess *sp)
AZ(sp->vbe);
/* sp->wrk->http[0] is (still) bereq */
sp->wrk->beresp = &sp->wrk->http[1];
sp->wrk->beresp = sp->wrk->http[1];
http_Setup(sp->wrk->beresp, sp->wrk->ws);
i = FetchHdr(sp);
......@@ -441,7 +441,7 @@ cnt_fetch(struct sess *sp)
* Save a copy before it might get mangled in VCL. When it comes to
* dealing with the body, we want to see the unadultered headers.
*/
sp->wrk->beresp1 = &sp->wrk->http[2];
sp->wrk->beresp1 = sp->wrk->http[2];
*sp->wrk->beresp1 = *sp->wrk->beresp;
if (i) {
......@@ -529,7 +529,7 @@ cnt_fetch(struct sess *sp)
AZ(sp->objcore);
}
l = http_EstimateWS(sp->wrk->beresp, HTTPH_A_INS);
l = http_EstimateWS(sp->wrk->beresp, HTTPH_A_INS, &nhttp);
if (vary != NULL)
l += varyl;
......@@ -542,7 +542,7 @@ cnt_fetch(struct sess *sp)
* XXX: also.
*/
sp->obj = STV_NewObject(sp, l, sp->wrk->ttl);
sp->obj = STV_NewObject(sp, l, sp->wrk->ttl, nhttp);
if (sp->objhead != NULL) {
CHECK_OBJ_NOTNULL(sp->objhead, OBJHEAD_MAGIC);
......@@ -861,7 +861,7 @@ cnt_miss(struct sess *sp)
AN(sp->objcore);
AN(sp->objhead);
WS_Reset(sp->wrk->ws, NULL);
sp->wrk->bereq = &sp->wrk->http[0];
sp->wrk->bereq = sp->wrk->http[0];
http_Setup(sp->wrk->bereq, sp->wrk->ws);
http_FilterHeader(sp, HTTPH_R_FETCH);
VCL_miss_method(sp);
......@@ -926,7 +926,7 @@ cnt_pass(struct sess *sp)
AZ(sp->obj);
WS_Reset(sp->wrk->ws, NULL);
sp->wrk->bereq = &sp->wrk->http[0];
sp->wrk->bereq = sp->wrk->http[0];
http_Setup(sp->wrk->bereq, sp->wrk->ws);
http_FilterHeader(sp, HTTPH_R_PASS);
......@@ -976,7 +976,7 @@ cnt_pipe(struct sess *sp)
sp->acct_req.pipe++;
WS_Reset(sp->wrk->ws, NULL);
sp->wrk->bereq = &sp->wrk->http[0];
sp->wrk->bereq = sp->wrk->http[0];
http_Setup(sp->wrk->bereq, sp->wrk->ws);
http_FilterHeader(sp, HTTPH_R_PIPE);
......
......@@ -117,14 +117,49 @@ http_StatusMessage(unsigned status)
/*--------------------------------------------------------------------*/
unsigned
HTTP_estimate(unsigned nhttp)
{
/* XXX: We trust the structs to size-aligned as necessary */
return (sizeof (struct http) + (sizeof (txt) + 1) * nhttp);
}
struct http *
HTTP_create(void *p, unsigned nhttp)
{
struct http *hp;
hp = p;
hp->magic = HTTP_MAGIC;
hp->hd = (void*)(hp + 1);
hp->shd = nhttp;
hp->hdf = (void*)(hp->hd + nhttp);
return (hp);
}
/*--------------------------------------------------------------------*/
void
http_Setup(struct http *hp, struct ws *ws)
{
unsigned shd;
txt *hd;
unsigned char *hdf;
/* XXX: This is not elegant, is it efficient ? */
shd = hp->shd;
hd = hp->hd;
hdf = hp->hdf;
memset(hp, 0, sizeof *hp);
memset(hd, 0, sizeof *hd * shd);
memset(hdf, 0, sizeof *hdf * shd);
hp->magic = HTTP_MAGIC;
hp->ws = ws;
hp->nhd = HTTP_HDR_FIRST;
hp->shd = shd;
hp->hd = hd;
hp->hdf = hdf;
}
/*--------------------------------------------------------------------*/
......@@ -367,7 +402,7 @@ http_dissect_hdrs(struct worker *w, struct http *hp, int fd, char *p, txt t)
q--;
*q = '\0';
if (hp->nhd < HTTP_HDR_MAX) {
if (hp->nhd < hp->shd) {
hp->hdf[hp->nhd] = 0;
hp->hd[hp->nhd].b = p;
hp->hd[hp->nhd].e = q;
......@@ -556,7 +591,7 @@ void
http_SetH(struct http *to, unsigned n, const char *fm)
{
assert(n < HTTP_HDR_MAX);
assert(n < to->shd);
AN(fm);
to->hd[n].b = TRUST_ME(fm);
to->hd[n].e = strchr(to->hd[n].b, '\0');
......@@ -567,7 +602,7 @@ static void
http_copyh(struct http *to, const struct http *fm, unsigned n)
{
assert(n < HTTP_HDR_MAX);
assert(n < to->shd);
Tcheck(fm->hd[n]);
to->hd[n] = fm->hd[n];
to->hdf[n] = fm->hdf[n];
......@@ -612,9 +647,9 @@ http_copyheader(struct worker *w, int fd, struct http *to,
CHECK_OBJ_NOTNULL(fm, HTTP_MAGIC);
CHECK_OBJ_NOTNULL(to, HTTP_MAGIC);
assert(n < HTTP_HDR_MAX);
assert(n < to->shd);
Tcheck(fm->hd[n]);
if (to->nhd < HTTP_HDR_MAX) {
if (to->nhd < to->shd) {
to->hd[to->nhd] = fm->hd[n];
to->hdf[to->nhd] = 0;
to->nhd++;
......@@ -630,11 +665,12 @@ http_copyheader(struct worker *w, int fd, struct http *to,
*/
unsigned
http_EstimateWS(const struct http *fm, unsigned how)
http_EstimateWS(const struct http *fm, unsigned how, unsigned *nhd)
{
unsigned u, l;
l = 0;
*nhd = HTTP_HDR_FIRST;
CHECK_OBJ_NOTNULL(fm, HTTP_MAGIC);
for (u = 0; u < fm->nhd; u++) {
if (fm->hd[u].b == NULL)
......@@ -647,6 +683,7 @@ http_EstimateWS(const struct http *fm, unsigned how)
#include "http_headers.h"
#undef HTTPH
l += Tlen(fm->hd[u]) + 1;
(*nhd)++;
// fm->hdf[u] |= HDF_COPY;
}
return (l);
......@@ -755,7 +792,7 @@ http_SetHeader(struct worker *w, int fd, struct http *to, const char *hdr)
{
CHECK_OBJ_NOTNULL(to, HTTP_MAGIC);
if (to->nhd >= HTTP_HDR_MAX) {
if (to->nhd >= to->shd) {
VSL_stats->losthdr++;
WSL(w, SLT_LostHeader, fd, "%s", hdr);
return;
......@@ -827,7 +864,7 @@ http_PrintfHeader(struct worker *w, int fd, struct http *to,
va_start(ap, fmt);
n = vsnprintf(to->ws->f, l, fmt, ap);
va_end(ap);
if (n + 1 >= l || to->nhd >= HTTP_HDR_MAX) {
if (n + 1 >= l || to->nhd >= to->shd) {
VSL_stats->losthdr++;
WSL(w, SLT_LostHeader, fd, "%s", to->ws->f);
WS_Release(to->ws, 0);
......
......@@ -117,11 +117,16 @@ WRK_SumStat(struct worker *w)
/*--------------------------------------------------------------------*/
static void *
wrk_thread_real(struct wq *qp, unsigned shm_workspace, unsigned sess_workspace)
wrk_thread_real(struct wq *qp, unsigned shm_workspace, unsigned sess_workspace,
unsigned nhttp, unsigned http_space, unsigned siov)
{
struct worker *w, ww;
unsigned char wlog[shm_workspace];
unsigned char ws[sess_workspace];
unsigned char http0[http_space];
unsigned char http1[http_space];
unsigned char http2[http_space];
struct iovec iov[siov];
struct SHA256Context sha256;
int stats_clean;
......@@ -133,6 +138,11 @@ wrk_thread_real(struct wq *qp, unsigned shm_workspace, unsigned sess_workspace)
w->wlb = w->wlp = wlog;
w->wle = wlog + sizeof wlog;
w->sha256ctx = &sha256;
w->http[0] = HTTP_create(http0, nhttp);
w->http[1] = HTTP_create(http1, nhttp);
w->http[2] = HTTP_create(http2, nhttp);
w->iov = iov;
w->siov = siov;
AZ(pthread_cond_init(&w->cond, NULL));
WS_Init(w->ws, "wrk", ws, sess_workspace);
......@@ -202,12 +212,19 @@ static void *
wrk_thread(void *priv)
{
struct wq *qp;
volatile unsigned nhttp;
unsigned siov;
CAST_OBJ_NOTNULL(qp, priv, WQ_MAGIC);
/* We need to snapshot these two for consistency */
nhttp = params->http_headers;
siov = nhttp * 2; /* XXX param ? */
if (siov > IOV_MAX)
siov = IOV_MAX;
return (wrk_thread_real(qp,
params->shm_workspace,
params->sess_workspace));
params->sess_workspace,
nhttp, HTTP_estimate(nhttp), siov));
}
/*--------------------------------------------------------------------
......
......@@ -60,8 +60,9 @@ struct sessmem {
#define SESSMEM_MAGIC 0x555859c5
struct sess sess;
struct http http[2];
unsigned workspace;
void *wsp;
struct http *http[2];
VTAILQ_ENTRY(sessmem) list;
struct sockaddr_storage sockaddr[2];
};
......@@ -99,25 +100,40 @@ static struct sess *
ses_setup(struct sessmem *sm, const struct sockaddr *addr, unsigned len)
{
struct sess *sp;
volatile unsigned u;
unsigned char *p;
volatile unsigned nws;
volatile unsigned nhttp;
unsigned l, hl;
if (sm == NULL) {
if (VSL_stats->n_sess_mem >= params->max_sess)
return (NULL);
/*
* It is not necessary to lock mem_workspace, but we
* need to cache it locally, to make sure we get a
* consistent view of it.
* It is not necessary to lock these, but we need to
* cache them locally, to make sure we get a consistent
* view of the value.
*/
u = params->sess_workspace;
sm = malloc(sizeof *sm + u);
if (sm == NULL)
nws = params->sess_workspace;
nhttp = params->http_headers;
hl = HTTP_estimate(nhttp);
l = sizeof *sm + nws + 2 * hl;
p = malloc(l);
if (p == NULL)
return (NULL);
/* Don't waste time zeroing the workspace */
memset(sm, 0, sizeof *sm);
memset(p, 0, l - nws);
sm = (void*)p;
p += sizeof *sm;
sm->magic = SESSMEM_MAGIC;
sm->workspace = u;
sm->workspace = nws;
VSL_stats->n_sess_mem++;
sm->http[0] = HTTP_create(p, nhttp);
p += hl;
sm->http[1] = HTTP_create(p, nhttp);
p += hl;
sm->wsp = p;
}
CHECK_OBJ_NOTNULL(sm, SESSMEM_MAGIC);
VSL_stats->n_sess++;
......@@ -142,9 +158,9 @@ ses_setup(struct sessmem *sm, const struct sockaddr *addr, unsigned len)
sp->sockaddrlen = len;
}
WS_Init(sp->ws, "sess", (void *)(sm + 1), sm->workspace);
sp->http = &sm->http[0];
sp->http0 = &sm->http[1];
WS_Init(sp->ws, "sess", sm->wsp, sm->workspace);
sp->http = sm->http[0];
sp->http0 = sm->http[1];
SES_ResetBackendTimeouts(sp);
......@@ -193,7 +209,7 @@ SES_Delete(struct sess *sp)
{
struct acct *b = &sp->acct;
struct sessmem *sm;
unsigned workspace;
// unsigned workspace;
CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
sm = sp->mem;
......@@ -213,10 +229,10 @@ SES_Delete(struct sess *sp)
free(sm);
} else {
/* Clean and prepare for reuse */
workspace = sm->workspace;
memset(sm, 0, sizeof *sm);
sm->magic = SESSMEM_MAGIC;
sm->workspace = workspace;
// workspace = sm->workspace;
// memset(sm, 0, sizeof *sm);
// sm->magic = SESSMEM_MAGIC;
// sm->workspace = workspace;
Lck_Lock(&ses_mem_mtx);
VTAILQ_INSERT_HEAD(&ses_free_mem[1 - ses_qp], sm, list);
......
......@@ -153,7 +153,7 @@ WRW_Write(struct worker *w, const void *ptr, int len)
return (0);
if (len == -1)
len = strlen(ptr);
if (w->niov == MAX_IOVS)
if (w->niov == w->siov)
(void)WRW_Flush(w);
w->iov[w->niov].iov_base = TRUST_ME(ptr);
w->iov[w->niov].iov_len = len;
......@@ -193,7 +193,7 @@ WRW_Sendfile(struct worker *w, int fd, off_t off, unsigned len)
} while (0);
#elif defined(__sun) && defined(HAVE_SENDFILEV)
do {
sendfilevec_t svvec[HTTP_HDR_MAX * 2 + 1];
sendfilevec_t svvec[params->http_headers * 2 + 1];
size_t xferred = 0, expected = 0;
int i;
for (i = 0; i < w->niov; i++) {
......
......@@ -101,6 +101,7 @@ struct params {
unsigned sess_workspace;
unsigned obj_workspace;
unsigned shm_workspace;
unsigned http_headers;
unsigned shm_reclen;
......
......@@ -493,6 +493,12 @@ static const struct parspec input_parspec[] = {
"Minimum is 1024 bytes.",
DELAYED_EFFECT,
"16384", "bytes" },
{ "http_headers", tweak_uint, &master.http_headers, 32, UINT_MAX,
"Maximum number of HTTP headers we will deal with.\n"
"This space is preallocated in sessions and workthreads only "
"objects allocate only space for the headers they store.\n",
0,
"64", "header lines" },
{ "obj_workspace", tweak_uint, &master.obj_workspace, 0, UINT_MAX,
"Bytes of HTTP protocol workspace allocated for objects. "
"This space must be big enough for the entire HTTP protocol "
......
......@@ -88,13 +88,15 @@ stv_pick_stevedore(void)
/*********************************************************************/
static void
STV_InitObj(struct sess *sp, struct object *o, unsigned wsl)
STV_InitObj(struct sess *sp, struct object *o, unsigned wsl, unsigned lhttp,
unsigned nhttp)
{
memset(o, 0, sizeof *o);
o->magic = OBJECT_MAGIC;
WS_Init(o->ws_o, "obj", (o + 1), wsl);
o->http = HTTP_create(o + 1, nhttp);
WS_Init(o->ws_o, "obj", (char *)(o + 1) + lhttp, wsl);
WS_Assert(o->ws_o);
http_Setup(o->http, o->ws_o);
......@@ -109,10 +111,11 @@ STV_InitObj(struct sess *sp, struct object *o, unsigned wsl)
/*********************************************************************/
struct object *
STV_NewObject(struct sess *sp, unsigned l, double ttl)
STV_NewObject(struct sess *sp, unsigned l, double ttl, unsigned nhttp)
{
struct object *o;
struct storage *st;
unsigned lh;
(void)ttl;
if (l == 0)
......@@ -120,21 +123,23 @@ STV_NewObject(struct sess *sp, unsigned l, double ttl)
if (params->obj_workspace > 0 && params->obj_workspace > l)
l = params->obj_workspace;
lh = HTTP_estimate(nhttp);
if (!sp->wrk->cacheable) {
o = malloc(sizeof *o + l);
o = malloc(sizeof *o + l + lh);
XXXAN(o);
STV_InitObj(sp, o, l);
STV_InitObj(sp, o, l, lh, nhttp);
return (o);
}
st = STV_alloc(sp, sizeof *o + l, sp->objcore);
st = STV_alloc(sp, sizeof *o + l + lh, sp->objcore);
XXXAN(st);
xxxassert(st->space >= (sizeof *o + l));
xxxassert(st->space >= (sizeof *o + l + lh));
st->len = st->space;
o = (void *)st->ptr; /* XXX: align ? */
STV_InitObj(sp, o, st->space - sizeof *o);
STV_InitObj(sp, o, st->space - (sizeof *o + lh), lh, nhttp);
o->objstore = st;
return (o);
}
......
......@@ -68,7 +68,8 @@ struct stevedore {
VTAILQ_ENTRY(stevedore) list;
};
struct object *STV_NewObject(struct sess *sp, unsigned len, double ttl);
struct object *STV_NewObject(struct sess *sp, unsigned len, double ttl,
unsigned nhttp);
struct storage *STV_alloc(struct sess *sp, size_t size, struct objcore *oc);
void STV_trim(struct storage *st, size_t size);
void STV_free(struct storage *st);
......
......@@ -9,7 +9,7 @@ server s1 {
txresp -body "012345\n"
} -start
varnish v1 -vcl+backend {} -start
varnish v1 -arg "-smalloc,1m" -vcl+backend {} -start
varnish v1 -expect n_object == 0
varnish v1 -expect client_conn == 0
......
......@@ -415,13 +415,6 @@ else
fi
AC_DEFINE_UNQUOTED([VCC_CC],"$VCC_CC",[C compiler command line for VCL code])
# Define HTTP_HDR_MAX_VAL
AC_ARG_WITH(max-header-fields,
AS_HELP_STRING([--with-max-header-fields=NUM],
[How many header fields to support (default=64)]),
[],
[with_max_header_fields=64])
AC_DEFINE_UNQUOTED(HTTP_HDR_MAX_VAL, $with_max_header_fields, [Define maximum number of header fields supported by varnish ])
# Use jemalloc on Linux
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment