Commit 685a4eaf authored by Peter Maydell's avatar Peter Maydell
Browse files

Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging



Block layer patches

# gpg: Signature made Tue 13 Feb 2018 17:03:11 GMT
# gpg:                using RSA key 7F09B272C88F2FD6
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>"
# Primary key fingerprint: DC3D EB15 9A9A F95D 3D74  56FE 7F09 B272 C88F 2FD6

* remotes/kevin/tags/for-upstream: (55 commits)
  iotests: Add l2-cache-entry-size to iotest 137
  iotests: Test downgrading an image using a small L2 slice size
  iotests: Test valid values of l2-cache-entry-size
  qcow2: Allow configuring the L2 slice size
  qcow2: Rename l2_table in count_cow_clusters()
  qcow2: Rename l2_table in count_contiguous_clusters_unallocated()
  qcow2: Rename l2_table in count_contiguous_clusters()
  qcow2: Rename l2_table in qcow2_alloc_compressed_cluster_offset()
  qcow2: Update qcow2_truncate() to support L2 slices
  qcow2: Update expand_zero_clusters_in_l1() to support L2 slices
  qcow2: Prepare expand_zero_clusters_in_l1() for adding L2 slice support
  qcow2: Read refcount before L2 table in expand_zero_clusters_in_l1()
  qcow2: Update qcow2_update_snapshot_refcount() to support L2 slices
  qcow2: Prepare qcow2_update_snapshot_refcount() for adding L2 slice support
  qcow2: Update zero_single_l2() to support L2 slices
  qcow2: Update discard_single_l2() to support L2 slices
  qcow2: Update handle_alloc() to support L2 slices
  qcow2: Update handle_copied() to support L2 slices
  qcow2: Update qcow2_alloc_cluster_link_l2() to support L2 slices
  qcow2: Update qcow2_get_cluster_offset() to support L2 slices
  ...

Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parents bec9c64e 0a4dc980
Loading
Loading
Loading
Loading
+0 −18
Original line number Diff line number Diff line
@@ -52,8 +52,6 @@ struct BdrvDirtyBitmap {
                                   Such operations must fail and both the image
                                   and this bitmap must remain unchanged while
                                   this flag is set. */
    bool autoload;              /* For persistent bitmaps: bitmap must be
                                   autoloaded on image opening */
    bool persistent;            /* bitmap must be saved to owner disk image */
    QLIST_ENTRY(BdrvDirtyBitmap) list;
};
@@ -104,7 +102,6 @@ void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap)
    g_free(bitmap->name);
    bitmap->name = NULL;
    bitmap->persistent = false;
    bitmap->autoload = false;
}

/* Called with BQL taken.  */
@@ -261,8 +258,6 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs,
    bitmap->successor = NULL;
    successor->persistent = bitmap->persistent;
    bitmap->persistent = false;
    successor->autoload = bitmap->autoload;
    bitmap->autoload = false;
    bdrv_release_dirty_bitmap(bs, bitmap);

    return successor;
@@ -666,19 +661,6 @@ bool bdrv_has_readonly_bitmaps(BlockDriverState *bs)
    return false;
}

/* Called with BQL taken. */
void bdrv_dirty_bitmap_set_autoload(BdrvDirtyBitmap *bitmap, bool autoload)
{
    qemu_mutex_lock(bitmap->mutex);
    bitmap->autoload = autoload;
    qemu_mutex_unlock(bitmap->mutex);
}

bool bdrv_dirty_bitmap_get_autoload(const BdrvDirtyBitmap *bitmap)
{
    return bitmap->autoload;
}

/* Called with BQL taken. */
void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent)
{
+63 −53
Original line number Diff line number Diff line
@@ -965,12 +965,68 @@ static coroutine_fn int qemu_gluster_co_pwrite_zeroes(BlockDriverState *bs,
}
#endif

static int qemu_gluster_do_truncate(struct glfs_fd *fd, int64_t offset,
                                    PreallocMode prealloc, Error **errp)
{
    int64_t current_length;

    current_length = glfs_lseek(fd, 0, SEEK_END);
    if (current_length < 0) {
        error_setg_errno(errp, errno, "Failed to determine current size");
        return -errno;
    }

    if (current_length > offset && prealloc != PREALLOC_MODE_OFF) {
        error_setg(errp, "Cannot use preallocation for shrinking files");
        return -ENOTSUP;
    }

    if (current_length == offset) {
        return 0;
    }

    switch (prealloc) {
#ifdef CONFIG_GLUSTERFS_FALLOCATE
    case PREALLOC_MODE_FALLOC:
        if (glfs_fallocate(fd, 0, current_length, offset - current_length)) {
            error_setg_errno(errp, errno, "Could not preallocate data");
            return -errno;
        }
        break;
#endif /* CONFIG_GLUSTERFS_FALLOCATE */
#ifdef CONFIG_GLUSTERFS_ZEROFILL
    case PREALLOC_MODE_FULL:
        if (glfs_ftruncate(fd, offset)) {
            error_setg_errno(errp, errno, "Could not resize file");
            return -errno;
        }
        if (glfs_zerofill(fd, current_length, offset - current_length)) {
            error_setg_errno(errp, errno, "Could not zerofill the new area");
            return -errno;
        }
        break;
#endif /* CONFIG_GLUSTERFS_ZEROFILL */
    case PREALLOC_MODE_OFF:
        if (glfs_ftruncate(fd, offset)) {
            error_setg_errno(errp, errno, "Could not resize file");
            return -errno;
        }
        break;
    default:
        error_setg(errp, "Unsupported preallocation mode: %s",
                   PreallocMode_str(prealloc));
        return -EINVAL;
    }

    return 0;
}

static int qemu_gluster_create(const char *filename,
                               QemuOpts *opts, Error **errp)
{
    BlockdevOptionsGluster *gconf;
    struct glfs *glfs;
    struct glfs_fd *fd;
    struct glfs_fd *fd = NULL;
    int ret = 0;
    PreallocMode prealloc;
    int64_t total_size = 0;
@@ -1019,45 +1075,14 @@ static int qemu_gluster_create(const char *filename,
        goto out;
    }

    switch (prealloc) {
#ifdef CONFIG_GLUSTERFS_FALLOCATE
    case PREALLOC_MODE_FALLOC:
        if (glfs_fallocate(fd, 0, 0, total_size)) {
            error_setg(errp, "Could not preallocate data for the new file");
            ret = -errno;
        }
        break;
#endif /* CONFIG_GLUSTERFS_FALLOCATE */
#ifdef CONFIG_GLUSTERFS_ZEROFILL
    case PREALLOC_MODE_FULL:
        if (!glfs_ftruncate(fd, total_size)) {
            if (glfs_zerofill(fd, 0, total_size)) {
                error_setg(errp, "Could not zerofill the new file");
                ret = -errno;
            }
        } else {
            error_setg(errp, "Could not resize file");
            ret = -errno;
        }
        break;
#endif /* CONFIG_GLUSTERFS_ZEROFILL */
    case PREALLOC_MODE_OFF:
        if (glfs_ftruncate(fd, total_size) != 0) {
            ret = -errno;
            error_setg(errp, "Could not resize file");
        }
        break;
    default:
        ret = -EINVAL;
        error_setg(errp, "Unsupported preallocation mode: %s",
                   PreallocMode_str(prealloc));
        break;
    }
    ret = qemu_gluster_do_truncate(fd, total_size, prealloc, errp);

    if (glfs_close(fd) != 0) {
out:
    if (fd) {
        if (glfs_close(fd) != 0 && ret == 0) {
            ret = -errno;
        }
out:
    }
    qapi_free_BlockdevOptionsGluster(gconf);
    glfs_clear_preopened(glfs);
    return ret;
@@ -1097,23 +1122,8 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs,
static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset,
                                 PreallocMode prealloc, Error **errp)
{
    int ret;
    BDRVGlusterState *s = bs->opaque;

    if (prealloc != PREALLOC_MODE_OFF) {
        error_setg(errp, "Unsupported preallocation mode '%s'",
                   PreallocMode_str(prealloc));
        return -ENOTSUP;
    }

    ret = glfs_ftruncate(s->fd, offset);
    if (ret < 0) {
        ret = -errno;
        error_setg_errno(errp, -ret, "Failed to truncate file");
        return ret;
    }

    return 0;
    return qemu_gluster_do_truncate(s->fd, offset, prealloc, errp);
}

static coroutine_fn int qemu_gluster_co_readv(BlockDriverState *bs,
+7 −5
Original line number Diff line number Diff line
@@ -933,14 +933,14 @@ static void set_readonly_helper(gpointer bitmap, gpointer value)
    bdrv_dirty_bitmap_set_readonly(bitmap, (bool)value);
}

/* qcow2_load_autoloading_dirty_bitmaps()
/* qcow2_load_dirty_bitmaps()
 * Return value is a hint for caller: true means that the Qcow2 header was
 * updated. (false doesn't mean that the header should be updated by the
 * caller, it just means that updating was not needed or the image cannot be
 * written to).
 * On failure the function returns false.
 */
bool qcow2_load_autoloading_dirty_bitmaps(BlockDriverState *bs, Error **errp)
bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
{
    BDRVQcow2State *s = bs->opaque;
    Qcow2BitmapList *bm_list;
@@ -960,14 +960,16 @@ bool qcow2_load_autoloading_dirty_bitmaps(BlockDriverState *bs, Error **errp)
    }

    QSIMPLEQ_FOREACH(bm, bm_list, entry) {
        if ((bm->flags & BME_FLAG_AUTO) && !(bm->flags & BME_FLAG_IN_USE)) {
        if (!(bm->flags & BME_FLAG_IN_USE)) {
            BdrvDirtyBitmap *bitmap = load_bitmap(bs, bm, errp);
            if (bitmap == NULL) {
                goto fail;
            }

            if (!(bm->flags & BME_FLAG_AUTO)) {
                bdrv_disable_dirty_bitmap(bitmap);
            }
            bdrv_dirty_bitmap_set_persistance(bitmap, true);
            bdrv_dirty_bitmap_set_autoload(bitmap, true);
            bm->flags |= BME_FLAG_IN_USE;
            created_dirty_bitmaps =
                    g_slist_append(created_dirty_bitmaps, bitmap);
@@ -1369,7 +1371,7 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
            bm->table.size = 0;
            QSIMPLEQ_INSERT_TAIL(&drop_tables, tb, entry);
        }
        bm->flags = bdrv_dirty_bitmap_get_autoload(bitmap) ? BME_FLAG_AUTO : 0;
        bm->flags = bdrv_dirty_bitmap_enabled(bitmap) ? BME_FLAG_AUTO : 0;
        bm->granularity_bits = ctz32(bdrv_dirty_bitmap_granularity(bitmap));
        bm->dirty_bitmap = bitmap;
    }
+40 −40
Original line number Diff line number Diff line
@@ -39,26 +39,23 @@ struct Qcow2Cache {
    Qcow2CachedTable       *entries;
    struct Qcow2Cache      *depends;
    int                     size;
    int                     table_size;
    bool                    depends_on_flush;
    void                   *table_array;
    uint64_t                lru_counter;
    uint64_t                cache_clean_lru_counter;
};

static inline void *qcow2_cache_get_table_addr(BlockDriverState *bs,
                    Qcow2Cache *c, int table)
static inline void *qcow2_cache_get_table_addr(Qcow2Cache *c, int table)
{
    BDRVQcow2State *s = bs->opaque;
    return (uint8_t *) c->table_array + (size_t) table * s->cluster_size;
    return (uint8_t *) c->table_array + (size_t) table * c->table_size;
}

static inline int qcow2_cache_get_table_idx(BlockDriverState *bs,
                  Qcow2Cache *c, void *table)
static inline int qcow2_cache_get_table_idx(Qcow2Cache *c, void *table)
{
    BDRVQcow2State *s = bs->opaque;
    ptrdiff_t table_offset = (uint8_t *) table - (uint8_t *) c->table_array;
    int idx = table_offset / s->cluster_size;
    assert(idx >= 0 && idx < c->size && table_offset % s->cluster_size == 0);
    int idx = table_offset / c->table_size;
    assert(idx >= 0 && idx < c->size && table_offset % c->table_size == 0);
    return idx;
}

@@ -74,15 +71,13 @@ static inline const char *qcow2_cache_get_name(BDRVQcow2State *s, Qcow2Cache *c)
    }
}

static void qcow2_cache_table_release(BlockDriverState *bs, Qcow2Cache *c,
                                      int i, int num_tables)
static void qcow2_cache_table_release(Qcow2Cache *c, int i, int num_tables)
{
/* Using MADV_DONTNEED to discard memory is a Linux-specific feature */
#ifdef CONFIG_LINUX
    BDRVQcow2State *s = bs->opaque;
    void *t = qcow2_cache_get_table_addr(bs, c, i);
    void *t = qcow2_cache_get_table_addr(c, i);
    int align = getpagesize();
    size_t mem_size = (size_t) s->cluster_size * num_tables;
    size_t mem_size = (size_t) c->table_size * num_tables;
    size_t offset = QEMU_ALIGN_UP((uintptr_t) t, align) - (uintptr_t) t;
    size_t length = QEMU_ALIGN_DOWN(mem_size - offset, align);
    if (mem_size > offset && length > 0) {
@@ -98,7 +93,7 @@ static inline bool can_clean_entry(Qcow2Cache *c, int i)
        t->lru_counter <= c->cache_clean_lru_counter;
}

void qcow2_cache_clean_unused(BlockDriverState *bs, Qcow2Cache *c)
void qcow2_cache_clean_unused(Qcow2Cache *c)
{
    int i = 0;
    while (i < c->size) {
@@ -118,23 +113,30 @@ void qcow2_cache_clean_unused(BlockDriverState *bs, Qcow2Cache *c)
        }

        if (to_clean > 0) {
            qcow2_cache_table_release(bs, c, i - to_clean, to_clean);
            qcow2_cache_table_release(c, i - to_clean, to_clean);
        }
    }

    c->cache_clean_lru_counter = c->lru_counter;
}

Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables)
Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables,
                               unsigned table_size)
{
    BDRVQcow2State *s = bs->opaque;
    Qcow2Cache *c;

    assert(num_tables > 0);
    assert(is_power_of_2(table_size));
    assert(table_size >= (1 << MIN_CLUSTER_BITS));
    assert(table_size <= s->cluster_size);

    c = g_new0(Qcow2Cache, 1);
    c->size = num_tables;
    c->table_size = table_size;
    c->entries = g_try_new0(Qcow2CachedTable, num_tables);
    c->table_array = qemu_try_blockalign(bs->file->bs,
                                         (size_t) num_tables * s->cluster_size);
                                         (size_t) num_tables * c->table_size);

    if (!c->entries || !c->table_array) {
        qemu_vfree(c->table_array);
@@ -146,7 +148,7 @@ Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables)
    return c;
}

int qcow2_cache_destroy(BlockDriverState *bs, Qcow2Cache *c)
int qcow2_cache_destroy(Qcow2Cache *c)
{
    int i;

@@ -203,13 +205,13 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)

    if (c == s->refcount_block_cache) {
        ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_REFCOUNT_BLOCK,
                c->entries[i].offset, s->cluster_size);
                c->entries[i].offset, c->table_size);
    } else if (c == s->l2_table_cache) {
        ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2,
                c->entries[i].offset, s->cluster_size);
                c->entries[i].offset, c->table_size);
    } else {
        ret = qcow2_pre_write_overlap_check(bs, 0,
                c->entries[i].offset, s->cluster_size);
                c->entries[i].offset, c->table_size);
    }

    if (ret < 0) {
@@ -223,7 +225,7 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
    }

    ret = bdrv_pwrite(bs->file, c->entries[i].offset,
                      qcow2_cache_get_table_addr(bs, c, i), s->cluster_size);
                      qcow2_cache_get_table_addr(c, i), c->table_size);
    if (ret < 0) {
        return ret;
    }
@@ -309,7 +311,7 @@ int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c)
        c->entries[i].lru_counter = 0;
    }

    qcow2_cache_table_release(bs, c, 0, c->size);
    qcow2_cache_table_release(c, 0, c->size);

    c->lru_counter = 0;

@@ -331,7 +333,7 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c,
    trace_qcow2_cache_get(qemu_coroutine_self(), c == s->l2_table_cache,
                          offset, read_from_disk);

    if (offset_into_cluster(s, offset)) {
    if (!QEMU_IS_ALIGNED(offset, c->table_size)) {
        qcow2_signal_corruption(bs, true, -1, -1, "Cannot get entry from %s "
                                "cache: Offset %#" PRIx64 " is unaligned",
                                qcow2_cache_get_name(s, c), offset);
@@ -339,7 +341,7 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c,
    }

    /* Check if the table is already cached */
    i = lookup_index = (offset / s->cluster_size * 4) % c->size;
    i = lookup_index = (offset / c->table_size * 4) % c->size;
    do {
        const Qcow2CachedTable *t = &c->entries[i];
        if (t->offset == offset) {
@@ -379,8 +381,8 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c,
        }

        ret = bdrv_pread(bs->file, offset,
                         qcow2_cache_get_table_addr(bs, c, i),
                         s->cluster_size);
                         qcow2_cache_get_table_addr(c, i),
                         c->table_size);
        if (ret < 0) {
            return ret;
        }
@@ -391,7 +393,7 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c,
    /* And return the right table */
found:
    c->entries[i].ref++;
    *table = qcow2_cache_get_table_addr(bs, c, i);
    *table = qcow2_cache_get_table_addr(c, i);

    trace_qcow2_cache_get_done(qemu_coroutine_self(),
                               c == s->l2_table_cache, i);
@@ -411,9 +413,9 @@ int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
    return qcow2_cache_do_get(bs, c, offset, table, false);
}

void qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table)
void qcow2_cache_put(Qcow2Cache *c, void **table)
{
    int i = qcow2_cache_get_table_idx(bs, c, *table);
    int i = qcow2_cache_get_table_idx(c, *table);

    c->entries[i].ref--;
    *table = NULL;
@@ -425,30 +427,28 @@ void qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table)
    assert(c->entries[i].ref >= 0);
}

void qcow2_cache_entry_mark_dirty(BlockDriverState *bs, Qcow2Cache *c,
     void *table)
void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table)
{
    int i = qcow2_cache_get_table_idx(bs, c, table);
    int i = qcow2_cache_get_table_idx(c, table);
    assert(c->entries[i].offset != 0);
    c->entries[i].dirty = true;
}

void *qcow2_cache_is_table_offset(BlockDriverState *bs, Qcow2Cache *c,
                                  uint64_t offset)
void *qcow2_cache_is_table_offset(Qcow2Cache *c, uint64_t offset)
{
    int i;

    for (i = 0; i < c->size; i++) {
        if (c->entries[i].offset == offset) {
            return qcow2_cache_get_table_addr(bs, c, i);
            return qcow2_cache_get_table_addr(c, i);
        }
    }
    return NULL;
}

void qcow2_cache_discard(BlockDriverState *bs, Qcow2Cache *c, void *table)
void qcow2_cache_discard(Qcow2Cache *c, void *table)
{
    int i = qcow2_cache_get_table_idx(bs, c, table);
    int i = qcow2_cache_get_table_idx(c, table);

    assert(c->entries[i].ref == 0);

@@ -456,5 +456,5 @@ void qcow2_cache_discard(BlockDriverState *bs, Qcow2Cache *c, void *table)
    c->entries[i].lru_counter = 0;
    c->entries[i].dirty = false;

    qcow2_cache_table_release(bs, c, i, 1);
    qcow2_cache_table_release(c, i, 1);
}
+276 −243

File changed.

Preview size limit exceeded, changes collapsed.

Loading