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);