summaryrefslogtreecommitdiffstats
path: root/crypto
diff options
context:
space:
mode:
authorGeoff Thorpe <geoff@openssl.org>2014-05-04 16:19:22 -0400
committerGeoff Thorpe <geoff@openssl.org>2014-05-06 18:23:49 -0400
commita721216f0f6f7ca16b5fa3630ee06d577542ab15 (patch)
tree3adc6831126c1d7878a4e99b1c079fce81b9bf12 /crypto
parent47f689ac096dcf6e507765a722cd3ed3a9bf92db (diff)
bignum: allow concurrent BN_MONT_CTX_set_locked()
The lazy-initialisation of BN_MONT_CTX was serialising all threads, as noted by Daniel Sands and co at Sandia. This was to handle the case that 2 or more threads race to lazy-init the same context, but stunted all scalability in the case where 2 or more threads are doing unrelated things! We favour the latter case by punishing the former. The init work gets done by each thread that finds the context to be uninitialised, and we then lock the "set" logic after that work is done - the winning thread's work gets used, the losing threads throw away what they've done. Signed-off-by: Geoff Thorpe <geoff@openssl.org>
Diffstat (limited to 'crypto')
-rw-r--r--crypto/bn/bn_mont.c46
1 files changed, 26 insertions, 20 deletions
diff --git a/crypto/bn/bn_mont.c b/crypto/bn/bn_mont.c
index 4799b152dd..27cafb1f36 100644
--- a/crypto/bn/bn_mont.c
+++ b/crypto/bn/bn_mont.c
@@ -701,32 +701,38 @@ BN_MONT_CTX *BN_MONT_CTX_copy(BN_MONT_CTX *to, BN_MONT_CTX *from)
BN_MONT_CTX *BN_MONT_CTX_set_locked(BN_MONT_CTX **pmont, int lock,
const BIGNUM *mod, BN_CTX *ctx)
{
- int got_write_lock = 0;
BN_MONT_CTX *ret;
CRYPTO_r_lock(lock);
- if (!*pmont)
+ ret = *pmont;
+ CRYPTO_r_unlock(lock);
+ if (ret)
+ return ret;
+
+ /* We don't want to serialise globally while doing our lazy-init math in
+ * BN_MONT_CTX_set. That punishes threads that are doing independent
+ * things. Instead, punish the case where more than one thread tries to
+ * lazy-init the same 'pmont', by having each do the lazy-init math work
+ * independently and only use the one from the thread that wins the race
+ * (the losers throw away the work they've done). */
+ ret = BN_MONT_CTX_new();
+ if (!ret)
+ return NULL;
+ if (!BN_MONT_CTX_set(ret, mod, ctx))
{
- CRYPTO_r_unlock(lock);
- CRYPTO_w_lock(lock);
- got_write_lock = 1;
+ BN_MONT_CTX_free(ret);
+ return NULL;
+ }
- if (!*pmont)
- {
- ret = BN_MONT_CTX_new();
- if (ret && !BN_MONT_CTX_set(ret, mod, ctx))
- BN_MONT_CTX_free(ret);
- else
- *pmont = ret;
- }
+ /* The locked compare-and-set, after the local work is done. */
+ CRYPTO_w_lock(lock);
+ if (*pmont)
+ {
+ BN_MONT_CTX_free(ret);
+ ret = *pmont;
}
-
- ret = *pmont;
-
- if (got_write_lock)
- CRYPTO_w_unlock(lock);
else
- CRYPTO_r_unlock(lock);
-
+ *pmont = ret;
+ CRYPTO_w_unlock(lock);
return ret;
}