Skip to content

Commit

Permalink
crypto: support multiple threads accessing one QCryptoBlock
Browse files Browse the repository at this point in the history
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 <[email protected]>
Reviewed-by: Alberto Garcia <[email protected]>
Signed-off-by: Daniel P. Berrangé <[email protected]>
  • Loading branch information
Vladimir Sementsov-Ogievskiy authored and berrange committed Dec 12, 2018
1 parent 0f0d596 commit c972fa1
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 44 deletions.
1 change: 1 addition & 0 deletions block/crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
block_crypto_read_func,
bs,
cflags,
1,
errp);

if (!crypto->block) {
Expand Down
2 changes: 1 addition & 1 deletion block/qcow.c
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
cflags |= QCRYPTO_BLOCK_OPEN_NO_IO;
}
s->crypto = qcrypto_block_open(crypto_opts, "encrypt.",
NULL, NULL, cflags, errp);
NULL, NULL, cflags, 1, errp);
if (!s->crypto) {
ret = -EINVAL;
goto fail;
Expand Down
4 changes: 2 additions & 2 deletions block/qcow2.c
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
}
s->crypto = qcrypto_block_open(s->crypto_opts, "encrypt.",
qcow2_crypto_hdr_read_func,
bs, cflags, errp);
bs, cflags, 1, errp);
if (!s->crypto) {
return -EINVAL;
}
Expand Down Expand Up @@ -1445,7 +1445,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
cflags |= QCRYPTO_BLOCK_OPEN_NO_IO;
}
s->crypto = qcrypto_block_open(s->crypto_opts, "encrypt.",
NULL, NULL, cflags, errp);
NULL, NULL, cflags, 1, errp);
if (!s->crypto) {
ret = -EINVAL;
goto fail;
Expand Down
22 changes: 10 additions & 12 deletions crypto/block-luks.c
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
QCryptoBlockReadFunc readfunc,
void *opaque,
unsigned int flags,
size_t n_threads,
Error **errp)
{
QCryptoBlockLUKS *luks;
Expand Down Expand Up @@ -836,11 +837,10 @@ qcrypto_block_luks_open(QCryptoBlock *block,
goto fail;
}

block->cipher = qcrypto_cipher_new(cipheralg,
ciphermode,
masterkey, masterkeylen,
errp);
if (!block->cipher) {
ret = qcrypto_block_init_cipher(block, cipheralg, ciphermode,
masterkey, masterkeylen, n_threads,
errp);
if (ret < 0) {
ret = -ENOTSUP;
goto fail;
}
Expand All @@ -863,7 +863,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,

fail:
g_free(masterkey);
qcrypto_cipher_free(block->cipher);
qcrypto_block_free_cipher(block);
qcrypto_ivgen_free(block->ivgen);
g_free(luks);
g_free(password);
Expand Down Expand Up @@ -1030,11 +1030,9 @@ qcrypto_block_luks_create(QCryptoBlock *block,


/* Setup the block device payload encryption objects */
block->cipher = qcrypto_cipher_new(luks_opts.cipher_alg,
luks_opts.cipher_mode,
masterkey, luks->header.key_bytes,
errp);
if (!block->cipher) {
if (qcrypto_block_init_cipher(block, luks_opts.cipher_alg,
luks_opts.cipher_mode, masterkey,
luks->header.key_bytes, 1, errp) < 0) {
goto error;
}

Expand Down Expand Up @@ -1341,7 +1339,7 @@ qcrypto_block_luks_create(QCryptoBlock *block,
qcrypto_ivgen_free(ivgen);
qcrypto_cipher_free(cipher);

qcrypto_cipher_free(block->cipher);
qcrypto_block_free_cipher(block);
qcrypto_ivgen_free(block->ivgen);

g_free(luks);
Expand Down
20 changes: 11 additions & 9 deletions crypto/block-qcow.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ qcrypto_block_qcow_has_format(const uint8_t *buf G_GNUC_UNUSED,
static int
qcrypto_block_qcow_init(QCryptoBlock *block,
const char *keysecret,
size_t n_threads,
Error **errp)
{
char *password;
Expand Down Expand Up @@ -71,11 +72,11 @@ qcrypto_block_qcow_init(QCryptoBlock *block,
goto fail;
}

block->cipher = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_128,
QCRYPTO_CIPHER_MODE_CBC,
keybuf, G_N_ELEMENTS(keybuf),
errp);
if (!block->cipher) {
ret = qcrypto_block_init_cipher(block, QCRYPTO_CIPHER_ALG_AES_128,
QCRYPTO_CIPHER_MODE_CBC,
keybuf, G_N_ELEMENTS(keybuf),
n_threads, errp);
if (ret < 0) {
ret = -ENOTSUP;
goto fail;
}
Expand All @@ -86,7 +87,7 @@ qcrypto_block_qcow_init(QCryptoBlock *block,
return 0;

fail:
qcrypto_cipher_free(block->cipher);
qcrypto_block_free_cipher(block);
qcrypto_ivgen_free(block->ivgen);
return ret;
}
Expand All @@ -99,6 +100,7 @@ qcrypto_block_qcow_open(QCryptoBlock *block,
QCryptoBlockReadFunc readfunc G_GNUC_UNUSED,
void *opaque G_GNUC_UNUSED,
unsigned int flags,
size_t n_threads,
Error **errp)
{
if (flags & QCRYPTO_BLOCK_OPEN_NO_IO) {
Expand All @@ -112,8 +114,8 @@ qcrypto_block_qcow_open(QCryptoBlock *block,
optprefix ? optprefix : "");
return -1;
}
return qcrypto_block_qcow_init(block,
options->u.qcow.key_secret, errp);
return qcrypto_block_qcow_init(block, options->u.qcow.key_secret,
n_threads, errp);
}
}

Expand All @@ -133,7 +135,7 @@ qcrypto_block_qcow_create(QCryptoBlock *block,
return -1;
}
/* QCow2 has no special header, since everything is hardwired */
return qcrypto_block_qcow_init(block, options->u.qcow.key_secret, errp);
return qcrypto_block_qcow_init(block, options->u.qcow.key_secret, 1, errp);
}


Expand Down
146 changes: 127 additions & 19 deletions crypto/block.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
}

Expand Down Expand Up @@ -105,6 +109,8 @@ QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options,
return NULL;
}

qemu_mutex_init(&block->mutex);

return block;
}

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
}

Expand All @@ -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,
Expand All @@ -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;
}

Expand Down Expand Up @@ -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);
}
Expand All @@ -273,25 +371,29 @@ 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,
uint8_t *buf,
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,
Expand All @@ -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;
}
Loading

0 comments on commit c972fa1

Please sign in to comment.