qemu/block/copy-on-read.c
<<
>>
Prefs
   1/*
   2 * Copy-on-read filter block driver
   3 *
   4 * Copyright (c) 2018 Red Hat, Inc.
   5 *
   6 * Author:
   7 *   Max Reitz <mreitz@redhat.com>
   8 *
   9 * This program is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU General Public License as
  11 * published by the Free Software Foundation; either version 2 or
  12 * (at your option) version 3 of the License.
  13 *
  14 * This program is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17 * GNU General Public License for more details.
  18 *
  19 * You should have received a copy of the GNU General Public License
  20 * along with this program; if not, see <http://www.gnu.org/licenses/>.
  21 */
  22
  23#include "qemu/osdep.h"
  24#include "block/block_int.h"
  25#include "qemu/module.h"
  26#include "qapi/error.h"
  27#include "qapi/qmp/qdict.h"
  28#include "block/copy-on-read.h"
  29
  30
  31typedef struct BDRVStateCOR {
  32    BlockDriverState *bottom_bs;
  33    bool chain_frozen;
  34} BDRVStateCOR;
  35
  36
  37static int cor_open(BlockDriverState *bs, QDict *options, int flags,
  38                    Error **errp)
  39{
  40    BlockDriverState *bottom_bs = NULL;
  41    BDRVStateCOR *state = bs->opaque;
  42    /* Find a bottom node name, if any */
  43    const char *bottom_node = qdict_get_try_str(options, "bottom");
  44
  45    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
  46                               BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
  47                               false, errp);
  48    if (!bs->file) {
  49        return -EINVAL;
  50    }
  51
  52    bs->supported_read_flags = BDRV_REQ_PREFETCH;
  53
  54    bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
  55        (BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
  56
  57    bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
  58        ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
  59            bs->file->bs->supported_zero_flags);
  60
  61    if (bottom_node) {
  62        bottom_bs = bdrv_find_node(bottom_node);
  63        if (!bottom_bs) {
  64            error_setg(errp, "Bottom node '%s' not found", bottom_node);
  65            qdict_del(options, "bottom");
  66            return -EINVAL;
  67        }
  68        qdict_del(options, "bottom");
  69
  70        if (!bottom_bs->drv) {
  71            error_setg(errp, "Bottom node '%s' not opened", bottom_node);
  72            return -EINVAL;
  73        }
  74
  75        if (bottom_bs->drv->is_filter) {
  76            error_setg(errp, "Bottom node '%s' is a filter", bottom_node);
  77            return -EINVAL;
  78        }
  79
  80        if (bdrv_freeze_backing_chain(bs, bottom_bs, errp) < 0) {
  81            return -EINVAL;
  82        }
  83        state->chain_frozen = true;
  84
  85        /*
  86         * We do freeze the chain, so it shouldn't be removed. Still, storing a
  87         * pointer worth bdrv_ref().
  88         */
  89        bdrv_ref(bottom_bs);
  90    }
  91    state->bottom_bs = bottom_bs;
  92
  93    /*
  94     * We don't need to call bdrv_child_refresh_perms() now as the permissions
  95     * will be updated later when the filter node gets its parent.
  96     */
  97
  98    return 0;
  99}
 100
 101
 102#define PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \
 103                          | BLK_PERM_WRITE \
 104                          | BLK_PERM_RESIZE)
 105#define PERM_UNCHANGED (BLK_PERM_ALL & ~PERM_PASSTHROUGH)
 106
 107static void cor_child_perm(BlockDriverState *bs, BdrvChild *c,
 108                           BdrvChildRole role,
 109                           BlockReopenQueue *reopen_queue,
 110                           uint64_t perm, uint64_t shared,
 111                           uint64_t *nperm, uint64_t *nshared)
 112{
 113    *nperm = perm & PERM_PASSTHROUGH;
 114    *nshared = (shared & PERM_PASSTHROUGH) | PERM_UNCHANGED;
 115
 116    /* We must not request write permissions for an inactive node, the child
 117     * cannot provide it. */
 118    if (!(bs->open_flags & BDRV_O_INACTIVE)) {
 119        *nperm |= BLK_PERM_WRITE_UNCHANGED;
 120    }
 121}
 122
 123
 124static int64_t cor_getlength(BlockDriverState *bs)
 125{
 126    return bdrv_getlength(bs->file->bs);
 127}
 128
 129
 130static int coroutine_fn cor_co_preadv_part(BlockDriverState *bs,
 131                                           int64_t offset, int64_t bytes,
 132                                           QEMUIOVector *qiov,
 133                                           size_t qiov_offset,
 134                                           BdrvRequestFlags flags)
 135{
 136    int64_t n;
 137    int local_flags;
 138    int ret;
 139    BDRVStateCOR *state = bs->opaque;
 140
 141    if (!state->bottom_bs) {
 142        return bdrv_co_preadv_part(bs->file, offset, bytes, qiov, qiov_offset,
 143                                   flags | BDRV_REQ_COPY_ON_READ);
 144    }
 145
 146    while (bytes) {
 147        local_flags = flags;
 148
 149        /* In case of failure, try to copy-on-read anyway */
 150        ret = bdrv_is_allocated(bs->file->bs, offset, bytes, &n);
 151        if (ret <= 0) {
 152            ret = bdrv_is_allocated_above(bdrv_backing_chain_next(bs->file->bs),
 153                                          state->bottom_bs, true, offset,
 154                                          n, &n);
 155            if (ret > 0 || ret < 0) {
 156                local_flags |= BDRV_REQ_COPY_ON_READ;
 157            }
 158            /* Finish earlier if the end of a backing file has been reached */
 159            if (n == 0) {
 160                break;
 161            }
 162        }
 163
 164        /* Skip if neither read nor write are needed */
 165        if ((local_flags & (BDRV_REQ_PREFETCH | BDRV_REQ_COPY_ON_READ)) !=
 166            BDRV_REQ_PREFETCH) {
 167            ret = bdrv_co_preadv_part(bs->file, offset, n, qiov, qiov_offset,
 168                                      local_flags);
 169            if (ret < 0) {
 170                return ret;
 171            }
 172        }
 173
 174        offset += n;
 175        qiov_offset += n;
 176        bytes -= n;
 177    }
 178
 179    return 0;
 180}
 181
 182
 183static int coroutine_fn cor_co_pwritev_part(BlockDriverState *bs,
 184                                            int64_t offset,
 185                                            int64_t bytes,
 186                                            QEMUIOVector *qiov,
 187                                            size_t qiov_offset,
 188                                            BdrvRequestFlags flags)
 189{
 190    return bdrv_co_pwritev_part(bs->file, offset, bytes, qiov, qiov_offset,
 191                                flags);
 192}
 193
 194
 195static int coroutine_fn cor_co_pwrite_zeroes(BlockDriverState *bs,
 196                                             int64_t offset, int64_t bytes,
 197                                             BdrvRequestFlags flags)
 198{
 199    return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
 200}
 201
 202
 203static int coroutine_fn cor_co_pdiscard(BlockDriverState *bs,
 204                                        int64_t offset, int64_t bytes)
 205{
 206    return bdrv_co_pdiscard(bs->file, offset, bytes);
 207}
 208
 209
 210static int coroutine_fn cor_co_pwritev_compressed(BlockDriverState *bs,
 211                                                  int64_t offset,
 212                                                  int64_t bytes,
 213                                                  QEMUIOVector *qiov)
 214{
 215    return bdrv_co_pwritev(bs->file, offset, bytes, qiov,
 216                           BDRV_REQ_WRITE_COMPRESSED);
 217}
 218
 219
 220static void cor_eject(BlockDriverState *bs, bool eject_flag)
 221{
 222    bdrv_eject(bs->file->bs, eject_flag);
 223}
 224
 225
 226static void cor_lock_medium(BlockDriverState *bs, bool locked)
 227{
 228    bdrv_lock_medium(bs->file->bs, locked);
 229}
 230
 231
 232static void cor_close(BlockDriverState *bs)
 233{
 234    BDRVStateCOR *s = bs->opaque;
 235
 236    if (s->chain_frozen) {
 237        s->chain_frozen = false;
 238        bdrv_unfreeze_backing_chain(bs, s->bottom_bs);
 239    }
 240
 241    bdrv_unref(s->bottom_bs);
 242}
 243
 244
 245static BlockDriver bdrv_copy_on_read = {
 246    .format_name                        = "copy-on-read",
 247    .instance_size                      = sizeof(BDRVStateCOR),
 248
 249    .bdrv_open                          = cor_open,
 250    .bdrv_close                         = cor_close,
 251    .bdrv_child_perm                    = cor_child_perm,
 252
 253    .bdrv_getlength                     = cor_getlength,
 254
 255    .bdrv_co_preadv_part                = cor_co_preadv_part,
 256    .bdrv_co_pwritev_part               = cor_co_pwritev_part,
 257    .bdrv_co_pwrite_zeroes              = cor_co_pwrite_zeroes,
 258    .bdrv_co_pdiscard                   = cor_co_pdiscard,
 259    .bdrv_co_pwritev_compressed         = cor_co_pwritev_compressed,
 260
 261    .bdrv_eject                         = cor_eject,
 262    .bdrv_lock_medium                   = cor_lock_medium,
 263
 264    .has_variable_length                = true,
 265    .is_filter                          = true,
 266};
 267
 268
 269void bdrv_cor_filter_drop(BlockDriverState *cor_filter_bs)
 270{
 271    BDRVStateCOR *s = cor_filter_bs->opaque;
 272
 273    /* unfreeze, as otherwise bdrv_replace_node() will fail */
 274    if (s->chain_frozen) {
 275        s->chain_frozen = false;
 276        bdrv_unfreeze_backing_chain(cor_filter_bs, s->bottom_bs);
 277    }
 278    bdrv_drop_filter(cor_filter_bs, &error_abort);
 279    bdrv_unref(cor_filter_bs);
 280}
 281
 282
 283static void bdrv_copy_on_read_init(void)
 284{
 285    bdrv_register(&bdrv_copy_on_read);
 286}
 287
 288block_init(bdrv_copy_on_read_init);
 289