From c972fa123c73501b4b0c6de7873754ea3205a5eb Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Fri, 7 Dec 2018 19:13:51 +0300 Subject: crypto: support multiple threads accessing one QCryptoBlock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two thing that should be handled are cipher and ivgen. For ivgen the solution is just mutex, as iv calculations should not be long in comparison with encryption/decryption. And for cipher let's just keep per-thread ciphers. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Alberto Garcia Signed-off-by: Daniel P. Berrangé --- crypto/block.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 127 insertions(+), 19 deletions(-) (limited to 'crypto/block.c') diff --git a/crypto/block.c b/crypto/block.c index 3fe3de2ef8..d70d401f87 100644 --- a/crypto/block.c +++ b/crypto/block.c @@ -52,6 +52,7 @@ QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options, QCryptoBlockReadFunc readfunc, void *opaque, unsigned int flags, + size_t n_threads, Error **errp) { QCryptoBlock *block = g_new0(QCryptoBlock, 1); @@ -69,11 +70,14 @@ QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options, block->driver = qcrypto_block_drivers[options->format]; if (block->driver->open(block, options, optprefix, - readfunc, opaque, flags, errp) < 0) { + readfunc, opaque, flags, n_threads, errp) < 0) + { g_free(block); return NULL; } + qemu_mutex_init(&block->mutex); + return block; } @@ -105,6 +109,8 @@ QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options, return NULL; } + qemu_mutex_init(&block->mutex); + return block; } @@ -148,12 +154,97 @@ int qcrypto_block_encrypt(QCryptoBlock *block, QCryptoCipher *qcrypto_block_get_cipher(QCryptoBlock *block) { - return block->cipher; + /* Ciphers should be accessed through pop/push method to be thread-safe. + * Better, they should not be accessed externally at all (note, that + * pop/push are static functions) + * This function is used only in test with one thread (it's safe to skip + * pop/push interface), so it's enough to assert it here: + */ + assert(block->n_ciphers <= 1); + return block->ciphers ? block->ciphers[0] : NULL; +} + + +static QCryptoCipher *qcrypto_block_pop_cipher(QCryptoBlock *block) +{ + QCryptoCipher *cipher; + + qemu_mutex_lock(&block->mutex); + + assert(block->n_free_ciphers > 0); + block->n_free_ciphers--; + cipher = block->ciphers[block->n_free_ciphers]; + + qemu_mutex_unlock(&block->mutex); + + return cipher; +} + + +static void qcrypto_block_push_cipher(QCryptoBlock *block, + QCryptoCipher *cipher) +{ + qemu_mutex_lock(&block->mutex); + + assert(block->n_free_ciphers < block->n_ciphers); + block->ciphers[block->n_free_ciphers] = cipher; + block->n_free_ciphers++; + + qemu_mutex_unlock(&block->mutex); +} + + +int qcrypto_block_init_cipher(QCryptoBlock *block, + QCryptoCipherAlgorithm alg, + QCryptoCipherMode mode, + const uint8_t *key, size_t nkey, + size_t n_threads, Error **errp) +{ + size_t i; + + assert(!block->ciphers && !block->n_ciphers && !block->n_free_ciphers); + + block->ciphers = g_new0(QCryptoCipher *, n_threads); + + for (i = 0; i < n_threads; i++) { + block->ciphers[i] = qcrypto_cipher_new(alg, mode, key, nkey, errp); + if (!block->ciphers[i]) { + qcrypto_block_free_cipher(block); + return -1; + } + block->n_ciphers++; + block->n_free_ciphers++; + } + + return 0; } +void qcrypto_block_free_cipher(QCryptoBlock *block) +{ + size_t i; + + if (!block->ciphers) { + return; + } + + assert(block->n_ciphers == block->n_free_ciphers); + + for (i = 0; i < block->n_ciphers; i++) { + qcrypto_cipher_free(block->ciphers[i]); + } + + g_free(block->ciphers); + block->ciphers = NULL; + block->n_ciphers = block->n_free_ciphers = 0; +} + QCryptoIVGen *qcrypto_block_get_ivgen(QCryptoBlock *block) { + /* ivgen should be accessed under mutex. However, this function is used only + * in test with one thread, so it's enough to assert it here: + */ + assert(block->n_ciphers <= 1); return block->ivgen; } @@ -184,8 +275,9 @@ void qcrypto_block_free(QCryptoBlock *block) block->driver->cleanup(block); - qcrypto_cipher_free(block->cipher); + qcrypto_block_free_cipher(block); qcrypto_ivgen_free(block->ivgen); + qemu_mutex_destroy(&block->mutex); g_free(block); } @@ -199,6 +291,7 @@ typedef int (*QCryptoCipherEncDecFunc)(QCryptoCipher *cipher, static int do_qcrypto_block_cipher_encdec(QCryptoCipher *cipher, size_t niv, QCryptoIVGen *ivgen, + QemuMutex *ivgen_mutex, int sectorsize, uint64_t offset, uint8_t *buf, @@ -218,10 +311,15 @@ static int do_qcrypto_block_cipher_encdec(QCryptoCipher *cipher, while (len > 0) { size_t nbytes; if (niv) { - if (qcrypto_ivgen_calculate(ivgen, - startsector, - iv, niv, - errp) < 0) { + if (ivgen_mutex) { + qemu_mutex_lock(ivgen_mutex); + } + ret = qcrypto_ivgen_calculate(ivgen, startsector, iv, niv, errp); + if (ivgen_mutex) { + qemu_mutex_unlock(ivgen_mutex); + } + + if (ret < 0) { goto cleanup; } @@ -258,7 +356,7 @@ int qcrypto_block_cipher_decrypt_helper(QCryptoCipher *cipher, size_t len, Error **errp) { - return do_qcrypto_block_cipher_encdec(cipher, niv, ivgen, sectorsize, + return do_qcrypto_block_cipher_encdec(cipher, niv, ivgen, NULL, sectorsize, offset, buf, len, qcrypto_cipher_decrypt, errp); } @@ -273,12 +371,11 @@ int qcrypto_block_cipher_encrypt_helper(QCryptoCipher *cipher, size_t len, Error **errp) { - return do_qcrypto_block_cipher_encdec(cipher, niv, ivgen, sectorsize, + return do_qcrypto_block_cipher_encdec(cipher, niv, ivgen, NULL, sectorsize, offset, buf, len, qcrypto_cipher_encrypt, errp); } - int qcrypto_block_decrypt_helper(QCryptoBlock *block, int sectorsize, uint64_t offset, @@ -286,12 +383,17 @@ int qcrypto_block_decrypt_helper(QCryptoBlock *block, size_t len, Error **errp) { - return do_qcrypto_block_cipher_encdec(block->cipher, block->niv, - block->ivgen, - sectorsize, offset, buf, len, - qcrypto_cipher_decrypt, errp); -} + int ret; + QCryptoCipher *cipher = qcrypto_block_pop_cipher(block); + ret = do_qcrypto_block_cipher_encdec(cipher, block->niv, block->ivgen, + &block->mutex, sectorsize, offset, buf, + len, qcrypto_cipher_decrypt, errp); + + qcrypto_block_push_cipher(block, cipher); + + return ret; +} int qcrypto_block_encrypt_helper(QCryptoBlock *block, int sectorsize, @@ -300,8 +402,14 @@ int qcrypto_block_encrypt_helper(QCryptoBlock *block, size_t len, Error **errp) { - return do_qcrypto_block_cipher_encdec(block->cipher, block->niv, - block->ivgen, - sectorsize, offset, buf, len, - qcrypto_cipher_encrypt, errp); + int ret; + QCryptoCipher *cipher = qcrypto_block_pop_cipher(block); + + ret = do_qcrypto_block_cipher_encdec(cipher, block->niv, block->ivgen, + &block->mutex, sectorsize, offset, buf, + len, qcrypto_cipher_encrypt, errp); + + qcrypto_block_push_cipher(block, cipher); + + return ret; } -- cgit v1.2.3