diff --git a/lib/isc/mem.c b/lib/isc/mem.c index 5d00eb6154..12136fc366 100644 --- a/lib/isc/mem.c +++ b/lib/isc/mem.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -131,7 +132,6 @@ struct isc_mem { char name[16]; atomic_size_t inuse; atomic_bool hi_called; - atomic_bool is_overmem; atomic_size_t hi_water; atomic_size_t lo_water; ISC_LIST(isc_mempool_t) pools; @@ -570,7 +570,6 @@ mem_create(isc_mem_t **ctxp, unsigned int debugging, unsigned int flags, atomic_init(&ctx->hi_water, 0); atomic_init(&ctx->lo_water, 0); atomic_init(&ctx->hi_called, false); - atomic_init(&ctx->is_overmem, false); ISC_LIST_INIT(ctx->pools); @@ -1017,48 +1016,30 @@ bool isc_mem_isovermem(isc_mem_t *ctx) { REQUIRE(VALID_CONTEXT(ctx)); - bool is_overmem = atomic_load_relaxed(&ctx->is_overmem); - - if (!is_overmem) { - /* We are not overmem, check whether we should be? */ - size_t hiwater = atomic_load_relaxed(&ctx->hi_water); - if (hiwater == 0) { - return false; - } - - size_t inuse = atomic_load_relaxed(&ctx->inuse); - if (inuse <= hiwater) { - return false; - } - - if ((isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0) { - fprintf(stderr, - "overmem mctx %p inuse %zu hi_water %zu\n", ctx, - inuse, hiwater); - } - - atomic_store_relaxed(&ctx->is_overmem, true); - return true; - } else { - /* We are overmem, check whether we should not be? */ - size_t lowater = atomic_load_relaxed(&ctx->lo_water); - if (lowater == 0) { - return false; - } - - size_t inuse = atomic_load_relaxed(&ctx->inuse); - if (inuse >= lowater) { - return true; - } - - if ((isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0) { - fprintf(stderr, - "overmem mctx %p inuse %zu lo_water %zu\n", ctx, - inuse, lowater); - } - atomic_store_relaxed(&ctx->is_overmem, false); + size_t hiwater = atomic_load_relaxed(&ctx->hi_water); + if (hiwater == 0) { return false; } + + size_t inuse = atomic_load_relaxed(&ctx->inuse); + if (inuse >= hiwater) { + return true; + } + + size_t lowater = atomic_load_relaxed(&ctx->lo_water); + if (inuse <= lowater) { + return false; + } + + /* + * Between lo_water and hi_water, return true with a probability + * that ramps linearly from 0 at lo_water to 1 at hi_water. This + * spreads cache cleaning across many inserts instead of triggering + * a thundering herd once the hi_water mark is crossed. + */ + uint32_t prob = (uint32_t)(((uint64_t)(inuse - lowater) * 256) / + (hiwater - lowater)); + return isc_random8() < prob; } void diff --git a/tests/dns/qpdb_test.c b/tests/dns/qpdb_test.c index c1888101d1..260b6c1dc3 100644 --- a/tests/dns/qpdb_test.c +++ b/tests/dns/qpdb_test.c @@ -137,7 +137,6 @@ ISC_LOOP_TEST_IMPL(overmempurge_bigrdata) { for (i = 0; !isc_mem_isovermem(mctx2) && i < (maxcache / 10); i++) { overmempurge_addrdataset(db, now, i, 50053, 0, false); } - assert_true(isc_mem_isovermem(mctx2)); /* * Then try to add the same number of entries, each has very large data. @@ -188,7 +187,6 @@ ISC_LOOP_TEST_IMPL(overmempurge_longname) { for (i = 0; !isc_mem_isovermem(mctx2) && i < (maxcache / 10); i++) { overmempurge_addrdataset(db, now, i, 50053, 0, false); } - assert_true(isc_mem_isovermem(mctx2)); /* * Then try to add the same number of entries, each has very long name. diff --git a/tests/isc/mem_test.c b/tests/isc/mem_test.c index 7754488fb0..2c26922c8d 100644 --- a/tests/isc/mem_test.c +++ b/tests/isc/mem_test.c @@ -291,6 +291,17 @@ ISC_RUN_TEST_IMPL(isc_mem_reallocate) { isc_mem_free(mctx, data); } +static bool +at_least_one_overmem(isc_mem_t *omctx) { + for (size_t i = 0; i < UINT16_MAX; i++) { + /* The overmem is probability based in this range */ + if (isc_mem_isovermem(omctx)) { + return true; + } + } + return false; +} + ISC_RUN_TEST_IMPL(isc_mem_overmem) { isc_mem_t *omctx = NULL; isc_mem_create(&omctx); @@ -298,27 +309,27 @@ ISC_RUN_TEST_IMPL(isc_mem_overmem) { isc_mem_setwater(omctx, 1024, 512); - /* inuse < lo_water */ + /* inuse <= lo_water is always false */ void *data1 = isc_mem_allocate(omctx, 256); assert_false(isc_mem_isovermem(omctx)); - /* lo_water < inuse < hi_water */ + /* lo_water < inuse < hi_water might be true or false */ void *data2 = isc_mem_allocate(omctx, 512); - assert_false(isc_mem_isovermem(omctx)); + assert_true(at_least_one_overmem(omctx)); - /* hi_water < inuse */ + /* hi_water <= inuse is always true */ void *data3 = isc_mem_allocate(omctx, 512); assert_true(isc_mem_isovermem(omctx)); - /* lo_water < inuse < hi_water */ + /* lo_water < inuse < hi_water might be true or false */ isc_mem_free(omctx, data2); - assert_true(isc_mem_isovermem(omctx)); + assert_true(at_least_one_overmem(omctx)); - /* inuse < lo_water */ + /* inuse <= lo_water is always false */ isc_mem_free(omctx, data3); assert_false(isc_mem_isovermem(omctx)); - /* inuse == 0 */ + /* inuse == 0 is always false */ isc_mem_free(omctx, data1); assert_false(isc_mem_isovermem(omctx));