Commit 9a7846cd authored by Poul-Henning Kamp's avatar Poul-Henning Kamp

Fix a deadlock in the critbit hasher.

Spotted by:	dormando
Testing by:	Kristian

Fixes:	#684



git-svn-id: http://www.varnish-cache.org/svn/trunk/varnish-cache@4717 d4fa192b-c00b-0410-8231-f00ffab90ce4
parent 324a47fc
...@@ -160,7 +160,9 @@ hcb_l_y(uintptr_t u) ...@@ -160,7 +160,9 @@ hcb_l_y(uintptr_t u)
return ((struct hcb_y *)(u & ~HCB_BIT_Y)); return ((struct hcb_y *)(u & ~HCB_BIT_Y));
} }
/**********************************************************************/ /**********************************************************************
* Find the "critical" bit that separates these two digests
*/
static unsigned static unsigned
hcb_crit_bit(const struct objhead *oh1, const struct objhead *oh2, hcb_crit_bit(const struct objhead *oh1, const struct objhead *oh2,
...@@ -238,6 +240,7 @@ hcb_insert(struct hcb_root *root, struct objhead *oh, int has_lock) ...@@ -238,6 +240,7 @@ hcb_insert(struct hcb_root *root, struct objhead *oh, int has_lock)
while(hcb_is_y(*p)) { while(hcb_is_y(*p)) {
y = hcb_l_y(*p); y = hcb_l_y(*p);
assert(y->critbit != y2->critbit);
if (y->critbit > y2->critbit) if (y->critbit > y2->critbit)
break; break;
assert(y->ptr < DIGEST_LEN); assert(y->ptr < DIGEST_LEN);
...@@ -353,7 +356,6 @@ hcb_cleaner(void *priv) ...@@ -353,7 +356,6 @@ hcb_cleaner(void *priv)
THR_SetName("hcb_cleaner"); THR_SetName("hcb_cleaner");
(void)priv; (void)priv;
while (1) { while (1) {
(void)sleep(1);
Lck_Lock(&hcb_mtx); Lck_Lock(&hcb_mtx);
VTAILQ_FOREACH_SAFE(oh, &laylow, coollist, oh2) { VTAILQ_FOREACH_SAFE(oh, &laylow, coollist, oh2) {
CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC); CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
...@@ -363,14 +365,16 @@ hcb_cleaner(void *priv) ...@@ -363,14 +365,16 @@ hcb_cleaner(void *priv)
continue; continue;
if (++oh->digest[0] > params->critbit_cooloff) { if (++oh->digest[0] > params->critbit_cooloff) {
VTAILQ_REMOVE(&laylow, oh, coollist); VTAILQ_REMOVE(&laylow, oh, coollist);
#ifdef PHK break;
fprintf(stderr, "OH %p is cold enough\n", oh);
#endif
HSH_DeleteObjHead(&ww, oh);
} }
} }
Lck_Unlock(&hcb_mtx); Lck_Unlock(&hcb_mtx);
WRK_SumStat(&ww); if (oh == NULL) {
WRK_SumStat(&ww);
(void)sleep(1);
} else {
HSH_DeleteObjHead(&ww, oh);
}
} }
NEEDLESS_RETURN(NULL); NEEDLESS_RETURN(NULL);
} }
...@@ -385,11 +389,11 @@ hcb_start(void) ...@@ -385,11 +389,11 @@ hcb_start(void)
(void)oh; (void)oh;
CLI_AddFuncs(hcb_cmds); CLI_AddFuncs(hcb_cmds);
Lck_New(&hcb_mtx);
AZ(pthread_create(&tp, NULL, hcb_cleaner, NULL)); AZ(pthread_create(&tp, NULL, hcb_cleaner, NULL));
assert(sizeof(struct hcb_y) <= sizeof(oh->u)); assert(sizeof(struct hcb_y) <= sizeof(oh->u));
memset(&hcb_root, 0, sizeof hcb_root); memset(&hcb_root, 0, sizeof hcb_root);
hcb_build_bittbl(); hcb_build_bittbl();
Lck_New(&hcb_mtx);
} }
static int static int
...@@ -401,8 +405,8 @@ hcb_deref(struct objhead *oh) ...@@ -401,8 +405,8 @@ hcb_deref(struct objhead *oh)
CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC); CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
Lck_Lock(&oh->mtx); Lck_Lock(&oh->mtx);
assert(oh->refcnt > 0); assert(oh->refcnt > 0);
if (oh->refcnt == 1) { oh->refcnt--;
/* Remove from tree before decrementing refcnt to zero */ if (oh->refcnt == 0) {
Lck_Lock(&hcb_mtx); Lck_Lock(&hcb_mtx);
hcb_delete(&hcb_root, oh); hcb_delete(&hcb_root, oh);
assert(VTAILQ_EMPTY(&oh->objcs)); assert(VTAILQ_EMPTY(&oh->objcs));
...@@ -411,7 +415,6 @@ hcb_deref(struct objhead *oh) ...@@ -411,7 +415,6 @@ hcb_deref(struct objhead *oh)
VTAILQ_INSERT_TAIL(&laylow, oh, coollist); VTAILQ_INSERT_TAIL(&laylow, oh, coollist);
Lck_Unlock(&hcb_mtx); Lck_Unlock(&hcb_mtx);
} }
oh->refcnt--;
Lck_Unlock(&oh->mtx); Lck_Unlock(&oh->mtx);
#ifdef PHK #ifdef PHK
fprintf(stderr, "hcb_defef %d %d <%s>\n", __LINE__, r, oh->hash); fprintf(stderr, "hcb_defef %d %d <%s>\n", __LINE__, r, oh->hash);
...@@ -423,54 +426,52 @@ static struct objhead * ...@@ -423,54 +426,52 @@ static struct objhead *
hcb_lookup(const struct sess *sp, struct objhead *noh) hcb_lookup(const struct sess *sp, struct objhead *noh)
{ {
struct objhead *oh; struct objhead *oh;
unsigned u; volatile unsigned u;
unsigned with_lock;
(void)sp; (void)sp;
oh = hcb_insert(&hcb_root, noh, 0);
if (oh != NULL) { with_lock = 0;
/* Assert that we didn't muck with the tree without lock */ while (1) {
assert(oh != noh); if (with_lock) {
Lck_Lock(&hcb_mtx);
VSL_stats->hcb_lock++;
assert(noh->refcnt == 1);
oh = hcb_insert(&hcb_root, noh, 1);
Lck_Unlock(&hcb_mtx);
} else {
VSL_stats->hcb_nolock++;
oh = hcb_insert(&hcb_root, noh, 0);
}
if (oh != NULL && oh == noh) {
/* Assert that we only muck with the tree with a lock */
assert(with_lock);
VSL_stats->hcb_insert++;
assert(oh->refcnt > 0);
return (oh);
}
if (oh == NULL) {
assert(!with_lock);
/* Try again, with lock */
with_lock = 1;
continue;
}
CHECK_OBJ_NOTNULL(noh, OBJHEAD_MAGIC);
Lck_Lock(&oh->mtx); Lck_Lock(&oh->mtx);
/* /*
* A refcount of zero indicates that the tree changed * A refcount of zero indicates that the tree changed
* under us, so fall through and try with the lock held. * under us, so fall through and try with the lock held.
*/ */
u = oh->refcnt; u = oh->refcnt;
if (u) if (u > 0)
oh->refcnt++; oh->refcnt++;
Lck_Unlock(&oh->mtx); Lck_Unlock(&oh->mtx);
if (u) { if (u > 0)
VSL_stats->hcb_nolock++;
return (oh); return (oh);
}
}
/*
* Try again, holding lock and fully ready objhead, so that if
* somebody else beats us back, they do not get surprised.
*/
Lck_Lock(&hcb_mtx);
assert(noh->refcnt == 1);
oh = hcb_insert(&hcb_root, noh, 1);
if (oh == noh) {
VSL_stats->hcb_insert++;
assert(oh->refcnt > 0);
#ifdef PHK
fprintf(stderr, "hcb_lookup %d\n", __LINE__);
#endif
} else {
CHECK_OBJ_NOTNULL(noh, OBJHEAD_MAGIC);
VSL_stats->hcb_lock++;
#ifdef PHK
fprintf(stderr, "hcb_lookup %d\n", __LINE__);
#endif
Lck_Lock(&oh->mtx);
assert(oh->refcnt > 0);
oh->refcnt++;
Lck_Unlock(&oh->mtx);
} }
Lck_Unlock(&hcb_mtx);
return (oh);
} }
......
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