EVP_MD performance fix (refcount cache contention) Partial fix for #17064. Avoid excessive writes to the cache line containing the refcount for an EVP_MD object to avoid extreme cache contention when using a single EVP_MD at high frequency on multiple threads. This changes performance in 3.0 from being double that of 1.1 to only slightly higher than that of 1.1. Reviewed-by: Tim Hudson <tjh@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> Reviewed-by: Paul Dale <pauli@openssl.org> (Merged from https://github.com/openssl/openssl/pull/17857)
diff --git a/crypto/evp/digest.c b/crypto/evp/digest.c index db2eed6..cd930eb 100644 --- a/crypto/evp/digest.c +++ b/crypto/evp/digest.c
@@ -40,7 +40,7 @@ } } -void evp_md_ctx_clear_digest(EVP_MD_CTX *ctx, int force) +void evp_md_ctx_clear_digest(EVP_MD_CTX *ctx, int force, int keep_fetched) { if (ctx->algctx != NULL) { if (ctx->digest != NULL && ctx->digest->freectx != NULL) @@ -65,13 +65,14 @@ #endif /* Non legacy code, this has to be later than the ctx->digest cleaning */ - EVP_MD_free(ctx->fetched_digest); - ctx->fetched_digest = NULL; - ctx->reqdigest = NULL; + if (!keep_fetched) { + EVP_MD_free(ctx->fetched_digest); + ctx->fetched_digest = NULL; + ctx->reqdigest = NULL; + } } -/* This call frees resources associated with the context */ -int EVP_MD_CTX_reset(EVP_MD_CTX *ctx) +static int evp_md_ctx_reset_ex(EVP_MD_CTX *ctx, int keep_fetched) { if (ctx == NULL) return 1; @@ -87,12 +88,19 @@ } #endif - evp_md_ctx_clear_digest(ctx, 0); - OPENSSL_cleanse(ctx, sizeof(*ctx)); + evp_md_ctx_clear_digest(ctx, 0, keep_fetched); + if (!keep_fetched) + OPENSSL_cleanse(ctx, sizeof(*ctx)); return 1; } +/* This call frees resources associated with the context */ +int EVP_MD_CTX_reset(EVP_MD_CTX *ctx) +{ + return evp_md_ctx_reset_ex(ctx, 0); +} + #ifndef FIPS_MODULE EVP_MD_CTX *evp_md_ctx_new_ex(EVP_PKEY *pkey, const ASN1_OCTET_STRING *id, OSSL_LIB_CTX *libctx, const char *propq) @@ -524,6 +532,7 @@ int EVP_MD_CTX_copy_ex(EVP_MD_CTX *out, const EVP_MD_CTX *in) { + int digest_change = 0; unsigned char *tmp_buf; if (in == NULL) { @@ -549,15 +558,16 @@ return 0; } - EVP_MD_CTX_reset(out); - if (out->fetched_digest != NULL) + evp_md_ctx_reset_ex(out, 1); + digest_change = (out->fetched_digest != in->fetched_digest); + if (digest_change && out->fetched_digest != NULL) EVP_MD_free(out->fetched_digest); *out = *in; /* NULL out pointers in case of error */ out->pctx = NULL; out->algctx = NULL; - if (in->fetched_digest != NULL) + if (digest_change && in->fetched_digest != NULL) EVP_MD_up_ref(in->fetched_digest); if (in->algctx != NULL) {
diff --git a/crypto/evp/m_sigver.c b/crypto/evp/m_sigver.c index 0993de0..371bca5 100644 --- a/crypto/evp/m_sigver.c +++ b/crypto/evp/m_sigver.c
@@ -231,7 +231,7 @@ * We're about to get a new digest so clear anything associated with * an old digest. */ - evp_md_ctx_clear_digest(ctx, 1); + evp_md_ctx_clear_digest(ctx, 1, 0); /* legacy code support for engines */ ERR_set_mark();
diff --git a/include/crypto/evp.h b/include/crypto/evp.h index c5d3a93..6fb0530 100644 --- a/include/crypto/evp.h +++ b/include/crypto/evp.h
@@ -900,7 +900,7 @@ int loadconfig, int mirrored); char *evp_get_global_properties_str(OSSL_LIB_CTX *libctx, int loadconfig); -void evp_md_ctx_clear_digest(EVP_MD_CTX *ctx, int force); +void evp_md_ctx_clear_digest(EVP_MD_CTX *ctx, int force, int keep_digest); /* Three possible states: */ # define EVP_PKEY_STATE_UNKNOWN 0