Commit 6ca0a15d98 for openssl.org
commit 6ca0a15d9822fd6741e59a1b5ce8b688b899606a
Author: Neil Horman <nhorman@openssl.org>
Date: Tue Apr 28 09:41:17 2026 -0400
remove ossl_method_store cache culling
Theres no point in thrashing the cache like this, it just gives us more
opportunities to dirty the cpu cache by taking the write lock
Reviewed-by: Saša NedvÄ›dický <sashan@openssl.org>
Reviewed-by: Bob Beck <beck@openssl.org>
Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
MergeDate: Tue Jun 9 18:17:03 2026
(Merged from https://github.com/openssl/openssl/pull/31018)
diff --git a/crypto/property/property.c b/crypto/property/property.c
index 487fefb220..024d857467 100644
--- a/crypto/property/property.c
+++ b/crypto/property/property.c
@@ -30,35 +30,13 @@
/*
* The shard count was determined through performance testing with the evp_fetch
* tool on an Intel Xeon Gold 6248R CPU @ 3.00GHz. Testing showed that 4 shards
- * combined with CACHE_SIZE delivered the best performance for 16 or
+ * delivered the best performance for 16 or
* more threads, and close to best performance at below 16 threads.
*/
#ifndef NUM_SHARDS
#define NUM_SHARDS 4
#endif
-#ifndef CACHE_SIZE
-#define CACHE_SIZE 512
-#endif
-
-/*
- * To keep random cull distributions from being unbiased, we should keep both
- * CACHE_SIZE and NUM_SHARDS as powers of 2
- */
-#if (CACHE_SIZE != 0 && (CACHE_SIZE & (CACHE_SIZE - 1)))
-#error "CACHE_SIZE must be a power of 2"
-#endif
-
-#if (NUM_SHARDS != 0 && (NUM_SHARDS & (NUM_SHARDS - 1)))
-#error "NUM_SHARDS must be a power of 2"
-#endif
-/*
- * The number of elements in the query cache before we initiate a flush.
- * If reducing this, also ensure the stochastic test in test/property_test.c
- * isn't likely to fail.
- */
-#define IMPL_CACHE_FLUSH_THRESHOLD (CACHE_SIZE / NUM_SHARDS)
-
#if defined(__GNUC__) || defined(__clang__)
/*
* ALLOW_VLA enables the use of dynamically sized arrays
@@ -98,7 +76,6 @@ typedef struct query_st {
uint64_t specific_hash; /* hash of [nid,prop_query,prov] tuple */
uint64_t generic_hash; /* hash of [nid,prop_query] tuple */
METHOD method; /* METHOD for this query */
- TSAN_QUALIFIER uint32_t used; /* flag to indicate used since last cull */
} QUERY;
/*
@@ -956,98 +933,6 @@ int ossl_method_store_cache_flush_all(OSSL_METHOD_STORE *store)
return 1;
}
-/*
- * Generate some randomness in our hash table when we need it, since
- * The use of this particular code occurs before our algorithms are
- * registered, preventing the use of the RAND_bytes apis.
- * Based off of:
- * https://doi.org/10.18637/jss.v008.i14
- */
-static ossl_inline void generate_random_seed(uint32_t *seed)
-{
- OSSL_TIME ts;
-
- if (*seed == 0) {
- *seed = OPENSSL_rdtsc();
- if (*seed == 0) {
- ts = ossl_time_now();
- *seed = (uint32_t)ts.t;
- }
- }
-
- *seed ^= *seed << 13;
- *seed ^= *seed >> 17;
- *seed ^= *seed << 5;
-}
-
-/*
- * Cull items from the QUERY cache.
- *
- * We don't want to let our QUERY cache grow too large, so if we grow beyond
- * its threshold, randomly discard some entries.
- *
- * We do this with an lru-like heuristic, and some randomness.
- *
- * the process is:
- * 1) Iterate over each element in the QUERY hashtable, for each element:
- * 2) If its used flag is set, its been recently used, so skip it.
- * 3) Otherwise, consult the sa->seed value. Check the low order bit of sa->seed,
- * which at cache creation is randomly generated. If the bit is set, select the QUERY
- * precomputed hash value, and delete it form the table.
- * 4) Shift the seed value right one bit.
- * 5) Repeat steps 1-4 until the number of requested entries have been removed
- * 6) Update our sa->seed by xoring the current sa->seed with the last hash that was eliminated.
- */
-static void QUERY_cache_select_cull(ALGORITHM *alg, STORED_ALGORITHMS *sa,
- size_t cullcount, uint32_t seed)
-{
- size_t culled = 0;
- uint64_t hash = 0;
- uint32_t used = 0;
- QUERY *q, *qn;
- QUERY_KEY key;
-
-cull_again:
- OSSL_LIST_FOREACH_DELSAFE(q, qn, lru_entry, &sa->lru_list)
- {
- /*
- * Skip QUERY elements that have been recently used
- * reset this flag so all elements have to continuously
- * demonstrate use.
- */
- used = tsan_load(&q->used);
- tsan_store(&q->used, 0);
- if (used)
- continue;
- /*
- * If the low order bit in the seed is set, we can delete this entry
- */
- if (seed & 0x1) {
- /*
- * Select the hash value to delete in priority order, specific if its
- * given, generic otherwise
- */
- hash = (q->specific_hash != 0) ? q->specific_hash : q->generic_hash;
- HT_INIT_KEY_CACHED(&key, hash);
- if (ossl_ht_delete(sa->cache, TO_HT_KEY(&key))) {
- culled++;
- if (culled == cullcount)
- break;
- }
- generate_random_seed(&seed);
- } else {
- seed = seed >> 1;
- }
- }
- /*
- * If we didn't cull our requested number of entries
- * try again. Note that the used flag is cleared on
- * all entries now, so every entry is fair game
- */
- if (culled < cullcount)
- goto cull_again;
-}
-
static ossl_inline int ossl_method_store_cache_get_locked(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
int nid, const char *prop_query, size_t keylen, STORED_ALGORITHMS *sa, QUERY **post_insert,
void **method)
@@ -1118,7 +1003,6 @@ static ossl_inline int ossl_method_store_cache_get_locked(OSSL_METHOD_STORE *sto
goto err;
r->generic_hash = generic_hash;
r->specific_hash = 0;
- r->used = 0;
ossl_list_lru_entry_init_elem(r);
HT_INIT_KEY_CACHED(&key, generic_hash);
/*
@@ -1143,7 +1027,6 @@ static ossl_inline int ossl_method_store_cache_get_locked(OSSL_METHOD_STORE *sto
if (r == NULL)
goto err;
}
- tsan_store(&r->used, 1);
if (ossl_method_up_ref(&r->method)) {
*method = r->method.method;
res = 1;
@@ -1233,7 +1116,6 @@ static ossl_inline int ossl_method_store_cache_set_locked(OSSL_METHOD_STORE *sto
QUERY_KEY key;
int res = 1;
int insert_rc;
- size_t cullcount;
#ifdef ALLOW_VLA
uint8_t keybuf[keylen];
#else
@@ -1249,25 +1131,6 @@ static ossl_inline int ossl_method_store_cache_set_locked(OSSL_METHOD_STORE *sto
alg = ossl_method_store_retrieve(sa, nid);
if (alg == NULL)
goto err;
- if (ossl_ht_count(sa->cache) > IMPL_CACHE_FLUSH_THRESHOLD) {
- uint32_t seed = 0;
-
- generate_random_seed(&seed);
- /*
- * Cull between 1 and 25% of this cache
- */
- cullcount = ossl_ht_count(sa->cache);
- /*
- * If this cache has less than 25% of the total entries
- * in the STORED_ALGORITHMS shard, don't bother culling
- * Just wait until we try to add to a larger cache
- */
- if (cullcount >= 4) {
- cullcount = seed % (cullcount >> 2);
- cullcount = (cullcount < 1) ? 1 : cullcount;
- QUERY_cache_select_cull(alg, sa, cullcount, seed);
- }
- }
/*
* Marshall our lookup key
@@ -1291,7 +1154,6 @@ static ossl_inline int ossl_method_store_cache_set_locked(OSSL_METHOD_STORE *sto
if (p != NULL) {
p->saptr = sa;
p->nid = nid;
- p->used = 0;
ossl_list_lru_entry_init_elem(p);
p->method.method = method;
p->method.up_ref = method_up_ref;
diff --git a/test/property_test.c b/test/property_test.c
index a945c89296..7d072d1b82 100644
--- a/test/property_test.c
+++ b/test/property_test.c
@@ -596,7 +596,7 @@ err:
static int test_query_cache_stochastic(void)
{
- const int max = 10000, tail = 10;
+ const int max = 10000;
OSSL_METHOD_STORE *store;
int i, res = 0;
char buf[50];
@@ -635,7 +635,7 @@ static int test_query_cache_stochastic(void)
errors++;
}
/* There is a tiny probability that this will fail when it shouldn't */
- res = TEST_int_gt(errors, tail) && TEST_int_lt(errors, max - tail);
+ res = !TEST_int_gt(errors, 0);
err:
ossl_method_store_free(store);