From 504e72fc1a1432d5266bd6e8909648c49884a36c Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Fri, 12 Jan 2024 10:55:04 -0500 Subject: [PATCH] Add exemplar use case for rcu locks To demonstrate the use of RCU locks, convert CONF_MOD api to using rcu rather than RW locks Reviewed-by: Hugo Landau Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/22729) --- crypto/conf/conf_mod.c | 134 +++++++++++++++++++++++++++-------------- 1 file changed, 88 insertions(+), 46 deletions(-) diff --git a/crypto/conf/conf_mod.c b/crypto/conf/conf_mod.c index d6a5f3ff3556b..e10c18a29faae 100644 --- a/crypto/conf/conf_mod.c +++ b/crypto/conf/conf_mod.c @@ -11,6 +11,7 @@ #define OPENSSL_SUPPRESS_DEPRECATED #include "internal/cryptlib.h" +#include "internal/rcu.h" #include #include #include @@ -63,7 +64,7 @@ struct conf_imodule_st { }; static CRYPTO_ONCE init_module_list_lock = CRYPTO_ONCE_STATIC_INIT; -static CRYPTO_RWLOCK *module_list_lock = NULL; +static CRYPTO_RCU_LOCK *module_list_lock = NULL; static STACK_OF(CONF_MODULE) *supported_modules = NULL; /* protected by lock */ static STACK_OF(CONF_IMODULE) *initialized_modules = NULL; /* protected by lock */ @@ -86,7 +87,7 @@ static int conf_modules_finish_int(void); static void module_lists_free(void) { - CRYPTO_THREAD_lock_free(module_list_lock); + ossl_rcu_lock_free(module_list_lock); module_list_lock = NULL; sk_CONF_MODULE_free(supported_modules); @@ -98,7 +99,7 @@ static void module_lists_free(void) DEFINE_RUN_ONCE_STATIC(do_init_module_list_lock) { - module_list_lock = CRYPTO_THREAD_lock_new(); + module_list_lock = ossl_rcu_lock_new(1); if (module_list_lock == NULL) { ERR_raise(ERR_LIB_CONF, ERR_R_CRYPTO_LIB); return 0; @@ -327,17 +328,24 @@ static CONF_MODULE *module_add(DSO *dso, const char *name, conf_init_func *ifunc, conf_finish_func *ffunc) { CONF_MODULE *tmod = NULL; + STACK_OF(CONF_MODULE) *old_modules; + STACK_OF(CONF_MODULE) *new_modules; if (!RUN_ONCE(&init_module_list_lock, do_init_module_list_lock)) return NULL; - if (!CRYPTO_THREAD_write_lock(module_list_lock)) - return NULL; + ossl_rcu_write_lock(module_list_lock); + + old_modules = ossl_rcu_deref(&supported_modules); + + if (old_modules == NULL) + new_modules = sk_CONF_MODULE_new_null(); + else + new_modules = sk_CONF_MODULE_dup(old_modules); - if (supported_modules == NULL) - supported_modules = sk_CONF_MODULE_new_null(); - if (supported_modules == NULL) + if (new_modules == NULL) goto err; + if ((tmod = OPENSSL_zalloc(sizeof(*tmod))) == NULL) goto err; @@ -348,14 +356,18 @@ static CONF_MODULE *module_add(DSO *dso, const char *name, if (tmod->name == NULL) goto err; - if (!sk_CONF_MODULE_push(supported_modules, tmod)) + if (!sk_CONF_MODULE_push(new_modules, tmod)) goto err; - CRYPTO_THREAD_unlock(module_list_lock); + ossl_rcu_assign_ptr(&supported_modules, &new_modules); + ossl_rcu_write_unlock(module_list_lock); + ossl_synchronize_rcu(module_list_lock); + + sk_CONF_MODULE_free(old_modules); return tmod; err: - CRYPTO_THREAD_unlock(module_list_lock); + ossl_rcu_write_unlock(module_list_lock); if (tmod != NULL) { OPENSSL_free(tmod->name); OPENSSL_free(tmod); @@ -374,6 +386,8 @@ static CONF_MODULE *module_find(const char *name) CONF_MODULE *tmod; int i, nchar; char *p; + STACK_OF(CONF_MODULE) *mods; + p = strrchr(name, '.'); if (p) @@ -384,18 +398,18 @@ static CONF_MODULE *module_find(const char *name) if (!RUN_ONCE(&init_module_list_lock, do_init_module_list_lock)) return NULL; - if (!CRYPTO_THREAD_read_lock(module_list_lock)) - return NULL; + ossl_rcu_read_lock(module_list_lock); + mods = ossl_rcu_deref(&supported_modules); - for (i = 0; i < sk_CONF_MODULE_num(supported_modules); i++) { - tmod = sk_CONF_MODULE_value(supported_modules, i); + for (i = 0; i < sk_CONF_MODULE_num(mods); i++) { + tmod = sk_CONF_MODULE_value(mods, i); if (strncmp(tmod->name, name, nchar) == 0) { - CRYPTO_THREAD_unlock(module_list_lock); + ossl_rcu_read_unlock(module_list_lock); return tmod; } } - CRYPTO_THREAD_unlock(module_list_lock); + ossl_rcu_read_unlock(module_list_lock); return NULL; } @@ -406,6 +420,8 @@ static int module_init(CONF_MODULE *pmod, const char *name, const char *value, int ret = 1; int init_called = 0; CONF_IMODULE *imod = NULL; + STACK_OF(CONF_IMODULE) *old_modules; + STACK_OF(CONF_IMODULE) *new_modules; /* Otherwise add initialized module to list */ imod = OPENSSL_malloc(sizeof(*imod)); @@ -432,27 +448,33 @@ static int module_init(CONF_MODULE *pmod, const char *name, const char *value, if (!RUN_ONCE(&init_module_list_lock, do_init_module_list_lock)) goto err; - if (!CRYPTO_THREAD_write_lock(module_list_lock)) - goto err; + ossl_rcu_write_lock(module_list_lock); - if (initialized_modules == NULL) { - initialized_modules = sk_CONF_IMODULE_new_null(); - if (initialized_modules == NULL) { - CRYPTO_THREAD_unlock(module_list_lock); - ERR_raise(ERR_LIB_CONF, ERR_R_CRYPTO_LIB); - goto err; - } + old_modules = ossl_rcu_deref(&initialized_modules); + + if (old_modules == NULL) + new_modules = sk_CONF_IMODULE_new_null(); + else + new_modules = sk_CONF_IMODULE_dup(old_modules); + + if (new_modules == NULL) { + ossl_rcu_write_unlock(module_list_lock); + ERR_raise(ERR_LIB_CONF, ERR_R_CRYPTO_LIB); + goto err; } - if (!sk_CONF_IMODULE_push(initialized_modules, imod)) { - CRYPTO_THREAD_unlock(module_list_lock); + if (!sk_CONF_IMODULE_push(new_modules, imod)) { + ossl_rcu_write_unlock(module_list_lock); ERR_raise(ERR_LIB_CONF, ERR_R_CRYPTO_LIB); goto err; } pmod->links++; - CRYPTO_THREAD_unlock(module_list_lock); + ossl_rcu_assign_ptr(&initialized_modules, &new_modules); + ossl_rcu_write_unlock(module_list_lock); + ossl_synchronize_rcu(module_list_lock); + sk_CONF_IMODULE_free(old_modules); return ret; err: @@ -482,30 +504,46 @@ void CONF_modules_unload(int all) { int i; CONF_MODULE *md; + STACK_OF(CONF_MODULE) *old_modules; + STACK_OF(CONF_MODULE) *new_modules; + STACK_OF(CONF_MODULE) *to_delete; if (!conf_modules_finish_int()) /* also inits module list lock */ return; - if (!CRYPTO_THREAD_write_lock(module_list_lock)) + ossl_rcu_write_lock(module_list_lock); + + old_modules = ossl_rcu_deref(&supported_modules); + new_modules = sk_CONF_MODULE_dup(old_modules); + to_delete = sk_CONF_MODULE_new_null(); + + if (new_modules == NULL) { + ossl_rcu_write_unlock(module_list_lock); return; + } /* unload modules in reverse order */ - for (i = sk_CONF_MODULE_num(supported_modules) - 1; i >= 0; i--) { - md = sk_CONF_MODULE_value(supported_modules, i); + for (i = sk_CONF_MODULE_num(new_modules) - 1; i >= 0; i--) { + md = sk_CONF_MODULE_value(new_modules, i); /* If static or in use and 'all' not set ignore it */ if (((md->links > 0) || !md->dso) && !all) continue; /* Since we're working in reverse this is OK */ - (void)sk_CONF_MODULE_delete(supported_modules, i); - module_free(md); + (void)sk_CONF_MODULE_delete(new_modules, i); + sk_CONF_MODULE_push(to_delete, md); } - if (sk_CONF_MODULE_num(supported_modules) == 0) { - sk_CONF_MODULE_free(supported_modules); - supported_modules = NULL; + if (sk_CONF_MODULE_num(new_modules) == 0) { + sk_CONF_MODULE_free(new_modules); + new_modules = NULL; } - CRYPTO_THREAD_unlock(module_list_lock); + ossl_rcu_assign_ptr(&supported_modules, &new_modules); + ossl_rcu_write_unlock(module_list_lock); + ossl_synchronize_rcu(module_list_lock); + sk_CONF_MODULE_free(old_modules); + sk_CONF_MODULE_pop_free(to_delete, module_free); + } /* unload a single module */ @@ -521,23 +559,27 @@ static void module_free(CONF_MODULE *md) static int conf_modules_finish_int(void) { CONF_IMODULE *imod; + STACK_OF(CONF_IMODULE) *old_modules; + STACK_OF(CONF_IMODULE) *new_modules = NULL; if (!RUN_ONCE(&init_module_list_lock, do_init_module_list_lock)) return 0; /* If module_list_lock is NULL here it means we were already unloaded */ - if (module_list_lock == NULL - || !CRYPTO_THREAD_write_lock(module_list_lock)) + if (module_list_lock == NULL) return 0; - while (sk_CONF_IMODULE_num(initialized_modules) > 0) { - imod = sk_CONF_IMODULE_pop(initialized_modules); + ossl_rcu_write_lock(module_list_lock); + old_modules = ossl_rcu_deref(&initialized_modules); + ossl_rcu_assign_ptr(&initialized_modules, &new_modules); + ossl_rcu_write_unlock(module_list_lock); + ossl_synchronize_rcu(module_list_lock); + + while (sk_CONF_IMODULE_num(old_modules) > 0) { + imod = sk_CONF_IMODULE_pop(old_modules); module_finish(imod); } - sk_CONF_IMODULE_free(initialized_modules); - initialized_modules = NULL; - - CRYPTO_THREAD_unlock(module_list_lock); + sk_CONF_IMODULE_free(old_modules); return 1; }