aboutsummaryrefslogtreecommitdiff
path: root/crypto/block.c
diff options
context:
space:
mode:
authorVladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>2018-12-07 19:13:51 +0300
committerDaniel P. Berrangé <berrange@redhat.com>2018-12-12 11:16:49 +0000
commitc972fa123c73501b4b0c6de7873754ea3205a5eb (patch)
treebde968050ae58bb203988e66bfe1cbe7110d0fef /crypto/block.c
parent0f0d596cb16a43314c8bc4a9afa2f966203fb05f (diff)
crypto: support multiple threads accessing one QCryptoBlock
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 <vsementsov@virtuozzo.com> Reviewed-by: Alberto Garcia <berto@igalia.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
Diffstat (limited to 'crypto/block.c')
-rw-r--r--crypto/block.c146
1 files changed, 127 insertions, 19 deletions
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;
}