qemu/block/qcow2-threads.c
<<
>>
Prefs
   1/*
   2 * Threaded data processing for Qcow2: compression, encryption
   3 *
   4 * Copyright (c) 2004-2006 Fabrice Bellard
   5 * Copyright (c) 2018 Virtuozzo International GmbH. All rights reserved.
   6 *
   7 * Permission is hereby granted, free of charge, to any person obtaining a copy
   8 * of this software and associated documentation files (the "Software"), to deal
   9 * in the Software without restriction, including without limitation the rights
  10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11 * copies of the Software, and to permit persons to whom the Software is
  12 * furnished to do so, subject to the following conditions:
  13 *
  14 * The above copyright notice and this permission notice shall be included in
  15 * all copies or substantial portions of the Software.
  16 *
  17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23 * THE SOFTWARE.
  24 */
  25
  26#include "qemu/osdep.h"
  27
  28#define ZLIB_CONST
  29#include <zlib.h>
  30
  31#include "qcow2.h"
  32#include "block/thread-pool.h"
  33#include "crypto.h"
  34
  35static int coroutine_fn
  36qcow2_co_process(BlockDriverState *bs, ThreadPoolFunc *func, void *arg)
  37{
  38    int ret;
  39    BDRVQcow2State *s = bs->opaque;
  40    ThreadPool *pool = aio_get_thread_pool(bdrv_get_aio_context(bs));
  41
  42    qemu_co_mutex_lock(&s->lock);
  43    while (s->nb_threads >= QCOW2_MAX_THREADS) {
  44        qemu_co_queue_wait(&s->thread_task_queue, &s->lock);
  45    }
  46    s->nb_threads++;
  47    qemu_co_mutex_unlock(&s->lock);
  48
  49    ret = thread_pool_submit_co(pool, func, arg);
  50
  51    qemu_co_mutex_lock(&s->lock);
  52    s->nb_threads--;
  53    qemu_co_queue_next(&s->thread_task_queue);
  54    qemu_co_mutex_unlock(&s->lock);
  55
  56    return ret;
  57}
  58
  59
  60/*
  61 * Compression
  62 */
  63
  64typedef ssize_t (*Qcow2CompressFunc)(void *dest, size_t dest_size,
  65                                     const void *src, size_t src_size);
  66typedef struct Qcow2CompressData {
  67    void *dest;
  68    size_t dest_size;
  69    const void *src;
  70    size_t src_size;
  71    ssize_t ret;
  72
  73    Qcow2CompressFunc func;
  74} Qcow2CompressData;
  75
  76/*
  77 * qcow2_compress()
  78 *
  79 * @dest - destination buffer, @dest_size bytes
  80 * @src - source buffer, @src_size bytes
  81 *
  82 * Returns: compressed size on success
  83 *          -ENOMEM destination buffer is not enough to store compressed data
  84 *          -EIO    on any other error
  85 */
  86static ssize_t qcow2_compress(void *dest, size_t dest_size,
  87                              const void *src, size_t src_size)
  88{
  89    ssize_t ret;
  90    z_stream strm;
  91
  92    /* best compression, small window, no zlib header */
  93    memset(&strm, 0, sizeof(strm));
  94    ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
  95                       -12, 9, Z_DEFAULT_STRATEGY);
  96    if (ret != Z_OK) {
  97        return -EIO;
  98    }
  99
 100    /*
 101     * strm.next_in is not const in old zlib versions, such as those used on
 102     * OpenBSD/NetBSD, so cast the const away
 103     */
 104    strm.avail_in = src_size;
 105    strm.next_in = (void *) src;
 106    strm.avail_out = dest_size;
 107    strm.next_out = dest;
 108
 109    ret = deflate(&strm, Z_FINISH);
 110    if (ret == Z_STREAM_END) {
 111        ret = dest_size - strm.avail_out;
 112    } else {
 113        ret = (ret == Z_OK ? -ENOMEM : -EIO);
 114    }
 115
 116    deflateEnd(&strm);
 117
 118    return ret;
 119}
 120
 121/*
 122 * qcow2_decompress()
 123 *
 124 * Decompress some data (not more than @src_size bytes) to produce exactly
 125 * @dest_size bytes.
 126 *
 127 * @dest - destination buffer, @dest_size bytes
 128 * @src - source buffer, @src_size bytes
 129 *
 130 * Returns: 0 on success
 131 *          -EIO on fail
 132 */
 133static ssize_t qcow2_decompress(void *dest, size_t dest_size,
 134                                const void *src, size_t src_size)
 135{
 136    int ret;
 137    z_stream strm;
 138
 139    memset(&strm, 0, sizeof(strm));
 140    strm.avail_in = src_size;
 141    strm.next_in = (void *) src;
 142    strm.avail_out = dest_size;
 143    strm.next_out = dest;
 144
 145    ret = inflateInit2(&strm, -12);
 146    if (ret != Z_OK) {
 147        return -EIO;
 148    }
 149
 150    ret = inflate(&strm, Z_FINISH);
 151    if ((ret == Z_STREAM_END || ret == Z_BUF_ERROR) && strm.avail_out == 0) {
 152        /*
 153         * We approve Z_BUF_ERROR because we need @dest buffer to be filled, but
 154         * @src buffer may be processed partly (because in qcow2 we know size of
 155         * compressed data with precision of one sector)
 156         */
 157        ret = 0;
 158    } else {
 159        ret = -EIO;
 160    }
 161
 162    inflateEnd(&strm);
 163
 164    return ret;
 165}
 166
 167static int qcow2_compress_pool_func(void *opaque)
 168{
 169    Qcow2CompressData *data = opaque;
 170
 171    data->ret = data->func(data->dest, data->dest_size,
 172                           data->src, data->src_size);
 173
 174    return 0;
 175}
 176
 177static ssize_t coroutine_fn
 178qcow2_co_do_compress(BlockDriverState *bs, void *dest, size_t dest_size,
 179                     const void *src, size_t src_size, Qcow2CompressFunc func)
 180{
 181    Qcow2CompressData arg = {
 182        .dest = dest,
 183        .dest_size = dest_size,
 184        .src = src,
 185        .src_size = src_size,
 186        .func = func,
 187    };
 188
 189    qcow2_co_process(bs, qcow2_compress_pool_func, &arg);
 190
 191    return arg.ret;
 192}
 193
 194ssize_t coroutine_fn
 195qcow2_co_compress(BlockDriverState *bs, void *dest, size_t dest_size,
 196                  const void *src, size_t src_size)
 197{
 198    return qcow2_co_do_compress(bs, dest, dest_size, src, src_size,
 199                                qcow2_compress);
 200}
 201
 202ssize_t coroutine_fn
 203qcow2_co_decompress(BlockDriverState *bs, void *dest, size_t dest_size,
 204                    const void *src, size_t src_size)
 205{
 206    return qcow2_co_do_compress(bs, dest, dest_size, src, src_size,
 207                                qcow2_decompress);
 208}
 209
 210
 211/*
 212 * Cryptography
 213 */
 214
 215/*
 216 * Qcow2EncDecFunc: common prototype of qcrypto_block_encrypt() and
 217 * qcrypto_block_decrypt() functions.
 218 */
 219typedef int (*Qcow2EncDecFunc)(QCryptoBlock *block, uint64_t offset,
 220                               uint8_t *buf, size_t len, Error **errp);
 221
 222typedef struct Qcow2EncDecData {
 223    QCryptoBlock *block;
 224    uint64_t offset;
 225    uint8_t *buf;
 226    size_t len;
 227
 228    Qcow2EncDecFunc func;
 229} Qcow2EncDecData;
 230
 231static int qcow2_encdec_pool_func(void *opaque)
 232{
 233    Qcow2EncDecData *data = opaque;
 234
 235    return data->func(data->block, data->offset, data->buf, data->len, NULL);
 236}
 237
 238static int coroutine_fn
 239qcow2_co_encdec(BlockDriverState *bs, uint64_t host_offset,
 240                uint64_t guest_offset, void *buf, size_t len,
 241                Qcow2EncDecFunc func)
 242{
 243    BDRVQcow2State *s = bs->opaque;
 244    Qcow2EncDecData arg = {
 245        .block = s->crypto,
 246        .offset = s->crypt_physical_offset ? host_offset : guest_offset,
 247        .buf = buf,
 248        .len = len,
 249        .func = func,
 250    };
 251    uint64_t sector_size;
 252
 253    assert(s->crypto);
 254
 255    sector_size = qcrypto_block_get_sector_size(s->crypto);
 256    assert(QEMU_IS_ALIGNED(guest_offset, sector_size));
 257    assert(QEMU_IS_ALIGNED(host_offset, sector_size));
 258    assert(QEMU_IS_ALIGNED(len, sector_size));
 259
 260    return len == 0 ? 0 : qcow2_co_process(bs, qcow2_encdec_pool_func, &arg);
 261}
 262
 263/*
 264 * qcow2_co_encrypt()
 265 *
 266 * Encrypts one or more contiguous aligned sectors
 267 *
 268 * @host_offset - underlying storage offset of the first sector of the
 269 * data to be encrypted
 270 *
 271 * @guest_offset - guest (virtual) offset of the first sector of the
 272 * data to be encrypted
 273 *
 274 * @buf - buffer with the data to encrypt, that after encryption
 275 *        will be written to the underlying storage device at
 276 *        @host_offset
 277 *
 278 * @len - length of the buffer (must be a multiple of the encryption
 279 *        sector size)
 280 *
 281 * Depending on the encryption method, @host_offset and/or @guest_offset
 282 * may be used for generating the initialization vector for
 283 * encryption.
 284 *
 285 * Note that while the whole range must be aligned on sectors, it
 286 * does not have to be aligned on clusters and can also cross cluster
 287 * boundaries
 288 */
 289int coroutine_fn
 290qcow2_co_encrypt(BlockDriverState *bs, uint64_t host_offset,
 291                 uint64_t guest_offset, void *buf, size_t len)
 292{
 293    return qcow2_co_encdec(bs, host_offset, guest_offset, buf, len,
 294                           qcrypto_block_encrypt);
 295}
 296
 297/*
 298 * qcow2_co_decrypt()
 299 *
 300 * Decrypts one or more contiguous aligned sectors
 301 * Similar to qcow2_co_encrypt
 302 */
 303int coroutine_fn
 304qcow2_co_decrypt(BlockDriverState *bs, uint64_t host_offset,
 305                 uint64_t guest_offset, void *buf, size_t len)
 306{
 307    return qcow2_co_encdec(bs, host_offset, guest_offset, buf, len,
 308                           qcrypto_block_decrypt);
 309}
 310