Commit 6bd2c9f1 authored by Geoff Simmons's avatar Geoff Simmons

Unmap when VCL goes cold, re-map on the warm event.

The PRIV_VCL list is now a list of reader objects. On the warm event,
we restart the timer and then wait for the first check to run, as
in object init.
parent 68852c62
...@@ -25,47 +25,95 @@ varnish v1 -arg "-p debug=+vclrel" -vcl { ...@@ -25,47 +25,95 @@ varnish v1 -arg "-p debug=+vclrel" -vcl {
} -start } -start
logexpect l1 -v v1 -d 1 -g raw -q "Debug" { logexpect l1 -v v1 -d 1 -g raw -q "Debug" {
expect * * Debug {^vmod file: vcl1\.rdr\d: check for \S+ running at} expect * 0 Debug {^vmod file: vcl1\.rdr\d: check for \S+ running at}
expect * * Debug {^vmod file: vcl1\.rdr\d: updating \S+ at} expect 0 0 Debug {^vmod file: vcl1\.rdr\d: updating \S+ at}
expect * * Debug {^vmod file: vcl1\.rdr\d: check for \S+ finished successfully at} expect 0 0 Debug {^vmod file: vcl1\.rdr\d: check for \S+ finished successfully at}
expect * * Debug {^vmod file: vcl1\.rdr\d: check for \S+ running at} expect * 0 Debug {^vmod file: vcl1\.rdr\d: check for \S+ running at}
expect * * Debug {^vmod file: vcl1\.rdr\d: updating \S+ at} expect 0 0 Debug {^vmod file: vcl1\.rdr\d: updating \S+ at}
expect * * Debug {^vmod file: vcl1\.rdr\d: check for \S+ finished successfully at} expect 0 0 Debug {^vmod file: vcl1\.rdr\d: check for \S+ finished successfully at}
expect * * Debug {^vmod file: vcl1\.rdr\d: check for \S+ running at} expect * 0 Debug {^vmod file: vcl1\.rdr\d: check for \S+ running at}
expect * * Debug {^vmod file: vcl1\.rdr\d: updating \S+ at} expect 0 0 Debug {^vmod file: vcl1\.rdr\d: updating \S+ at}
expect * * Debug {^vmod file: vcl1\.rdr\d: check for \S+ finished successfully at} expect 0 0 Debug {^vmod file: vcl1\.rdr\d: check for \S+ finished successfully at}
expect * * Debug {^vmod file: vcl1\.rdr\d: check for \S+ running at} expect * 0 Debug {^vmod file: vcl1\.rdr\d: check for \S+ running at}
expect * * Debug {^vmod file: vcl1\.rdr\d: updating \S+ at} expect 0 0 Debug {^vmod file: vcl1\.rdr\d: updating \S+ at}
expect * * Debug {^vmod file: vcl1\.rdr\d: check for \S+ finished successfully at} expect 0 0 Debug {^vmod file: vcl1\.rdr\d: check for \S+ finished successfully at}
expect * * Debug {^vmod file: vcl1\.rdr\d: check for \S+ running at} expect * 0 Debug {^vmod file: vcl1\.rdr\d: check for \S+ running at}
expect * * Debug {^vmod file: vcl1\.rdr\d: updating \S+ at} expect 0 0 Debug {^vmod file: vcl1\.rdr\d: updating \S+ at}
expect * * Debug {^vmod file: vcl1\.rdr\d: check for \S+ finished successfully at} expect 0 0 Debug {^vmod file: vcl1\.rdr\d: check for \S+ finished successfully at}
} -run } -run
varnish v1 -vcl { backend b None; } varnish v1 -vcl { backend b None; }
logexpect l1 -v v1 -d 0 -g raw -q "Debug" {
expect * 0 Debug {^vmod file: vcl1\.rdr\d: timer suspended$}
expect 0 0 Debug {^vmod file: vcl1\.rdr\d: unmapped$}
expect * 0 Debug {^vmod file: vcl1\.rdr\d: timer suspended$}
expect 0 0 Debug {^vmod file: vcl1\.rdr\d: unmapped$}
expect * 0 Debug {^vmod file: vcl1\.rdr\d: timer suspended$}
expect 0 0 Debug {^vmod file: vcl1\.rdr\d: unmapped$}
expect * 0 Debug {^vmod file: vcl1\.rdr\d: timer suspended$}
expect 0 0 Debug {^vmod file: vcl1\.rdr\d: unmapped$}
expect * 0 Debug {^vmod file: vcl1\.rdr\d: timer suspended$}
expect 0 0 Debug {^vmod file: vcl1\.rdr\d: unmapped$}
} -start
varnish v1 -cliok "vcl.state vcl1 cold" varnish v1 -cliok "vcl.state vcl1 cold"
logexpect l1 -wait
varnish v1 -cliok "vcl.list" varnish v1 -cliok "vcl.list"
delay .5 delay .5
# No checks run in the cold state, must be verified manually in the log. # No checks run in the cold state, must be verified manually in the log.
logexpect l1 -v v1 -d 0 -g raw -q "Debug" { logexpect l1 -v v1 -d 0 -g raw -q "Debug" {
expect * * Debug {^vmod file: vcl1\.rdr\d: check for \S+ running at} expect * 0 Debug {^vmod file: vcl1\.rdr\d: timer restarted$}
expect * 0 Debug {^vmod file: vcl1\.rdr\d: re-mapped$}
expect * 0 Debug {^vmod file: vcl1\.rdr\d: timer restarted$}
expect * 0 Debug {^vmod file: vcl1\.rdr\d: re-mapped$}
expect * 0 Debug {^vmod file: vcl1\.rdr\d: timer restarted$}
expect * 0 Debug {^vmod file: vcl1\.rdr\d: re-mapped$}
expect * 0 Debug {^vmod file: vcl1\.rdr\d: timer restarted$}
expect * 0 Debug {^vmod file: vcl1\.rdr\d: re-mapped$}
expect * 0 Debug {^vmod file: vcl1\.rdr\d: timer restarted$}
expect * 0 Debug {^vmod file: vcl1\.rdr\d: re-mapped$}
} -start
logexpect l2 -v v1 -d 0 -g raw -q "Debug" {
expect * 0 Debug {^vmod file: vcl1\.rdr\d: check for \S+ running at}
expect * 0 Debug {^vmod file: vcl1\.rdr\d: updating \S+ at}
expect * 0 Debug {^vmod file: vcl1\.rdr\d: check for \S+ finished successfully at}
expect * * Debug {^vmod file: vcl1\.rdr\d: check for \S+ running at} expect * 0 Debug {^vmod file: vcl1\.rdr\d: check for \S+ running at}
expect * 0 Debug {^vmod file: vcl1\.rdr\d: updating \S+ at}
expect * 0 Debug {^vmod file: vcl1\.rdr\d: check for \S+ finished successfully at}
expect * * Debug {^vmod file: vcl1\.rdr\d: check for \S+ running at} expect * 0 Debug {^vmod file: vcl1\.rdr\d: check for \S+ running at}
expect * 0 Debug {^vmod file: vcl1\.rdr\d: updating \S+ at}
expect * 0 Debug {^vmod file: vcl1\.rdr\d: check for \S+ finished successfully at}
expect * * Debug {^vmod file: vcl1\.rdr\d: check for \S+ running at} expect * 0 Debug {^vmod file: vcl1\.rdr\d: check for \S+ running at}
expect * 0 Debug {^vmod file: vcl1\.rdr\d: updating \S+ at}
expect * 0 Debug {^vmod file: vcl1\.rdr\d: check for \S+ finished successfully at}
expect * * Debug {^vmod file: vcl1\.rdr\d: check for \S+ running at} expect * 0 Debug {^vmod file: vcl1\.rdr\d: check for \S+ running at}
expect * 0 Debug {^vmod file: vcl1\.rdr\d: updating \S+ at}
expect * 0 Debug {^vmod file: vcl1\.rdr\d: check for \S+ finished successfully at}
} -start } -start
varnish v1 -cliok "vcl.state vcl1 warm" varnish v1 -cliok "vcl.state vcl1 warm"
logexpect l1 -wait logexpect l1 -wait
logexpect l2 -wait
...@@ -85,6 +85,7 @@ struct file_info { ...@@ -85,6 +85,7 @@ struct file_info {
#define RDR_MAPPED (1 << 2) #define RDR_MAPPED (1 << 2)
#define RDR_TIMER_INIT (1 << 3) #define RDR_TIMER_INIT (1 << 3)
#define RDR_DELETED (1 << 4) #define RDR_DELETED (1 << 4)
#define RDR_WARMUP (1 << 5)
struct VPFX(file_reader) { struct VPFX(file_reader) {
unsigned magic; unsigned magic;
...@@ -100,14 +101,14 @@ struct VPFX(file_reader) { ...@@ -100,14 +101,14 @@ struct VPFX(file_reader) {
unsigned short errlen; unsigned short errlen;
}; };
struct timer_entry { struct obj_entry {
unsigned magic; unsigned magic;
#define TIMER_ENTRY_MAGIC 0xa0059ebd #define OBJ_ENTRY_MAGIC 0xace45740
VSLIST_ENTRY(timer_entry) list; VSLIST_ENTRY(obj_entry) list;
timer_t timerid; struct VPFX(file_reader) *obj;
}; };
VSLIST_HEAD(timer_head, timer_entry); VSLIST_HEAD(obj_head, obj_entry);
static void static void
check(union sigval val) check(union sigval val)
...@@ -185,7 +186,7 @@ check(union sigval val) ...@@ -185,7 +186,7 @@ check(union sigval val)
goto out; goto out;
} }
if ((flags & (RDR_INITIALIZED | RDR_MAPPED)) if ((flags & (RDR_INITIALIZED | RDR_MAPPED) & !(flags & RDR_WARMUP))
&& info->mtime.tv_sec == st.st_mtim.tv_sec && info->mtime.tv_sec == st.st_mtim.tv_sec
&& info->mtime.tv_nsec == st.st_mtim.tv_nsec && info->mtime.tv_nsec == st.st_mtim.tv_nsec
&& info->dev == st.st_dev && info->ino == st.st_ino) { && info->dev == st.st_dev && info->ino == st.st_ino) {
...@@ -248,7 +249,7 @@ check(union sigval val) ...@@ -248,7 +249,7 @@ check(union sigval val)
info->ino = st.st_ino; info->ino = st.st_ino;
info->len = st.st_size + 1; info->len = st.st_size + 1;
flags &= ~RDR_ERROR; flags &= ~(RDR_ERROR | RDR_WARMUP);
flags |= RDR_INITIALIZED; flags |= RDR_INITIALIZED;
out: out:
...@@ -296,21 +297,21 @@ check(union sigval val) ...@@ -296,21 +297,21 @@ check(union sigval val)
return; return;
} }
static struct timer_head * static struct obj_head *
init_priv_vcl(struct vmod_priv *priv) init_priv_vcl(struct vmod_priv *priv)
{ {
struct timer_head *th; struct obj_head *objh;
AN(priv); AN(priv);
if (priv->priv == NULL) { if (priv->priv == NULL) {
th = malloc(sizeof(*th)); objh = malloc(sizeof(*objh));
AN(th); AN(objh);
priv->priv = th; priv->priv = objh;
VSLIST_INIT(th); VSLIST_INIT(objh);
} }
else else
th = priv->priv; objh = priv->priv;
return (th); return (objh);
} }
VCL_VOID VCL_VOID
...@@ -324,8 +325,8 @@ vmod_reader__init(VRT_CTX, struct VPFX(file_reader) **rdrp, ...@@ -324,8 +325,8 @@ vmod_reader__init(VRT_CTX, struct VPFX(file_reader) **rdrp,
struct sigevent sigev; struct sigevent sigev;
timer_t timerid; timer_t timerid;
struct itimerspec timerspec; struct itimerspec timerspec;
struct timer_head *th; struct obj_head *objh;
struct timer_entry *tent; struct obj_entry *objent;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
AN(rdrp); AN(rdrp);
...@@ -469,17 +470,17 @@ vmod_reader__init(VRT_CTX, struct VPFX(file_reader) **rdrp, ...@@ -469,17 +470,17 @@ vmod_reader__init(VRT_CTX, struct VPFX(file_reader) **rdrp,
timerspec.it_interval.tv_nsec timerspec.it_interval.tv_nsec
= (long)(1e9 * (ttl - timerspec.it_interval.tv_sec)); = (long)(1e9 * (ttl - timerspec.it_interval.tv_sec));
th = init_priv_vcl(priv); objh = init_priv_vcl(priv);
AN(th); AN(objh);
errno = 0; errno = 0;
ALLOC_OBJ(tent, TIMER_ENTRY_MAGIC); ALLOC_OBJ(objent, OBJ_ENTRY_MAGIC);
if (tent == NULL) { if (objent == NULL) {
VFAIL(ctx, "new %s: allocating timer list entry: %s", vcl_name, VFAIL(ctx, "new %s: allocating object list entry: %s", vcl_name,
vstrerror(errno)); vstrerror(errno));
return; return;
} }
tent->timerid = timerid; objent->obj = rdr;
VSLIST_INSERT_HEAD(th, tent, list); VSLIST_INSERT_HEAD(objh, objent, list);
AZ(rdr->addr); AZ(rdr->addr);
AZ(rdr->info->mtime.tv_sec); AZ(rdr->info->mtime.tv_sec);
...@@ -534,6 +535,7 @@ vmod_reader__fini(struct VPFX(file_reader) **rdrp) ...@@ -534,6 +535,7 @@ vmod_reader__fini(struct VPFX(file_reader) **rdrp)
CHECK_OBJ_NOTNULL(rdr->info, FILE_INFO_MAGIC); CHECK_OBJ_NOTNULL(rdr->info, FILE_INFO_MAGIC);
AN(rdr->addr); AN(rdr->addr);
AN(rdr->obj_name); AN(rdr->obj_name);
AN(rdr->vcl_name);
errno = 0; errno = 0;
if (munmap(rdr->addr, rdr->info->len) != 0) if (munmap(rdr->addr, rdr->info->len) != 0)
...@@ -806,33 +808,48 @@ vmod_version(VRT_CTX) ...@@ -806,33 +808,48 @@ vmod_version(VRT_CTX)
int int
VPFX(event)(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e) VPFX(event)(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e)
{ {
struct timer_head *th; struct obj_head *objh;
struct timer_entry *ent; struct obj_entry *ent;
struct itimerspec timer; struct itimerspec timer;
struct VPFX(file_reader) *rdr;
int flags;
ASSERT_CLI(); ASSERT_CLI();
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
AN(priv); AN(priv);
th = init_priv_vcl(priv); objh = init_priv_vcl(priv);
AN(th); AN(objh);
switch (e) { switch (e) {
case VCL_EVENT_DISCARD: case VCL_EVENT_DISCARD:
while (!VSLIST_EMPTY(th)) { /* just free the list, object .fini unmaps and deletes timers */
/* object .fini deletes the timers */ while (!VSLIST_EMPTY(objh)) {
ent = VSLIST_FIRST(th); ent = VSLIST_FIRST(objh);
CHECK_OBJ_NOTNULL(ent, TIMER_ENTRY_MAGIC); CHECK_OBJ_NOTNULL(ent, OBJ_ENTRY_MAGIC);
VSLIST_REMOVE_HEAD(th, list); VSLIST_REMOVE_HEAD(objh, list);
FREE_OBJ(ent); FREE_OBJ(ent);
} }
free(th); free(objh);
return (0); return (0);
case VCL_EVENT_WARM: case VCL_EVENT_WARM:
VSLIST_FOREACH(ent, th, list) { VSLIST_FOREACH(ent, objh, list) {
CHECK_OBJ_NOTNULL(ent, TIMER_ENTRY_MAGIC); CHECK_OBJ_NOTNULL(ent, OBJ_ENTRY_MAGIC);
CHECK_OBJ_NOTNULL(ent->obj, FILE_READER_MAGIC);
rdr = ent->obj;
AN(rdr->lock);
AN(rdr->vcl_name);
AN(rdr->obj_name);
AN(rdr->errbuf);
if (rdr->flags & RDR_TIMER_INIT)
continue;
rdr->flags |= RDR_WARMUP;
rdr->flags &= ~(RDR_ERROR | RDR_DELETED);
errno = 0; errno = 0;
if (timer_gettime(ent->timerid, &timer) != 0) { if (timer_gettime(rdr->timerid, &timer) != 0) {
VSB_printf(ctx->msg, VSB_printf(ctx->msg,
"vmod file: reading timer: %s", "vmod file: reading timer: %s",
vstrerror(errno)); vstrerror(errno));
...@@ -840,32 +857,88 @@ VPFX(event)(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e) ...@@ -840,32 +857,88 @@ VPFX(event)(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e)
} }
timer.it_value.tv_sec = 0; timer.it_value.tv_sec = 0;
timer.it_value.tv_nsec = 1; timer.it_value.tv_nsec = 1;
if (timer_settime(ent->timerid, 0, &timer, NULL) != 0) { if (timer_settime(rdr->timerid, 0, &timer, NULL) != 0) {
VSB_printf(ctx->msg, VSB_printf(ctx->msg,
"vmod file: restarting timer: %s", "vmod file: restarting timer: %s",
vstrerror(errno)); vstrerror(errno));
return (-1); return (-1);
} }
VSL(SLT_Debug, 0, "vmod file: %s.%s: timer restarted",
rdr->vcl_name, rdr->obj_name);
if (rdr->flags & RDR_MAPPED)
continue;
/* wait for the initial update, as in object init */
do {
VTIM_sleep(INIT_SLEEP_INTERVAL);
AZ(pthread_rwlock_rdlock(rdr->lock));
flags = rdr->flags;
AZ(pthread_rwlock_unlock(rdr->lock));
} while (flags & RDR_WARMUP);
if (rdr->flags & RDR_ERROR) {
AN(strcmp(rdr->errbuf, NO_ERR));
VSB_printf(ctx->msg, "vmod file: %s.%s: %s",
rdr->vcl_name, rdr->obj_name,
rdr->errbuf);
return (-1);
}
AN(rdr->flags & RDR_MAPPED);
AZ(rdr->flags & RDR_DELETED);
AN(rdr->addr);
VSL(SLT_Debug, 0, "vmod file: %s.%s: re-mapped",
rdr->vcl_name, rdr->obj_name);
} }
return (0); return (0);
case VCL_EVENT_COLD: case VCL_EVENT_COLD:
VSLIST_FOREACH(ent, th, list) { VSLIST_FOREACH(ent, objh, list) {
CHECK_OBJ_NOTNULL(ent, TIMER_ENTRY_MAGIC); CHECK_OBJ_NOTNULL(ent, OBJ_ENTRY_MAGIC);
CHECK_OBJ_NOTNULL(ent->obj, FILE_READER_MAGIC);
rdr = ent->obj;
AN(rdr->vcl_name);
AN(rdr->obj_name);
AN(rdr->errbuf);
errno = 0; errno = 0;
if (timer_gettime(ent->timerid, &timer) != 0) { if (timer_gettime(rdr->timerid, &timer) != 0) {
VSL(SLT_Error, 0, VSL(SLT_Error, 0,
"vmod file: reading timer: %s", "vmod file: %s.%s: reading timer: %s",
rdr->vcl_name, rdr->obj_name,
vstrerror(errno)); vstrerror(errno));
continue; continue;
} }
timer.it_value.tv_sec = 0; timer.it_value.tv_sec = 0;
timer.it_value.tv_nsec = 0; timer.it_value.tv_nsec = 0;
if (timer_settime(ent->timerid, 0, &timer, NULL) != 0) { if (timer_settime(rdr->timerid, 0, &timer, NULL) != 0) {
VSL(SLT_Debug, 0, VSL(SLT_Error, 0,
"vmod file: suspending timer: %s", "vmod file: %s.%s: suspending timer: %s",
rdr->vcl_name, rdr->obj_name,
vstrerror(errno));
continue;
}
rdr->flags &= ~RDR_TIMER_INIT;
VSL(SLT_Debug, 0, "vmod file: %s.%s: timer suspended",
rdr->vcl_name, rdr->obj_name);
if ((rdr->flags & RDR_MAPPED) == 0)
continue;
CHECK_OBJ_NOTNULL(rdr->info, FILE_INFO_MAGIC);
AN(rdr->addr);
errno = 0;
if (munmap(rdr->addr, rdr->info->len) != 0) {
VSL(SLT_Error, 0,
"vmod file: %s.%s: unmap failed: %s",
rdr->vcl_name, rdr->obj_name,
vstrerror(errno)); vstrerror(errno));
continue; continue;
} }
rdr->flags &= ~RDR_MAPPED;
rdr->addr = NULL;
VSL(SLT_Debug, 0, "vmod file: %s.%s: unmapped",
rdr->vcl_name, rdr->obj_name);
} }
return (0); return (0);
case VCL_EVENT_LOAD: case VCL_EVENT_LOAD:
......
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