mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/maxreitz/tags/pull-block-2020-01-06' into staging
Block patches:
- Minor fixes and tests from the freeze period (too minor to be included
in 4.2)
- Allow many bash iotests to test qcow2's external data file feature
- Add compress filter driver
- Fix Python iotests after 6f6e1698a6
- Fix for the backup job
# gpg: Signature made Mon 06 Jan 2020 14:33:06 GMT
# gpg: using RSA key 91BEB60A30DB3E8857D11829F407DB0061D5CF40
# gpg: issuer "mreitz@redhat.com"
# gpg: Good signature from "Max Reitz <mreitz@redhat.com>" [full]
# Primary key fingerprint: 91BE B60A 30DB 3E88 57D1 1829 F407 DB00 61D5 CF40
* remotes/maxreitz/tags/pull-block-2020-01-06: (34 commits)
backup-top: Begin drain earlier
tests/qemu-iotests: Update tests to recent desugarized -accel option
tests/qemu-iotests: add case to write compressed data of multiple clusters
qcow2: Allow writing compressed data of multiple clusters
block: introduce compress filter driver
iotests: Allow check -o data_file
iotests: Disable data_file where it cannot be used
iotests: Make 198 work with data_file
iotests: Make 137 work with data_file
iotests: Make 110 work with data_file
iotests: Make 091 work with data_file
iotests: Avoid cp/mv of test images
iotests: Use _rm_test_img for deleting test images
iotests: Avoid qemu-img create
iotests: Drop IMGOPTS use in 267
iotests: Replace IMGOPTS='' by --no-opts
iotests: Replace IMGOPTS= by -o
iotests: Inject space into -ocompat=0.10 in 051
iotests: Add -o and --no-opts to _make_test_img
iotests: Let _make_test_img parse its parameters
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
@@ -2227,6 +2227,24 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
|
||||
*nshared = shared;
|
||||
}
|
||||
|
||||
uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm)
|
||||
{
|
||||
static const uint64_t permissions[] = {
|
||||
[BLOCK_PERMISSION_CONSISTENT_READ] = BLK_PERM_CONSISTENT_READ,
|
||||
[BLOCK_PERMISSION_WRITE] = BLK_PERM_WRITE,
|
||||
[BLOCK_PERMISSION_WRITE_UNCHANGED] = BLK_PERM_WRITE_UNCHANGED,
|
||||
[BLOCK_PERMISSION_RESIZE] = BLK_PERM_RESIZE,
|
||||
[BLOCK_PERMISSION_GRAPH_MOD] = BLK_PERM_GRAPH_MOD,
|
||||
};
|
||||
|
||||
QEMU_BUILD_BUG_ON(ARRAY_SIZE(permissions) != BLOCK_PERMISSION__MAX);
|
||||
QEMU_BUILD_BUG_ON(1UL << ARRAY_SIZE(permissions) != BLK_PERM_ALL + 1);
|
||||
|
||||
assert(qapi_perm < BLOCK_PERMISSION__MAX);
|
||||
|
||||
return permissions[qapi_perm];
|
||||
}
|
||||
|
||||
static void bdrv_replace_child_noperm(BdrvChild *child,
|
||||
BlockDriverState *new_bs)
|
||||
{
|
||||
@@ -4854,36 +4872,23 @@ static void xdbg_graph_add_node(XDbgBlockGraphConstructor *gr, void *node,
|
||||
static void xdbg_graph_add_edge(XDbgBlockGraphConstructor *gr, void *parent,
|
||||
const BdrvChild *child)
|
||||
{
|
||||
typedef struct {
|
||||
unsigned int flag;
|
||||
BlockPermission num;
|
||||
} PermissionMap;
|
||||
|
||||
static const PermissionMap permissions[] = {
|
||||
{ BLK_PERM_CONSISTENT_READ, BLOCK_PERMISSION_CONSISTENT_READ },
|
||||
{ BLK_PERM_WRITE, BLOCK_PERMISSION_WRITE },
|
||||
{ BLK_PERM_WRITE_UNCHANGED, BLOCK_PERMISSION_WRITE_UNCHANGED },
|
||||
{ BLK_PERM_RESIZE, BLOCK_PERMISSION_RESIZE },
|
||||
{ BLK_PERM_GRAPH_MOD, BLOCK_PERMISSION_GRAPH_MOD },
|
||||
{ 0, 0 }
|
||||
};
|
||||
const PermissionMap *p;
|
||||
BlockPermission qapi_perm;
|
||||
XDbgBlockGraphEdge *edge;
|
||||
|
||||
QEMU_BUILD_BUG_ON(1UL << (ARRAY_SIZE(permissions) - 1) != BLK_PERM_ALL + 1);
|
||||
|
||||
edge = g_new0(XDbgBlockGraphEdge, 1);
|
||||
|
||||
edge->parent = xdbg_graph_node_num(gr, parent);
|
||||
edge->child = xdbg_graph_node_num(gr, child->bs);
|
||||
edge->name = g_strdup(child->name);
|
||||
|
||||
for (p = permissions; p->flag; p++) {
|
||||
if (p->flag & child->perm) {
|
||||
QAPI_LIST_ADD(edge->perm, p->num);
|
||||
for (qapi_perm = 0; qapi_perm < BLOCK_PERMISSION__MAX; qapi_perm++) {
|
||||
uint64_t flag = bdrv_qapi_perm_to_blk_perm(qapi_perm);
|
||||
|
||||
if (flag & child->perm) {
|
||||
QAPI_LIST_ADD(edge->perm, qapi_perm);
|
||||
}
|
||||
if (p->flag & child->shared_perm) {
|
||||
QAPI_LIST_ADD(edge->shared_perm, p->num);
|
||||
if (flag & child->shared_perm) {
|
||||
QAPI_LIST_ADD(edge->shared_perm, qapi_perm);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ block-obj-y += crypto.o
|
||||
|
||||
block-obj-y += aio_task.o
|
||||
block-obj-y += backup-top.o
|
||||
block-obj-y += filter-compress.o
|
||||
|
||||
common-obj-y += stream.o
|
||||
|
||||
|
||||
+2
-2
@@ -257,12 +257,12 @@ void bdrv_backup_top_drop(BlockDriverState *bs)
|
||||
BDRVBackupTopState *s = bs->opaque;
|
||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
block_copy_state_free(s->bcs);
|
||||
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
bdrv_drained_begin(bs);
|
||||
|
||||
block_copy_state_free(s->bcs);
|
||||
|
||||
s->active = false;
|
||||
bdrv_child_refresh_perms(bs, bs->backing, &error_abort);
|
||||
bdrv_replace_node(bs, backing_bs(bs), &error_abort);
|
||||
|
||||
+92
-1
@@ -28,10 +28,14 @@
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qlist.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "sysemu/qtest.h"
|
||||
|
||||
typedef struct BDRVBlkdebugState {
|
||||
@@ -44,6 +48,9 @@ typedef struct BDRVBlkdebugState {
|
||||
uint64_t opt_discard;
|
||||
uint64_t max_discard;
|
||||
|
||||
uint64_t take_child_perms;
|
||||
uint64_t unshare_child_perms;
|
||||
|
||||
/* For blkdebug_refresh_filename() */
|
||||
char *config_file;
|
||||
|
||||
@@ -344,6 +351,69 @@ static void blkdebug_parse_filename(const char *filename, QDict *options,
|
||||
qdict_put_str(options, "x-image", filename);
|
||||
}
|
||||
|
||||
static int blkdebug_parse_perm_list(uint64_t *dest, QDict *options,
|
||||
const char *prefix, Error **errp)
|
||||
{
|
||||
int ret = 0;
|
||||
QDict *subqdict = NULL;
|
||||
QObject *crumpled_subqdict = NULL;
|
||||
Visitor *v = NULL;
|
||||
BlockPermissionList *perm_list = NULL, *element;
|
||||
Error *local_err = NULL;
|
||||
|
||||
*dest = 0;
|
||||
|
||||
qdict_extract_subqdict(options, &subqdict, prefix);
|
||||
if (!qdict_size(subqdict)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
crumpled_subqdict = qdict_crumple(subqdict, errp);
|
||||
if (!crumpled_subqdict) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
v = qobject_input_visitor_new(crumpled_subqdict);
|
||||
visit_type_BlockPermissionList(v, NULL, &perm_list, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (element = perm_list; element; element = element->next) {
|
||||
*dest |= bdrv_qapi_perm_to_blk_perm(element->value);
|
||||
}
|
||||
|
||||
out:
|
||||
qapi_free_BlockPermissionList(perm_list);
|
||||
visit_free(v);
|
||||
qobject_unref(subqdict);
|
||||
qobject_unref(crumpled_subqdict);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int blkdebug_parse_perms(BDRVBlkdebugState *s, QDict *options,
|
||||
Error **errp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = blkdebug_parse_perm_list(&s->take_child_perms, options,
|
||||
"take-child-perms.", errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = blkdebug_parse_perm_list(&s->unshare_child_perms, options,
|
||||
"unshare-child-perms.", errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static QemuOptsList runtime_opts = {
|
||||
.name = "blkdebug",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
|
||||
@@ -419,6 +489,12 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
/* Set initial state */
|
||||
s->state = 1;
|
||||
|
||||
/* Parse permissions modifiers before opening the image file */
|
||||
ret = blkdebug_parse_perms(s, options, errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Open the image file */
|
||||
bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image",
|
||||
bs, &child_file, false, &local_err);
|
||||
@@ -916,6 +992,21 @@ static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void blkdebug_child_perm(BlockDriverState *bs, BdrvChild *c,
|
||||
const BdrvChildRole *role,
|
||||
BlockReopenQueue *reopen_queue,
|
||||
uint64_t perm, uint64_t shared,
|
||||
uint64_t *nperm, uint64_t *nshared)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
|
||||
bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
|
||||
nperm, nshared);
|
||||
|
||||
*nperm |= s->take_child_perms;
|
||||
*nshared &= ~s->unshare_child_perms;
|
||||
}
|
||||
|
||||
static const char *const blkdebug_strong_runtime_opts[] = {
|
||||
"config",
|
||||
"inject-error.",
|
||||
@@ -940,7 +1031,7 @@ static BlockDriver bdrv_blkdebug = {
|
||||
.bdrv_file_open = blkdebug_open,
|
||||
.bdrv_close = blkdebug_close,
|
||||
.bdrv_reopen_prepare = blkdebug_reopen_prepare,
|
||||
.bdrv_child_perm = bdrv_filter_default_perms,
|
||||
.bdrv_child_perm = blkdebug_child_perm,
|
||||
|
||||
.bdrv_getlength = blkdebug_getlength,
|
||||
.bdrv_refresh_filename = blkdebug_refresh_filename,
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Compress filter block driver
|
||||
*
|
||||
* Copyright (c) 2019 Virtuozzo International GmbH
|
||||
*
|
||||
* Author:
|
||||
* Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
|
||||
* (based on block/copy-on-read.c by Max Reitz)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 or
|
||||
* (at your option) any later version of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
|
||||
static int compress_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false,
|
||||
errp);
|
||||
if (!bs->file) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!bs->file->bs->drv || !block_driver_can_compress(bs->file->bs->drv)) {
|
||||
error_setg(errp,
|
||||
"Compression is not supported for underlying format: %s",
|
||||
bdrv_get_format_name(bs->file->bs) ?: "(no format)");
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
|
||||
(BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
|
||||
|
||||
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
|
||||
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
|
||||
bs->file->bs->supported_zero_flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int64_t compress_getlength(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_getlength(bs->file->bs);
|
||||
}
|
||||
|
||||
|
||||
static int coroutine_fn compress_co_preadv_part(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov,
|
||||
size_t qiov_offset,
|
||||
int flags)
|
||||
{
|
||||
return bdrv_co_preadv_part(bs->file, offset, bytes, qiov, qiov_offset,
|
||||
flags);
|
||||
}
|
||||
|
||||
|
||||
static int coroutine_fn compress_co_pwritev_part(BlockDriverState *bs,
|
||||
uint64_t offset,
|
||||
uint64_t bytes,
|
||||
QEMUIOVector *qiov,
|
||||
size_t qiov_offset, int flags)
|
||||
{
|
||||
return bdrv_co_pwritev_part(bs->file, offset, bytes, qiov, qiov_offset,
|
||||
flags | BDRV_REQ_WRITE_COMPRESSED);
|
||||
}
|
||||
|
||||
|
||||
static int coroutine_fn compress_co_pwrite_zeroes(BlockDriverState *bs,
|
||||
int64_t offset, int bytes,
|
||||
BdrvRequestFlags flags)
|
||||
{
|
||||
return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
|
||||
}
|
||||
|
||||
|
||||
static int coroutine_fn compress_co_pdiscard(BlockDriverState *bs,
|
||||
int64_t offset, int bytes)
|
||||
{
|
||||
return bdrv_co_pdiscard(bs->file, offset, bytes);
|
||||
}
|
||||
|
||||
|
||||
static void compress_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
BlockDriverInfo bdi;
|
||||
int ret;
|
||||
|
||||
if (!bs->file) {
|
||||
return;
|
||||
}
|
||||
|
||||
ret = bdrv_get_info(bs->file->bs, &bdi);
|
||||
if (ret < 0 || bdi.cluster_size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
bs->bl.request_alignment = bdi.cluster_size;
|
||||
}
|
||||
|
||||
|
||||
static void compress_eject(BlockDriverState *bs, bool eject_flag)
|
||||
{
|
||||
bdrv_eject(bs->file->bs, eject_flag);
|
||||
}
|
||||
|
||||
|
||||
static void compress_lock_medium(BlockDriverState *bs, bool locked)
|
||||
{
|
||||
bdrv_lock_medium(bs->file->bs, locked);
|
||||
}
|
||||
|
||||
|
||||
static bool compress_recurse_is_first_non_filter(BlockDriverState *bs,
|
||||
BlockDriverState *candidate)
|
||||
{
|
||||
return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
|
||||
}
|
||||
|
||||
|
||||
static BlockDriver bdrv_compress = {
|
||||
.format_name = "compress",
|
||||
|
||||
.bdrv_open = compress_open,
|
||||
.bdrv_child_perm = bdrv_filter_default_perms,
|
||||
|
||||
.bdrv_getlength = compress_getlength,
|
||||
|
||||
.bdrv_co_preadv_part = compress_co_preadv_part,
|
||||
.bdrv_co_pwritev_part = compress_co_pwritev_part,
|
||||
.bdrv_co_pwrite_zeroes = compress_co_pwrite_zeroes,
|
||||
.bdrv_co_pdiscard = compress_co_pdiscard,
|
||||
.bdrv_refresh_limits = compress_refresh_limits,
|
||||
|
||||
.bdrv_eject = compress_eject,
|
||||
.bdrv_lock_medium = compress_lock_medium,
|
||||
|
||||
.bdrv_co_block_status = bdrv_co_block_status_from_file,
|
||||
|
||||
.bdrv_recurse_is_first_non_filter = compress_recurse_is_first_non_filter,
|
||||
|
||||
.has_variable_length = true,
|
||||
.is_filter = true,
|
||||
};
|
||||
|
||||
static void bdrv_compress_init(void)
|
||||
{
|
||||
bdrv_register(&bdrv_compress);
|
||||
}
|
||||
|
||||
block_init(bdrv_compress_init);
|
||||
+18
-23
@@ -1703,8 +1703,14 @@ bool coroutine_fn qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
bool found;
|
||||
Qcow2BitmapList *bm_list;
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
uint64_t bitmap_directory_size = 0;
|
||||
uint32_t nb_bitmaps = 0;
|
||||
|
||||
if (bdrv_find_dirty_bitmap(bs, name)) {
|
||||
error_setg(errp, "Bitmap already exists: %s", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s->qcow_version < 3) {
|
||||
/* Without autoclear_features, we would always have to assume
|
||||
@@ -1720,38 +1726,27 @@ bool coroutine_fn qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (s->nb_bitmaps == 0) {
|
||||
return true;
|
||||
FOR_EACH_DIRTY_BITMAP(bs, bitmap) {
|
||||
if (bdrv_dirty_bitmap_get_persistence(bitmap)) {
|
||||
nb_bitmaps++;
|
||||
bitmap_directory_size +=
|
||||
calc_dir_entry_size(strlen(bdrv_dirty_bitmap_name(bitmap)), 0);
|
||||
}
|
||||
}
|
||||
nb_bitmaps++;
|
||||
bitmap_directory_size += calc_dir_entry_size(strlen(name), 0);
|
||||
|
||||
if (s->nb_bitmaps >= QCOW2_MAX_BITMAPS) {
|
||||
if (nb_bitmaps > QCOW2_MAX_BITMAPS) {
|
||||
error_setg(errp,
|
||||
"Maximum number of persistent bitmaps is already reached");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (s->bitmap_directory_size + calc_dir_entry_size(strlen(name), 0) >
|
||||
QCOW2_MAX_BITMAP_DIRECTORY_SIZE)
|
||||
{
|
||||
if (bitmap_directory_size > QCOW2_MAX_BITMAP_DIRECTORY_SIZE) {
|
||||
error_setg(errp, "Not enough space in the bitmap directory");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
|
||||
s->bitmap_directory_size, errp);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
if (bm_list == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
found = find_bitmap_by_name(bm_list, name);
|
||||
bitmap_list_free(bm_list);
|
||||
if (found) {
|
||||
error_setg(errp, "Bitmap with the same name is already stored");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
fail:
|
||||
|
||||
+75
-27
@@ -4221,10 +4221,8 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* XXX: put compressed sectors first, then all the cluster aligned
|
||||
tables to avoid losing bytes in alignment */
|
||||
static coroutine_fn int
|
||||
qcow2_co_pwritev_compressed_part(BlockDriverState *bs,
|
||||
qcow2_co_pwritev_compressed_task(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, size_t qiov_offset)
|
||||
{
|
||||
@@ -4234,32 +4232,11 @@ qcow2_co_pwritev_compressed_part(BlockDriverState *bs,
|
||||
uint8_t *buf, *out_buf;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
if (has_data_file(bs)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (bytes == 0) {
|
||||
/* align end of file to a sector boundary to ease reading with
|
||||
sector based I/Os */
|
||||
int64_t len = bdrv_getlength(bs->file->bs);
|
||||
if (len < 0) {
|
||||
return len;
|
||||
}
|
||||
return bdrv_co_truncate(bs->file, len, false, PREALLOC_MODE_OFF, NULL);
|
||||
}
|
||||
|
||||
if (offset_into_cluster(s, offset)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
assert(bytes == s->cluster_size || (bytes < s->cluster_size &&
|
||||
(offset + bytes == bs->total_sectors << BDRV_SECTOR_BITS)));
|
||||
|
||||
buf = qemu_blockalign(bs, s->cluster_size);
|
||||
if (bytes != s->cluster_size) {
|
||||
if (bytes > s->cluster_size ||
|
||||
offset + bytes != bs->total_sectors << BDRV_SECTOR_BITS)
|
||||
{
|
||||
qemu_vfree(buf);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (bytes < s->cluster_size) {
|
||||
/* Zero-pad last write if image size is not cluster aligned */
|
||||
memset(buf + bytes, 0, s->cluster_size - bytes);
|
||||
}
|
||||
@@ -4308,6 +4285,77 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static coroutine_fn int qcow2_co_pwritev_compressed_task_entry(AioTask *task)
|
||||
{
|
||||
Qcow2AioTask *t = container_of(task, Qcow2AioTask, task);
|
||||
|
||||
assert(!t->cluster_type && !t->l2meta);
|
||||
|
||||
return qcow2_co_pwritev_compressed_task(t->bs, t->offset, t->bytes, t->qiov,
|
||||
t->qiov_offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX: put compressed sectors first, then all the cluster aligned
|
||||
* tables to avoid losing bytes in alignment
|
||||
*/
|
||||
static coroutine_fn int
|
||||
qcow2_co_pwritev_compressed_part(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, size_t qiov_offset)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
AioTaskPool *aio = NULL;
|
||||
int ret = 0;
|
||||
|
||||
if (has_data_file(bs)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (bytes == 0) {
|
||||
/*
|
||||
* align end of file to a sector boundary to ease reading with
|
||||
* sector based I/Os
|
||||
*/
|
||||
int64_t len = bdrv_getlength(bs->file->bs);
|
||||
if (len < 0) {
|
||||
return len;
|
||||
}
|
||||
return bdrv_co_truncate(bs->file, len, false, PREALLOC_MODE_OFF, NULL);
|
||||
}
|
||||
|
||||
if (offset_into_cluster(s, offset)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
while (bytes && aio_task_pool_status(aio) == 0) {
|
||||
uint64_t chunk_size = MIN(bytes, s->cluster_size);
|
||||
|
||||
if (!aio && chunk_size != bytes) {
|
||||
aio = aio_task_pool_new(QCOW2_MAX_WORKERS);
|
||||
}
|
||||
|
||||
ret = qcow2_add_task(bs, aio, qcow2_co_pwritev_compressed_task_entry,
|
||||
0, 0, offset, chunk_size, qiov, qiov_offset, NULL);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
qiov_offset += chunk_size;
|
||||
offset += chunk_size;
|
||||
bytes -= chunk_size;
|
||||
}
|
||||
|
||||
if (aio) {
|
||||
aio_task_pool_wait_all(aio);
|
||||
if (ret == 0) {
|
||||
ret = aio_task_pool_status(aio);
|
||||
}
|
||||
g_free(aio);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn
|
||||
qcow2_co_preadv_compressed(BlockDriverState *bs,
|
||||
uint64_t file_cluster_offset,
|
||||
|
||||
@@ -893,8 +893,7 @@ static void throttle_group_set_limits(Object *obj, Visitor *v,
|
||||
{
|
||||
ThrottleGroup *tg = THROTTLE_GROUP(obj);
|
||||
ThrottleConfig cfg;
|
||||
ThrottleLimits arg = { 0 };
|
||||
ThrottleLimits *argp = &arg;
|
||||
ThrottleLimits *argp;
|
||||
Error *local_err = NULL;
|
||||
|
||||
visit_type_ThrottleLimits(v, name, &argp, &local_err);
|
||||
@@ -912,6 +911,7 @@ static void throttle_group_set_limits(Object *obj, Visitor *v,
|
||||
unlock:
|
||||
qemu_mutex_unlock(&tg->lock);
|
||||
ret:
|
||||
qapi_free_ThrottleLimits(argp);
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -280,6 +280,7 @@ enum {
|
||||
};
|
||||
|
||||
char *bdrv_perm_names(uint64_t perm);
|
||||
uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm);
|
||||
|
||||
/* disk I/O throttling */
|
||||
void bdrv_init(void);
|
||||
|
||||
+19
-5
@@ -2884,15 +2884,16 @@
|
||||
# @copy-on-read: Since 3.0
|
||||
# @blklogwrites: Since 3.0
|
||||
# @blkreplay: Since 4.2
|
||||
# @compress: Since 5.0
|
||||
#
|
||||
# Since: 2.9
|
||||
##
|
||||
{ 'enum': 'BlockdevDriver',
|
||||
'data': [ 'blkdebug', 'blklogwrites', 'blkreplay', 'blkverify', 'bochs',
|
||||
'cloop', 'copy-on-read', 'dmg', 'file', 'ftp', 'ftps', 'gluster',
|
||||
'host_cdrom', 'host_device', 'http', 'https', 'iscsi', 'luks',
|
||||
'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels', 'qcow',
|
||||
'qcow2', 'qed', 'quorum', 'raw', 'rbd',
|
||||
'cloop', 'compress', 'copy-on-read', 'dmg', 'file', 'ftp', 'ftps',
|
||||
'gluster', 'host_cdrom', 'host_device', 'http', 'https', 'iscsi',
|
||||
'luks', 'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels',
|
||||
'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'rbd',
|
||||
{ 'name': 'replication', 'if': 'defined(CONFIG_REPLICATION)' },
|
||||
'sheepdog',
|
||||
'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
|
||||
@@ -3454,6 +3455,16 @@
|
||||
#
|
||||
# @set-state: array of state-change descriptions
|
||||
#
|
||||
# @take-child-perms: Permissions to take on @image in addition to what
|
||||
# is necessary anyway (which depends on how the
|
||||
# blkdebug node is used). Defaults to none.
|
||||
# (since 5.0)
|
||||
#
|
||||
# @unshare-child-perms: Permissions not to share on @image in addition
|
||||
# to what cannot be shared anyway (which depends
|
||||
# on how the blkdebug node is used). Defaults
|
||||
# to none. (since 5.0)
|
||||
#
|
||||
# Since: 2.9
|
||||
##
|
||||
{ 'struct': 'BlockdevOptionsBlkdebug',
|
||||
@@ -3463,7 +3474,9 @@
|
||||
'*opt-write-zero': 'int32', '*max-write-zero': 'int32',
|
||||
'*opt-discard': 'int32', '*max-discard': 'int32',
|
||||
'*inject-error': ['BlkdebugInjectErrorOptions'],
|
||||
'*set-state': ['BlkdebugSetStateOptions'] } }
|
||||
'*set-state': ['BlkdebugSetStateOptions'],
|
||||
'*take-child-perms': ['BlockPermission'],
|
||||
'*unshare-child-perms': ['BlockPermission'] } }
|
||||
|
||||
##
|
||||
# @BlockdevOptionsBlklogwrites:
|
||||
@@ -4048,6 +4061,7 @@
|
||||
'blkreplay': 'BlockdevOptionsBlkreplay',
|
||||
'bochs': 'BlockdevOptionsGenericFormat',
|
||||
'cloop': 'BlockdevOptionsGenericFormat',
|
||||
'compress': 'BlockdevOptionsGenericFormat',
|
||||
'copy-on-read':'BlockdevOptionsGenericFormat',
|
||||
'dmg': 'BlockdevOptionsGenericFormat',
|
||||
'file': 'BlockdevOptionsFile',
|
||||
|
||||
@@ -41,8 +41,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
_supported_fmt qcow2
|
||||
_supported_proto generic
|
||||
# refcount_bits must be at least 4 so we can create ten internal snapshots
|
||||
# (1 bit supports none, 2 bits support two, 4 bits support 14)
|
||||
_unsupported_imgopts 'refcount_bits=\(1\|2\)[^0-9]'
|
||||
# (1 bit supports none, 2 bits support two, 4 bits support 14);
|
||||
# snapshot are generally impossible with external data files
|
||||
_unsupported_imgopts 'refcount_bits=\(1\|2\)[^0-9]' data_file
|
||||
|
||||
echo
|
||||
echo "creating image"
|
||||
|
||||
@@ -43,6 +43,8 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_os Linux
|
||||
# Compression and snapshots do not work with external data files
|
||||
_unsupported_imgopts data_file
|
||||
|
||||
TEST_OFFSETS="0 4294967296"
|
||||
TEST_OPS="writev read write readv"
|
||||
|
||||
@@ -40,8 +40,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
# actually any format that supports snapshots
|
||||
_supported_fmt qcow2
|
||||
_supported_proto generic
|
||||
# Internal snapshots are (currently) impossible with refcount_bits=1
|
||||
_unsupported_imgopts 'refcount_bits=1[^0-9]'
|
||||
# Internal snapshots are (currently) impossible with refcount_bits=1,
|
||||
# and generally impossible with external data files
|
||||
_unsupported_imgopts 'refcount_bits=1[^0-9]' data_file
|
||||
|
||||
echo
|
||||
echo "creating image"
|
||||
|
||||
@@ -30,9 +30,9 @@ status=1 # failure is the default!
|
||||
|
||||
_cleanup()
|
||||
{
|
||||
_cleanup_test_img
|
||||
rm -f "$TEST_IMG.base"
|
||||
rm -f "$TEST_IMG.orig"
|
||||
_cleanup_test_img
|
||||
_rm_test_img "$TEST_IMG.base"
|
||||
_rm_test_img "$TEST_IMG.orig"
|
||||
}
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
|
||||
@@ -28,9 +28,9 @@ status=1 # failure is the default!
|
||||
|
||||
_cleanup()
|
||||
{
|
||||
_cleanup_test_img
|
||||
rm -f "$TEST_IMG.base"
|
||||
rm -f "$TEST_IMG.orig"
|
||||
_cleanup_test_img
|
||||
_rm_test_img "$TEST_IMG.base"
|
||||
_rm_test_img "$TEST_IMG.orig"
|
||||
}
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
|
||||
@@ -29,12 +29,12 @@ status=1 # failure is the default!
|
||||
_cleanup()
|
||||
{
|
||||
_cleanup_test_img
|
||||
rm -f "$TEST_DIR/t.$IMGFMT.base_old"
|
||||
rm -f "$TEST_DIR/t.$IMGFMT.base_new"
|
||||
_rm_test_img "$TEST_DIR/t.$IMGFMT.base_old"
|
||||
_rm_test_img "$TEST_DIR/t.$IMGFMT.base_new"
|
||||
|
||||
rm -f "$TEST_DIR/subdir/t.$IMGFMT"
|
||||
rm -f "$TEST_DIR/subdir/t.$IMGFMT.base_old"
|
||||
rm -f "$TEST_DIR/subdir/t.$IMGFMT.base_new"
|
||||
_rm_test_img "$TEST_DIR/subdir/t.$IMGFMT"
|
||||
_rm_test_img "$TEST_DIR/subdir/t.$IMGFMT.base_old"
|
||||
_rm_test_img "$TEST_DIR/subdir/t.$IMGFMT.base_new"
|
||||
rmdir "$TEST_DIR/subdir" 2> /dev/null
|
||||
}
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
@@ -49,7 +49,10 @@ _supported_cache_modes writethrough none
|
||||
# 32 and 64 bits do not work either, however, due to different leaked cluster
|
||||
# count on error.
|
||||
# Thus, the only remaining option is refcount_bits=16.
|
||||
_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)'
|
||||
#
|
||||
# As for data_file, none of the refcount tests can work for it.
|
||||
_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' \
|
||||
data_file
|
||||
|
||||
echo "Errors while writing 128 kB"
|
||||
echo
|
||||
|
||||
@@ -32,7 +32,7 @@ status=1 # failure is the default!
|
||||
_cleanup()
|
||||
{
|
||||
_cleanup_qemu
|
||||
rm -f "${TEST_IMG}.copy"
|
||||
_rm_test_img "${TEST_IMG}.copy"
|
||||
_cleanup_test_img
|
||||
}
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
@@ -28,7 +28,7 @@ status=1 # failure is the default!
|
||||
|
||||
_cleanup()
|
||||
{
|
||||
rm -f $TEST_IMG.snap
|
||||
_rm_test_img "$TEST_IMG.snap"
|
||||
_cleanup_test_img
|
||||
}
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
@@ -42,8 +42,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
_supported_fmt qcow2
|
||||
_supported_proto generic
|
||||
_unsupported_proto vxhs
|
||||
# Internal snapshots are (currently) impossible with refcount_bits=1
|
||||
_unsupported_imgopts 'refcount_bits=1[^0-9]'
|
||||
# Internal snapshots are (currently) impossible with refcount_bits=1,
|
||||
# and generally impossible with external data files
|
||||
_unsupported_imgopts 'refcount_bits=1[^0-9]' data_file
|
||||
|
||||
offset_size=24
|
||||
offset_l1_size=36
|
||||
|
||||
@@ -40,19 +40,22 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
# This tests qcow2-specific low-level functionality
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
# We want to test compat=0.10, which does not support external data
|
||||
# files or refcount widths other than 16
|
||||
_unsupported_imgopts data_file 'refcount_bits=\([^1]\|.\([^6]\|$\)\)'
|
||||
|
||||
CLUSTER_SIZE=65536
|
||||
|
||||
# qcow2.py output depends on the exact options used, so override the command
|
||||
# line here as an exception
|
||||
for IMGOPTS in "compat=0.10" "compat=1.1"; do
|
||||
for compat in "compat=0.10" "compat=1.1"; do
|
||||
|
||||
echo
|
||||
echo ===== Testing with -o $IMGOPTS =====
|
||||
echo ===== Testing with -o $compat =====
|
||||
echo
|
||||
echo === Create image with unknown header extension ===
|
||||
echo
|
||||
_make_test_img 64M
|
||||
_make_test_img -o $compat 64M
|
||||
$PYTHON qcow2.py "$TEST_IMG" add-header-ext 0x12345678 "This is a test header extension"
|
||||
$PYTHON qcow2.py "$TEST_IMG" dump-header
|
||||
_check_test_img
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user