Commit baf8673c authored by Anthony Liguori's avatar Anthony Liguori
Browse files

Merge remote-tracking branch 'stefanha/block' into staging



# By Kevin Wolf (22) and Fam Zheng (1)
# Via Stefan Hajnoczi
* stefanha/block: (23 commits)
  vmdk: refuse to open higher version than supported
  block: Always enable discard on the protocol level
  qcow2: Batch discards
  qcow2: Options to enable discard for freed clusters
  qcow2: Add refcount update reason to all callers
  Revert "block: Disable driver-specific options for 1.5"
  ide: Clean up ide_exec_cmd()
  ide: Convert SMART commands to ide_cmd_table handler
  ide: Convert CF-ATA commands to ide_cmd_table handler
  ide: Convert ATAPI commands to ide_cmd_table handler
  ide: Convert SEEK to ide_cmd_table handler
  ide: Convert FLUSH CACHE to ide_cmd_table handler
  ide: Convert SET FEATURES to ide_cmd_table handler
  ide: Convert CHECK POWER MDOE to ide_cmd_table handler
  ide: Convert READ NATIVE MAX ADDRESS to ide_cmd_table handler
  ide: Convert DMA read/write commands to ide_cmd_table handler
  ide: Convert PIO read/write commands to ide_cmd_table handler
  ide: Convert read/write multiple commands to ide_cmd_table handler
  ide: Convert verify commands to ide_cmd_table handler
  ide: Convert cmd_nop commands to ide_cmd_table handler
  ...

Message-id: 1372065035-19601-1-git-send-email-stefanha@redhat.com
Signed-off-by: default avatarAnthony Liguori <aliguori@us.ibm.com>
parents 9fbbf0d1 96c51eb5
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -1045,7 +1045,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
    extract_subqdict(options, &file_options, "file.");

    ret = bdrv_file_open(&file, filename, file_options,
                         bdrv_open_flags(bs, flags));
                         bdrv_open_flags(bs, flags | BDRV_O_UNMAP));
    if (ret < 0) {
        goto fail;
    }
+31 −10
Original line number Diff line number Diff line
@@ -98,14 +98,16 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
        goto fail;
    }
    g_free(s->l1_table);
    qcow2_free_clusters(bs, s->l1_table_offset, s->l1_size * sizeof(uint64_t));
    qcow2_free_clusters(bs, s->l1_table_offset, s->l1_size * sizeof(uint64_t),
                        QCOW2_DISCARD_OTHER);
    s->l1_table_offset = new_l1_table_offset;
    s->l1_table = new_l1_table;
    s->l1_size = new_l1_size;
    return 0;
 fail:
    g_free(new_l1_table);
    qcow2_free_clusters(bs, new_l1_table_offset, new_l1_size2);
    qcow2_free_clusters(bs, new_l1_table_offset, new_l1_size2,
                        QCOW2_DISCARD_OTHER);
    return ret;
}

@@ -548,7 +550,8 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,

        /* Then decrease the refcount of the old table */
        if (l2_offset) {
            qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t));
            qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t),
                                QCOW2_DISCARD_OTHER);
        }
    }

@@ -715,10 +718,14 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
    /*
     * If this was a COW, we need to decrease the refcount of the old cluster.
     * Also flush bs->file to get the right order for L2 and refcount update.
     *
     * Don't discard clusters that reach a refcount of 0 (e.g. compressed
     * clusters), the next write will reuse them anyway.
     */
    if (j != 0) {
        for (i = 0; i < j; i++) {
            qcow2_free_any_clusters(bs, be64_to_cpu(old_cluster[i]), 1);
            qcow2_free_any_clusters(bs, be64_to_cpu(old_cluster[i]), 1,
                                    QCOW2_DISCARD_NEVER);
        }
    }

@@ -1339,7 +1346,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
        l2_table[l2_index + i] = cpu_to_be64(0);

        /* Then decrease the refcount */
        qcow2_free_any_clusters(bs, old_offset, 1);
        qcow2_free_any_clusters(bs, old_offset, 1, QCOW2_DISCARD_REQUEST);
    }

    ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
@@ -1370,18 +1377,25 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,

    nb_clusters = size_to_clusters(s, end_offset - offset);

    s->cache_discards = true;

    /* Each L2 table is handled by its own loop iteration */
    while (nb_clusters > 0) {
        ret = discard_single_l2(bs, offset, nb_clusters);
        if (ret < 0) {
            return ret;
            goto fail;
        }

        nb_clusters -= ret;
        offset += (ret * s->cluster_size);
    }

    return 0;
    ret = 0;
fail:
    s->cache_discards = false;
    qcow2_process_discards(bs, ret);

    return ret;
}

/*
@@ -1415,7 +1429,7 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset,
        qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
        if (old_offset & QCOW_OFLAG_COMPRESSED) {
            l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO);
            qcow2_free_any_clusters(bs, old_offset, 1);
            qcow2_free_any_clusters(bs, old_offset, 1, QCOW2_DISCARD_REQUEST);
        } else {
            l2_table[l2_index + i] |= cpu_to_be64(QCOW_OFLAG_ZERO);
        }
@@ -1443,15 +1457,22 @@ int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors)
    /* Each L2 table is handled by its own loop iteration */
    nb_clusters = size_to_clusters(s, nb_sectors << BDRV_SECTOR_BITS);

    s->cache_discards = true;

    while (nb_clusters > 0) {
        ret = zero_single_l2(bs, offset, nb_clusters);
        if (ret < 0) {
            return ret;
            goto fail;
        }

        nb_clusters -= ret;
        offset += (ret * s->cluster_size);
    }

    return 0;
    ret = 0;
fail:
    s->cache_discards = false;
    qcow2_process_discards(bs, ret);

    return ret;
}
+115 −21
Original line number Diff line number Diff line
@@ -29,7 +29,7 @@
static int64_t alloc_clusters_noref(BlockDriverState *bs, int64_t size);
static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
                            int64_t offset, int64_t length,
                            int addend);
                            int addend, enum qcow2_discard_type type);


/*********************************************************/
@@ -235,7 +235,8 @@ static int alloc_refcount_block(BlockDriverState *bs,
    } else {
        /* Described somewhere else. This can recurse at most twice before we
         * arrive at a block that describes itself. */
        ret = update_refcount(bs, new_block, s->cluster_size, 1);
        ret = update_refcount(bs, new_block, s->cluster_size, 1,
                              QCOW2_DISCARD_NEVER);
        if (ret < 0) {
            goto fail_block;
        }
@@ -399,7 +400,8 @@ static int alloc_refcount_block(BlockDriverState *bs,

    /* Free old table. Remember, we must not change free_cluster_index */
    uint64_t old_free_cluster_index = s->free_cluster_index;
    qcow2_free_clusters(bs, old_table_offset, old_table_size * sizeof(uint64_t));
    qcow2_free_clusters(bs, old_table_offset, old_table_size * sizeof(uint64_t),
                        QCOW2_DISCARD_OTHER);
    s->free_cluster_index = old_free_cluster_index;

    ret = load_refcount_block(bs, new_block, (void**) refcount_block);
@@ -418,9 +420,77 @@ fail_block:
    return ret;
}

void qcow2_process_discards(BlockDriverState *bs, int ret)
{
    BDRVQcowState *s = bs->opaque;
    Qcow2DiscardRegion *d, *next;

    QTAILQ_FOREACH_SAFE(d, &s->discards, next, next) {
        QTAILQ_REMOVE(&s->discards, d, next);

        /* Discard is optional, ignore the return value */
        if (ret >= 0) {
            bdrv_discard(bs->file,
                         d->offset >> BDRV_SECTOR_BITS,
                         d->bytes >> BDRV_SECTOR_BITS);
        }

        g_free(d);
    }
}

static void update_refcount_discard(BlockDriverState *bs,
                                    uint64_t offset, uint64_t length)
{
    BDRVQcowState *s = bs->opaque;
    Qcow2DiscardRegion *d, *p, *next;

    QTAILQ_FOREACH(d, &s->discards, next) {
        uint64_t new_start = MIN(offset, d->offset);
        uint64_t new_end = MAX(offset + length, d->offset + d->bytes);

        if (new_end - new_start <= length + d->bytes) {
            /* There can't be any overlap, areas ending up here have no
             * references any more and therefore shouldn't get freed another
             * time. */
            assert(d->bytes + length == new_end - new_start);
            d->offset = new_start;
            d->bytes = new_end - new_start;
            goto found;
        }
    }

    d = g_malloc(sizeof(*d));
    *d = (Qcow2DiscardRegion) {
        .bs     = bs,
        .offset = offset,
        .bytes  = length,
    };
    QTAILQ_INSERT_TAIL(&s->discards, d, next);

found:
    /* Merge discard requests if they are adjacent now */
    QTAILQ_FOREACH_SAFE(p, &s->discards, next, next) {
        if (p == d
            || p->offset > d->offset + d->bytes
            || d->offset > p->offset + p->bytes)
        {
            continue;
        }

        /* Still no overlap possible */
        assert(p->offset == d->offset + d->bytes
            || d->offset == p->offset + p->bytes);

        QTAILQ_REMOVE(&s->discards, p, next);
        d->offset = MIN(d->offset, p->offset);
        d->bytes += p->bytes;
    }
}

/* XXX: cache several refcount block clusters ? */
static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
    int64_t offset, int64_t length, int addend)
    int64_t offset, int64_t length, int addend, enum qcow2_discard_type type)
{
    BDRVQcowState *s = bs->opaque;
    int64_t start, last, cluster_offset;
@@ -486,10 +556,18 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
            s->free_cluster_index = cluster_index;
        }
        refcount_block[block_index] = cpu_to_be16(refcount);

        if (refcount == 0 && s->discard_passthrough[type]) {
            update_refcount_discard(bs, cluster_offset, s->cluster_size);
        }
    }

    ret = 0;
fail:
    if (!s->cache_discards) {
        qcow2_process_discards(bs, ret);
    }

    /* Write last changed block to disk */
    if (refcount_block) {
        int wret;
@@ -506,7 +584,8 @@ fail:
     */
    if (ret < 0) {
        int dummy;
        dummy = update_refcount(bs, offset, cluster_offset - offset, -addend);
        dummy = update_refcount(bs, offset, cluster_offset - offset, -addend,
                                QCOW2_DISCARD_NEVER);
        (void)dummy;
    }

@@ -522,12 +601,14 @@ fail:
 */
static int update_cluster_refcount(BlockDriverState *bs,
                                   int64_t cluster_index,
                                   int addend)
                                   int addend,
                                   enum qcow2_discard_type type)
{
    BDRVQcowState *s = bs->opaque;
    int ret;

    ret = update_refcount(bs, cluster_index << s->cluster_bits, 1, addend);
    ret = update_refcount(bs, cluster_index << s->cluster_bits, 1, addend,
                          type);
    if (ret < 0) {
        return ret;
    }
@@ -579,7 +660,7 @@ int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size)
        return offset;
    }

    ret = update_refcount(bs, offset, size, 1);
    ret = update_refcount(bs, offset, size, 1, QCOW2_DISCARD_NEVER);
    if (ret < 0) {
        return ret;
    }
@@ -611,7 +692,8 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
    old_free_cluster_index = s->free_cluster_index;
    s->free_cluster_index = cluster_index + i;

    ret = update_refcount(bs, offset, i << s->cluster_bits, 1);
    ret = update_refcount(bs, offset, i << s->cluster_bits, 1,
                          QCOW2_DISCARD_NEVER);
    if (ret < 0) {
        return ret;
    }
@@ -649,7 +731,8 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
        if (free_in_cluster == 0)
            s->free_byte_offset = 0;
        if ((offset & (s->cluster_size - 1)) != 0)
            update_cluster_refcount(bs, offset >> s->cluster_bits, 1);
            update_cluster_refcount(bs, offset >> s->cluster_bits, 1,
                                    QCOW2_DISCARD_NEVER);
    } else {
        offset = qcow2_alloc_clusters(bs, s->cluster_size);
        if (offset < 0) {
@@ -659,7 +742,8 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
        if ((cluster_offset + s->cluster_size) == offset) {
            /* we are lucky: contiguous data */
            offset = s->free_byte_offset;
            update_cluster_refcount(bs, offset >> s->cluster_bits, 1);
            update_cluster_refcount(bs, offset >> s->cluster_bits, 1,
                                    QCOW2_DISCARD_NEVER);
            s->free_byte_offset += size;
        } else {
            s->free_byte_offset = offset;
@@ -676,12 +760,13 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
}

void qcow2_free_clusters(BlockDriverState *bs,
                          int64_t offset, int64_t size)
                          int64_t offset, int64_t size,
                          enum qcow2_discard_type type)
{
    int ret;

    BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_FREE);
    ret = update_refcount(bs, offset, size, -1);
    ret = update_refcount(bs, offset, size, -1, type);
    if (ret < 0) {
        fprintf(stderr, "qcow2_free_clusters failed: %s\n", strerror(-ret));
        /* TODO Remember the clusters to free them later and avoid leaking */
@@ -692,8 +777,8 @@ void qcow2_free_clusters(BlockDriverState *bs,
 * Free a cluster using its L2 entry (handles clusters of all types, e.g.
 * normal cluster, compressed cluster, etc.)
 */
void qcow2_free_any_clusters(BlockDriverState *bs,
    uint64_t l2_entry, int nb_clusters)
void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
                             int nb_clusters, enum qcow2_discard_type type)
{
    BDRVQcowState *s = bs->opaque;

@@ -705,12 +790,12 @@ void qcow2_free_any_clusters(BlockDriverState *bs,
                           s->csize_mask) + 1;
            qcow2_free_clusters(bs,
                (l2_entry & s->cluster_offset_mask) & ~511,
                nb_csectors * 512);
                nb_csectors * 512, type);
        }
        break;
    case QCOW2_CLUSTER_NORMAL:
        qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
                            nb_clusters << s->cluster_bits);
                            nb_clusters << s->cluster_bits, type);
        break;
    case QCOW2_CLUSTER_UNALLOCATED:
    case QCOW2_CLUSTER_ZERO:
@@ -741,6 +826,8 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
    l1_table = NULL;
    l1_size2 = l1_size * sizeof(uint64_t);

    s->cache_discards = true;

    /* WARNING: qcow2_snapshot_goto relies on this function not using the
     * l1_table_offset when it is the current s->l1_table_offset! Be careful
     * when changing this! */
@@ -785,7 +872,8 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
                            int ret;
                            ret = update_refcount(bs,
                                (offset & s->cluster_offset_mask) & ~511,
                                nb_csectors * 512, addend);
                                nb_csectors * 512, addend,
                                QCOW2_DISCARD_SNAPSHOT);
                            if (ret < 0) {
                                goto fail;
                            }
@@ -795,7 +883,8 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
                    } else {
                        uint64_t cluster_index = (offset & L2E_OFFSET_MASK) >> s->cluster_bits;
                        if (addend != 0) {
                            refcount = update_cluster_refcount(bs, cluster_index, addend);
                            refcount = update_cluster_refcount(bs, cluster_index, addend,
                                                               QCOW2_DISCARD_SNAPSHOT);
                        } else {
                            refcount = get_refcount(bs, cluster_index);
                        }
@@ -827,7 +916,8 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,


            if (addend != 0) {
                refcount = update_cluster_refcount(bs, l2_offset >> s->cluster_bits, addend);
                refcount = update_cluster_refcount(bs, l2_offset >> s->cluster_bits, addend,
                                                   QCOW2_DISCARD_SNAPSHOT);
            } else {
                refcount = get_refcount(bs, l2_offset >> s->cluster_bits);
            }
@@ -850,6 +940,9 @@ fail:
        qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
    }

    s->cache_discards = false;
    qcow2_process_discards(bs, ret);

    /* Update L1 only if it isn't deleted anyway (addend = -1) */
    if (ret == 0 && addend >= 0 && l1_modified) {
        for (i = 0; i < l1_size; i++) {
@@ -1253,7 +1346,8 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,

            if (num_fixed) {
                ret = update_refcount(bs, i << s->cluster_bits, 1,
                                      refcount2 - refcount1);
                                      refcount2 - refcount1,
                                      QCOW2_DISCARD_ALWAYS);
                if (ret >= 0) {
                    (*num_fixed)++;
                    continue;
+4 −2
Original line number Diff line number Diff line
@@ -262,7 +262,8 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
    }

    /* free the old snapshot table */
    qcow2_free_clusters(bs, s->snapshots_offset, s->snapshots_size);
    qcow2_free_clusters(bs, s->snapshots_offset, s->snapshots_size,
                        QCOW2_DISCARD_SNAPSHOT);
    s->snapshots_offset = snapshots_offset;
    s->snapshots_size = snapshots_size;
    return 0;
@@ -569,7 +570,8 @@ int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
    if (ret < 0) {
        return ret;
    }
    qcow2_free_clusters(bs, sn.l1_table_offset, sn.l1_size * sizeof(uint64_t));
    qcow2_free_clusters(bs, sn.l1_table_offset, sn.l1_size * sizeof(uint64_t),
                        QCOW2_DISCARD_SNAPSHOT);

    /* must update the copied flag on the current cluster offsets */
    ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0);
+29 −1
Original line number Diff line number Diff line
@@ -295,6 +295,22 @@ static QemuOptsList qcow2_runtime_opts = {
            .type = QEMU_OPT_BOOL,
            .help = "Postpone refcount updates",
        },
        {
            .name = QCOW2_OPT_DISCARD_REQUEST,
            .type = QEMU_OPT_BOOL,
            .help = "Pass guest discard requests to the layer below",
        },
        {
            .name = QCOW2_OPT_DISCARD_SNAPSHOT,
            .type = QEMU_OPT_BOOL,
            .help = "Generate discard requests when snapshot related space "
                    "is freed",
        },
        {
            .name = QCOW2_OPT_DISCARD_OTHER,
            .type = QEMU_OPT_BOOL,
            .help = "Generate discard requests when other clusters are freed",
        },
        { /* end of list */ }
    },
};
@@ -470,6 +486,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
    }

    QLIST_INIT(&s->cluster_allocs);
    QTAILQ_INIT(&s->discards);

    /* read qcow2 extensions */
    if (qcow2_read_extensions(bs, header.header_length, ext_end, NULL)) {
@@ -532,6 +549,16 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
    s->use_lazy_refcounts = qemu_opt_get_bool(opts, QCOW2_OPT_LAZY_REFCOUNTS,
        (s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS));

    s->discard_passthrough[QCOW2_DISCARD_NEVER] = false;
    s->discard_passthrough[QCOW2_DISCARD_ALWAYS] = true;
    s->discard_passthrough[QCOW2_DISCARD_REQUEST] =
        qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_REQUEST,
                          flags & BDRV_O_UNMAP);
    s->discard_passthrough[QCOW2_DISCARD_SNAPSHOT] =
        qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_SNAPSHOT, true);
    s->discard_passthrough[QCOW2_DISCARD_OTHER] =
        qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_OTHER, false);

    qemu_opts_del(opts);

    if (s->use_lazy_refcounts && s->qcow_version < 3) {
@@ -1196,7 +1223,8 @@ static int preallocate(BlockDriverState *bs)

        ret = qcow2_alloc_cluster_link_l2(bs, meta);
        if (ret < 0) {
            qcow2_free_any_clusters(bs, meta->alloc_offset, meta->nb_clusters);
            qcow2_free_any_clusters(bs, meta->alloc_offset, meta->nb_clusters,
                                    QCOW2_DISCARD_NEVER);
            return ret;
        }

Loading