• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
Keine Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

Commit MetaInfo

Revisionf3d0bec9f80e4ed7796fffa834ba0a53f2094f7f (tree)
Zeit2019-06-14 22:46:13
AutorPeter Maydell <peter.maydell@lina...>
CommiterPeter Maydell

Log Message

Block patches:
- Allow blockdev-backup from nodes that are not in qemu's main AIO
context to newly added nodes
- Add salvaging mode to qemu-img convert
- Minor fixes to tests, documentation, and for less Valgrind annoyance
-----BEGIN PGP SIGNATURE-----

iQFGBAABCAAwFiEEkb62CjDbPohX0Rgp9AfbAGHVz0AFAl0Do0MSHG1yZWl0ekBy
ZWRoYXQuY29tAAoJEPQH2wBh1c9AYfAH/3Z4Ua5Pwown99TH57jdfHBzdWnhwQV6
fEJs1viFOufb9RS/3UB3JlPZ71Jf+4y97g9NrRY05BQ3ZJPwlm9moSdM7GPQ6Ni3
OVqHP6shnOHlBHI897JRDPjpLGEujIVk59EE9wB7e/T8rpGo21yJ91u5Ha4T9M2/
jsHwL/1NPCe8OxdCMSNJmIPA2PqouBjvEGdbCwa6sOVNe+pFJnhStEZOjnP+rmmb
wt5vk+NR40rdsuAoUZ7mTr0GhUDtJAL9vkJgW1uH7b9mzAsxklsWpct6oxDwEYJL
BURVLsT2g8QQwX7pebaOkys0ktPGkegtv785yh8KhPf2Yf6S8L1vS4M=
=o8kg
-----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/maxreitz/tags/pull-block-2019-06-14' into staging

Block patches:
- Allow blockdev-backup from nodes that are not in qemu's main AIO

context to newly added nodes

- Add salvaging mode to qemu-img convert
- Minor fixes to tests, documentation, and for less Valgrind annoyance

# gpg: Signature made Fri 14 Jun 2019 14:38:11 BST
# gpg: using RSA key 91BEB60A30DB3E8857D11829F407DB0061D5CF40
# gpg: issuer "mreitz@redhat.com"
# gpg: Good signature from "Max Reitz <mreitz@redhat.com>" [full]
# Primary key fingerprint: 91BE B60A 30DB 3E88 57D1 1829 F407 DB00 61D5 CF40

* remotes/maxreitz/tags/pull-block-2019-06-14:

iotests: Test qemu-img convert -C --salvage
iotests: Test qemu-img convert --salvage
blkdebug: Inject errors on .bdrv_co_block_status()
blkdebug: Add "none" event
blkdebug: Add @iotype error option
qemu-img: Add salvaging mode to convert
qemu-img: Move quiet into ImgConvertState
blockdev: Overlays are not snapshots
qapi/block-core: Overlays are not snapshots
qemu-img: Fix options leakage in img_rebase()
iotests: restrict 254 to support only qcow2
hw/block/fdc: floppy command FIFO memory initialization
iotests: Fix intermittent failure in 219
iotests: Filter 175's allocation information
event_match: always match on None value
iotests: add iotest 256 for testing blockdev-backup across iothread contexts
iotests.py: rewrite run_job to be pickier
QEMUMachine: add events_wait method
iotests.py: do not use infinite waits
blockdev-backup: don't check aio_context too early

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

Ändern Zusammenfassung

Diff

--- a/block/blkdebug.c
+++ b/block/blkdebug.c
@@ -75,6 +75,7 @@ typedef struct BlkdebugRule {
7575 int state;
7676 union {
7777 struct {
78+ uint64_t iotype_mask;
7879 int error;
7980 int immediately;
8081 int once;
@@ -91,6 +92,9 @@ typedef struct BlkdebugRule {
9192 QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
9293 } BlkdebugRule;
9394
95+QEMU_BUILD_BUG_MSG(BLKDEBUG_IO_TYPE__MAX > 64,
96+ "BlkdebugIOType mask does not fit into an uint64_t");
97+
9498 static QemuOptsList inject_error_opts = {
9599 .name = "inject-error",
96100 .head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head),
@@ -104,6 +108,10 @@ static QemuOptsList inject_error_opts = {
104108 .type = QEMU_OPT_NUMBER,
105109 },
106110 {
111+ .name = "iotype",
112+ .type = QEMU_OPT_STRING,
113+ },
114+ {
107115 .name = "errno",
108116 .type = QEMU_OPT_NUMBER,
109117 },
@@ -162,6 +170,8 @@ static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
162170 int event;
163171 struct BlkdebugRule *rule;
164172 int64_t sector;
173+ BlkdebugIOType iotype;
174+ Error *local_error = NULL;
165175
166176 /* Find the right event for the rule */
167177 event_name = qemu_opt_get(opts, "event");
@@ -192,6 +202,26 @@ static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
192202 sector = qemu_opt_get_number(opts, "sector", -1);
193203 rule->options.inject.offset =
194204 sector == -1 ? -1 : sector * BDRV_SECTOR_SIZE;
205+
206+ iotype = qapi_enum_parse(&BlkdebugIOType_lookup,
207+ qemu_opt_get(opts, "iotype"),
208+ BLKDEBUG_IO_TYPE__MAX, &local_error);
209+ if (local_error) {
210+ error_propagate(errp, local_error);
211+ return -1;
212+ }
213+ if (iotype != BLKDEBUG_IO_TYPE__MAX) {
214+ rule->options.inject.iotype_mask = (1ull << iotype);
215+ } else {
216+ /* Apply the default */
217+ rule->options.inject.iotype_mask =
218+ (1ull << BLKDEBUG_IO_TYPE_READ)
219+ | (1ull << BLKDEBUG_IO_TYPE_WRITE)
220+ | (1ull << BLKDEBUG_IO_TYPE_WRITE_ZEROES)
221+ | (1ull << BLKDEBUG_IO_TYPE_DISCARD)
222+ | (1ull << BLKDEBUG_IO_TYPE_FLUSH);
223+ }
224+
195225 break;
196226
197227 case ACTION_SET_STATE:
@@ -461,6 +491,8 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
461491 goto out;
462492 }
463493
494+ bdrv_debug_event(bs, BLKDBG_NONE);
495+
464496 ret = 0;
465497 out:
466498 if (ret < 0) {
@@ -470,7 +502,8 @@ out:
470502 return ret;
471503 }
472504
473-static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes)
505+static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
506+ BlkdebugIOType iotype)
474507 {
475508 BDRVBlkdebugState *s = bs->opaque;
476509 BlkdebugRule *rule = NULL;
@@ -480,9 +513,10 @@ static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes)
480513 QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
481514 uint64_t inject_offset = rule->options.inject.offset;
482515
483- if (inject_offset == -1 ||
484- (bytes && inject_offset >= offset &&
485- inject_offset < offset + bytes))
516+ if ((inject_offset == -1 ||
517+ (bytes && inject_offset >= offset &&
518+ inject_offset < offset + bytes)) &&
519+ (rule->options.inject.iotype_mask & (1ull << iotype)))
486520 {
487521 break;
488522 }
@@ -521,7 +555,7 @@ blkdebug_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
521555 assert(bytes <= bs->bl.max_transfer);
522556 }
523557
524- err = rule_check(bs, offset, bytes);
558+ err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_READ);
525559 if (err) {
526560 return err;
527561 }
@@ -542,7 +576,7 @@ blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
542576 assert(bytes <= bs->bl.max_transfer);
543577 }
544578
545- err = rule_check(bs, offset, bytes);
579+ err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_WRITE);
546580 if (err) {
547581 return err;
548582 }
@@ -552,7 +586,7 @@ blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
552586
553587 static int blkdebug_co_flush(BlockDriverState *bs)
554588 {
555- int err = rule_check(bs, 0, 0);
589+ int err = rule_check(bs, 0, 0, BLKDEBUG_IO_TYPE_FLUSH);
556590
557591 if (err) {
558592 return err;
@@ -586,7 +620,7 @@ static int coroutine_fn blkdebug_co_pwrite_zeroes(BlockDriverState *bs,
586620 assert(bytes <= bs->bl.max_pwrite_zeroes);
587621 }
588622
589- err = rule_check(bs, offset, bytes);
623+ err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_WRITE_ZEROES);
590624 if (err) {
591625 return err;
592626 }
@@ -620,7 +654,7 @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
620654 assert(bytes <= bs->bl.max_pdiscard);
621655 }
622656
623- err = rule_check(bs, offset, bytes);
657+ err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_DISCARD);
624658 if (err) {
625659 return err;
626660 }
@@ -636,7 +670,15 @@ static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs,
636670 int64_t *map,
637671 BlockDriverState **file)
638672 {
673+ int err;
674+
639675 assert(QEMU_IS_ALIGNED(offset | bytes, bs->bl.request_alignment));
676+
677+ err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_BLOCK_STATUS);
678+ if (err) {
679+ return err;
680+ }
681+
640682 return bdrv_co_block_status_from_file(bs, want_zero, offset, bytes,
641683 pnum, map, file);
642684 }
--- a/blockdev.c
+++ b/blockdev.c
@@ -1608,13 +1608,13 @@ static void external_snapshot_prepare(BlkActionState *common,
16081608 s->has_snapshot_node_name ? s->snapshot_node_name : NULL;
16091609
16101610 if (node_name && !snapshot_node_name) {
1611- error_setg(errp, "New snapshot node name missing");
1611+ error_setg(errp, "New overlay node name missing");
16121612 goto out;
16131613 }
16141614
16151615 if (snapshot_node_name &&
16161616 bdrv_lookup_bs(snapshot_node_name, snapshot_node_name, NULL)) {
1617- error_setg(errp, "New snapshot node name already in use");
1617+ error_setg(errp, "New overlay node name already in use");
16181618 goto out;
16191619 }
16201620
@@ -1656,7 +1656,7 @@ static void external_snapshot_prepare(BlkActionState *common,
16561656 }
16571657
16581658 if (bdrv_has_blk(state->new_bs)) {
1659- error_setg(errp, "The snapshot is already in use");
1659+ error_setg(errp, "The overlay is already in use");
16601660 goto out;
16611661 }
16621662
@@ -1666,12 +1666,12 @@ static void external_snapshot_prepare(BlkActionState *common,
16661666 }
16671667
16681668 if (state->new_bs->backing != NULL) {
1669- error_setg(errp, "The snapshot already has a backing image");
1669+ error_setg(errp, "The overlay already has a backing image");
16701670 goto out;
16711671 }
16721672
16731673 if (!state->new_bs->drv->supports_backing) {
1674- error_setg(errp, "The snapshot does not support backing images");
1674+ error_setg(errp, "The overlay does not support backing images");
16751675 goto out;
16761676 }
16771677
@@ -1876,10 +1876,6 @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
18761876 }
18771877
18781878 aio_context = bdrv_get_aio_context(bs);
1879- if (aio_context != bdrv_get_aio_context(target)) {
1880- error_setg(errp, "Backup between two IO threads is not implemented");
1881- return;
1882- }
18831879 aio_context_acquire(aio_context);
18841880 state->bs = bs;
18851881
--- a/hw/block/fdc.c
+++ b/hw/block/fdc.c
@@ -2648,6 +2648,7 @@ static void fdctrl_realize_common(DeviceState *dev, FDCtrl *fdctrl,
26482648
26492649 FLOPPY_DPRINTF("init controller\n");
26502650 fdctrl->fifo = qemu_memalign(512, FD_SECTOR_LEN);
2651+ memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
26512652 fdctrl->fifo_size = 512;
26522653 fdctrl->result_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
26532654 fdctrl_result_timer, fdctrl);
--- a/python/qemu/__init__.py
+++ b/python/qemu/__init__.py
@@ -402,42 +402,75 @@ class QEMUMachine(object):
402402 self._qmp.clear_events()
403403 return events
404404
405- def event_wait(self, name, timeout=60.0, match=None):
405+ @staticmethod
406+ def event_match(event, match=None):
406407 """
407- Wait for specified timeout on named event in QMP; optionally filter
408- results by match.
408+ Check if an event matches optional match criteria.
409+
410+ The match criteria takes the form of a matching subdict. The event is
411+ checked to be a superset of the subdict, recursively, with matching
412+ values whenever the subdict values are not None.
409413
410- The 'match' is checked to be a recursive subset of the 'event'; skips
411- branch processing on match's value None
412- {"foo": {"bar": 1}} matches {"foo": None}
413- {"foo": {"bar": 1}} does not matches {"foo": {"baz": None}}
414+ This has a limitation that you cannot explicitly check for None values.
415+
416+ Examples, with the subdict queries on the left:
417+ - None matches any object.
418+ - {"foo": None} matches {"foo": {"bar": 1}}
419+ - {"foo": None} matches {"foo": 5}
420+ - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
421+ - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
414422 """
415- def event_match(event, match=None):
416- if match is None:
417- return True
423+ if match is None:
424+ return True
418425
426+ try:
419427 for key in match:
420428 if key in event:
421- if isinstance(event[key], dict):
422- if not event_match(event[key], match[key]):
423- return False
424- elif event[key] != match[key]:
429+ if not QEMUMachine.event_match(event[key], match[key]):
425430 return False
426431 else:
427432 return False
428-
429433 return True
434+ except TypeError:
435+ # either match or event wasn't iterable (not a dict)
436+ return match == event
437+
438+ def event_wait(self, name, timeout=60.0, match=None):
439+ """
440+ event_wait waits for and returns a named event from QMP with a timeout.
441+
442+ name: The event to wait for.
443+ timeout: QEMUMonitorProtocol.pull_event timeout parameter.
444+ match: Optional match criteria. See event_match for details.
445+ """
446+ return self.events_wait([(name, match)], timeout)
447+
448+ def events_wait(self, events, timeout=60.0):
449+ """
450+ events_wait waits for and returns a named event from QMP with a timeout.
451+
452+ events: a sequence of (name, match_criteria) tuples.
453+ The match criteria are optional and may be None.
454+ See event_match for details.
455+ timeout: QEMUMonitorProtocol.pull_event timeout parameter.
456+ """
457+ def _match(event):
458+ for name, match in events:
459+ if (event['event'] == name and
460+ self.event_match(event, match)):
461+ return True
462+ return False
430463
431464 # Search cached events
432465 for event in self._events:
433- if (event['event'] == name) and event_match(event, match):
466+ if _match(event):
434467 self._events.remove(event)
435468 return event
436469
437470 # Poll for new events
438471 while True:
439472 event = self._qmp.pull_event(wait=timeout)
440- if (event['event'] == name) and event_match(event, match):
473+ if _match(event):
441474 return event
442475 self._events.append(event)
443476
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -1279,17 +1279,17 @@
12791279 #
12801280 # Either @device or @node-name must be set but not both.
12811281 #
1282-# @device: the name of the device to generate the snapshot from.
1282+# @device: the name of the device to take a snapshot of.
12831283 #
12841284 # @node-name: graph node name to generate the snapshot from (Since 2.0)
12851285 #
1286-# @snapshot-file: the target of the new image. If the file exists, or
1287-# if it is a device, the snapshot will be created in the existing
1288-# file/device. Otherwise, a new file will be created.
1286+# @snapshot-file: the target of the new overlay image. If the file
1287+# exists, or if it is a device, the overlay will be created in the
1288+# existing file/device. Otherwise, a new file will be created.
12891289 #
12901290 # @snapshot-node-name: the graph node name of the new image (Since 2.0)
12911291 #
1292-# @format: the format of the snapshot image, default is 'qcow2'.
1292+# @format: the format of the overlay image, default is 'qcow2'.
12931293 #
12941294 # @mode: whether and how QEMU should create a new image, default is
12951295 # 'absolute-paths'.
@@ -1302,10 +1302,10 @@
13021302 ##
13031303 # @BlockdevSnapshot:
13041304 #
1305-# @node: device or node name that will have a snapshot created.
1305+# @node: device or node name that will have a snapshot taken.
13061306 #
13071307 # @overlay: reference to the existing block device that will become
1308-# the overlay of @node, as part of creating the snapshot.
1308+# the overlay of @node, as part of taking the snapshot.
13091309 # It must not have a current backing file (this can be
13101310 # achieved by passing "backing": null to blockdev-add).
13111311 #
@@ -1443,7 +1443,7 @@
14431443 ##
14441444 # @blockdev-snapshot-sync:
14451445 #
1446-# Generates a synchronous snapshot of a block device.
1446+# Takes a synchronous snapshot of a block device.
14471447 #
14481448 # For the arguments, see the documentation of BlockdevSnapshotSync.
14491449 #
@@ -1469,9 +1469,9 @@
14691469 ##
14701470 # @blockdev-snapshot:
14711471 #
1472-# Generates a snapshot of a block device.
1472+# Takes a snapshot of a block device.
14731473 #
1474-# Create a snapshot, by installing 'node' as the backing image of
1474+# Take a snapshot, by installing 'node' as the backing image of
14751475 # 'overlay'. Additionally, if 'node' is associated with a block
14761476 # device, the block device changes to using 'overlay' as its new active
14771477 # image.
@@ -3244,6 +3244,8 @@
32443244 #
32453245 # @cluster_alloc_space: an allocation of file space for a cluster (since 4.1)
32463246 #
3247+# @none: triggers once at creation of the blkdebug node (since 4.1)
3248+#
32473249 # Since: 2.9
32483250 ##
32493251 { 'enum': 'BlkdebugEvent', 'prefix': 'BLKDBG',
@@ -3262,7 +3264,30 @@
32623264 'pwritev_rmw_tail', 'pwritev_rmw_after_tail', 'pwritev',
32633265 'pwritev_zero', 'pwritev_done', 'empty_image_prepare',
32643266 'l1_shrink_write_table', 'l1_shrink_free_l2_clusters',
3265- 'cor_write', 'cluster_alloc_space'] }
3267+ 'cor_write', 'cluster_alloc_space', 'none'] }
3268+
3269+##
3270+# @BlkdebugIOType:
3271+#
3272+# Kinds of I/O that blkdebug can inject errors in.
3273+#
3274+# @read: .bdrv_co_preadv()
3275+#
3276+# @write: .bdrv_co_pwritev()
3277+#
3278+# @write-zeroes: .bdrv_co_pwrite_zeroes()
3279+#
3280+# @discard: .bdrv_co_pdiscard()
3281+#
3282+# @flush: .bdrv_co_flush_to_disk()
3283+#
3284+# @block-status: .bdrv_co_block_status()
3285+#
3286+# Since: 4.1
3287+##
3288+{ 'enum': 'BlkdebugIOType', 'prefix': 'BLKDEBUG_IO_TYPE',
3289+ 'data': [ 'read', 'write', 'write-zeroes', 'discard', 'flush',
3290+ 'block-status' ] }
32663291
32673292 ##
32683293 # @BlkdebugInjectErrorOptions:
@@ -3274,6 +3299,11 @@
32743299 # @state: the state identifier blkdebug needs to be in to
32753300 # actually trigger the event; defaults to "any"
32763301 #
3302+# @iotype: the type of I/O operations on which this error should
3303+# be injected; defaults to "all read, write,
3304+# write-zeroes, discard, and flush operations"
3305+# (since: 4.1)
3306+#
32773307 # @errno: error identifier (errno) to be returned; defaults to
32783308 # EIO
32793309 #
@@ -3291,6 +3321,7 @@
32913321 { 'struct': 'BlkdebugInjectErrorOptions',
32923322 'data': { 'event': 'BlkdebugEvent',
32933323 '*state': 'int',
3324+ '*iotype': 'BlkdebugIOType',
32943325 '*errno': 'int',
32953326 '*sector': 'int',
32963327 '*once': 'bool',
--- a/qemu-img-cmds.hx
+++ b/qemu-img-cmds.hx
@@ -44,9 +44,9 @@ STEXI
4444 ETEXI
4545
4646 DEF("convert", img_convert,
47- "convert [--object objectdef] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename")
47+ "convert [--object objectdef] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] [--salvage] filename [filename2 [...]] output_filename")
4848 STEXI
49-@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename}
49+@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] [--salvage] @var{filename} [@var{filename2} [...]] @var{output_filename}
5050 ETEXI
5151
5252 DEF("create", img_create,
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -69,6 +69,7 @@ enum {
6969 OPTION_SIZE = 264,
7070 OPTION_PREALLOCATION = 265,
7171 OPTION_SHRINK = 266,
72+ OPTION_SALVAGE = 267,
7273 };
7374
7475 typedef enum OutputFormat {
@@ -1581,6 +1582,8 @@ typedef struct ImgConvertState {
15811582 int64_t target_backing_sectors; /* negative if unknown */
15821583 bool wr_in_order;
15831584 bool copy_range;
1585+ bool salvage;
1586+ bool quiet;
15841587 int min_sparse;
15851588 int alignment;
15861589 size_t cluster_sectors;
@@ -1627,25 +1630,44 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num)
16271630 }
16281631
16291632 if (s->sector_next_status <= sector_num) {
1630- int64_t count = n * BDRV_SECTOR_SIZE;
1633+ uint64_t offset = (sector_num - src_cur_offset) * BDRV_SECTOR_SIZE;
1634+ int64_t count;
16311635
1632- if (s->target_has_backing) {
1636+ do {
1637+ count = n * BDRV_SECTOR_SIZE;
1638+
1639+ if (s->target_has_backing) {
1640+ ret = bdrv_block_status(blk_bs(s->src[src_cur]), offset,
1641+ count, &count, NULL, NULL);
1642+ } else {
1643+ ret = bdrv_block_status_above(blk_bs(s->src[src_cur]), NULL,
1644+ offset, count, &count, NULL,
1645+ NULL);
1646+ }
1647+
1648+ if (ret < 0) {
1649+ if (s->salvage) {
1650+ if (n == 1) {
1651+ if (!s->quiet) {
1652+ warn_report("error while reading block status at "
1653+ "offset %" PRIu64 ": %s", offset,
1654+ strerror(-ret));
1655+ }
1656+ /* Just try to read the data, then */
1657+ ret = BDRV_BLOCK_DATA;
1658+ count = BDRV_SECTOR_SIZE;
1659+ } else {
1660+ /* Retry on a shorter range */
1661+ n = DIV_ROUND_UP(n, 4);
1662+ }
1663+ } else {
1664+ error_report("error while reading block status at offset "
1665+ "%" PRIu64 ": %s", offset, strerror(-ret));
1666+ return ret;
1667+ }
1668+ }
1669+ } while (ret < 0);
16331670
1634- ret = bdrv_block_status(blk_bs(s->src[src_cur]),
1635- (sector_num - src_cur_offset) *
1636- BDRV_SECTOR_SIZE,
1637- count, &count, NULL, NULL);
1638- } else {
1639- ret = bdrv_block_status_above(blk_bs(s->src[src_cur]), NULL,
1640- (sector_num - src_cur_offset) *
1641- BDRV_SECTOR_SIZE,
1642- count, &count, NULL, NULL);
1643- }
1644- if (ret < 0) {
1645- error_report("error while reading block status of sector %" PRId64
1646- ": %s", sector_num, strerror(-ret));
1647- return ret;
1648- }
16491671 n = DIV_ROUND_UP(count, BDRV_SECTOR_SIZE);
16501672
16511673 if (ret & BDRV_BLOCK_ZERO) {
@@ -1682,6 +1704,7 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num)
16821704 static int coroutine_fn convert_co_read(ImgConvertState *s, int64_t sector_num,
16831705 int nb_sectors, uint8_t *buf)
16841706 {
1707+ uint64_t single_read_until = 0;
16851708 int n, ret;
16861709
16871710 assert(nb_sectors <= s->buf_sectors);
@@ -1689,6 +1712,7 @@ static int coroutine_fn convert_co_read(ImgConvertState *s, int64_t sector_num,
16891712 BlockBackend *blk;
16901713 int src_cur;
16911714 int64_t bs_sectors, src_cur_offset;
1715+ uint64_t offset;
16921716
16931717 /* In the case of compression with multiple source files, we can get a
16941718 * nb_sectors that spreads into the next part. So we must be able to
@@ -1697,13 +1721,29 @@ static int coroutine_fn convert_co_read(ImgConvertState *s, int64_t sector_num,
16971721 blk = s->src[src_cur];
16981722 bs_sectors = s->src_sectors[src_cur];
16991723
1724+ offset = (sector_num - src_cur_offset) << BDRV_SECTOR_BITS;
1725+
17001726 n = MIN(nb_sectors, bs_sectors - (sector_num - src_cur_offset));
1727+ if (single_read_until > offset) {
1728+ n = 1;
1729+ }
17011730
1702- ret = blk_co_pread(
1703- blk, (sector_num - src_cur_offset) << BDRV_SECTOR_BITS,
1704- n << BDRV_SECTOR_BITS, buf, 0);
1731+ ret = blk_co_pread(blk, offset, n << BDRV_SECTOR_BITS, buf, 0);
17051732 if (ret < 0) {
1706- return ret;
1733+ if (s->salvage) {
1734+ if (n > 1) {
1735+ single_read_until = offset + (n << BDRV_SECTOR_BITS);
1736+ continue;
1737+ } else {
1738+ if (!s->quiet) {
1739+ warn_report("error while reading offset %" PRIu64
1740+ ": %s", offset, strerror(-ret));
1741+ }
1742+ memset(buf, 0, BDRV_SECTOR_SIZE);
1743+ }
1744+ } else {
1745+ return ret;
1746+ }
17071747 }
17081748
17091749 sector_num += n;
@@ -2012,7 +2052,7 @@ static int img_convert(int argc, char **argv)
20122052 QDict *open_opts = NULL;
20132053 char *options = NULL;
20142054 Error *local_err = NULL;
2015- bool writethrough, src_writethrough, quiet = false, image_opts = false,
2055+ bool writethrough, src_writethrough, image_opts = false,
20162056 skip_create = false, progress = false, tgt_image_opts = false;
20172057 int64_t ret = -EINVAL;
20182058 bool force_share = false;
@@ -2034,6 +2074,7 @@ static int img_convert(int argc, char **argv)
20342074 {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
20352075 {"force-share", no_argument, 0, 'U'},
20362076 {"target-image-opts", no_argument, 0, OPTION_TARGET_IMAGE_OPTS},
2077+ {"salvage", no_argument, 0, OPTION_SALVAGE},
20372078 {0, 0, 0, 0}
20382079 };
20392080 c = getopt_long(argc, argv, ":hf:O:B:Cco:l:S:pt:T:qnm:WU",
@@ -2120,7 +2161,7 @@ static int img_convert(int argc, char **argv)
21202161 src_cache = optarg;
21212162 break;
21222163 case 'q':
2123- quiet = true;
2164+ s.quiet = true;
21242165 break;
21252166 case 'n':
21262167 skip_create = true;
@@ -2151,6 +2192,9 @@ static int img_convert(int argc, char **argv)
21512192 case OPTION_IMAGE_OPTS:
21522193 image_opts = true;
21532194 break;
2195+ case OPTION_SALVAGE:
2196+ s.salvage = true;
2197+ break;
21542198 case OPTION_TARGET_IMAGE_OPTS:
21552199 tgt_image_opts = true;
21562200 break;
@@ -2177,6 +2221,11 @@ static int img_convert(int argc, char **argv)
21772221 goto fail_getopt;
21782222 }
21792223
2224+ if (s.copy_range && s.salvage) {
2225+ error_report("Cannot use copy offloading in salvaging mode");
2226+ goto fail_getopt;
2227+ }
2228+
21802229 if (tgt_image_opts && !skip_create) {
21812230 error_report("--target-image-opts requires use of -n flag");
21822231 goto fail_getopt;
@@ -2209,7 +2258,7 @@ static int img_convert(int argc, char **argv)
22092258 }
22102259
22112260 /* Initialize before goto out */
2212- if (quiet) {
2261+ if (s.quiet) {
22132262 progress = false;
22142263 }
22152264 qemu_progress_init(progress, 1.0);
@@ -2220,7 +2269,7 @@ static int img_convert(int argc, char **argv)
22202269
22212270 for (bs_i = 0; bs_i < s.src_num; bs_i++) {
22222271 s.src[bs_i] = img_open(image_opts, argv[optind + bs_i],
2223- fmt, src_flags, src_writethrough, quiet,
2272+ fmt, src_flags, src_writethrough, s.quiet,
22242273 force_share);
22252274 if (!s.src[bs_i]) {
22262275 ret = -1;
@@ -2383,7 +2432,7 @@ static int img_convert(int argc, char **argv)
23832432
23842433 if (skip_create) {
23852434 s.target = img_open(tgt_image_opts, out_filename, out_fmt,
2386- flags, writethrough, quiet, false);
2435+ flags, writethrough, s.quiet, false);
23872436 } else {
23882437 /* TODO ultimately we should allow --target-image-opts
23892438 * to be used even when -n is not given.
@@ -2391,7 +2440,7 @@ static int img_convert(int argc, char **argv)
23912440 * to allow filenames in option syntax
23922441 */
23932442 s.target = img_open_file(out_filename, open_opts, out_fmt,
2394- flags, writethrough, quiet, false);
2443+ flags, writethrough, s.quiet, false);
23952444 open_opts = NULL; /* blk_new_open will have freed it */
23962445 }
23972446 if (!s.target) {
@@ -3350,6 +3399,7 @@ static int img_rebase(int argc, char **argv)
33503399 out_baseimg,
33513400 &local_err);
33523401 if (local_err) {
3402+ qobject_unref(options);
33533403 error_reportf_err(local_err,
33543404 "Could not resolve backing filename: ");
33553405 ret = -1;
@@ -3362,7 +3412,9 @@ static int img_rebase(int argc, char **argv)
33623412 */
33633413 prefix_chain_bs = bdrv_find_backing_image(bs, out_real_path);
33643414 if (prefix_chain_bs) {
3415+ qobject_unref(options);
33653416 g_free(out_real_path);
3417+
33663418 blk_new_backing = blk_new(qemu_get_aio_context(),
33673419 BLK_PERM_CONSISTENT_READ,
33683420 BLK_PERM_ALL);
--- a/qemu-img.texi
+++ b/qemu-img.texi
@@ -175,6 +175,10 @@ improve performance if the data is remote, such as with NFS or iSCSI backends,
175175 but will not automatically sparsify zero sectors, and may result in a fully
176176 allocated target image depending on the host support for getting allocation
177177 information.
178+@item --salvage
179+Try to ignore I/O errors when reading. Unless in quiet mode (@code{-q}), errors
180+will still be printed. Areas that cannot be read from the source will be
181+treated as containing only zeroes.
178182 @end table
179183
180184 Parameters to dd subcommand:
--- a/tests/qemu-iotests/082
+++ b/tests/qemu-iotests/082
@@ -162,6 +162,7 @@ echo === convert: -C and other options ===
162162 run_qemu_img convert -C -S 4k -O $IMGFMT "$TEST_IMG" "$TEST_IMG".target
163163 run_qemu_img convert -C -S 8k -O $IMGFMT "$TEST_IMG" "$TEST_IMG".target
164164 run_qemu_img convert -C -c -O $IMGFMT "$TEST_IMG" "$TEST_IMG".target
165+run_qemu_img convert -C --salvage -O $IMGFMT "$TEST_IMG" "$TEST_IMG".target
165166
166167 echo
167168 echo === amend: Options specified more than once ===
--- a/tests/qemu-iotests/082.out
+++ b/tests/qemu-iotests/082.out
@@ -567,6 +567,9 @@ qemu-img: Cannot enable copy offloading when -S is used
567567 Testing: convert -C -c -O qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.target
568568 qemu-img: Cannot enable copy offloading when -c is used
569569
570+Testing: convert -C --salvage -O qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.target
571+qemu-img: Cannot use copy offloading in salvaging mode
572+
570573 === amend: Options specified more than once ===
571574
572575 Testing: amend -f foo -f qcow2 -o lazy_refcounts=on TEST_DIR/t.qcow2
--- a/tests/qemu-iotests/085.out
+++ b/tests/qemu-iotests/085.out
@@ -64,13 +64,13 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/
6464
6565 === Invalid command - cannot create a snapshot using a file BDS ===
6666
67-{"error": {"class": "GenericError", "desc": "The snapshot does not support backing images"}}
67+{"error": {"class": "GenericError", "desc": "The overlay does not support backing images"}}
6868
6969 === Invalid command - snapshot node used as active layer ===
7070
71-{"error": {"class": "GenericError", "desc": "The snapshot is already in use"}}
72-{"error": {"class": "GenericError", "desc": "The snapshot is already in use"}}
73-{"error": {"class": "GenericError", "desc": "The snapshot is already in use"}}
71+{"error": {"class": "GenericError", "desc": "The overlay is already in use"}}
72+{"error": {"class": "GenericError", "desc": "The overlay is already in use"}}
73+{"error": {"class": "GenericError", "desc": "The overlay is already in use"}}
7474
7575 === Invalid command - snapshot node used as backing hd ===
7676
@@ -81,7 +81,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/
8181 Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
8282 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
8383 {"return": {}}
84-{"error": {"class": "GenericError", "desc": "The snapshot already has a backing image"}}
84+{"error": {"class": "GenericError", "desc": "The overlay already has a backing image"}}
8585
8686 === Invalid command - The node does not exist ===
8787
--- a/tests/qemu-iotests/175
+++ b/tests/qemu-iotests/175
@@ -28,10 +28,25 @@ status=1 # failure is the default!
2828
2929 _cleanup()
3030 {
31- _cleanup_test_img
31+ _cleanup_test_img
32+ rm -f "$TEST_DIR/empty"
3233 }
3334 trap "_cleanup; exit \$status" 0 1 2 3 15
3435
36+# Some file systems sometimes allocate extra blocks independently of
37+# the file size. This function hides the resulting difference in the
38+# stat -c '%b' output.
39+# Parameter 1: Number of blocks an empty file occupies
40+# Parameter 2: Image size in bytes
41+_filter_blocks()
42+{
43+ extra_blocks=$1
44+ img_size=$2
45+
46+ sed -e "s/blocks=$extra_blocks\\(\$\\|[^0-9]\\)/nothing allocated/" \
47+ -e "s/blocks=$((extra_blocks + img_size / 512))\\(\$\\|[^0-9]\\)/everything allocated/"
48+}
49+
3550 # get standard environment, filters and checks
3651 . ./common.rc
3752 . ./common.filter
@@ -40,18 +55,21 @@ _supported_fmt raw
4055 _supported_proto file
4156 _supported_os Linux
4257
43-size=1m
58+size=$((1 * 1024 * 1024))
59+
60+touch "$TEST_DIR/empty"
61+extra_blocks=$(stat -c '%b' "$TEST_DIR/empty")
4462
4563 echo
4664 echo "== creating image with default preallocation =="
4765 _make_test_img $size | _filter_imgfmt
48-stat -c "size=%s, blocks=%b" $TEST_IMG
66+stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $size
4967
5068 for mode in off full falloc; do
5169 echo
5270 echo "== creating image with preallocation $mode =="
5371 IMGOPTS=preallocation=$mode _make_test_img $size | _filter_imgfmt
54- stat -c "size=%s, blocks=%b" $TEST_IMG
72+ stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $size
5573 done
5674
5775 # success, all done
--- a/tests/qemu-iotests/175.out
+++ b/tests/qemu-iotests/175.out
@@ -2,17 +2,17 @@ QA output created by 175
22
33 == creating image with default preallocation ==
44 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
5-size=1048576, blocks=0
5+size=1048576, nothing allocated
66
77 == creating image with preallocation off ==
88 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 preallocation=off
9-size=1048576, blocks=0
9+size=1048576, nothing allocated
1010
1111 == creating image with preallocation full ==
1212 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 preallocation=full
13-size=1048576, blocks=2048
13+size=1048576, everything allocated
1414
1515 == creating image with preallocation falloc ==
1616 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 preallocation=falloc
17-size=1048576, blocks=2048
17+size=1048576, everything allocated
1818 *** done
--- a/tests/qemu-iotests/219
+++ b/tests/qemu-iotests/219
@@ -23,6 +23,8 @@ import iotests
2323
2424 iotests.verify_image_format(supported_fmts=['qcow2'])
2525
26+img_size = 4 * 1024 * 1024
27+
2628 def pause_wait(vm, job_id):
2729 with iotests.Timeout(3, "Timeout waiting for job to pause"):
2830 while True:
@@ -62,6 +64,8 @@ def test_pause_resume(vm):
6264 iotests.log(vm.qmp('query-jobs'))
6365
6466 def test_job_lifecycle(vm, job, job_args, has_ready=False):
67+ global img_size
68+
6569 iotests.log('')
6670 iotests.log('')
6771 iotests.log('Starting block job: %s (auto-finalize: %s; auto-dismiss: %s)' %
@@ -84,6 +88,10 @@ def test_job_lifecycle(vm, job, job_args, has_ready=False):
8488 iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
8589 iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
8690
91+ # Wait for total-progress to stabilize
92+ while vm.qmp('query-jobs')['return'][0]['total-progress'] < img_size:
93+ pass
94+
8795 # RUNNING state:
8896 # pause/resume should work, complete/finalize/dismiss should error out
8997 iotests.log('')
@@ -173,9 +181,8 @@ with iotests.FilePath('disk.img') as disk_path, \
173181 iotests.FilePath('copy.img') as copy_path, \
174182 iotests.VM() as vm:
175183
176- img_size = '4M'
177- iotests.qemu_img_create('-f', iotests.imgfmt, disk_path, img_size)
178- iotests.qemu_io('-c', 'write 0 %s' % (img_size),
184+ iotests.qemu_img_create('-f', iotests.imgfmt, disk_path, str(img_size))
185+ iotests.qemu_io('-c', 'write 0 %i' % (img_size),
179186 '-f', iotests.imgfmt, disk_path)
180187
181188 iotests.log('Launching VM...')
--- /dev/null
+++ b/tests/qemu-iotests/251
@@ -0,0 +1,170 @@
1+#!/usr/bin/env bash
2+#
3+# Test qemu-img convert --salvage
4+#
5+# Copyright (C) 2019 Red Hat, Inc.
6+#
7+# This program is free software; you can redistribute it and/or modify
8+# it under the terms of the GNU General Public License as published by
9+# the Free Software Foundation; either version 2 of the License, or
10+# (at your option) any later version.
11+#
12+# This program is distributed in the hope that it will be useful,
13+# but WITHOUT ANY WARRANTY; without even the implied warranty of
14+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+# GNU General Public License for more details.
16+#
17+# You should have received a copy of the GNU General Public License
18+# along with this program. If not, see <http://www.gnu.org/licenses/>.
19+#
20+
21+# creator
22+owner=mreitz@redhat.com
23+
24+seq=$(basename $0)
25+echo "QA output created by $seq"
26+
27+status=1 # failure is the default!
28+
29+_cleanup()
30+{
31+ _cleanup_test_img
32+}
33+trap "_cleanup; exit \$status" 0 1 2 3 15
34+
35+# get standard environment, filters and checks
36+. ./common.rc
37+. ./common.filter
38+. ./common.qemu
39+
40+_supported_fmt generic
41+_supported_proto file
42+_supported_os Linux
43+
44+if [ "$IMGOPTSSYNTAX" = "true" ]; then
45+ # We use json:{} filenames here, so we cannot work with additional options.
46+ _unsupported_fmt $IMGFMT
47+else
48+ # With VDI, the output is ordered differently. Just disable it.
49+ _unsupported_fmt vdi
50+fi
51+
52+
53+TEST_IMG="$TEST_IMG.orig" _make_test_img 64M
54+
55+$QEMU_IO -c 'write -P 42 0 64M' "$TEST_IMG.orig" | _filter_qemu_io
56+
57+
58+sector_size=512
59+
60+# Offsets on which to fail block-status. Keep in ascending order so
61+# the indexing done by _filter_offsets will appear in ascending order
62+# in the output as well.
63+status_fail_offsets="$((16 * 1024 * 1024 + 8192))
64+ $((33 * 1024 * 1024 + 512))"
65+
66+# Offsets on which to fail reads. Keep in ascending order for the
67+# same reason.
68+# The second element is shared with $status_fail_offsets on purpose.
69+# Starting with the third element, we test what happens when a
70+# continuous range of sectors is inaccessible.
71+read_fail_offsets="$((32 * 1024 * 1024 - 65536))
72+ $((33 * 1024 * 1024 + 512))
73+ $(seq $((34 * 1024 * 1024)) $sector_size \
74+ $((34 * 1024 * 1024 + 4096 - $sector_size)))"
75+
76+
77+# blkdebug must be above the format layer so it can intercept all
78+# block-status events
79+source_img="json:{'driver': 'blkdebug',
80+ 'image': {
81+ 'driver': '$IMGFMT',
82+ 'file': {
83+ 'driver': 'file',
84+ 'filename': '$TEST_IMG.orig'
85+ }
86+ },
87+ 'inject-error': ["
88+
89+for ofs in $status_fail_offsets
90+do
91+ source_img+="{ 'event': 'none',
92+ 'iotype': 'block-status',
93+ 'errno': 5,
94+ 'sector': $((ofs / sector_size)) },"
95+done
96+
97+for ofs in $read_fail_offsets
98+do
99+ source_img+="{ 'event': 'none',
100+ 'iotype': 'read',
101+ 'errno': 5,
102+ 'sector': $((ofs / sector_size)) },"
103+done
104+
105+# Remove the trailing comma and terminate @inject-error and json:{}
106+source_img="${source_img%,} ] }"
107+
108+
109+echo
110+
111+
112+_filter_offsets() {
113+ filters=
114+
115+ index=0
116+ for ofs in $1
117+ do
118+ filters+=" -e s/$ofs/status_fail_offset_$index/"
119+ index=$((index + 1))
120+ done
121+
122+ index=0
123+ for ofs in $2
124+ do
125+ filters+=" -e s/$ofs/read_fail_offset_$index/"
126+ index=$((index + 1))
127+ done
128+
129+ sed $filters
130+}
131+
132+# While determining the number of allocated sectors in the input
133+# image, we should see one block status warning per element of
134+# $status_fail_offsets.
135+#
136+# Then, the image is read. Since the block status is queried in
137+# basically the same way, the same warnings as in the previous step
138+# should reappear. Interleaved with those we should see a read
139+# warning per element of $read_fail_offsets.
140+# Note that $read_fail_offsets and $status_fail_offsets share an
141+# element (read_fail_offset_1 == status_fail_offset_1), so
142+# "status_fail_offset_1" in the output is the same as
143+# "read_fail_offset_1".
144+$QEMU_IMG convert --salvage "$source_img" "$TEST_IMG" 2>&1 \
145+ | _filter_offsets "$status_fail_offsets" "$read_fail_offsets"
146+
147+echo
148+
149+# The offsets where the block status could not be determined should
150+# have been treated as containing data and thus should be correct in
151+# the output image.
152+# The offsets where reading failed altogether should be 0. Make them
153+# 0 in the input image, too, so we can compare both images.
154+for ofs in $read_fail_offsets
155+do
156+ $QEMU_IO -c "write -z $ofs $sector_size" "$TEST_IMG.orig" \
157+ | _filter_qemu_io \
158+ | _filter_offsets '' "$read_fail_offsets"
159+done
160+
161+echo
162+
163+# These should be equal now.
164+$QEMU_IMG compare "$TEST_IMG.orig" "$TEST_IMG"
165+
166+
167+# success, all done
168+echo "*** done"
169+rm -f $seq.full
170+status=0
--- /dev/null
+++ b/tests/qemu-iotests/251.out
@@ -0,0 +1,43 @@
1+QA output created by 251
2+Formatting 'TEST_DIR/t.IMGFMT.orig', fmt=IMGFMT size=67108864
3+wrote 67108864/67108864 bytes at offset 0
4+64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
5+
6+qemu-img: warning: error while reading block status at offset status_fail_offset_0: Input/output error
7+qemu-img: warning: error while reading block status at offset status_fail_offset_1: Input/output error
8+qemu-img: warning: error while reading block status at offset status_fail_offset_0: Input/output error
9+qemu-img: warning: error while reading offset read_fail_offset_0: Input/output error
10+qemu-img: warning: error while reading block status at offset status_fail_offset_1: Input/output error
11+qemu-img: warning: error while reading offset status_fail_offset_1: Input/output error
12+qemu-img: warning: error while reading offset read_fail_offset_2: Input/output error
13+qemu-img: warning: error while reading offset read_fail_offset_3: Input/output error
14+qemu-img: warning: error while reading offset read_fail_offset_4: Input/output error
15+qemu-img: warning: error while reading offset read_fail_offset_5: Input/output error
16+qemu-img: warning: error while reading offset read_fail_offset_6: Input/output error
17+qemu-img: warning: error while reading offset read_fail_offset_7: Input/output error
18+qemu-img: warning: error while reading offset read_fail_offset_8: Input/output error
19+qemu-img: warning: error while reading offset read_fail_offset_9: Input/output error
20+
21+wrote 512/512 bytes at offset read_fail_offset_0
22+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
23+wrote 512/512 bytes at offset read_fail_offset_1
24+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
25+wrote 512/512 bytes at offset read_fail_offset_2
26+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
27+wrote 512/512 bytes at offset read_fail_offset_3
28+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
29+wrote 512/512 bytes at offset read_fail_offset_4
30+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
31+wrote 512/512 bytes at offset read_fail_offset_5
32+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
33+wrote 512/512 bytes at offset read_fail_offset_6
34+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
35+wrote 512/512 bytes at offset read_fail_offset_7
36+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
37+wrote 512/512 bytes at offset read_fail_offset_8
38+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
39+wrote 512/512 bytes at offset read_fail_offset_9
40+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
41+
42+Images are identical.
43+*** done
--- a/tests/qemu-iotests/254
+++ b/tests/qemu-iotests/254
@@ -21,6 +21,8 @@
2121 import iotests
2222 from iotests import qemu_img_create, file_path, log
2323
24+iotests.verify_image_format(supported_fmts=['qcow2'])
25+
2426 disk, top = file_path('disk', 'top')
2527 size = 1024 * 1024
2628
--- /dev/null
+++ b/tests/qemu-iotests/256
@@ -0,0 +1,122 @@
1+#!/usr/bin/env python
2+#
3+# Test incremental/backup across iothread contexts
4+#
5+# Copyright (c) 2019 John Snow for Red Hat, Inc.
6+#
7+# This program is free software; you can redistribute it and/or modify
8+# it under the terms of the GNU General Public License as published by
9+# the Free Software Foundation; either version 2 of the License, or
10+# (at your option) any later version.
11+#
12+# This program is distributed in the hope that it will be useful,
13+# but WITHOUT ANY WARRANTY; without even the implied warranty of
14+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+# GNU General Public License for more details.
16+#
17+# You should have received a copy of the GNU General Public License
18+# along with this program. If not, see <http://www.gnu.org/licenses/>.
19+#
20+# owner=jsnow@redhat.com
21+
22+import os
23+import iotests
24+from iotests import log
25+
26+iotests.verify_image_format(supported_fmts=['qcow2'])
27+size = 64 * 1024 * 1024
28+
29+with iotests.FilePath('img0') as img0_path, \
30+ iotests.FilePath('img1') as img1_path, \
31+ iotests.FilePath('img0-full') as img0_full_path, \
32+ iotests.FilePath('img1-full') as img1_full_path, \
33+ iotests.FilePath('img0-incr') as img0_incr_path, \
34+ iotests.FilePath('img1-incr') as img1_incr_path, \
35+ iotests.VM() as vm:
36+
37+ def create_target(filepath, name, size):
38+ basename = os.path.basename(filepath)
39+ nodename = "file_{}".format(basename)
40+ log(vm.command('blockdev-create', job_id='job1',
41+ options={
42+ 'driver': 'file',
43+ 'filename': filepath,
44+ 'size': 0,
45+ }))
46+ vm.run_job('job1')
47+ log(vm.command('blockdev-add', driver='file',
48+ node_name=nodename, filename=filepath))
49+ log(vm.command('blockdev-create', job_id='job2',
50+ options={
51+ 'driver': iotests.imgfmt,
52+ 'file': nodename,
53+ 'size': size,
54+ }))
55+ vm.run_job('job2')
56+ log(vm.command('blockdev-add', driver=iotests.imgfmt,
57+ node_name=name,
58+ file=nodename))
59+
60+ log('--- Preparing images & VM ---\n')
61+ vm.add_object('iothread,id=iothread0')
62+ vm.add_object('iothread,id=iothread1')
63+ vm.add_device('virtio-scsi-pci,id=scsi0,iothread=iothread0')
64+ vm.add_device('virtio-scsi-pci,id=scsi1,iothread=iothread1')
65+ iotests.qemu_img_create('-f', iotests.imgfmt, img0_path, str(size))
66+ iotests.qemu_img_create('-f', iotests.imgfmt, img1_path, str(size))
67+ vm.add_drive(img0_path, interface='none')
68+ vm.add_device('scsi-hd,id=device0,drive=drive0,bus=scsi0.0')
69+ vm.add_drive(img1_path, interface='none')
70+ vm.add_device('scsi-hd,id=device1,drive=drive1,bus=scsi1.0')
71+
72+ log('--- Starting VM ---\n')
73+ vm.launch()
74+
75+ log('--- Create Targets & Full Backups ---\n')
76+ create_target(img0_full_path, 'img0-full', size)
77+ create_target(img1_full_path, 'img1-full', size)
78+ ret = vm.qmp_log('transaction', indent=2, actions=[
79+ { 'type': 'block-dirty-bitmap-add',
80+ 'data': { 'node': 'drive0', 'name': 'bitmap0' }},
81+ { 'type': 'block-dirty-bitmap-add',
82+ 'data': { 'node': 'drive1', 'name': 'bitmap1' }},
83+ { 'type': 'blockdev-backup',
84+ 'data': { 'device': 'drive0',
85+ 'target': 'img0-full',
86+ 'sync': 'full',
87+ 'job-id': 'j0' }},
88+ { 'type': 'blockdev-backup',
89+ 'data': { 'device': 'drive1',
90+ 'target': 'img1-full',
91+ 'sync': 'full',
92+ 'job-id': 'j1' }}
93+ ])
94+ if "error" in ret:
95+ raise Exception(ret['error']['desc'])
96+ vm.run_job('j0', auto_dismiss=True)
97+ vm.run_job('j1', auto_dismiss=True)
98+
99+ log('\n--- Create Targets & Incremental Backups ---\n')
100+ create_target(img0_incr_path, 'img0-incr', size)
101+ create_target(img1_incr_path, 'img1-incr', size)
102+ ret = vm.qmp_log('transaction', indent=2, actions=[
103+ { 'type': 'blockdev-backup',
104+ 'data': { 'device': 'drive0',
105+ 'target': 'img0-incr',
106+ 'sync': 'incremental',
107+ 'bitmap': 'bitmap0',
108+ 'job-id': 'j2' }},
109+ { 'type': 'blockdev-backup',
110+ 'data': { 'device': 'drive1',
111+ 'target': 'img1-incr',
112+ 'sync': 'incremental',
113+ 'bitmap': 'bitmap1',
114+ 'job-id': 'j3' }}
115+ ])
116+ if "error" in ret:
117+ raise Exception(ret['error']['desc'])
118+ vm.run_job('j2', auto_dismiss=True)
119+ vm.run_job('j3', auto_dismiss=True)
120+
121+ log('\n--- Done ---')
122+ vm.shutdown()
--- /dev/null
+++ b/tests/qemu-iotests/256.out
@@ -0,0 +1,119 @@
1+--- Preparing images & VM ---
2+
3+--- Starting VM ---
4+
5+--- Create Targets & Full Backups ---
6+
7+{}
8+{"execute": "job-dismiss", "arguments": {"id": "job1"}}
9+{"return": {}}
10+{}
11+{}
12+{"execute": "job-dismiss", "arguments": {"id": "job2"}}
13+{"return": {}}
14+{}
15+{}
16+{"execute": "job-dismiss", "arguments": {"id": "job1"}}
17+{"return": {}}
18+{}
19+{}
20+{"execute": "job-dismiss", "arguments": {"id": "job2"}}
21+{"return": {}}
22+{}
23+{
24+ "execute": "transaction",
25+ "arguments": {
26+ "actions": [
27+ {
28+ "data": {
29+ "name": "bitmap0",
30+ "node": "drive0"
31+ },
32+ "type": "block-dirty-bitmap-add"
33+ },
34+ {
35+ "data": {
36+ "name": "bitmap1",
37+ "node": "drive1"
38+ },
39+ "type": "block-dirty-bitmap-add"
40+ },
41+ {
42+ "data": {
43+ "device": "drive0",
44+ "job-id": "j0",
45+ "sync": "full",
46+ "target": "img0-full"
47+ },
48+ "type": "blockdev-backup"
49+ },
50+ {
51+ "data": {
52+ "device": "drive1",
53+ "job-id": "j1",
54+ "sync": "full",
55+ "target": "img1-full"
56+ },
57+ "type": "blockdev-backup"
58+ }
59+ ]
60+ }
61+}
62+{
63+ "return": {}
64+}
65+{"data": {"device": "j0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
66+{"data": {"device": "j1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
67+
68+--- Create Targets & Incremental Backups ---
69+
70+{}
71+{"execute": "job-dismiss", "arguments": {"id": "job1"}}
72+{"return": {}}
73+{}
74+{}
75+{"execute": "job-dismiss", "arguments": {"id": "job2"}}
76+{"return": {}}
77+{}
78+{}
79+{"execute": "job-dismiss", "arguments": {"id": "job1"}}
80+{"return": {}}
81+{}
82+{}
83+{"execute": "job-dismiss", "arguments": {"id": "job2"}}
84+{"return": {}}
85+{}
86+{
87+ "execute": "transaction",
88+ "arguments": {
89+ "actions": [
90+ {
91+ "data": {
92+ "bitmap": "bitmap0",
93+ "device": "drive0",
94+ "job-id": "j2",
95+ "sync": "incremental",
96+ "target": "img0-incr"
97+ },
98+ "type": "blockdev-backup"
99+ },
100+ {
101+ "data": {
102+ "bitmap": "bitmap1",
103+ "device": "drive1",
104+ "job-id": "j3",
105+ "sync": "incremental",
106+ "target": "img1-incr"
107+ },
108+ "type": "blockdev-backup"
109+ }
110+ ]
111+ }
112+}
113+{
114+ "return": {}
115+}
116+{"data": {"device": "j2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
117+{"data": {"device": "j3", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
118+
119+--- Done ---
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -263,7 +263,9 @@
263263 248 rw quick
264264 249 rw auto quick
265265 250 rw auto quick
266+251 rw auto quick
266267 252 rw auto backing quick
267268 253 rw auto quick
268269 254 rw auto backing quick
269270 255 rw auto quick
271+256 rw auto quick
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -524,7 +524,7 @@ class VM(qtest.QEMUQtestMachine):
524524 output_list += [key + '=' + obj[key]]
525525 return ','.join(output_list)
526526
527- def get_qmp_events_filtered(self, wait=True):
527+ def get_qmp_events_filtered(self, wait=60.0):
528528 result = []
529529 for ev in self.get_qmp_events(wait=wait):
530530 result.append(filter_qmp_event(ev))
@@ -542,28 +542,38 @@ class VM(qtest.QEMUQtestMachine):
542542
543543 # Returns None on success, and an error string on failure
544544 def run_job(self, job, auto_finalize=True, auto_dismiss=False,
545- pre_finalize=None):
545+ pre_finalize=None, wait=60.0):
546+ match_device = {'data': {'device': job}}
547+ match_id = {'data': {'id': job}}
548+ events = [
549+ ('BLOCK_JOB_COMPLETED', match_device),
550+ ('BLOCK_JOB_CANCELLED', match_device),
551+ ('BLOCK_JOB_ERROR', match_device),
552+ ('BLOCK_JOB_READY', match_device),
553+ ('BLOCK_JOB_PENDING', match_id),
554+ ('JOB_STATUS_CHANGE', match_id)
555+ ]
546556 error = None
547557 while True:
548- for ev in self.get_qmp_events_filtered(wait=True):
549- if ev['event'] == 'JOB_STATUS_CHANGE':
550- status = ev['data']['status']
551- if status == 'aborting':
552- result = self.qmp('query-jobs')
553- for j in result['return']:
554- if j['id'] == job:
555- error = j['error']
556- log('Job failed: %s' % (j['error']))
557- elif status == 'pending' and not auto_finalize:
558- if pre_finalize:
559- pre_finalize()
560- self.qmp_log('job-finalize', id=job)
561- elif status == 'concluded' and not auto_dismiss:
562- self.qmp_log('job-dismiss', id=job)
563- elif status == 'null':
564- return error
565- else:
566- log(ev)
558+ ev = filter_qmp_event(self.events_wait(events))
559+ if ev['event'] != 'JOB_STATUS_CHANGE':
560+ log(ev)
561+ continue
562+ status = ev['data']['status']
563+ if status == 'aborting':
564+ result = self.qmp('query-jobs')
565+ for j in result['return']:
566+ if j['id'] == job:
567+ error = j['error']
568+ log('Job failed: %s' % (j['error']))
569+ elif status == 'pending' and not auto_finalize:
570+ if pre_finalize:
571+ pre_finalize()
572+ self.qmp_log('job-finalize', id=job)
573+ elif status == 'concluded' and not auto_dismiss:
574+ self.qmp_log('job-dismiss', id=job)
575+ elif status == 'null':
576+ return error
567577
568578 def node_info(self, node_name):
569579 nodes = self.qmp('query-named-block-nodes')
@@ -650,7 +660,7 @@ class QMPTestCase(unittest.TestCase):
650660 self.assertEqual(self.vm.flatten_qmp_object(json.loads(json_filename[5:])),
651661 self.vm.flatten_qmp_object(reference))
652662
653- def cancel_and_wait(self, drive='drive0', force=False, resume=False):
663+ def cancel_and_wait(self, drive='drive0', force=False, resume=False, wait=60.0):
654664 '''Cancel a block job and wait for it to finish, returning the event'''
655665 result = self.vm.qmp('block-job-cancel', device=drive, force=force)
656666 self.assert_qmp(result, 'return', {})
@@ -661,7 +671,7 @@ class QMPTestCase(unittest.TestCase):
661671 cancelled = False
662672 result = None
663673 while not cancelled:
664- for event in self.vm.get_qmp_events(wait=True):
674+ for event in self.vm.get_qmp_events(wait=wait):
665675 if event['event'] == 'BLOCK_JOB_COMPLETED' or \
666676 event['event'] == 'BLOCK_JOB_CANCELLED':
667677 self.assert_qmp(event, 'data/device', drive)
@@ -674,10 +684,10 @@ class QMPTestCase(unittest.TestCase):
674684 self.assert_no_active_block_jobs()
675685 return result
676686
677- def wait_until_completed(self, drive='drive0', check_offset=True):
687+ def wait_until_completed(self, drive='drive0', check_offset=True, wait=60.0):
678688 '''Wait for a block job to finish, returning the event'''
679689 while True:
680- for event in self.vm.get_qmp_events(wait=True):
690+ for event in self.vm.get_qmp_events(wait=wait):
681691 if event['event'] == 'BLOCK_JOB_COMPLETED':
682692 self.assert_qmp(event, 'data/device', drive)
683693 self.assert_qmp_absent(event, 'data/error')