diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2019-10-15 13:25:05 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2019-10-15 13:25:05 +0100 |
commit | 3af78db68176a049e2570822f64604e0692c1447 (patch) | |
tree | 8ea13e03319180d5b01c7354fd792f5151bbfc3b | |
parent | 9020e9526cd08c4dc99d54dba48730de2908c970 (diff) | |
parent | a1406a9262a087d9ec9627b88da13c4590b61dae (diff) |
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>
-rw-r--r-- | block/blkreplay.c | 8 | ||||
-rw-r--r-- | block/block-backend.c | 9 | ||||
-rw-r--r-- | block/io.c | 39 | ||||
-rw-r--r-- | block/iscsi.c | 5 | ||||
-rw-r--r-- | block/nfs.c | 6 | ||||
-rw-r--r-- | block/null.c | 4 | ||||
-rw-r--r-- | block/nvme.c | 6 | ||||
-rw-r--r-- | block/qcow2-cluster.c | 5 | ||||
-rw-r--r-- | block/rbd.c | 5 | ||||
-rw-r--r-- | block/vhdx.c | 120 | ||||
-rw-r--r-- | block/vxhs.c | 5 | ||||
-rw-r--r-- | cpus.c | 2 | ||||
-rw-r--r-- | docs/replay.txt | 12 | ||||
-rw-r--r-- | include/qom/object_interfaces.h | 12 | ||||
-rw-r--r-- | include/sysemu/replay.h | 4 | ||||
-rw-r--r-- | qemu-img.c | 34 | ||||
-rw-r--r-- | qemu-io.c | 9 | ||||
-rw-r--r-- | qemu-nbd.c | 9 | ||||
-rw-r--r-- | qom/object_interfaces.c | 61 | ||||
-rw-r--r-- | replay/replay-events.c | 16 | ||||
-rw-r--r-- | replay/replay-internal.h | 1 | ||||
-rw-r--r-- | replay/replay.c | 2 | ||||
-rw-r--r-- | stubs/Makefile.objs | 1 | ||||
-rw-r--r-- | stubs/replay-user.c | 9 | ||||
-rwxr-xr-x | tests/qemu-iotests/028 | 11 | ||||
-rw-r--r-- | tests/qemu-iotests/028.out | 1 | ||||
-rwxr-xr-x | tests/qemu-iotests/268 | 55 | ||||
-rw-r--r-- | tests/qemu-iotests/268.out | 7 | ||||
-rwxr-xr-x | tests/qemu-iotests/270 | 83 | ||||
-rw-r--r-- | tests/qemu-iotests/270.out | 9 | ||||
-rw-r--r-- | tests/qemu-iotests/group | 2 | ||||
-rw-r--r-- | vl.c | 63 |
32 files changed, 504 insertions, 111 deletions
diff --git a/block/blkreplay.c b/block/blkreplay.c index 2b7931b940..c96ac8f4bc 100644 --- a/block/blkreplay.c +++ b/block/blkreplay.c @@ -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) diff --git a/block/block-backend.c b/block/block-backend.c index 1c605d5444..eb22ff306e 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -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; diff --git a/block/io.c b/block/io.c index 4f9ee97c2b..f0b86c1d19 100644 --- a/block/io.c +++ b/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; diff --git a/block/iscsi.c b/block/iscsi.c index 506bf5f875..2ced15066a 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -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; } diff --git a/block/nfs.c b/block/nfs.c index f39acfdb28..40f23495a0 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -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, diff --git a/block/null.c b/block/null.c index 699aa295cb..15e1d56746 100644 --- a/block/null.c +++ b/block/null.c @@ -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; } diff --git a/block/nvme.c b/block/nvme.c index 5be3a39b63..910872ec59 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -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, diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 8d5fa1539c..8982b7b762 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -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; diff --git a/block/rbd.c b/block/rbd.c index 057af43d48..c71e45d7c3 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -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, diff --git a/block/vhdx.c b/block/vhdx.c index 6a09d0a55c..371f226286 100644 --- a/block/vhdx.c +++ b/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; } diff --git a/block/vxhs.c b/block/vxhs.c index 77fd5eb20d..d79fc97df6 100644 --- a/block/vxhs.c +++ b/block/vxhs.c @@ -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: @@ -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); diff --git a/docs/replay.txt b/docs/replay.txt index ee6aee9861..ce97c3f72f 100644 --- a/docs/replay.txt +++ b/docs/replay.txt @@ -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. diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h index 682ba1d9b0..3e4e1d928b 100644 --- a/include/qom/object_interfaces.h +++ b/include/qom/object_interfaces.h @@ -133,6 +133,18 @@ 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 * @errp: if an error occurs, a pointer to an area to store the error diff --git a/include/sysemu/replay.h b/include/sysemu/replay.h index dfc7a31c66..8df517298c 100644 --- a/include/sysemu/replay.h +++ b/include/sysemu/replay.h @@ -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 */ diff --git a/qemu-img.c b/qemu-img.c index 384c6f38bc..8b03ef8171 100644 --- a/qemu-img.c +++ b/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); diff --git a/qemu-nbd.c b/qemu-nbd.c index 9032b6de2a..caacf0ed73 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -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); diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index cb5809934a..46cd6eab5c 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -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) { diff --git a/replay/replay-events.c b/replay/replay-events.c index 008e80f636..302b84043a 100644 --- a/replay/replay-events.c +++ b/replay/replay-events.c @@ -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(); } diff --git a/replay/replay-internal.h b/replay/replay-internal.h index afba9a3e0c..55fca1ac6b 100644 --- a/replay/replay-internal.h +++ b/replay/replay-internal.h @@ -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, diff --git a/replay/replay.c b/replay/replay.c index 713395b33d..5cc25bd2f8 100644 --- a/replay/replay.c +++ b/replay/replay.c @@ -385,6 +385,8 @@ void replay_finish(void) g_free(replay_snapshot); replay_snapshot = NULL; + replay_mode = REPLAY_MODE_NONE; + replay_finish_events(); } diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs index 9c7393b08c..4a50e95ec3 100644 --- a/stubs/Makefile.objs +++ b/stubs/Makefile.objs @@ -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 diff --git a/stubs/replay-user.c b/stubs/replay-user.c new file mode 100644 index 0000000000..2ad9e27203 --- /dev/null +++ b/stubs/replay-user.c @@ -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); +} diff --git a/tests/qemu-iotests/028 b/tests/qemu-iotests/028 index 71301ec6e5..bba1ee59ae 100755 --- a/tests/qemu-iotests/028 +++ b/tests/qemu-iotests/028 @@ -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' "" diff --git a/tests/qemu-iotests/028.out b/tests/qemu-iotests/028.out index 7d54aeb003..37aed84436 100644 --- a/tests/qemu-iotests/028.out +++ b/tests/qemu-iotests/028.out @@ -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 diff --git a/tests/qemu-iotests/268 b/tests/qemu-iotests/268 new file mode 100755 index 0000000000..78c3f4db3a --- /dev/null +++ b/tests/qemu-iotests/268 @@ -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 diff --git a/tests/qemu-iotests/268.out b/tests/qemu-iotests/268.out new file mode 100644 index 0000000000..2ed6c68529 --- /dev/null +++ b/tests/qemu-iotests/268.out @@ -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 diff --git a/tests/qemu-iotests/270 b/tests/qemu-iotests/270 new file mode 100755 index 0000000000..b9a12b908c --- /dev/null +++ b/tests/qemu-iotests/270 @@ -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 diff --git a/tests/qemu-iotests/270.out b/tests/qemu-iotests/270.out new file mode 100644 index 0000000000..c7be111014 --- /dev/null +++ b/tests/qemu-iotests/270.out @@ -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 diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 5805a79d9e..7dac79a783 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -278,3 +278,5 @@ 265 rw auto quick 266 rw quick 267 rw auto quick snapshot +268 rw auto quick +270 rw backing quick @@ -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(); |