mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-24 12:09:58 +00:00
Block layer patches:
- block: Fix crash with qcow2 partial cluster COW with small cluster sizes (misaligned write requests with BDRV_REQ_NO_FALLBACK) - qcow2: Fix integer overflow potentially causing corruption with huge requests - vhdx: Detect truncated image files - tools: Support help options for --object - Various block-related replay improvements - iotests/028: Fix for long $TEST_DIRs -----BEGIN PGP SIGNATURE----- iQIcBAABAgAGBQJdpJwuAAoJEH8JsnLIjy/W4EIP/ieDG6LwYIkxk6UPAnPm2ZtT jEBj1jZNbPPMVPCzeVryS2qgoSB4ItnKlSpbW9Z+DX2EL8gr/sHZot4J6BXkxaJV mxHa4KCLZUYHfLbVBX+ubb/fufu2ItGUtY2LcdxyWASk/6mWKYVivPunwaK4gEiD CBl9RgSMSy1bpBGIky2NMylOCr5KlSeweH/XL3J3Jodpp/3nnDpZ96iy953R7Bes OpwZEsDHQmYhDufPqIWVzyq2hEUn22kGe/Rn2KfOHO7SR6aqr+DqtUwlDE9mxxBf hFhm6bqbJhHQOTMbTrzqiYwDirR4S5/FlynI9+YbngU9fnkbCIOheTKL5M8PlSow H+ZUtmU1Avp0wG3RZVmtCT9upFV7hpC4/fiMr8bdXCyuWy/7d7WB1G4e9ELiX7uo VCl6gVviDQbEgnoNS7v6JbP/xjhHuu7Fxh5K0xgT6wtwP53cBqbxORMkwv2u3zCI QRuiKOHZW3wv8tdRP/5qhdtIxTy6w20v/lAO/s0Xqn8YlnyfrH71LCNWmG4MOfgP ZXwCv9nxpzVsTPU2nLowl0avCwmDVY8Iv/0sN+eybo8xp47pCmPV9dKa0rJ+RhFe N5blnnwsmJFPW+QD5gBZn7eH3jafHxN2URhG3cwNdWxQS6SiTcVXDsTfn5YB6PjN Tb9P2aUJYw94BvUJF2c+ =gCHy -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging Block layer patches: - block: Fix crash with qcow2 partial cluster COW with small cluster sizes (misaligned write requests with BDRV_REQ_NO_FALLBACK) - qcow2: Fix integer overflow potentially causing corruption with huge requests - vhdx: Detect truncated image files - tools: Support help options for --object - Various block-related replay improvements - iotests/028: Fix for long $TEST_DIRs # gpg: Signature made Mon 14 Oct 2019 17:02: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: Test large write request to qcow2 file qcow2: Limit total allocation range to INT_MAX qemu-nbd: Support help options for --object qemu-img: Support help options for --object qemu-io: Support help options for --object vl: Split off user_creatable_print_help() iotests/028: Fix for long $TEST_DIRs block: Reject misaligned write requests with BDRV_REQ_NO_FALLBACK replay: add BH oneshot event for block layer replay: finish record/replay before closing the disks replay: don't drain/flush bdrv queue while RR is working replay: update docs for record/replay with block devices replay: disable default snapshot for record/replay block: implement bdrv_snapshot_goto for blkreplay block/vhdx: add check for truncated image files Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
3af78db681
@ -126,6 +126,12 @@ static int coroutine_fn blkreplay_co_flush(BlockDriverState *bs)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int blkreplay_snapshot_goto(BlockDriverState *bs,
|
||||
const char *snapshot_id)
|
||||
{
|
||||
return bdrv_snapshot_goto(bs->file->bs, snapshot_id, NULL);
|
||||
}
|
||||
|
||||
static BlockDriver bdrv_blkreplay = {
|
||||
.format_name = "blkreplay",
|
||||
.instance_size = 0,
|
||||
@ -140,6 +146,8 @@ static BlockDriver bdrv_blkreplay = {
|
||||
.bdrv_co_pwrite_zeroes = blkreplay_co_pwrite_zeroes,
|
||||
.bdrv_co_pdiscard = blkreplay_co_pdiscard,
|
||||
.bdrv_co_flush = blkreplay_co_flush,
|
||||
|
||||
.bdrv_snapshot_goto = blkreplay_snapshot_goto,
|
||||
};
|
||||
|
||||
static void bdrv_blkreplay_init(void)
|
||||
|
@ -18,6 +18,8 @@
|
||||
#include "hw/qdev-core.h"
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qapi-events-block.h"
|
||||
#include "qemu/id.h"
|
||||
@ -1306,7 +1308,8 @@ BlockAIOCB *blk_abort_aio_request(BlockBackend *blk,
|
||||
acb->blk = blk;
|
||||
acb->ret = ret;
|
||||
|
||||
aio_bh_schedule_oneshot(blk_get_aio_context(blk), error_callback_bh, acb);
|
||||
replay_bh_schedule_oneshot_event(blk_get_aio_context(blk),
|
||||
error_callback_bh, acb);
|
||||
return &acb->common;
|
||||
}
|
||||
|
||||
@ -1362,8 +1365,8 @@ static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, int bytes,
|
||||
|
||||
acb->has_returned = true;
|
||||
if (acb->rwco.ret != NOT_DONE) {
|
||||
aio_bh_schedule_oneshot(blk_get_aio_context(blk),
|
||||
blk_aio_complete_bh, acb);
|
||||
replay_bh_schedule_oneshot_event(blk_get_aio_context(blk),
|
||||
blk_aio_complete_bh, acb);
|
||||
}
|
||||
|
||||
return &acb->common;
|
||||
|
39
block/io.c
39
block/io.c
@ -33,6 +33,7 @@
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "sysemu/replay.h"
|
||||
|
||||
#define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */
|
||||
|
||||
@ -368,8 +369,8 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
|
||||
if (bs) {
|
||||
bdrv_inc_in_flight(bs);
|
||||
}
|
||||
aio_bh_schedule_oneshot(bdrv_get_aio_context(bs),
|
||||
bdrv_co_drain_bh_cb, &data);
|
||||
replay_bh_schedule_oneshot_event(bdrv_get_aio_context(bs),
|
||||
bdrv_co_drain_bh_cb, &data);
|
||||
|
||||
qemu_coroutine_yield();
|
||||
/* If we are resumed from some other event (such as an aio completion or a
|
||||
@ -600,6 +601,15 @@ void bdrv_drain_all_begin(void)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* bdrv queue is managed by record/replay,
|
||||
* waiting for finishing the I/O requests may
|
||||
* be infinite
|
||||
*/
|
||||
if (replay_events_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* AIO_WAIT_WHILE() with a NULL context can only be called from the main
|
||||
* loop AioContext, so make sure we're in the main context. */
|
||||
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
|
||||
@ -629,6 +639,15 @@ void bdrv_drain_all_end(void)
|
||||
BlockDriverState *bs = NULL;
|
||||
int drained_end_counter = 0;
|
||||
|
||||
/*
|
||||
* bdrv queue is managed by record/replay,
|
||||
* waiting for finishing the I/O requests may
|
||||
* be endless
|
||||
*/
|
||||
if (replay_events_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
while ((bs = bdrv_next_all_states(bs))) {
|
||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
@ -2071,6 +2090,13 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* If the request is misaligned then we can't make it efficient */
|
||||
if ((flags & BDRV_REQ_NO_FALLBACK) &&
|
||||
!QEMU_IS_ALIGNED(offset | bytes, align))
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
bdrv_inc_in_flight(bs);
|
||||
/*
|
||||
* Align write if necessary by performing a read-modify-write cycle.
|
||||
@ -2124,6 +2150,15 @@ int bdrv_flush_all(void)
|
||||
BlockDriverState *bs = NULL;
|
||||
int result = 0;
|
||||
|
||||
/*
|
||||
* bdrv queue is managed by record/replay,
|
||||
* creating new flush request for stopping
|
||||
* the VM may break the determinism
|
||||
*/
|
||||
if (replay_events_enabled()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
|
||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||
int ret;
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/uuid.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qapi-commands-misc.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
@ -280,8 +281,8 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status,
|
||||
}
|
||||
|
||||
if (iTask->co) {
|
||||
aio_bh_schedule_oneshot(iTask->iscsilun->aio_context,
|
||||
iscsi_co_generic_bh_cb, iTask);
|
||||
replay_bh_schedule_oneshot_event(iTask->iscsilun->aio_context,
|
||||
iscsi_co_generic_bh_cb, iTask);
|
||||
} else {
|
||||
iTask->complete = 1;
|
||||
}
|
||||
|
@ -37,6 +37,8 @@
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/uri.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
@ -257,8 +259,8 @@ nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data,
|
||||
if (task->ret < 0) {
|
||||
error_report("NFS Error: %s", nfs_get_error(nfs));
|
||||
}
|
||||
aio_bh_schedule_oneshot(task->client->aio_context,
|
||||
nfs_co_generic_bh_cb, task);
|
||||
replay_bh_schedule_oneshot_event(task->client->aio_context,
|
||||
nfs_co_generic_bh_cb, task);
|
||||
}
|
||||
|
||||
static int coroutine_fn nfs_co_preadv(BlockDriverState *bs, uint64_t offset,
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
#include "block/block_int.h"
|
||||
#include "sysemu/replay.h"
|
||||
|
||||
#define NULL_OPT_LATENCY "latency-ns"
|
||||
#define NULL_OPT_ZEROES "read-zeroes"
|
||||
@ -179,7 +180,8 @@ static inline BlockAIOCB *null_aio_common(BlockDriverState *bs,
|
||||
timer_mod_ns(&acb->timer,
|
||||
qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + s->latency_ns);
|
||||
} else {
|
||||
aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), null_bh_cb, acb);
|
||||
replay_bh_schedule_oneshot_event(bdrv_get_aio_context(bs),
|
||||
null_bh_cb, acb);
|
||||
}
|
||||
return &acb->common;
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/vfio-helpers.h"
|
||||
#include "block/block_int.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "trace.h"
|
||||
|
||||
#include "block/nvme.h"
|
||||
@ -351,7 +352,8 @@ static bool nvme_process_completion(BDRVNVMeState *s, NVMeQueuePair *q)
|
||||
smp_mb_release();
|
||||
*q->cq.doorbell = cpu_to_le32(q->cq.head);
|
||||
if (!qemu_co_queue_empty(&q->free_req_queue)) {
|
||||
aio_bh_schedule_oneshot(s->aio_context, nvme_free_req_queue_cb, q);
|
||||
replay_bh_schedule_oneshot_event(s->aio_context,
|
||||
nvme_free_req_queue_cb, q);
|
||||
}
|
||||
}
|
||||
q->busy = false;
|
||||
@ -935,7 +937,7 @@ static void nvme_rw_cb(void *opaque, int ret)
|
||||
/* The rw coroutine hasn't yielded, don't try to enter. */
|
||||
return;
|
||||
}
|
||||
aio_bh_schedule_oneshot(data->ctx, nvme_rw_cb_bh, data);
|
||||
replay_bh_schedule_oneshot_event(data->ctx, nvme_rw_cb_bh, data);
|
||||
}
|
||||
|
||||
static coroutine_fn int nvme_co_prw_aligned(BlockDriverState *bs,
|
||||
|
@ -1330,6 +1330,9 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
|
||||
nb_clusters = MIN(nb_clusters, s->l2_slice_size - l2_index);
|
||||
assert(nb_clusters <= INT_MAX);
|
||||
|
||||
/* Limit total allocation byte count to INT_MAX */
|
||||
nb_clusters = MIN(nb_clusters, INT_MAX >> s->cluster_bits);
|
||||
|
||||
/* Find L2 entry for the first involved cluster */
|
||||
ret = get_cluster_table(bs, guest_offset, &l2_slice, &l2_index);
|
||||
if (ret < 0) {
|
||||
@ -1412,7 +1415,7 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
|
||||
* request actually writes to (excluding COW at the end)
|
||||
*/
|
||||
uint64_t requested_bytes = *bytes + offset_into_cluster(s, guest_offset);
|
||||
int avail_bytes = MIN(INT_MAX, nb_clusters << s->cluster_bits);
|
||||
int avail_bytes = nb_clusters << s->cluster_bits;
|
||||
int nb_bytes = MIN(requested_bytes, avail_bytes);
|
||||
QCowL2Meta *old_m = *m;
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "block/qdict.h"
|
||||
#include "crypto/secret.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qjson.h"
|
||||
@ -884,8 +885,8 @@ static void rbd_finish_aiocb(rbd_completion_t c, RADOSCB *rcb)
|
||||
rcb->ret = rbd_aio_get_return_value(c);
|
||||
rbd_aio_release(c);
|
||||
|
||||
aio_bh_schedule_oneshot(bdrv_get_aio_context(acb->common.bs),
|
||||
rbd_finish_bh, rcb);
|
||||
replay_bh_schedule_oneshot_event(bdrv_get_aio_context(acb->common.bs),
|
||||
rbd_finish_bh, rcb);
|
||||
}
|
||||
|
||||
static int rbd_aio_discard_wrapper(rbd_image_t image,
|
||||
|
120
block/vhdx.c
120
block/vhdx.c
@ -24,6 +24,7 @@
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/crc32c.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "vhdx.h"
|
||||
#include "migration/blocker.h"
|
||||
#include "qemu/uuid.h"
|
||||
@ -235,6 +236,9 @@ static int vhdx_region_check(BDRVVHDXState *s, uint64_t start, uint64_t length)
|
||||
end = start + length;
|
||||
QLIST_FOREACH(r, &s->regions, entries) {
|
||||
if (!((start >= r->end) || (end <= r->start))) {
|
||||
error_report("VHDX region %" PRIu64 "-%" PRIu64 " overlaps with "
|
||||
"region %" PRIu64 "-%." PRIu64, start, end, r->start,
|
||||
r->end);
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
@ -877,6 +881,95 @@ static void vhdx_calc_bat_entries(BDRVVHDXState *s)
|
||||
|
||||
}
|
||||
|
||||
static int vhdx_check_bat_entries(BlockDriverState *bs, int *errcnt)
|
||||
{
|
||||
BDRVVHDXState *s = bs->opaque;
|
||||
int64_t image_file_size = bdrv_getlength(bs->file->bs);
|
||||
uint64_t payblocks = s->chunk_ratio;
|
||||
uint64_t i;
|
||||
int ret = 0;
|
||||
|
||||
if (image_file_size < 0) {
|
||||
error_report("Could not determinate VHDX image file size.");
|
||||
return image_file_size;
|
||||
}
|
||||
|
||||
for (i = 0; i < s->bat_entries; i++) {
|
||||
if ((s->bat[i] & VHDX_BAT_STATE_BIT_MASK) ==
|
||||
PAYLOAD_BLOCK_FULLY_PRESENT) {
|
||||
uint64_t offset = s->bat[i] & VHDX_BAT_FILE_OFF_MASK;
|
||||
/*
|
||||
* Allow that the last block exists only partially. The VHDX spec
|
||||
* states that the image file can only grow in blocksize increments,
|
||||
* but QEMU created images with partial last blocks in the past.
|
||||
*/
|
||||
uint32_t block_length = MIN(s->block_size,
|
||||
bs->total_sectors * BDRV_SECTOR_SIZE - i * s->block_size);
|
||||
/*
|
||||
* Check for BAT entry overflow.
|
||||
*/
|
||||
if (offset > INT64_MAX - s->block_size) {
|
||||
error_report("VHDX BAT entry %" PRIu64 " offset overflow.", i);
|
||||
ret = -EINVAL;
|
||||
if (!errcnt) {
|
||||
break;
|
||||
}
|
||||
(*errcnt)++;
|
||||
}
|
||||
/*
|
||||
* Check if fully allocated BAT entries do not reside after
|
||||
* end of the image file.
|
||||
*/
|
||||
if (offset >= image_file_size) {
|
||||
error_report("VHDX BAT entry %" PRIu64 " start offset %" PRIu64
|
||||
" points after end of file (%" PRIi64 "). Image"
|
||||
" has probably been truncated.",
|
||||
i, offset, image_file_size);
|
||||
ret = -EINVAL;
|
||||
if (!errcnt) {
|
||||
break;
|
||||
}
|
||||
(*errcnt)++;
|
||||
} else if (offset + block_length > image_file_size) {
|
||||
error_report("VHDX BAT entry %" PRIu64 " end offset %" PRIu64
|
||||
" points after end of file (%" PRIi64 "). Image"
|
||||
" has probably been truncated.",
|
||||
i, offset + block_length - 1, image_file_size);
|
||||
ret = -EINVAL;
|
||||
if (!errcnt) {
|
||||
break;
|
||||
}
|
||||
(*errcnt)++;
|
||||
}
|
||||
|
||||
/*
|
||||
* verify populated BAT field file offsets against
|
||||
* region table and log entries
|
||||
*/
|
||||
if (payblocks--) {
|
||||
/* payload bat entries */
|
||||
int ret2;
|
||||
ret2 = vhdx_region_check(s, offset, s->block_size);
|
||||
if (ret2 < 0) {
|
||||
ret = -EINVAL;
|
||||
if (!errcnt) {
|
||||
break;
|
||||
}
|
||||
(*errcnt)++;
|
||||
}
|
||||
} else {
|
||||
payblocks = s->chunk_ratio;
|
||||
/*
|
||||
* Once differencing files are supported, verify sector bitmap
|
||||
* blocks here
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vhdx_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVVHDXState *s = bs->opaque;
|
||||
@ -981,25 +1074,15 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
uint64_t payblocks = s->chunk_ratio;
|
||||
/* endian convert, and verify populated BAT field file offsets against
|
||||
* region table and log entries */
|
||||
/* endian convert populated BAT field entires */
|
||||
for (i = 0; i < s->bat_entries; i++) {
|
||||
s->bat[i] = le64_to_cpu(s->bat[i]);
|
||||
if (payblocks--) {
|
||||
/* payload bat entries */
|
||||
if ((s->bat[i] & VHDX_BAT_STATE_BIT_MASK) ==
|
||||
PAYLOAD_BLOCK_FULLY_PRESENT) {
|
||||
ret = vhdx_region_check(s, s->bat[i] & VHDX_BAT_FILE_OFF_MASK,
|
||||
s->block_size);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
payblocks = s->chunk_ratio;
|
||||
/* Once differencing files are supported, verify sector bitmap
|
||||
* blocks here */
|
||||
}
|
||||
|
||||
if (!(flags & BDRV_O_CHECK)) {
|
||||
ret = vhdx_check_bat_entries(bs, NULL);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2072,6 +2155,9 @@ static int coroutine_fn vhdx_co_check(BlockDriverState *bs,
|
||||
if (s->log_replayed_on_open) {
|
||||
result->corruptions_fixed++;
|
||||
}
|
||||
|
||||
vhdx_check_bat_entries(bs, &result->corruptions);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/uuid.h"
|
||||
#include "crypto/tlscredsx509.h"
|
||||
#include "sysemu/replay.h"
|
||||
|
||||
#define VXHS_OPT_FILENAME "filename"
|
||||
#define VXHS_OPT_VDISK_ID "vdisk-id"
|
||||
@ -105,8 +106,8 @@ static void vxhs_iio_callback(void *ctx, uint32_t opcode, uint32_t error)
|
||||
trace_vxhs_iio_callback(error);
|
||||
}
|
||||
|
||||
aio_bh_schedule_oneshot(bdrv_get_aio_context(acb->common.bs),
|
||||
vxhs_complete_aio_bh, acb);
|
||||
replay_bh_schedule_oneshot_event(bdrv_get_aio_context(acb->common.bs),
|
||||
vxhs_complete_aio_bh, acb);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
2
cpus.c
2
cpus.c
@ -1097,7 +1097,6 @@ static int do_vm_stop(RunState state, bool send_stop)
|
||||
}
|
||||
|
||||
bdrv_drain_all();
|
||||
replay_disable_events();
|
||||
ret = bdrv_flush_all();
|
||||
|
||||
return ret;
|
||||
@ -2181,7 +2180,6 @@ int vm_prepare_start(void)
|
||||
/* We are sending this now, but the CPUs will be resumed shortly later */
|
||||
qapi_event_send_resume();
|
||||
|
||||
replay_enable_events();
|
||||
cpu_enable_ticks();
|
||||
runstate_set(RUN_STATE_RUNNING);
|
||||
vm_state_notify(1, RUN_STATE_RUNNING);
|
||||
|
@ -27,7 +27,7 @@ Usage of the record/replay:
|
||||
* First, record the execution with the following command line:
|
||||
qemu-system-i386 \
|
||||
-icount shift=7,rr=record,rrfile=replay.bin \
|
||||
-drive file=disk.qcow2,if=none,id=img-direct \
|
||||
-drive file=disk.qcow2,if=none,snapshot,id=img-direct \
|
||||
-drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay \
|
||||
-device ide-hd,drive=img-blkreplay \
|
||||
-netdev user,id=net1 -device rtl8139,netdev=net1 \
|
||||
@ -35,7 +35,7 @@ Usage of the record/replay:
|
||||
* After recording, you can replay it by using another command line:
|
||||
qemu-system-i386 \
|
||||
-icount shift=7,rr=replay,rrfile=replay.bin \
|
||||
-drive file=disk.qcow2,if=none,id=img-direct \
|
||||
-drive file=disk.qcow2,if=none,snapshot,id=img-direct \
|
||||
-drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay \
|
||||
-device ide-hd,drive=img-blkreplay \
|
||||
-netdev user,id=net1 -device rtl8139,netdev=net1 \
|
||||
@ -223,7 +223,7 @@ Block devices record/replay module intercepts calls of
|
||||
bdrv coroutine functions at the top of block drivers stack.
|
||||
To record and replay block operations the drive must be configured
|
||||
as following:
|
||||
-drive file=disk.qcow2,if=none,id=img-direct
|
||||
-drive file=disk.qcow2,if=none,snapshot,id=img-direct
|
||||
-drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay
|
||||
-device ide-hd,drive=img-blkreplay
|
||||
|
||||
@ -252,6 +252,12 @@ This snapshot is created at start of recording and restored at start
|
||||
of replaying. It also can be loaded while replaying to roll back
|
||||
the execution.
|
||||
|
||||
'snapshot' flag of the disk image must be removed to save the snapshots
|
||||
in the overlay (or original image) instead of using the temporary overlay.
|
||||
-drive file=disk.ovl,if=none,id=img-direct
|
||||
-drive driver=blkreplay,if=none,image=img-direct,id=img-blkreplay
|
||||
-device ide-hd,drive=img-blkreplay
|
||||
|
||||
Use QEMU monitor to create additional snapshots. 'savevm <name>' command
|
||||
created the snapshot and 'loadvm <name>' restores it. To prevent corruption
|
||||
of the original disk image, use overlay files linked to the original images.
|
||||
|
@ -132,6 +132,18 @@ typedef bool (*user_creatable_add_opts_predicate)(const char *type);
|
||||
int user_creatable_add_opts_foreach(void *opaque,
|
||||
QemuOpts *opts, Error **errp);
|
||||
|
||||
/**
|
||||
* user_creatable_print_help:
|
||||
* @type: the QOM type to be added
|
||||
* @opts: options to create
|
||||
*
|
||||
* Prints help if requested in @opts.
|
||||
*
|
||||
* Returns: true if @opts contained a help option and help was printed, false
|
||||
* if no help option was found.
|
||||
*/
|
||||
bool user_creatable_print_help(const char *type, QemuOpts *opts);
|
||||
|
||||
/**
|
||||
* user_creatable_del:
|
||||
* @id: the unique ID for the object
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "qapi/qapi-types-misc.h"
|
||||
#include "qapi/qapi-types-run-state.h"
|
||||
#include "qapi/qapi-types-ui.h"
|
||||
#include "block/aio.h"
|
||||
|
||||
/* replay clock kinds */
|
||||
enum ReplayClockKind {
|
||||
@ -140,6 +141,9 @@ void replay_enable_events(void);
|
||||
bool replay_events_enabled(void);
|
||||
/*! Adds bottom half event to the queue */
|
||||
void replay_bh_schedule_event(QEMUBH *bh);
|
||||
/* Adds oneshot bottom half event to the queue */
|
||||
void replay_bh_schedule_oneshot_event(AioContext *ctx,
|
||||
QEMUBHFunc *cb, void *opaque);
|
||||
/*! Adds input event to the queue */
|
||||
void replay_input_event(QemuConsole *src, InputEvent *evt);
|
||||
/*! Adds input sync event to the queue */
|
||||
|
34
qemu-img.c
34
qemu-img.c
@ -214,6 +214,14 @@ static QemuOptsList qemu_object_opts = {
|
||||
},
|
||||
};
|
||||
|
||||
static bool qemu_img_object_print_help(const char *type, QemuOpts *opts)
|
||||
{
|
||||
if (user_creatable_print_help(type, opts)) {
|
||||
exit(0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static QemuOptsList qemu_source_opts = {
|
||||
.name = "source",
|
||||
.implied_opt_name = "file",
|
||||
@ -516,7 +524,7 @@ static int img_create(int argc, char **argv)
|
||||
|
||||
if (qemu_opts_foreach(&qemu_object_opts,
|
||||
user_creatable_add_opts_foreach,
|
||||
NULL, &error_fatal)) {
|
||||
qemu_img_object_print_help, &error_fatal)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -766,7 +774,7 @@ static int img_check(int argc, char **argv)
|
||||
|
||||
if (qemu_opts_foreach(&qemu_object_opts,
|
||||
user_creatable_add_opts_foreach,
|
||||
NULL, &error_fatal)) {
|
||||
qemu_img_object_print_help, &error_fatal)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -979,7 +987,7 @@ static int img_commit(int argc, char **argv)
|
||||
|
||||
if (qemu_opts_foreach(&qemu_object_opts,
|
||||
user_creatable_add_opts_foreach,
|
||||
NULL, &error_fatal)) {
|
||||
qemu_img_object_print_help, &error_fatal)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -1362,7 +1370,7 @@ static int img_compare(int argc, char **argv)
|
||||
|
||||
if (qemu_opts_foreach(&qemu_object_opts,
|
||||
user_creatable_add_opts_foreach,
|
||||
NULL, &error_fatal)) {
|
||||
qemu_img_object_print_help, &error_fatal)) {
|
||||
ret = 2;
|
||||
goto out4;
|
||||
}
|
||||
@ -2210,7 +2218,7 @@ static int img_convert(int argc, char **argv)
|
||||
|
||||
if (qemu_opts_foreach(&qemu_object_opts,
|
||||
user_creatable_add_opts_foreach,
|
||||
NULL, &error_fatal)) {
|
||||
qemu_img_object_print_help, &error_fatal)) {
|
||||
goto fail_getopt;
|
||||
}
|
||||
|
||||
@ -2776,7 +2784,7 @@ static int img_info(int argc, char **argv)
|
||||
|
||||
if (qemu_opts_foreach(&qemu_object_opts,
|
||||
user_creatable_add_opts_foreach,
|
||||
NULL, &error_fatal)) {
|
||||
qemu_img_object_print_help, &error_fatal)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -3002,7 +3010,7 @@ static int img_map(int argc, char **argv)
|
||||
|
||||
if (qemu_opts_foreach(&qemu_object_opts,
|
||||
user_creatable_add_opts_foreach,
|
||||
NULL, &error_fatal)) {
|
||||
qemu_img_object_print_help, &error_fatal)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -3154,7 +3162,7 @@ static int img_snapshot(int argc, char **argv)
|
||||
|
||||
if (qemu_opts_foreach(&qemu_object_opts,
|
||||
user_creatable_add_opts_foreach,
|
||||
NULL, &error_fatal)) {
|
||||
qemu_img_object_print_help, &error_fatal)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -3321,7 +3329,7 @@ static int img_rebase(int argc, char **argv)
|
||||
|
||||
if (qemu_opts_foreach(&qemu_object_opts,
|
||||
user_creatable_add_opts_foreach,
|
||||
NULL, &error_fatal)) {
|
||||
qemu_img_object_print_help, &error_fatal)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -3742,7 +3750,7 @@ static int img_resize(int argc, char **argv)
|
||||
|
||||
if (qemu_opts_foreach(&qemu_object_opts,
|
||||
user_creatable_add_opts_foreach,
|
||||
NULL, &error_fatal)) {
|
||||
qemu_img_object_print_help, &error_fatal)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -3986,7 +3994,7 @@ static int img_amend(int argc, char **argv)
|
||||
|
||||
if (qemu_opts_foreach(&qemu_object_opts,
|
||||
user_creatable_add_opts_foreach,
|
||||
NULL, &error_fatal)) {
|
||||
qemu_img_object_print_help, &error_fatal)) {
|
||||
ret = -1;
|
||||
goto out_no_progress;
|
||||
}
|
||||
@ -4630,7 +4638,7 @@ static int img_dd(int argc, char **argv)
|
||||
|
||||
if (qemu_opts_foreach(&qemu_object_opts,
|
||||
user_creatable_add_opts_foreach,
|
||||
NULL, &error_fatal)) {
|
||||
qemu_img_object_print_help, &error_fatal)) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
@ -4907,7 +4915,7 @@ static int img_measure(int argc, char **argv)
|
||||
|
||||
if (qemu_opts_foreach(&qemu_object_opts,
|
||||
user_creatable_add_opts_foreach,
|
||||
NULL, &error_fatal)) {
|
||||
qemu_img_object_print_help, &error_fatal)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -475,6 +475,13 @@ static QemuOptsList qemu_object_opts = {
|
||||
},
|
||||
};
|
||||
|
||||
static bool qemu_io_object_print_help(const char *type, QemuOpts *opts)
|
||||
{
|
||||
if (user_creatable_print_help(type, opts)) {
|
||||
exit(0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static QemuOptsList file_opts = {
|
||||
.name = "file",
|
||||
@ -622,7 +629,7 @@ int main(int argc, char **argv)
|
||||
|
||||
qemu_opts_foreach(&qemu_object_opts,
|
||||
user_creatable_add_opts_foreach,
|
||||
NULL, &error_fatal);
|
||||
qemu_io_object_print_help, &error_fatal);
|
||||
|
||||
if (!trace_init_backends()) {
|
||||
exit(1);
|
||||
|
@ -507,6 +507,13 @@ static QemuOptsList qemu_object_opts = {
|
||||
},
|
||||
};
|
||||
|
||||
static bool qemu_nbd_object_print_help(const char *type, QemuOpts *opts)
|
||||
{
|
||||
if (user_creatable_print_help(type, opts)) {
|
||||
exit(0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, bool list,
|
||||
@ -902,7 +909,7 @@ int main(int argc, char **argv)
|
||||
|
||||
qemu_opts_foreach(&qemu_object_opts,
|
||||
user_creatable_add_opts_foreach,
|
||||
NULL, &error_fatal);
|
||||
qemu_nbd_object_print_help, &error_fatal);
|
||||
|
||||
if (!trace_init_backends()) {
|
||||
exit(1);
|
||||
|
@ -1,8 +1,11 @@
|
||||
#include "qemu/osdep.h"
|
||||
|
||||
#include "qemu/cutils.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "qemu/help_option.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qapi/opts-visitor.h"
|
||||
@ -155,6 +158,64 @@ int user_creatable_add_opts_foreach(void *opaque, QemuOpts *opts, Error **errp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool user_creatable_print_help(const char *type, QemuOpts *opts)
|
||||
{
|
||||
ObjectClass *klass;
|
||||
|
||||
if (is_help_option(type)) {
|
||||
GSList *l, *list;
|
||||
|
||||
printf("List of user creatable objects:\n");
|
||||
list = object_class_get_list_sorted(TYPE_USER_CREATABLE, false);
|
||||
for (l = list; l != NULL; l = l->next) {
|
||||
ObjectClass *oc = OBJECT_CLASS(l->data);
|
||||
printf(" %s\n", object_class_get_name(oc));
|
||||
}
|
||||
g_slist_free(list);
|
||||
return true;
|
||||
}
|
||||
|
||||
klass = object_class_by_name(type);
|
||||
if (klass && qemu_opt_has_help_opt(opts)) {
|
||||
ObjectPropertyIterator iter;
|
||||
ObjectProperty *prop;
|
||||
GPtrArray *array = g_ptr_array_new();
|
||||
int i;
|
||||
|
||||
object_class_property_iter_init(&iter, klass);
|
||||
while ((prop = object_property_iter_next(&iter))) {
|
||||
GString *str;
|
||||
|
||||
if (!prop->set) {
|
||||
continue;
|
||||
}
|
||||
|
||||
str = g_string_new(NULL);
|
||||
g_string_append_printf(str, " %s=<%s>", prop->name, prop->type);
|
||||
if (prop->description) {
|
||||
if (str->len < 24) {
|
||||
g_string_append_printf(str, "%*s", 24 - (int)str->len, "");
|
||||
}
|
||||
g_string_append_printf(str, " - %s", prop->description);
|
||||
}
|
||||
g_ptr_array_add(array, g_string_free(str, false));
|
||||
}
|
||||
g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0);
|
||||
if (array->len > 0) {
|
||||
printf("%s options:\n", type);
|
||||
} else {
|
||||
printf("There are no options for %s.\n", type);
|
||||
}
|
||||
for (i = 0; i < array->len; i++) {
|
||||
printf("%s\n", (char *)array->pdata[i]);
|
||||
}
|
||||
g_ptr_array_set_free_func(array, g_free);
|
||||
g_ptr_array_free(array, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void user_creatable_del(const char *id, Error **errp)
|
||||
{
|
||||
|
@ -36,6 +36,9 @@ static void replay_run_event(Event *event)
|
||||
case REPLAY_ASYNC_EVENT_BH:
|
||||
aio_bh_call(event->opaque);
|
||||
break;
|
||||
case REPLAY_ASYNC_EVENT_BH_ONESHOT:
|
||||
((QEMUBHFunc *)event->opaque)(event->opaque2);
|
||||
break;
|
||||
case REPLAY_ASYNC_EVENT_INPUT:
|
||||
qemu_input_event_send_impl(NULL, (InputEvent *)event->opaque);
|
||||
qapi_free_InputEvent((InputEvent *)event->opaque);
|
||||
@ -131,6 +134,17 @@ void replay_bh_schedule_event(QEMUBH *bh)
|
||||
}
|
||||
}
|
||||
|
||||
void replay_bh_schedule_oneshot_event(AioContext *ctx,
|
||||
QEMUBHFunc *cb, void *opaque)
|
||||
{
|
||||
if (events_enabled) {
|
||||
uint64_t id = replay_get_current_icount();
|
||||
replay_add_event(REPLAY_ASYNC_EVENT_BH_ONESHOT, cb, opaque, id);
|
||||
} else {
|
||||
aio_bh_schedule_oneshot(ctx, cb, opaque);
|
||||
}
|
||||
}
|
||||
|
||||
void replay_add_input_event(struct InputEvent *event)
|
||||
{
|
||||
replay_add_event(REPLAY_ASYNC_EVENT_INPUT, event, NULL, 0);
|
||||
@ -161,6 +175,7 @@ static void replay_save_event(Event *event, int checkpoint)
|
||||
/* save event-specific data */
|
||||
switch (event->event_kind) {
|
||||
case REPLAY_ASYNC_EVENT_BH:
|
||||
case REPLAY_ASYNC_EVENT_BH_ONESHOT:
|
||||
replay_put_qword(event->id);
|
||||
break;
|
||||
case REPLAY_ASYNC_EVENT_INPUT:
|
||||
@ -216,6 +231,7 @@ static Event *replay_read_event(int checkpoint)
|
||||
/* Events that has not to be in the queue */
|
||||
switch (replay_state.read_event_kind) {
|
||||
case REPLAY_ASYNC_EVENT_BH:
|
||||
case REPLAY_ASYNC_EVENT_BH_ONESHOT:
|
||||
if (replay_state.read_event_id == -1) {
|
||||
replay_state.read_event_id = replay_get_qword();
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ enum ReplayEvents {
|
||||
|
||||
enum ReplayAsyncEventKind {
|
||||
REPLAY_ASYNC_EVENT_BH,
|
||||
REPLAY_ASYNC_EVENT_BH_ONESHOT,
|
||||
REPLAY_ASYNC_EVENT_INPUT,
|
||||
REPLAY_ASYNC_EVENT_INPUT_SYNC,
|
||||
REPLAY_ASYNC_EVENT_CHAR_READ,
|
||||
|
@ -385,6 +385,8 @@ void replay_finish(void)
|
||||
g_free(replay_snapshot);
|
||||
replay_snapshot = NULL;
|
||||
|
||||
replay_mode = REPLAY_MODE_NONE;
|
||||
|
||||
replay_finish_events();
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ stub-obj-y += monitor.o
|
||||
stub-obj-y += notify-event.o
|
||||
stub-obj-y += qtest.o
|
||||
stub-obj-y += replay.o
|
||||
stub-obj-y += replay-user.o
|
||||
stub-obj-y += runstate-check.o
|
||||
stub-obj-y += set-fd-handler.o
|
||||
stub-obj-y += sysbus.o
|
||||
|
9
stubs/replay-user.c
Normal file
9
stubs/replay-user.c
Normal file
@ -0,0 +1,9 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
void replay_bh_schedule_oneshot_event(AioContext *ctx,
|
||||
QEMUBHFunc *cb, void *opaque)
|
||||
{
|
||||
aio_bh_schedule_oneshot(ctx, cb, opaque);
|
||||
}
|
@ -119,9 +119,14 @@ fi
|
||||
# Silence output since it contains the disk image path and QEMU's readline
|
||||
# character echoing makes it very hard to filter the output. Plus, there
|
||||
# is no telling how many times the command will repeat before succeeding.
|
||||
_send_qemu_cmd $h "drive_backup disk ${TEST_IMG}.copy" "(qemu)" >/dev/null
|
||||
_send_qemu_cmd $h "" "Formatting" | _filter_img_create
|
||||
qemu_cmd_repeat=20 _send_qemu_cmd $h "info block-jobs" "No active jobs" >/dev/null
|
||||
# (Note that creating the image results in a "Formatting..." message over
|
||||
# stdout, which is the same channel the monitor uses. We cannot reliably
|
||||
# wait for it because the monitor output may interact with it in such a
|
||||
# way that _timed_wait_for cannot read it. However, once the block job is
|
||||
# done, we know that the "Formatting..." message must have appeared
|
||||
# already, so the output is still deterministic.)
|
||||
silent=y _send_qemu_cmd $h "drive_backup disk ${TEST_IMG}.copy" "(qemu)"
|
||||
silent=y qemu_cmd_repeat=20 _send_qemu_cmd $h "info block-jobs" "No active jobs"
|
||||
_send_qemu_cmd $h "info block-jobs" "No active jobs"
|
||||
_send_qemu_cmd $h 'quit' ""
|
||||
|
||||
|
@ -468,7 +468,6 @@ No errors were found on the image.
|
||||
|
||||
block-backup
|
||||
|
||||
Formatting 'TEST_DIR/t.IMGFMT.copy', fmt=IMGFMT size=4294968832 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT
|
||||
(qemu) info block-jobs
|
||||
No active jobs
|
||||
=== IO: pattern 195
|
||||
|
55
tests/qemu-iotests/268
Executable file
55
tests/qemu-iotests/268
Executable file
@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Test write request with required alignment larger than the cluster size
|
||||
#
|
||||
# Copyright (C) 2019 Igalia, S.L.
|
||||
# Author: Alberto Garcia <berto@igalia.com>
|
||||
#
|
||||
# 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=berto@igalia.com
|
||||
|
||||
seq=`basename $0`
|
||||
echo "QA output created by $seq"
|
||||
|
||||
status=1 # failure is the default!
|
||||
|
||||
_cleanup()
|
||||
{
|
||||
_cleanup_test_img
|
||||
}
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# get standard environment, filters and checks
|
||||
. ./common.rc
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
|
||||
echo
|
||||
echo "== Required alignment larger than cluster size =="
|
||||
|
||||
CLUSTER_SIZE=2k _make_test_img 1M
|
||||
# Since commit c8bb23cbdb writing to an unallocated cluster fills the
|
||||
# empty COW areas with bdrv_write_zeroes(flags=BDRV_REQ_NO_FALLBACK)
|
||||
$QEMU_IO -c "open -o driver=$IMGFMT,file.align=4k blkdebug::$TEST_IMG" \
|
||||
-c "write 0 512" | _filter_qemu_io
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
rm -f $seq.full
|
||||
status=0
|
7
tests/qemu-iotests/268.out
Normal file
7
tests/qemu-iotests/268.out
Normal file
@ -0,0 +1,7 @@
|
||||
QA output created by 268
|
||||
|
||||
== Required alignment larger than cluster size ==
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
|
||||
wrote 512/512 bytes at offset 0
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
*** done
|
83
tests/qemu-iotests/270
Executable file
83
tests/qemu-iotests/270
Executable file
@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Test large write to a qcow2 image
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
seq=$(basename "$0")
|
||||
echo "QA output created by $seq"
|
||||
|
||||
status=1 # failure is the default!
|
||||
|
||||
_cleanup()
|
||||
{
|
||||
_cleanup_test_img
|
||||
}
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# get standard environment, filters and checks
|
||||
. ./common.rc
|
||||
. ./common.filter
|
||||
|
||||
# This is a qcow2 regression test
|
||||
_supported_fmt qcow2
|
||||
_supported_proto file
|
||||
_supported_os Linux
|
||||
|
||||
# We use our own external data file and our own cluster size, and we
|
||||
# require v3 images
|
||||
_unsupported_imgopts data_file cluster_size 'compat=0.10'
|
||||
|
||||
|
||||
# We need a backing file so that handle_alloc_space() will not do
|
||||
# anything. (If it were to do anything, it would simply fail its
|
||||
# write-zeroes request because the request range is too large.)
|
||||
TEST_IMG="$TEST_IMG.base" _make_test_img 4G
|
||||
$QEMU_IO -c 'write 0 512' "$TEST_IMG.base" | _filter_qemu_io
|
||||
|
||||
# (Use .orig because _cleanup_test_img will remove that file)
|
||||
# We need a large cluster size, see below for why (above the $QEMU_IO
|
||||
# invocation)
|
||||
_make_test_img -o cluster_size=2M,data_file="$TEST_IMG.orig" \
|
||||
-b "$TEST_IMG.base" 4G
|
||||
|
||||
# We want a null-co as the data file, because it allows us to quickly
|
||||
# "write" 2G of data without using any space.
|
||||
# (qemu-img create does not like it, though, because null-co does not
|
||||
# support image creation.)
|
||||
$QEMU_IMG amend -o data_file="json:{'driver':'null-co',,'size':'4294967296'}" \
|
||||
"$TEST_IMG"
|
||||
|
||||
# This gives us a range of:
|
||||
# 2^31 - 512 + 768 - 1 = 2^31 + 255 > 2^31
|
||||
# until the beginning of the end COW block. (The total allocation
|
||||
# size depends on the cluster size, but all that is important is that
|
||||
# it exceeds INT_MAX.)
|
||||
#
|
||||
# 2^31 - 512 is the maximum request size. We want this to result in a
|
||||
# single allocation, and because the qcow2 driver splits allocations
|
||||
# on L2 boundaries, we need large L2 tables; hence the cluster size of
|
||||
# 2 MB. (Anything from 256 kB should work, though, because then one L2
|
||||
# table covers 8 GB.)
|
||||
$QEMU_IO -c "write 768 $((2 ** 31 - 512))" "$TEST_IMG" | _filter_qemu_io
|
||||
|
||||
_check_test_img
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
rm -f $seq.full
|
||||
status=0
|
9
tests/qemu-iotests/270.out
Normal file
9
tests/qemu-iotests/270.out
Normal file
@ -0,0 +1,9 @@
|
||||
QA output created by 270
|
||||
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=4294967296
|
||||
wrote 512/512 bytes at offset 0
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 backing_file=TEST_DIR/t.IMGFMT.base data_file=TEST_DIR/t.IMGFMT.orig
|
||||
wrote 2147483136/2147483136 bytes at offset 768
|
||||
2 GiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
No errors were found on the image.
|
||||
*** done
|
@ -278,3 +278,5 @@
|
||||
265 rw auto quick
|
||||
266 rw quick
|
||||
267 rw auto quick snapshot
|
||||
268 rw auto quick
|
||||
270 rw backing quick
|
||||
|
63
vl.c
63
vl.c
@ -1203,7 +1203,7 @@ static void configure_blockdev(BlockdevOptionsQueue *bdo_queue,
|
||||
qapi_free_BlockdevOptions(bdo->bdo);
|
||||
g_free(bdo);
|
||||
}
|
||||
if (snapshot || replay_mode != REPLAY_MODE_NONE) {
|
||||
if (snapshot) {
|
||||
qemu_opts_foreach(qemu_find_opts("drive"), drive_enable_snapshot,
|
||||
NULL, NULL);
|
||||
}
|
||||
@ -2649,57 +2649,7 @@ static int machine_set_property(void *opaque,
|
||||
*/
|
||||
static bool object_create_initial(const char *type, QemuOpts *opts)
|
||||
{
|
||||
ObjectClass *klass;
|
||||
|
||||
if (is_help_option(type)) {
|
||||
GSList *l, *list;
|
||||
|
||||
printf("List of user creatable objects:\n");
|
||||
list = object_class_get_list_sorted(TYPE_USER_CREATABLE, false);
|
||||
for (l = list; l != NULL; l = l->next) {
|
||||
ObjectClass *oc = OBJECT_CLASS(l->data);
|
||||
printf(" %s\n", object_class_get_name(oc));
|
||||
}
|
||||
g_slist_free(list);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
klass = object_class_by_name(type);
|
||||
if (klass && qemu_opt_has_help_opt(opts)) {
|
||||
ObjectPropertyIterator iter;
|
||||
ObjectProperty *prop;
|
||||
GPtrArray *array = g_ptr_array_new();
|
||||
int i;
|
||||
|
||||
object_class_property_iter_init(&iter, klass);
|
||||
while ((prop = object_property_iter_next(&iter))) {
|
||||
GString *str;
|
||||
|
||||
if (!prop->set) {
|
||||
continue;
|
||||
}
|
||||
|
||||
str = g_string_new(NULL);
|
||||
g_string_append_printf(str, " %s=<%s>", prop->name, prop->type);
|
||||
if (prop->description) {
|
||||
if (str->len < 24) {
|
||||
g_string_append_printf(str, "%*s", 24 - (int)str->len, "");
|
||||
}
|
||||
g_string_append_printf(str, " - %s", prop->description);
|
||||
}
|
||||
g_ptr_array_add(array, g_string_free(str, false));
|
||||
}
|
||||
g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0);
|
||||
if (array->len > 0) {
|
||||
printf("%s options:\n", type);
|
||||
} else {
|
||||
printf("There are no options for %s.\n", type);
|
||||
}
|
||||
for (i = 0; i < array->len; i++) {
|
||||
printf("%s\n", (char *)array->pdata[i]);
|
||||
}
|
||||
g_ptr_array_set_free_func(array, g_free);
|
||||
g_ptr_array_free(array, true);
|
||||
if (user_creatable_print_help(type, opts)) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@ -3066,7 +3016,13 @@ int main(int argc, char **argv, char **envp)
|
||||
drive_add(IF_PFLASH, -1, optarg, PFLASH_OPTS);
|
||||
break;
|
||||
case QEMU_OPTION_snapshot:
|
||||
snapshot = 1;
|
||||
{
|
||||
Error *blocker = NULL;
|
||||
snapshot = 1;
|
||||
error_setg(&blocker, QERR_REPLAY_NOT_SUPPORTED,
|
||||
"-snapshot");
|
||||
replay_add_blocker(blocker);
|
||||
}
|
||||
break;
|
||||
case QEMU_OPTION_numa:
|
||||
opts = qemu_opts_parse_noisily(qemu_find_opts("numa"),
|
||||
@ -4518,6 +4474,7 @@ int main(int argc, char **argv, char **envp)
|
||||
|
||||
/* No more vcpu or device emulation activity beyond this point */
|
||||
vm_shutdown();
|
||||
replay_finish();
|
||||
|
||||
job_cancel_sync_all();
|
||||
bdrv_close_all();
|
||||
|
Loading…
Reference in New Issue
Block a user