Skip to content

Commit

Permalink
Merge remote-tracking branch 'remotes/maxreitz/tags/pull-block-2019-0…
Browse files Browse the repository at this point in the history
…6-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>
  • Loading branch information
pm215 committed Jun 14, 2019
2 parents 5ec2eca + 21c1ce5 commit f3d0bec
Show file tree
Hide file tree
Showing 21 changed files with 772 additions and 116 deletions.
60 changes: 51 additions & 9 deletions block/blkdebug.c
Expand Up @@ -75,6 +75,7 @@ typedef struct BlkdebugRule {
int state;
union {
struct {
uint64_t iotype_mask;
int error;
int immediately;
int once;
Expand All @@ -91,6 +92,9 @@ typedef struct BlkdebugRule {
QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
} BlkdebugRule;

QEMU_BUILD_BUG_MSG(BLKDEBUG_IO_TYPE__MAX > 64,
"BlkdebugIOType mask does not fit into an uint64_t");

static QemuOptsList inject_error_opts = {
.name = "inject-error",
.head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head),
Expand All @@ -103,6 +107,10 @@ static QemuOptsList inject_error_opts = {
.name = "state",
.type = QEMU_OPT_NUMBER,
},
{
.name = "iotype",
.type = QEMU_OPT_STRING,
},
{
.name = "errno",
.type = QEMU_OPT_NUMBER,
Expand Down Expand Up @@ -162,6 +170,8 @@ static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
int event;
struct BlkdebugRule *rule;
int64_t sector;
BlkdebugIOType iotype;
Error *local_error = NULL;

/* Find the right event for the rule */
event_name = qemu_opt_get(opts, "event");
Expand Down Expand Up @@ -192,6 +202,26 @@ static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
sector = qemu_opt_get_number(opts, "sector", -1);
rule->options.inject.offset =
sector == -1 ? -1 : sector * BDRV_SECTOR_SIZE;

iotype = qapi_enum_parse(&BlkdebugIOType_lookup,
qemu_opt_get(opts, "iotype"),
BLKDEBUG_IO_TYPE__MAX, &local_error);
if (local_error) {
error_propagate(errp, local_error);
return -1;
}
if (iotype != BLKDEBUG_IO_TYPE__MAX) {
rule->options.inject.iotype_mask = (1ull << iotype);
} else {
/* Apply the default */
rule->options.inject.iotype_mask =
(1ull << BLKDEBUG_IO_TYPE_READ)
| (1ull << BLKDEBUG_IO_TYPE_WRITE)
| (1ull << BLKDEBUG_IO_TYPE_WRITE_ZEROES)
| (1ull << BLKDEBUG_IO_TYPE_DISCARD)
| (1ull << BLKDEBUG_IO_TYPE_FLUSH);
}

break;

case ACTION_SET_STATE:
Expand Down Expand Up @@ -461,6 +491,8 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
goto out;
}

bdrv_debug_event(bs, BLKDBG_NONE);

ret = 0;
out:
if (ret < 0) {
Expand All @@ -470,7 +502,8 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
return ret;
}

static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes)
static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
BlkdebugIOType iotype)
{
BDRVBlkdebugState *s = bs->opaque;
BlkdebugRule *rule = NULL;
Expand All @@ -480,9 +513,10 @@ static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes)
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
uint64_t inject_offset = rule->options.inject.offset;

if (inject_offset == -1 ||
(bytes && inject_offset >= offset &&
inject_offset < offset + bytes))
if ((inject_offset == -1 ||
(bytes && inject_offset >= offset &&
inject_offset < offset + bytes)) &&
(rule->options.inject.iotype_mask & (1ull << iotype)))
{
break;
}
Expand Down Expand Up @@ -521,7 +555,7 @@ blkdebug_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
assert(bytes <= bs->bl.max_transfer);
}

err = rule_check(bs, offset, bytes);
err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_READ);
if (err) {
return err;
}
Expand All @@ -542,7 +576,7 @@ blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
assert(bytes <= bs->bl.max_transfer);
}

err = rule_check(bs, offset, bytes);
err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_WRITE);
if (err) {
return err;
}
Expand All @@ -552,7 +586,7 @@ blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,

static int blkdebug_co_flush(BlockDriverState *bs)
{
int err = rule_check(bs, 0, 0);
int err = rule_check(bs, 0, 0, BLKDEBUG_IO_TYPE_FLUSH);

if (err) {
return err;
Expand Down Expand Up @@ -586,7 +620,7 @@ static int coroutine_fn blkdebug_co_pwrite_zeroes(BlockDriverState *bs,
assert(bytes <= bs->bl.max_pwrite_zeroes);
}

err = rule_check(bs, offset, bytes);
err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_WRITE_ZEROES);
if (err) {
return err;
}
Expand Down Expand Up @@ -620,7 +654,7 @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
assert(bytes <= bs->bl.max_pdiscard);
}

err = rule_check(bs, offset, bytes);
err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_DISCARD);
if (err) {
return err;
}
Expand All @@ -636,7 +670,15 @@ static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs,
int64_t *map,
BlockDriverState **file)
{
int err;

assert(QEMU_IS_ALIGNED(offset | bytes, bs->bl.request_alignment));

err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_BLOCK_STATUS);
if (err) {
return err;
}

return bdrv_co_block_status_from_file(bs, want_zero, offset, bytes,
pnum, map, file);
}
Expand Down
14 changes: 5 additions & 9 deletions blockdev.c
Expand Up @@ -1608,13 +1608,13 @@ static void external_snapshot_prepare(BlkActionState *common,
s->has_snapshot_node_name ? s->snapshot_node_name : NULL;

if (node_name && !snapshot_node_name) {
error_setg(errp, "New snapshot node name missing");
error_setg(errp, "New overlay node name missing");
goto out;
}

if (snapshot_node_name &&
bdrv_lookup_bs(snapshot_node_name, snapshot_node_name, NULL)) {
error_setg(errp, "New snapshot node name already in use");
error_setg(errp, "New overlay node name already in use");
goto out;
}

Expand Down Expand Up @@ -1656,7 +1656,7 @@ static void external_snapshot_prepare(BlkActionState *common,
}

if (bdrv_has_blk(state->new_bs)) {
error_setg(errp, "The snapshot is already in use");
error_setg(errp, "The overlay is already in use");
goto out;
}

Expand All @@ -1666,12 +1666,12 @@ static void external_snapshot_prepare(BlkActionState *common,
}

if (state->new_bs->backing != NULL) {
error_setg(errp, "The snapshot already has a backing image");
error_setg(errp, "The overlay already has a backing image");
goto out;
}

if (!state->new_bs->drv->supports_backing) {
error_setg(errp, "The snapshot does not support backing images");
error_setg(errp, "The overlay does not support backing images");
goto out;
}

Expand Down Expand Up @@ -1876,10 +1876,6 @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
}

aio_context = bdrv_get_aio_context(bs);
if (aio_context != bdrv_get_aio_context(target)) {
error_setg(errp, "Backup between two IO threads is not implemented");
return;
}
aio_context_acquire(aio_context);
state->bs = bs;

Expand Down
1 change: 1 addition & 0 deletions hw/block/fdc.c
Expand Up @@ -2648,6 +2648,7 @@ static void fdctrl_realize_common(DeviceState *dev, FDCtrl *fdctrl,

FLOPPY_DPRINTF("init controller\n");
fdctrl->fifo = qemu_memalign(512, FD_SECTOR_LEN);
memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
fdctrl->fifo_size = 512;
fdctrl->result_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
fdctrl_result_timer, fdctrl);
Expand Down
67 changes: 50 additions & 17 deletions python/qemu/__init__.py
Expand Up @@ -402,42 +402,75 @@ def get_qmp_events(self, wait=False):
self._qmp.clear_events()
return events

def event_wait(self, name, timeout=60.0, match=None):
@staticmethod
def event_match(event, match=None):
"""
Wait for specified timeout on named event in QMP; optionally filter
results by match.
Check if an event matches optional match criteria.
The match criteria takes the form of a matching subdict. The event is
checked to be a superset of the subdict, recursively, with matching
values whenever the subdict values are not None.
The 'match' is checked to be a recursive subset of the 'event'; skips
branch processing on match's value None
{"foo": {"bar": 1}} matches {"foo": None}
{"foo": {"bar": 1}} does not matches {"foo": {"baz": None}}
This has a limitation that you cannot explicitly check for None values.
Examples, with the subdict queries on the left:
- None matches any object.
- {"foo": None} matches {"foo": {"bar": 1}}
- {"foo": None} matches {"foo": 5}
- {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
- {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
"""
def event_match(event, match=None):
if match is None:
return True
if match is None:
return True

try:
for key in match:
if key in event:
if isinstance(event[key], dict):
if not event_match(event[key], match[key]):
return False
elif event[key] != match[key]:
if not QEMUMachine.event_match(event[key], match[key]):
return False
else:
return False

return True
except TypeError:
# either match or event wasn't iterable (not a dict)
return match == event

def event_wait(self, name, timeout=60.0, match=None):
"""
event_wait waits for and returns a named event from QMP with a timeout.
name: The event to wait for.
timeout: QEMUMonitorProtocol.pull_event timeout parameter.
match: Optional match criteria. See event_match for details.
"""
return self.events_wait([(name, match)], timeout)

def events_wait(self, events, timeout=60.0):
"""
events_wait waits for and returns a named event from QMP with a timeout.
events: a sequence of (name, match_criteria) tuples.
The match criteria are optional and may be None.
See event_match for details.
timeout: QEMUMonitorProtocol.pull_event timeout parameter.
"""
def _match(event):
for name, match in events:
if (event['event'] == name and
self.event_match(event, match)):
return True
return False

# Search cached events
for event in self._events:
if (event['event'] == name) and event_match(event, match):
if _match(event):
self._events.remove(event)
return event

# Poll for new events
while True:
event = self._qmp.pull_event(wait=timeout)
if (event['event'] == name) and event_match(event, match):
if _match(event):
return event
self._events.append(event)

Expand Down

0 comments on commit f3d0bec

Please sign in to comment.