Block layer patches:

- block: Remove bdrv_read() and bdrv_write()
 - qemu-img: Allow rebase with no input base
 - blockjob: Fix coroutine thread after AioContext change
 - MAINTAINERS updates for pflash, curl and gluster
 -----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJc1ZtKAAoJEH8JsnLIjy/WNaUQAL8YXgDxkxG/a7KMYzebLraV
 1K9auhi2dC7kV43jUYIwj6sPrzedLzjhkTez3qFe4AJAjvrv+EorGWCv2ro4COzp
 1l6i2jqilhCTV0N1MBNwG4wQY9Y4IRxGls1W0U/83FqVi38tbBPZIMsFC2pljd2W
 8tRZ4KSI0BwBvGR3oeiBoOAIfPFp1lQjct7+HUpI6vcEn08+MXKDA66Y4LAG9AHR
 nAW9VFfUw6KBXGddFoE8WJwA1VHIJ8HUqqc4xv5cqtNO8bLYdO2FYHUf+LhI+96b
 nfdAL6T0isDbC0lIlC6Xue9tTFUDszEq6rNNglZ/FVHWkMtdNVyHNLLYpjC85fjH
 ZeUKfuEP7jEYXP415NFK0mRVZ+L6T/wlqzOz6OwKtRQsOQByonkrYcI5ejdoKC92
 f/azvIRszzt8OOJal1tq0sx+vCR5gydJNnhAmyzNfspmMWsMZswbaGftx5pzGWTT
 eDJSdJNMqCn7lK0TtwYSVK2p3aD4SNbszbD6gzZK+hmEUpY7T8lLyN6g12b/TFMY
 /Vod1ltZLb2e3CmNfYqEBBHTQHBiYClad5YPZ1wM+wZMS/UDfaUlJa85zNf1jO86
 JZti4hg4+IsAVNwJ2M2vsUWzl3W/4ErjpxJqGPskZ5XwpT6rOce9J1SS821eW7J5
 hOMhvYGW+oVlFYL9ypKk
 =fcNj
 -----END PGP SIGNATURE-----

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

Block layer patches:

- block: Remove bdrv_read() and bdrv_write()
- qemu-img: Allow rebase with no input base
- blockjob: Fix coroutine thread after AioContext change
- MAINTAINERS updates for pflash, curl and gluster

# gpg: Signature made Fri 10 May 2019 16:39:54 BST
# gpg:                using RSA key 7F09B272C88F2FD6
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" [full]
# Primary key fingerprint: DC3D EB15 9A9A F95D 3D74  56FE 7F09 B272 C88F 2FD6

* remotes/kevin/tags/for-upstream:
  iotests: Add test for rebase without input base
  qemu-img: Use zero writes after source backing EOF
  qemu-img: Allow rebase with no input base
  qcow2: Remove BDRVQcow2State.cluster_sectors
  block: Remove bdrv_read() and bdrv_write()
  vvfat: Replace bdrv_{read,write}() with bdrv_{pread,pwrite}()
  vdi: Replace bdrv_{read,write}() with bdrv_{pread,pwrite}()
  qcow2: Replace bdrv_write() with bdrv_pwrite()
  qemu-img: Use IEC binary prefixes for size constants
  test-block-iothread: Job coroutine thread after AioContext switch
  blockjob: Fix coroutine thread after AioContext change
  qemu-iotests: Fix cleanup for 192
  MAINTAINERS: Add an entry for the Parallel NOR Flash devices
  MAINTAINERS: Downgrade status of block sections without "M:" to "Odd Fixes"
  block: remove bs from lists before closing

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-05-10 18:44:59 +01:00
commit c9ba36ff2f
16 changed files with 358 additions and 92 deletions

View file

@ -1391,6 +1391,13 @@ F: include/hw/net/
F: tests/virtio-net-test.c F: tests/virtio-net-test.c
T: git https://github.com/jasowang/qemu.git net T: git https://github.com/jasowang/qemu.git net
Parallel NOR Flash devices
M: Philippe Mathieu-Daudé <philmd@redhat.com>
T: git https://gitlab.com/philmd/qemu.git pflash-next
S: Maintained
F: hw/block/pflash_cfi*.c
F: include/hw/block/flash.h
SCSI SCSI
M: Paolo Bonzini <pbonzini@redhat.com> M: Paolo Bonzini <pbonzini@redhat.com>
R: Fam Zheng <fam@euphon.net> R: Fam Zheng <fam@euphon.net>
@ -2404,12 +2411,13 @@ F: block/ssh.c
CURL CURL
L: qemu-block@nongnu.org L: qemu-block@nongnu.org
S: Supported S: Odd Fixes
F: block/curl.c F: block/curl.c
GLUSTER GLUSTER
L: qemu-block@nongnu.org L: qemu-block@nongnu.org
S: Supported L: integration@gluster.org
S: Odd Fixes
F: block/gluster.c F: block/gluster.c
Null Block Driver Null Block Driver

View file

@ -4082,14 +4082,14 @@ static void bdrv_delete(BlockDriverState *bs)
assert(bdrv_op_blocker_is_empty(bs)); assert(bdrv_op_blocker_is_empty(bs));
assert(!bs->refcnt); assert(!bs->refcnt);
bdrv_close(bs);
/* remove from list, if necessary */ /* remove from list, if necessary */
if (bs->node_name[0] != '\0') { if (bs->node_name[0] != '\0') {
QTAILQ_REMOVE(&graph_bdrv_states, bs, node_list); QTAILQ_REMOVE(&graph_bdrv_states, bs, node_list);
} }
QTAILQ_REMOVE(&all_bdrv_states, bs, bs_list); QTAILQ_REMOVE(&all_bdrv_states, bs, bs_list);
bdrv_close(bs);
g_free(bs); g_free(bs);
} }

View file

@ -837,42 +837,6 @@ static int bdrv_prwv_co(BdrvChild *child, int64_t offset,
return rwco.ret; return rwco.ret;
} }
/*
* Process a synchronous request using coroutines
*/
static int bdrv_rw_co(BdrvChild *child, int64_t sector_num, uint8_t *buf,
int nb_sectors, bool is_write, BdrvRequestFlags flags)
{
QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf,
nb_sectors * BDRV_SECTOR_SIZE);
if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) {
return -EINVAL;
}
return bdrv_prwv_co(child, sector_num << BDRV_SECTOR_BITS,
&qiov, is_write, flags);
}
/* return < 0 if error. See bdrv_write() for the return codes */
int bdrv_read(BdrvChild *child, int64_t sector_num,
uint8_t *buf, int nb_sectors)
{
return bdrv_rw_co(child, sector_num, buf, nb_sectors, false, 0);
}
/* Return < 0 if error. Important errors are:
-EIO generic I/O error (may happen for all errors)
-ENOMEDIUM No media inserted.
-EINVAL Invalid sector number or nb_sectors
-EACCES Trying to write a read-only device
*/
int bdrv_write(BdrvChild *child, int64_t sector_num,
const uint8_t *buf, int nb_sectors)
{
return bdrv_rw_co(child, sector_num, (uint8_t *)buf, nb_sectors, true, 0);
}
int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset,
int bytes, BdrvRequestFlags flags) int bytes, BdrvRequestFlags flags)
{ {
@ -935,6 +899,7 @@ int bdrv_preadv(BdrvChild *child, int64_t offset, QEMUIOVector *qiov)
return qiov->size; return qiov->size;
} }
/* See bdrv_pwrite() for the return codes */
int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int bytes) int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int bytes)
{ {
QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes);
@ -958,6 +923,12 @@ int bdrv_pwritev(BdrvChild *child, int64_t offset, QEMUIOVector *qiov)
return qiov->size; return qiov->size;
} }
/* Return no. of bytes on success or < 0 on error. Important errors are:
-EIO generic I/O error (may happen for all errors)
-ENOMEDIUM No media inserted.
-EINVAL Invalid offset or number of bytes
-EACCES Trying to write a read-only device
*/
int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, int bytes) int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, int bytes)
{ {
QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes);

View file

@ -2429,8 +2429,8 @@ write_refblocks:
on_disk_refblock = (void *)((char *) *refcount_table + on_disk_refblock = (void *)((char *) *refcount_table +
refblock_index * s->cluster_size); refblock_index * s->cluster_size);
ret = bdrv_write(bs->file, refblock_offset / BDRV_SECTOR_SIZE, ret = bdrv_pwrite(bs->file, refblock_offset, on_disk_refblock,
on_disk_refblock, s->cluster_sectors); s->cluster_size);
if (ret < 0) { if (ret < 0) {
fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret)); fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret));
goto fail; goto fail;

View file

@ -1259,7 +1259,6 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
s->cluster_bits = header.cluster_bits; s->cluster_bits = header.cluster_bits;
s->cluster_size = 1 << s->cluster_bits; s->cluster_size = 1 << s->cluster_bits;
s->cluster_sectors = 1 << (s->cluster_bits - BDRV_SECTOR_BITS);
/* Initialise version 3 header fields */ /* Initialise version 3 header fields */
if (header.version == 2) { if (header.version == 2) {

View file

@ -266,7 +266,6 @@ typedef struct Qcow2BitmapHeaderExt {
typedef struct BDRVQcow2State { typedef struct BDRVQcow2State {
int cluster_bits; int cluster_bits;
int cluster_size; int cluster_size;
int cluster_sectors;
int l2_slice_size; int l2_slice_size;
int l2_bits; int l2_bits;
int l2_size; int l2_size;

View file

@ -171,6 +171,8 @@ typedef struct {
uint64_t unused2[7]; uint64_t unused2[7];
} QEMU_PACKED VdiHeader; } QEMU_PACKED VdiHeader;
QEMU_BUILD_BUG_ON(sizeof(VdiHeader) != 512);
typedef struct { typedef struct {
/* The block map entries are little endian (even in memory). */ /* The block map entries are little endian (even in memory). */
uint32_t *bmap; uint32_t *bmap;
@ -384,7 +386,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
logout("\n"); logout("\n");
ret = bdrv_read(bs->file, 0, (uint8_t *)&header, 1); ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
} }
@ -484,8 +486,8 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
goto fail; goto fail;
} }
ret = bdrv_read(bs->file, s->bmap_sector, (uint8_t *)s->bmap, ret = bdrv_pread(bs->file, header.offset_bmap, s->bmap,
bmap_size); bmap_size * SECTOR_SIZE);
if (ret < 0) { if (ret < 0) {
goto fail_free_bmap; goto fail_free_bmap;
} }
@ -704,7 +706,7 @@ nonallocating_write:
assert(VDI_IS_ALLOCATED(bmap_first)); assert(VDI_IS_ALLOCATED(bmap_first));
*header = s->header; *header = s->header;
vdi_header_to_le(header); vdi_header_to_le(header);
ret = bdrv_write(bs->file, 0, block, 1); ret = bdrv_pwrite(bs->file, 0, block, sizeof(VdiHeader));
g_free(block); g_free(block);
block = NULL; block = NULL;
@ -722,10 +724,11 @@ nonallocating_write:
base = ((uint8_t *)&s->bmap[0]) + bmap_first * SECTOR_SIZE; base = ((uint8_t *)&s->bmap[0]) + bmap_first * SECTOR_SIZE;
logout("will write %u block map sectors starting from entry %u\n", logout("will write %u block map sectors starting from entry %u\n",
n_sectors, bmap_first); n_sectors, bmap_first);
ret = bdrv_write(bs->file, offset, base, n_sectors); ret = bdrv_pwrite(bs->file, offset * SECTOR_SIZE, base,
n_sectors * SECTOR_SIZE);
} }
return ret; return ret < 0 ? ret : 0;
} }
static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,

View file

@ -1494,8 +1494,8 @@ static int vvfat_read(BlockDriverState *bs, int64_t sector_num,
DLOG(fprintf(stderr, "sectors %" PRId64 "+%" PRId64 DLOG(fprintf(stderr, "sectors %" PRId64 "+%" PRId64
" allocated\n", sector_num, " allocated\n", sector_num,
n >> BDRV_SECTOR_BITS)); n >> BDRV_SECTOR_BITS));
if (bdrv_read(s->qcow, sector_num, buf + i * 0x200, if (bdrv_pread(s->qcow, sector_num * BDRV_SECTOR_SIZE,
n >> BDRV_SECTOR_BITS)) { buf + i * 0x200, n) < 0) {
return -1; return -1;
} }
i += (n >> BDRV_SECTOR_BITS) - 1; i += (n >> BDRV_SECTOR_BITS) - 1;
@ -1983,8 +1983,9 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s,
if (res) { if (res) {
return -1; return -1;
} }
res = bdrv_write(s->qcow, offset, s->cluster_buffer, 1); res = bdrv_pwrite(s->qcow, offset * BDRV_SECTOR_SIZE,
if (res) { s->cluster_buffer, BDRV_SECTOR_SIZE);
if (res < 0) {
return -2; return -2;
} }
} }
@ -3050,7 +3051,8 @@ DLOG(checkpoint());
* Use qcow backend. Commit later. * Use qcow backend. Commit later.
*/ */
DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sectors)); DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sectors));
ret = bdrv_write(s->qcow, sector_num, buf, nb_sectors); ret = bdrv_pwrite(s->qcow, sector_num * BDRV_SECTOR_SIZE, buf,
nb_sectors * BDRV_SECTOR_SIZE);
if (ret < 0) { if (ret < 0) {
fprintf(stderr, "Error writing to qcow backend\n"); fprintf(stderr, "Error writing to qcow backend\n");
return ret; return ret;

View file

@ -316,10 +316,6 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
BlockReopenQueue *queue, Error **errp); BlockReopenQueue *queue, Error **errp);
void bdrv_reopen_commit(BDRVReopenState *reopen_state); void bdrv_reopen_commit(BDRVReopenState *reopen_state);
void bdrv_reopen_abort(BDRVReopenState *reopen_state); void bdrv_reopen_abort(BDRVReopenState *reopen_state);
int bdrv_read(BdrvChild *child, int64_t sector_num,
uint8_t *buf, int nb_sectors);
int bdrv_write(BdrvChild *child, int64_t sector_num,
const uint8_t *buf, int nb_sectors);
int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset,
int bytes, BdrvRequestFlags flags); int bytes, BdrvRequestFlags flags);
int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags); int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags);

2
job.c
View file

@ -432,7 +432,7 @@ void job_enter_cond(Job *job, bool(*fn)(Job *job))
timer_del(&job->sleep_timer); timer_del(&job->sleep_timer);
job->busy = true; job->busy = true;
job_unlock(); job_unlock();
aio_co_wake(job->co); aio_co_enter(job->aio_context, job->co);
} }
void job_enter(Job *job) void job_enter(Job *job)

View file

@ -37,6 +37,7 @@
#include "qemu/option.h" #include "qemu/option.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qemu/log.h" #include "qemu/log.h"
#include "qemu/units.h"
#include "qom/object_interfaces.h" #include "qom/object_interfaces.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "sysemu/block-backend.h" #include "sysemu/block-backend.h"
@ -1216,7 +1217,7 @@ static int compare_buffers(const uint8_t *buf1, const uint8_t *buf2,
return res; return res;
} }
#define IO_BUF_SIZE (2 * 1024 * 1024) #define IO_BUF_SIZE (2 * MiB)
/* /*
* Check if passed sectors are empty (not allocated or contain only 0 bytes) * Check if passed sectors are empty (not allocated or contain only 0 bytes)
@ -2960,7 +2961,7 @@ static int img_map(int argc, char **argv)
int64_t n; int64_t n;
/* Probe up to 1 GiB at a time. */ /* Probe up to 1 GiB at a time. */
n = MIN(1 << 30, length - offset); n = MIN(1 * GiB, length - offset);
ret = get_block_status(bs, offset, n, &next); ret = get_block_status(bs, offset, n, &next);
if (ret < 0) { if (ret < 0) {
@ -3311,26 +3312,30 @@ static int img_rebase(int argc, char **argv)
char backing_name[PATH_MAX]; char backing_name[PATH_MAX];
QDict *options = NULL; QDict *options = NULL;
if (bs->backing_format[0] != '\0') { if (bs->backing) {
options = qdict_new(); if (bs->backing_format[0] != '\0') {
qdict_put_str(options, "driver", bs->backing_format);
}
if (force_share) {
if (!options) {
options = qdict_new(); options = qdict_new();
qdict_put_str(options, "driver", bs->backing_format);
} }
qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true);
} if (force_share) {
bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name)); if (!options) {
blk_old_backing = blk_new_open(backing_name, NULL, options = qdict_new();
options, src_flags, &local_err); }
if (!blk_old_backing) { qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true);
error_reportf_err(local_err, }
"Could not open old backing file '%s': ", bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name));
backing_name); blk_old_backing = blk_new_open(backing_name, NULL,
ret = -1; options, src_flags, &local_err);
goto out; if (!blk_old_backing) {
error_reportf_err(local_err,
"Could not open old backing file '%s': ",
backing_name);
ret = -1;
goto out;
}
} else {
blk_old_backing = NULL;
} }
if (out_baseimg[0]) { if (out_baseimg[0]) {
@ -3383,7 +3388,7 @@ static int img_rebase(int argc, char **argv)
*/ */
if (!unsafe) { if (!unsafe) {
int64_t size; int64_t size;
int64_t old_backing_size; int64_t old_backing_size = 0;
int64_t new_backing_size = 0; int64_t new_backing_size = 0;
uint64_t offset; uint64_t offset;
int64_t n; int64_t n;
@ -3399,15 +3404,18 @@ static int img_rebase(int argc, char **argv)
ret = -1; ret = -1;
goto out; goto out;
} }
old_backing_size = blk_getlength(blk_old_backing); if (blk_old_backing) {
if (old_backing_size < 0) { old_backing_size = blk_getlength(blk_old_backing);
char backing_name[PATH_MAX]; if (old_backing_size < 0) {
char backing_name[PATH_MAX];
bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name)); bdrv_get_backing_filename(bs, backing_name,
error_report("Could not get size of '%s': %s", sizeof(backing_name));
backing_name, strerror(-old_backing_size)); error_report("Could not get size of '%s': %s",
ret = -1; backing_name, strerror(-old_backing_size));
goto out; ret = -1;
goto out;
}
} }
if (blk_new_backing) { if (blk_new_backing) {
new_backing_size = blk_getlength(blk_new_backing); new_backing_size = blk_getlength(blk_new_backing);
@ -3424,6 +3432,8 @@ static int img_rebase(int argc, char **argv)
} }
for (offset = 0; offset < size; offset += n) { for (offset = 0; offset < size; offset += n) {
bool buf_old_is_zero = false;
/* How many bytes can we handle with the next read? */ /* How many bytes can we handle with the next read? */
n = MIN(IO_BUF_SIZE, size - offset); n = MIN(IO_BUF_SIZE, size - offset);
@ -3444,6 +3454,7 @@ static int img_rebase(int argc, char **argv)
*/ */
if (offset >= old_backing_size) { if (offset >= old_backing_size) {
memset(buf_old, 0, n); memset(buf_old, 0, n);
buf_old_is_zero = true;
} else { } else {
if (offset + n > old_backing_size) { if (offset + n > old_backing_size) {
n = old_backing_size - offset; n = old_backing_size - offset;
@ -3479,8 +3490,12 @@ static int img_rebase(int argc, char **argv)
if (compare_buffers(buf_old + written, buf_new + written, if (compare_buffers(buf_old + written, buf_new + written,
n - written, &pnum)) n - written, &pnum))
{ {
ret = blk_pwrite(blk, offset + written, if (buf_old_is_zero) {
buf_old + written, pnum, 0); ret = blk_pwrite_zeroes(blk, offset + written, pnum, 0);
} else {
ret = blk_pwrite(blk, offset + written,
buf_old + written, pnum, 0);
}
if (ret < 0) { if (ret < 0) {
error_report("Error while writing to COW image: %s", error_report("Error while writing to COW image: %s",
strerror(-ret)); strerror(-ret));

View file

@ -29,7 +29,9 @@ status=1 # failure is the default!
_cleanup() _cleanup()
{ {
_cleanup_test_img _cleanup_qemu
_cleanup_test_img
rm -f "$TEST_DIR/nbd"
} }
trap "_cleanup; exit \$status" 0 1 2 3 15 trap "_cleanup; exit \$status" 0 1 2 3 15

124
tests/qemu-iotests/252 Executable file
View file

@ -0,0 +1,124 @@
#!/usr/bin/env bash
#
# Tests for rebasing COW images that require zero cluster support
#
# Copyright (C) 2019 Red Hat, Inc.
#
# 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 of the License, or
# (at your option) any later version.
#
# 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/>.
#
# creator
owner=mreitz@redhat.com
seq=$(basename $0)
echo "QA output created by $seq"
status=1 # failure is the default!
_cleanup()
{
_cleanup_test_img
rm -f "$TEST_IMG.base_new"
}
trap "_cleanup; exit \$status" 0 1 2 3 15
# get standard environment, filters and checks
. ./common.rc
. ./common.filter
. ./common.pattern
# Currently only qcow2 and qed support rebasing, and only qcow2 v3 has
# zero cluster support
_supported_fmt qcow2
_unsupported_imgopts 'compat=0.10'
_supported_proto file
_supported_os Linux
CLUSTER_SIZE=65536
echo
echo "=== Test rebase without input base ==="
echo
# Cluster allocations to be tested:
#
# Backing (new) 11 -- 11 -- 11 --
# COW image 22 22 11 11 -- --
#
# Expected result:
#
# COW image 22 22 11 11 00 --
#
# (Cluster 2 might be "--" after the rebase, too, but rebase just
# compares the new backing file to the old one and disregards the
# overlay. Therefore, it will never discard overlay clusters.)
_make_test_img $((6 * CLUSTER_SIZE))
TEST_IMG="$TEST_IMG.base_new" _make_test_img $((6 * CLUSTER_SIZE))
echo
$QEMU_IO "$TEST_IMG" \
-c "write -P 0x22 $((0 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \
-c "write -P 0x11 $((2 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \
| _filter_qemu_io
$QEMU_IO "$TEST_IMG.base_new" \
-c "write -P 0x11 $((0 * CLUSTER_SIZE)) $CLUSTER_SIZE" \
-c "write -P 0x11 $((2 * CLUSTER_SIZE)) $CLUSTER_SIZE" \
-c "write -P 0x11 $((4 * CLUSTER_SIZE)) $CLUSTER_SIZE" \
| _filter_qemu_io
echo
# This should be a no-op
$QEMU_IMG rebase -b "" "$TEST_IMG"
# Verify the data is correct
$QEMU_IO "$TEST_IMG" \
-c "read -P 0x22 $((0 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \
-c "read -P 0x11 $((2 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \
-c "read -P 0x00 $((4 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \
| _filter_qemu_io
echo
# Verify the allocation status (first four cluster should be allocated
# in TEST_IMG, clusters 4 and 5 should be unallocated (marked as zero
# clusters here because there is no backing file))
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
echo
$QEMU_IMG rebase -b "$TEST_IMG.base_new" "$TEST_IMG"
# Verify the data is correct
$QEMU_IO "$TEST_IMG" \
-c "read -P 0x22 $((0 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \
-c "read -P 0x11 $((2 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \
-c "read -P 0x00 $((4 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \
| _filter_qemu_io
echo
# Verify the allocation status (first four cluster should be allocated
# in TEST_IMG, cluster 4 should be zero, and cluster 5 should be
# unallocated (signified by '"depth": 1'))
$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map
# success, all done
echo "*** done"
rm -f $seq.full
status=0

View file

@ -0,0 +1,39 @@
QA output created by 252
=== Test rebase without input base ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=393216
Formatting 'TEST_DIR/t.IMGFMT.base_new', fmt=IMGFMT size=393216
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 131072/131072 bytes at offset 131072
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset 131072
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset 262144
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 131072/131072 bytes at offset 131072
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 131072/131072 bytes at offset 262144
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
[{ "start": 0, "length": 262144, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
{ "start": 262144, "length": 131072, "depth": 0, "zero": true, "data": false}]
read 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 131072/131072 bytes at offset 131072
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 131072/131072 bytes at offset 262144
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
[{ "start": 0, "length": 262144, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
{ "start": 262144, "length": 65536, "depth": 0, "zero": true, "data": false},
{ "start": 327680, "length": 65536, "depth": 1, "zero": true, "data": false}]
*** done

View file

@ -249,3 +249,4 @@
247 rw auto quick 247 rw auto quick
248 rw auto quick 248 rw auto quick
249 rw auto quick 249 rw auto quick
252 rw auto backing quick

View file

@ -354,6 +354,111 @@ static void test_sync_op(const void *opaque)
blk_unref(blk); blk_unref(blk);
} }
typedef struct TestBlockJob {
BlockJob common;
bool should_complete;
int n;
} TestBlockJob;
static int test_job_prepare(Job *job)
{
g_assert(qemu_get_current_aio_context() == qemu_get_aio_context());
return 0;
}
static int coroutine_fn test_job_run(Job *job, Error **errp)
{
TestBlockJob *s = container_of(job, TestBlockJob, common.job);
job_transition_to_ready(&s->common.job);
while (!s->should_complete) {
s->n++;
g_assert(qemu_get_current_aio_context() == job->aio_context);
/* Avoid job_sleep_ns() because it marks the job as !busy. We want to
* emulate some actual activity (probably some I/O) here so that the
* drain involved in AioContext switches has to wait for this activity
* to stop. */
qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 1000000);
job_pause_point(&s->common.job);
}
g_assert(qemu_get_current_aio_context() == job->aio_context);
return 0;
}
static void test_job_complete(Job *job, Error **errp)
{
TestBlockJob *s = container_of(job, TestBlockJob, common.job);
s->should_complete = true;
}
BlockJobDriver test_job_driver = {
.job_driver = {
.instance_size = sizeof(TestBlockJob),
.free = block_job_free,
.user_resume = block_job_user_resume,
.drain = block_job_drain,
.run = test_job_run,
.complete = test_job_complete,
.prepare = test_job_prepare,
},
};
static void test_attach_blockjob(void)
{
IOThread *iothread = iothread_new();
AioContext *ctx = iothread_get_aio_context(iothread);
BlockBackend *blk;
BlockDriverState *bs;
TestBlockJob *tjob;
blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
bs = bdrv_new_open_driver(&bdrv_test, "base", BDRV_O_RDWR, &error_abort);
blk_insert_bs(blk, bs, &error_abort);
tjob = block_job_create("job0", &test_job_driver, NULL, bs,
0, BLK_PERM_ALL,
0, 0, NULL, NULL, &error_abort);
job_start(&tjob->common.job);
while (tjob->n == 0) {
aio_poll(qemu_get_aio_context(), false);
}
blk_set_aio_context(blk, ctx);
tjob->n = 0;
while (tjob->n == 0) {
aio_poll(qemu_get_aio_context(), false);
}
aio_context_acquire(ctx);
blk_set_aio_context(blk, qemu_get_aio_context());
aio_context_release(ctx);
tjob->n = 0;
while (tjob->n == 0) {
aio_poll(qemu_get_aio_context(), false);
}
blk_set_aio_context(blk, ctx);
tjob->n = 0;
while (tjob->n == 0) {
aio_poll(qemu_get_aio_context(), false);
}
aio_context_acquire(ctx);
job_complete_sync(&tjob->common.job, &error_abort);
blk_set_aio_context(blk, qemu_get_aio_context());
aio_context_release(ctx);
bdrv_unref(bs);
blk_unref(blk);
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int i; int i;
@ -368,5 +473,7 @@ int main(int argc, char **argv)
g_test_add_data_func(t->name, t, test_sync_op); g_test_add_data_func(t->name, t, test_sync_op);
} }
g_test_add_func("/attach/blockjob", test_attach_blockjob);
return g_test_run(); return g_test_run();
} }