Pull request

v2:
  * Fixed incorrect virtio_blk_data_plane_create() local_err refactoring in
    "hw/block: Use errp directly rather than local_err" that broke virtio-blk
    over virtio-mmio [Peter]
 -----BEGIN PGP SIGNATURE-----
 
 iQEcBAABAgAGBQJaOSteAAoJEJykq7OBq3PIllkH/RkxTY6JIe9K8PRVsaAX2fRN
 edO/3E09KTQe9eHEixoMKOIyeKi3RPdipcktXIbdLIDEY4z4vELmQslTrxK/q+8J
 pccdwu+7tEXr14ciYSnq0m6ksvU5JHlJGyAJEvbCmLHE3dPJszABwT1XLLCb1C8s
 hSOr3nR/O2U3LHlq/FuvEUK8fohgKlECtE94V/DUWyC774iMw+9OdvTA0VQWYnN6
 B0gpYSn4AXmdt5HmpgCa+5rZrT2DjdwhtR9X+iOItPoXJPP81toUxvshLbTgdL54
 fSodd12Tbn2Pxr/osD1kwzM9z6oYX8Ay8YZTabODiFo20fhZKZ2wLpL4rrsNnBk=
 =Qcx2
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/stefanha/tags/block-pull-request' into staging

Pull request

v2:
 * Fixed incorrect virtio_blk_data_plane_create() local_err refactoring in
   "hw/block: Use errp directly rather than local_err" that broke virtio-blk
   over virtio-mmio [Peter]

# gpg: Signature made Tue 19 Dec 2017 15:08:14 GMT
# gpg:                using RSA key 0x9CA4ABB381AB73C8
# gpg: Good signature from "Stefan Hajnoczi <stefanha@redhat.com>"
# gpg:                 aka "Stefan Hajnoczi <stefanha@gmail.com>"
# Primary key fingerprint: 8695 A8BF D3F9 7CDA AC35  775A 9CA4 ABB3 81AB 73C8

* remotes/stefanha/tags/block-pull-request: (23 commits)
  qemu-iotests: add 203 savevm with IOThreads test
  iothread: fix iothread_stop() race condition
  iotests: add VM.add_object()
  blockdev: add x-blockdev-set-iothread force boolean
  docs: mark nested AioContext locking as a legacy API
  block: avoid recursive AioContext acquire in bdrv_inactivate_all()
  virtio-blk: reject configs with logical block size > physical block size
  virtio-blk: make queue size configurable
  qemu-iotests: add 202 external snapshots IOThread test
  blockdev: add x-blockdev-set-iothread testing command
  iothread: add iothread_by_id() API
  block: drop unused BlockDirtyBitmapState->aio_context field
  block: don't keep AioContext acquired after internal_snapshot_prepare()
  block: don't keep AioContext acquired after blockdev_backup_prepare()
  block: don't keep AioContext acquired after drive_backup_prepare()
  block: don't keep AioContext acquired after external_snapshot_prepare()
  blockdev: hold AioContext for bdrv_unref() in external_snapshot_clean()
  qdev: drop unused #include "sysemu/iothread.h"
  dev-storage: Fix the unusual function name
  hw/block: Use errp directly rather than local_err
  ...

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

# Conflicts:
#	hw/core/qdev-properties-system.c
This commit is contained in:
Peter Maydell 2017-12-20 11:30:55 +00:00
commit f1faf2d59c
28 changed files with 532 additions and 172 deletions

14
block.c
View file

@ -4320,9 +4320,15 @@ int bdrv_inactivate_all(void)
BdrvNextIterator it; BdrvNextIterator it;
int ret = 0; int ret = 0;
int pass; int pass;
GSList *aio_ctxs = NULL, *ctx;
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
aio_context_acquire(bdrv_get_aio_context(bs)); AioContext *aio_context = bdrv_get_aio_context(bs);
if (!g_slist_find(aio_ctxs, aio_context)) {
aio_ctxs = g_slist_prepend(aio_ctxs, aio_context);
aio_context_acquire(aio_context);
}
} }
/* We do two passes of inactivation. The first pass calls to drivers' /* We do two passes of inactivation. The first pass calls to drivers'
@ -4340,9 +4346,11 @@ int bdrv_inactivate_all(void)
} }
out: out:
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { for (ctx = aio_ctxs; ctx != NULL; ctx = ctx->next) {
aio_context_release(bdrv_get_aio_context(bs)); AioContext *aio_context = ctx->data;
aio_context_release(aio_context);
} }
g_slist_free(aio_ctxs);
return ret; return ret;
} }

View file

@ -110,8 +110,7 @@ static coroutine_fn int null_co_common(BlockDriverState *bs)
BDRVNullState *s = bs->opaque; BDRVNullState *s = bs->opaque;
if (s->latency_ns) { if (s->latency_ns) {
co_aio_sleep_ns(bdrv_get_aio_context(bs), QEMU_CLOCK_REALTIME, qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, s->latency_ns);
s->latency_ns);
} }
return 0; return 0;
} }

View file

@ -776,8 +776,7 @@ static coroutine_fn void reconnect_to_sdog(void *opaque)
if (s->fd < 0) { if (s->fd < 0) {
DPRINTF("Wait for connection to be established\n"); DPRINTF("Wait for connection to be established\n");
error_report_err(local_err); error_report_err(local_err);
co_aio_sleep_ns(bdrv_get_aio_context(s->bs), QEMU_CLOCK_REALTIME, qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 1000000000ULL);
1000000000ULL);
} }
}; };

View file

@ -45,6 +45,7 @@
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
#include "qapi/qobject-output-visitor.h" #include "qapi/qobject-output-visitor.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "sysemu/iothread.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "qmp-commands.h" #include "qmp-commands.h"
#include "block/trace.h" #include "block/trace.h"
@ -1454,7 +1455,6 @@ struct BlkActionState {
typedef struct InternalSnapshotState { typedef struct InternalSnapshotState {
BlkActionState common; BlkActionState common;
BlockDriverState *bs; BlockDriverState *bs;
AioContext *aio_context;
QEMUSnapshotInfo sn; QEMUSnapshotInfo sn;
bool created; bool created;
} InternalSnapshotState; } InternalSnapshotState;
@ -1485,6 +1485,7 @@ static void internal_snapshot_prepare(BlkActionState *common,
qemu_timeval tv; qemu_timeval tv;
BlockdevSnapshotInternal *internal; BlockdevSnapshotInternal *internal;
InternalSnapshotState *state; InternalSnapshotState *state;
AioContext *aio_context;
int ret1; int ret1;
g_assert(common->action->type == g_assert(common->action->type ==
@ -1506,32 +1507,33 @@ static void internal_snapshot_prepare(BlkActionState *common,
return; return;
} }
/* AioContext is released in .clean() */ aio_context = bdrv_get_aio_context(bs);
state->aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context);
aio_context_acquire(state->aio_context);
state->bs = bs; state->bs = bs;
/* Paired with .clean() */
bdrv_drained_begin(bs); bdrv_drained_begin(bs);
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, errp)) { if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, errp)) {
return; goto out;
} }
if (bdrv_is_read_only(bs)) { if (bdrv_is_read_only(bs)) {
error_setg(errp, "Device '%s' is read only", device); error_setg(errp, "Device '%s' is read only", device);
return; goto out;
} }
if (!bdrv_can_snapshot(bs)) { if (!bdrv_can_snapshot(bs)) {
error_setg(errp, "Block format '%s' used by device '%s' " error_setg(errp, "Block format '%s' used by device '%s' "
"does not support internal snapshots", "does not support internal snapshots",
bs->drv->format_name, device); bs->drv->format_name, device);
return; goto out;
} }
if (!strlen(name)) { if (!strlen(name)) {
error_setg(errp, "Name is empty"); error_setg(errp, "Name is empty");
return; goto out;
} }
/* check whether a snapshot with name exist */ /* check whether a snapshot with name exist */
@ -1539,12 +1541,12 @@ static void internal_snapshot_prepare(BlkActionState *common,
&local_err); &local_err);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
return; goto out;
} else if (ret) { } else if (ret) {
error_setg(errp, error_setg(errp,
"Snapshot with name '%s' already exists on device '%s'", "Snapshot with name '%s' already exists on device '%s'",
name, device); name, device);
return; goto out;
} }
/* 3. take the snapshot */ /* 3. take the snapshot */
@ -1560,11 +1562,14 @@ static void internal_snapshot_prepare(BlkActionState *common,
error_setg_errno(errp, -ret1, error_setg_errno(errp, -ret1,
"Failed to create snapshot '%s' on device '%s'", "Failed to create snapshot '%s' on device '%s'",
name, device); name, device);
return; goto out;
} }
/* 4. succeed, mark a snapshot is created */ /* 4. succeed, mark a snapshot is created */
state->created = true; state->created = true;
out:
aio_context_release(aio_context);
} }
static void internal_snapshot_abort(BlkActionState *common) static void internal_snapshot_abort(BlkActionState *common)
@ -1573,12 +1578,16 @@ static void internal_snapshot_abort(BlkActionState *common)
DO_UPCAST(InternalSnapshotState, common, common); DO_UPCAST(InternalSnapshotState, common, common);
BlockDriverState *bs = state->bs; BlockDriverState *bs = state->bs;
QEMUSnapshotInfo *sn = &state->sn; QEMUSnapshotInfo *sn = &state->sn;
AioContext *aio_context;
Error *local_error = NULL; Error *local_error = NULL;
if (!state->created) { if (!state->created) {
return; return;
} }
aio_context = bdrv_get_aio_context(state->bs);
aio_context_acquire(aio_context);
if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) { if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) {
error_reportf_err(local_error, error_reportf_err(local_error,
"Failed to delete snapshot with id '%s' and " "Failed to delete snapshot with id '%s' and "
@ -1586,19 +1595,26 @@ static void internal_snapshot_abort(BlkActionState *common)
sn->id_str, sn->name, sn->id_str, sn->name,
bdrv_get_device_name(bs)); bdrv_get_device_name(bs));
} }
aio_context_release(aio_context);
} }
static void internal_snapshot_clean(BlkActionState *common) static void internal_snapshot_clean(BlkActionState *common)
{ {
InternalSnapshotState *state = DO_UPCAST(InternalSnapshotState, InternalSnapshotState *state = DO_UPCAST(InternalSnapshotState,
common, common); common, common);
AioContext *aio_context;
if (state->aio_context) { if (!state->bs) {
if (state->bs) { return;
bdrv_drained_end(state->bs);
}
aio_context_release(state->aio_context);
} }
aio_context = bdrv_get_aio_context(state->bs);
aio_context_acquire(aio_context);
bdrv_drained_end(state->bs);
aio_context_release(aio_context);
} }
/* external snapshot private data */ /* external snapshot private data */
@ -1606,7 +1622,6 @@ typedef struct ExternalSnapshotState {
BlkActionState common; BlkActionState common;
BlockDriverState *old_bs; BlockDriverState *old_bs;
BlockDriverState *new_bs; BlockDriverState *new_bs;
AioContext *aio_context;
bool overlay_appended; bool overlay_appended;
} ExternalSnapshotState; } ExternalSnapshotState;
@ -1626,6 +1641,7 @@ static void external_snapshot_prepare(BlkActionState *common,
ExternalSnapshotState *state = ExternalSnapshotState *state =
DO_UPCAST(ExternalSnapshotState, common, common); DO_UPCAST(ExternalSnapshotState, common, common);
TransactionAction *action = common->action; TransactionAction *action = common->action;
AioContext *aio_context;
/* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar /* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar
* purpose but a different set of parameters */ * purpose but a different set of parameters */
@ -1662,31 +1678,32 @@ static void external_snapshot_prepare(BlkActionState *common,
return; return;
} }
/* Acquire AioContext now so any threads operating on old_bs stop */ aio_context = bdrv_get_aio_context(state->old_bs);
state->aio_context = bdrv_get_aio_context(state->old_bs); aio_context_acquire(aio_context);
aio_context_acquire(state->aio_context);
/* Paired with .clean() */
bdrv_drained_begin(state->old_bs); bdrv_drained_begin(state->old_bs);
if (!bdrv_is_inserted(state->old_bs)) { if (!bdrv_is_inserted(state->old_bs)) {
error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
return; goto out;
} }
if (bdrv_op_is_blocked(state->old_bs, if (bdrv_op_is_blocked(state->old_bs,
BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, errp)) { BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, errp)) {
return; goto out;
} }
if (!bdrv_is_read_only(state->old_bs)) { if (!bdrv_is_read_only(state->old_bs)) {
if (bdrv_flush(state->old_bs)) { if (bdrv_flush(state->old_bs)) {
error_setg(errp, QERR_IO_ERROR); error_setg(errp, QERR_IO_ERROR);
return; goto out;
} }
} }
if (!bdrv_is_first_non_filter(state->old_bs)) { if (!bdrv_is_first_non_filter(state->old_bs)) {
error_setg(errp, QERR_FEATURE_DISABLED, "snapshot"); error_setg(errp, QERR_FEATURE_DISABLED, "snapshot");
return; goto out;
} }
if (action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC) { if (action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC) {
@ -1698,13 +1715,13 @@ static void external_snapshot_prepare(BlkActionState *common,
if (node_name && !snapshot_node_name) { if (node_name && !snapshot_node_name) {
error_setg(errp, "New snapshot node name missing"); error_setg(errp, "New snapshot node name missing");
return; goto out;
} }
if (snapshot_node_name && if (snapshot_node_name &&
bdrv_lookup_bs(snapshot_node_name, snapshot_node_name, NULL)) { bdrv_lookup_bs(snapshot_node_name, snapshot_node_name, NULL)) {
error_setg(errp, "New snapshot node name already in use"); error_setg(errp, "New snapshot node name already in use");
return; goto out;
} }
flags = state->old_bs->open_flags; flags = state->old_bs->open_flags;
@ -1717,7 +1734,7 @@ static void external_snapshot_prepare(BlkActionState *common,
int64_t size = bdrv_getlength(state->old_bs); int64_t size = bdrv_getlength(state->old_bs);
if (size < 0) { if (size < 0) {
error_setg_errno(errp, -size, "bdrv_getlength failed"); error_setg_errno(errp, -size, "bdrv_getlength failed");
return; goto out;
} }
bdrv_img_create(new_image_file, format, bdrv_img_create(new_image_file, format,
state->old_bs->filename, state->old_bs->filename,
@ -1725,7 +1742,7 @@ static void external_snapshot_prepare(BlkActionState *common,
NULL, size, flags, false, &local_err); NULL, size, flags, false, &local_err);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
return; goto out;
} }
} }
@ -1740,30 +1757,30 @@ static void external_snapshot_prepare(BlkActionState *common,
errp); errp);
/* We will manually add the backing_hd field to the bs later */ /* We will manually add the backing_hd field to the bs later */
if (!state->new_bs) { if (!state->new_bs) {
return; goto out;
} }
if (bdrv_has_blk(state->new_bs)) { if (bdrv_has_blk(state->new_bs)) {
error_setg(errp, "The snapshot is already in use"); error_setg(errp, "The snapshot is already in use");
return; goto out;
} }
if (bdrv_op_is_blocked(state->new_bs, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, if (bdrv_op_is_blocked(state->new_bs, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT,
errp)) { errp)) {
return; goto out;
} }
if (state->new_bs->backing != NULL) { if (state->new_bs->backing != NULL) {
error_setg(errp, "The snapshot already has a backing image"); error_setg(errp, "The snapshot already has a backing image");
return; goto out;
} }
if (!state->new_bs->drv->supports_backing) { if (!state->new_bs->drv->supports_backing) {
error_setg(errp, "The snapshot does not support backing images"); error_setg(errp, "The snapshot does not support backing images");
return; goto out;
} }
bdrv_set_aio_context(state->new_bs, state->aio_context); bdrv_set_aio_context(state->new_bs, aio_context);
/* This removes our old bs and adds the new bs. This is an operation that /* This removes our old bs and adds the new bs. This is an operation that
* can fail, so we need to do it in .prepare; undoing it for abort is * can fail, so we need to do it in .prepare; undoing it for abort is
@ -1772,15 +1789,22 @@ static void external_snapshot_prepare(BlkActionState *common,
bdrv_append(state->new_bs, state->old_bs, &local_err); bdrv_append(state->new_bs, state->old_bs, &local_err);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
return; goto out;
} }
state->overlay_appended = true; state->overlay_appended = true;
out:
aio_context_release(aio_context);
} }
static void external_snapshot_commit(BlkActionState *common) static void external_snapshot_commit(BlkActionState *common)
{ {
ExternalSnapshotState *state = ExternalSnapshotState *state =
DO_UPCAST(ExternalSnapshotState, common, common); DO_UPCAST(ExternalSnapshotState, common, common);
AioContext *aio_context;
aio_context = bdrv_get_aio_context(state->old_bs);
aio_context_acquire(aio_context);
/* We don't need (or want) to use the transactional /* We don't need (or want) to use the transactional
* bdrv_reopen_multiple() across all the entries at once, because we * bdrv_reopen_multiple() across all the entries at once, because we
@ -1789,6 +1813,8 @@ static void external_snapshot_commit(BlkActionState *common)
bdrv_reopen(state->old_bs, state->old_bs->open_flags & ~BDRV_O_RDWR, bdrv_reopen(state->old_bs, state->old_bs->open_flags & ~BDRV_O_RDWR,
NULL); NULL);
} }
aio_context_release(aio_context);
} }
static void external_snapshot_abort(BlkActionState *common) static void external_snapshot_abort(BlkActionState *common)
@ -1797,11 +1823,18 @@ static void external_snapshot_abort(BlkActionState *common)
DO_UPCAST(ExternalSnapshotState, common, common); DO_UPCAST(ExternalSnapshotState, common, common);
if (state->new_bs) { if (state->new_bs) {
if (state->overlay_appended) { if (state->overlay_appended) {
AioContext *aio_context;
aio_context = bdrv_get_aio_context(state->old_bs);
aio_context_acquire(aio_context);
bdrv_ref(state->old_bs); /* we can't let bdrv_set_backind_hd() bdrv_ref(state->old_bs); /* we can't let bdrv_set_backind_hd()
close state->old_bs; we need it */ close state->old_bs; we need it */
bdrv_set_backing_hd(state->new_bs, NULL, &error_abort); bdrv_set_backing_hd(state->new_bs, NULL, &error_abort);
bdrv_replace_node(state->new_bs, state->old_bs, &error_abort); bdrv_replace_node(state->new_bs, state->old_bs, &error_abort);
bdrv_unref(state->old_bs); /* bdrv_replace_node() ref'ed old_bs */ bdrv_unref(state->old_bs); /* bdrv_replace_node() ref'ed old_bs */
aio_context_release(aio_context);
} }
} }
} }
@ -1810,17 +1843,24 @@ static void external_snapshot_clean(BlkActionState *common)
{ {
ExternalSnapshotState *state = ExternalSnapshotState *state =
DO_UPCAST(ExternalSnapshotState, common, common); DO_UPCAST(ExternalSnapshotState, common, common);
if (state->aio_context) { AioContext *aio_context;
bdrv_drained_end(state->old_bs);
aio_context_release(state->aio_context); if (!state->old_bs) {
bdrv_unref(state->new_bs); return;
} }
aio_context = bdrv_get_aio_context(state->old_bs);
aio_context_acquire(aio_context);
bdrv_drained_end(state->old_bs);
bdrv_unref(state->new_bs);
aio_context_release(aio_context);
} }
typedef struct DriveBackupState { typedef struct DriveBackupState {
BlkActionState common; BlkActionState common;
BlockDriverState *bs; BlockDriverState *bs;
AioContext *aio_context;
BlockJob *job; BlockJob *job;
} DriveBackupState; } DriveBackupState;
@ -1832,6 +1872,7 @@ static void drive_backup_prepare(BlkActionState *common, Error **errp)
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common); DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
BlockDriverState *bs; BlockDriverState *bs;
DriveBackup *backup; DriveBackup *backup;
AioContext *aio_context;
Error *local_err = NULL; Error *local_err = NULL;
assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP); assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP);
@ -1842,24 +1883,36 @@ static void drive_backup_prepare(BlkActionState *common, Error **errp)
return; return;
} }
/* AioContext is released in .clean() */ aio_context = bdrv_get_aio_context(bs);
state->aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context);
aio_context_acquire(state->aio_context);
/* Paired with .clean() */
bdrv_drained_begin(bs); bdrv_drained_begin(bs);
state->bs = bs; state->bs = bs;
state->job = do_drive_backup(backup, common->block_job_txn, &local_err); state->job = do_drive_backup(backup, common->block_job_txn, &local_err);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
return; goto out;
} }
out:
aio_context_release(aio_context);
} }
static void drive_backup_commit(BlkActionState *common) static void drive_backup_commit(BlkActionState *common)
{ {
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common); DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
AioContext *aio_context;
aio_context = bdrv_get_aio_context(state->bs);
aio_context_acquire(aio_context);
assert(state->job); assert(state->job);
block_job_start(state->job); block_job_start(state->job);
aio_context_release(aio_context);
} }
static void drive_backup_abort(BlkActionState *common) static void drive_backup_abort(BlkActionState *common)
@ -1867,25 +1920,38 @@ static void drive_backup_abort(BlkActionState *common)
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common); DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
if (state->job) { if (state->job) {
AioContext *aio_context;
aio_context = bdrv_get_aio_context(state->bs);
aio_context_acquire(aio_context);
block_job_cancel_sync(state->job); block_job_cancel_sync(state->job);
aio_context_release(aio_context);
} }
} }
static void drive_backup_clean(BlkActionState *common) static void drive_backup_clean(BlkActionState *common)
{ {
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common); DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
AioContext *aio_context;
if (state->aio_context) { if (!state->bs) {
bdrv_drained_end(state->bs); return;
aio_context_release(state->aio_context);
} }
aio_context = bdrv_get_aio_context(state->bs);
aio_context_acquire(aio_context);
bdrv_drained_end(state->bs);
aio_context_release(aio_context);
} }
typedef struct BlockdevBackupState { typedef struct BlockdevBackupState {
BlkActionState common; BlkActionState common;
BlockDriverState *bs; BlockDriverState *bs;
BlockJob *job; BlockJob *job;
AioContext *aio_context;
} BlockdevBackupState; } BlockdevBackupState;
static BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, static BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
@ -1896,6 +1962,7 @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
BlockdevBackup *backup; BlockdevBackup *backup;
BlockDriverState *bs, *target; BlockDriverState *bs, *target;
AioContext *aio_context;
Error *local_err = NULL; Error *local_err = NULL;
assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP); assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP);
@ -1911,29 +1978,39 @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
return; return;
} }
/* AioContext is released in .clean() */ aio_context = bdrv_get_aio_context(bs);
state->aio_context = bdrv_get_aio_context(bs); if (aio_context != bdrv_get_aio_context(target)) {
if (state->aio_context != bdrv_get_aio_context(target)) {
state->aio_context = NULL;
error_setg(errp, "Backup between two IO threads is not implemented"); error_setg(errp, "Backup between two IO threads is not implemented");
return; return;
} }
aio_context_acquire(state->aio_context); aio_context_acquire(aio_context);
state->bs = bs; state->bs = bs;
/* Paired with .clean() */
bdrv_drained_begin(state->bs); bdrv_drained_begin(state->bs);
state->job = do_blockdev_backup(backup, common->block_job_txn, &local_err); state->job = do_blockdev_backup(backup, common->block_job_txn, &local_err);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
return; goto out;
} }
out:
aio_context_release(aio_context);
} }
static void blockdev_backup_commit(BlkActionState *common) static void blockdev_backup_commit(BlkActionState *common)
{ {
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
AioContext *aio_context;
aio_context = bdrv_get_aio_context(state->bs);
aio_context_acquire(aio_context);
assert(state->job); assert(state->job);
block_job_start(state->job); block_job_start(state->job);
aio_context_release(aio_context);
} }
static void blockdev_backup_abort(BlkActionState *common) static void blockdev_backup_abort(BlkActionState *common)
@ -1941,25 +2018,38 @@ static void blockdev_backup_abort(BlkActionState *common)
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
if (state->job) { if (state->job) {
AioContext *aio_context;
aio_context = bdrv_get_aio_context(state->bs);
aio_context_acquire(aio_context);
block_job_cancel_sync(state->job); block_job_cancel_sync(state->job);
aio_context_release(aio_context);
} }
} }
static void blockdev_backup_clean(BlkActionState *common) static void blockdev_backup_clean(BlkActionState *common)
{ {
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
AioContext *aio_context;
if (state->aio_context) { if (!state->bs) {
bdrv_drained_end(state->bs); return;
aio_context_release(state->aio_context);
} }
aio_context = bdrv_get_aio_context(state->bs);
aio_context_acquire(aio_context);
bdrv_drained_end(state->bs);
aio_context_release(aio_context);
} }
typedef struct BlockDirtyBitmapState { typedef struct BlockDirtyBitmapState {
BlkActionState common; BlkActionState common;
BdrvDirtyBitmap *bitmap; BdrvDirtyBitmap *bitmap;
BlockDriverState *bs; BlockDriverState *bs;
AioContext *aio_context;
HBitmap *backup; HBitmap *backup;
bool prepared; bool prepared;
} BlockDirtyBitmapState; } BlockDirtyBitmapState;
@ -2038,7 +2128,6 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common,
} }
bdrv_clear_dirty_bitmap(state->bitmap, &state->backup); bdrv_clear_dirty_bitmap(state->bitmap, &state->backup);
/* AioContext is released in .clean() */
} }
static void block_dirty_bitmap_clear_abort(BlkActionState *common) static void block_dirty_bitmap_clear_abort(BlkActionState *common)
@ -2059,16 +2148,6 @@ static void block_dirty_bitmap_clear_commit(BlkActionState *common)
hbitmap_free(state->backup); hbitmap_free(state->backup);
} }
static void block_dirty_bitmap_clear_clean(BlkActionState *common)
{
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
common, common);
if (state->aio_context) {
aio_context_release(state->aio_context);
}
}
static void abort_prepare(BlkActionState *common, Error **errp) static void abort_prepare(BlkActionState *common, Error **errp)
{ {
error_setg(errp, "Transaction aborted using Abort action"); error_setg(errp, "Transaction aborted using Abort action");
@ -2129,7 +2208,6 @@ static const BlkActionOps actions[] = {
.prepare = block_dirty_bitmap_clear_prepare, .prepare = block_dirty_bitmap_clear_prepare,
.commit = block_dirty_bitmap_clear_commit, .commit = block_dirty_bitmap_clear_commit,
.abort = block_dirty_bitmap_clear_abort, .abort = block_dirty_bitmap_clear_abort,
.clean = block_dirty_bitmap_clear_clean,
} }
}; };
@ -4052,6 +4130,47 @@ BlockJobInfoList *qmp_query_block_jobs(Error **errp)
return head; return head;
} }
void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
bool has_force, bool force, Error **errp)
{
AioContext *old_context;
AioContext *new_context;
BlockDriverState *bs;
bs = bdrv_find_node(node_name);
if (!bs) {
error_setg(errp, "Cannot find node %s", node_name);
return;
}
/* Protects against accidents. */
if (!(has_force && force) && bdrv_has_blk(bs)) {
error_setg(errp, "Node %s is associated with a BlockBackend and could "
"be in use (use force=true to override this check)",
node_name);
return;
}
if (iothread->type == QTYPE_QSTRING) {
IOThread *obj = iothread_by_id(iothread->u.s);
if (!obj) {
error_setg(errp, "Cannot find iothread %s", iothread->u.s);
return;
}
new_context = iothread_get_aio_context(obj);
} else {
new_context = qemu_get_aio_context();
}
old_context = bdrv_get_aio_context(bs);
aio_context_acquire(old_context);
bdrv_set_aio_context(bs, new_context);
aio_context_release(old_context);
}
QemuOptsList qemu_common_drive_opts = { QemuOptsList qemu_common_drive_opts = {
.name = "drive", .name = "drive",
.head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head), .head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head),

View file

@ -1,4 +1,4 @@
Copyright (c) 2014 Red Hat Inc. Copyright (c) 2014-2017 Red Hat Inc.
This work is licensed under the terms of the GNU GPL, version 2 or later. See This work is licensed under the terms of the GNU GPL, version 2 or later. See
the COPYING file in the top-level directory. the COPYING file in the top-level directory.
@ -92,8 +92,9 @@ aio_context_acquire()/aio_context_release() for mutual exclusion. Once the
context is acquired no other thread can access it or run event loop iterations context is acquired no other thread can access it or run event loop iterations
in this AioContext. in this AioContext.
aio_context_acquire()/aio_context_release() calls may be nested. This Legacy code sometimes nests aio_context_acquire()/aio_context_release() calls.
means you can call them if you're not sure whether #2 applies. Do not use nesting anymore, it is incompatible with the BDRV_POLL_WHILE() macro
used in the block layer and can lead to hangs.
There is currently no lock ordering rule if a thread needs to acquire multiple There is currently no lock ordering rule if a thread needs to acquire multiple
AioContexts simultaneously. Therefore, it is only safe for code holding the AioContexts simultaneously. Therefore, it is only safe for code holding the

View file

@ -51,7 +51,7 @@ void blkconf_blocksizes(BlockConf *conf)
} }
} }
void blkconf_apply_backend_options(BlockConf *conf, bool readonly, bool blkconf_apply_backend_options(BlockConf *conf, bool readonly,
bool resizable, Error **errp) bool resizable, Error **errp)
{ {
BlockBackend *blk = conf->blk; BlockBackend *blk = conf->blk;
@ -76,7 +76,7 @@ void blkconf_apply_backend_options(BlockConf *conf, bool readonly,
ret = blk_set_perm(blk, perm, shared_perm, errp); ret = blk_set_perm(blk, perm, shared_perm, errp);
if (ret < 0) { if (ret < 0) {
return; return false;
} }
switch (conf->wce) { switch (conf->wce) {
@ -99,9 +99,11 @@ void blkconf_apply_backend_options(BlockConf *conf, bool readonly,
blk_set_enable_write_cache(blk, wce); blk_set_enable_write_cache(blk, wce);
blk_set_on_error(blk, rerror, werror); blk_set_on_error(blk, rerror, werror);
return true;
} }
void blkconf_geometry(BlockConf *conf, int *ptrans, bool blkconf_geometry(BlockConf *conf, int *ptrans,
unsigned cyls_max, unsigned heads_max, unsigned secs_max, unsigned cyls_max, unsigned heads_max, unsigned secs_max,
Error **errp) Error **errp)
{ {
@ -129,15 +131,16 @@ void blkconf_geometry(BlockConf *conf, int *ptrans,
if (conf->cyls || conf->heads || conf->secs) { if (conf->cyls || conf->heads || conf->secs) {
if (conf->cyls < 1 || conf->cyls > cyls_max) { if (conf->cyls < 1 || conf->cyls > cyls_max) {
error_setg(errp, "cyls must be between 1 and %u", cyls_max); error_setg(errp, "cyls must be between 1 and %u", cyls_max);
return; return false;
} }
if (conf->heads < 1 || conf->heads > heads_max) { if (conf->heads < 1 || conf->heads > heads_max) {
error_setg(errp, "heads must be between 1 and %u", heads_max); error_setg(errp, "heads must be between 1 and %u", heads_max);
return; return false;
} }
if (conf->secs < 1 || conf->secs > secs_max) { if (conf->secs < 1 || conf->secs > secs_max) {
error_setg(errp, "secs must be between 1 and %u", secs_max); error_setg(errp, "secs must be between 1 and %u", secs_max);
return; return false;
} }
} }
return true;
} }

View file

@ -76,7 +76,7 @@ static void notify_guest_bh(void *opaque)
} }
/* Context: QEMU global mutex held */ /* Context: QEMU global mutex held */
void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, bool virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
VirtIOBlockDataPlane **dataplane, VirtIOBlockDataPlane **dataplane,
Error **errp) Error **errp)
{ {
@ -91,11 +91,11 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
error_setg(errp, error_setg(errp,
"device is incompatible with iothread " "device is incompatible with iothread "
"(transport does not support notifiers)"); "(transport does not support notifiers)");
return; return false;
} }
if (!virtio_device_ioeventfd_enabled(vdev)) { if (!virtio_device_ioeventfd_enabled(vdev)) {
error_setg(errp, "ioeventfd is required for iothread"); error_setg(errp, "ioeventfd is required for iothread");
return; return false;
} }
/* If dataplane is (re-)enabled while the guest is running there could /* If dataplane is (re-)enabled while the guest is running there could
@ -103,12 +103,12 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
*/ */
if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) {
error_prepend(errp, "cannot start virtio-blk dataplane: "); error_prepend(errp, "cannot start virtio-blk dataplane: ");
return; return false;
} }
} }
/* Don't try if transport does not support notifiers. */ /* Don't try if transport does not support notifiers. */
if (!virtio_device_ioeventfd_enabled(vdev)) { if (!virtio_device_ioeventfd_enabled(vdev)) {
return; return false;
} }
s = g_new0(VirtIOBlockDataPlane, 1); s = g_new0(VirtIOBlockDataPlane, 1);
@ -126,6 +126,8 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
s->batch_notify_vqs = bitmap_new(conf->num_queues); s->batch_notify_vqs = bitmap_new(conf->num_queues);
*dataplane = s; *dataplane = s;
return true;
} }
/* Context: QEMU global mutex held */ /* Context: QEMU global mutex held */

View file

@ -19,7 +19,7 @@
typedef struct VirtIOBlockDataPlane VirtIOBlockDataPlane; typedef struct VirtIOBlockDataPlane VirtIOBlockDataPlane;
void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, bool virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
VirtIOBlockDataPlane **dataplane, VirtIOBlockDataPlane **dataplane,
Error **errp); Error **errp);
void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s); void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s);

View file

@ -473,16 +473,13 @@ static void fd_revalidate(FDrive *drv)
static void fd_change_cb(void *opaque, bool load, Error **errp) static void fd_change_cb(void *opaque, bool load, Error **errp)
{ {
FDrive *drive = opaque; FDrive *drive = opaque;
Error *local_err = NULL;
if (!load) { if (!load) {
blk_set_perm(drive->blk, 0, BLK_PERM_ALL, &error_abort); blk_set_perm(drive->blk, 0, BLK_PERM_ALL, &error_abort);
} else { } else {
blkconf_apply_backend_options(drive->conf, if (!blkconf_apply_backend_options(drive->conf,
blk_is_read_only(drive->blk), false, blk_is_read_only(drive->blk), false,
&local_err); errp)) {
if (local_err) {
error_propagate(errp, local_err);
return; return;
} }
} }
@ -522,7 +519,6 @@ static void floppy_drive_realize(DeviceState *qdev, Error **errp)
FloppyDrive *dev = FLOPPY_DRIVE(qdev); FloppyDrive *dev = FLOPPY_DRIVE(qdev);
FloppyBus *bus = FLOPPY_BUS(qdev->parent_bus); FloppyBus *bus = FLOPPY_BUS(qdev->parent_bus);
FDrive *drive; FDrive *drive;
Error *local_err = NULL;
int ret; int ret;
if (dev->unit == -1) { if (dev->unit == -1) {
@ -568,10 +564,9 @@ static void floppy_drive_realize(DeviceState *qdev, Error **errp)
dev->conf.rerror = BLOCKDEV_ON_ERROR_AUTO; dev->conf.rerror = BLOCKDEV_ON_ERROR_AUTO;
dev->conf.werror = BLOCKDEV_ON_ERROR_AUTO; dev->conf.werror = BLOCKDEV_ON_ERROR_AUTO;
blkconf_apply_backend_options(&dev->conf, blk_is_read_only(dev->conf.blk), if (!blkconf_apply_backend_options(&dev->conf,
false, &local_err); blk_is_read_only(dev->conf.blk),
if (local_err) { false, errp)) {
error_propagate(errp, local_err);
return; return;
} }

View file

@ -920,7 +920,7 @@ static const MemoryRegionOps nvme_cmb_ops = {
}, },
}; };
static int nvme_init(PCIDevice *pci_dev) static void nvme_realize(PCIDevice *pci_dev, Error **errp)
{ {
NvmeCtrl *n = NVME(pci_dev); NvmeCtrl *n = NVME(pci_dev);
NvmeIdCtrl *id = &n->id_ctrl; NvmeIdCtrl *id = &n->id_ctrl;
@ -928,27 +928,27 @@ static int nvme_init(PCIDevice *pci_dev)
int i; int i;
int64_t bs_size; int64_t bs_size;
uint8_t *pci_conf; uint8_t *pci_conf;
Error *local_err = NULL;
if (!n->conf.blk) { if (!n->conf.blk) {
return -1; error_setg(errp, "drive property not set");
return;
} }
bs_size = blk_getlength(n->conf.blk); bs_size = blk_getlength(n->conf.blk);
if (bs_size < 0) { if (bs_size < 0) {
return -1; error_setg(errp, "could not get backing file size");
return;
} }
blkconf_serial(&n->conf, &n->serial); blkconf_serial(&n->conf, &n->serial);
if (!n->serial) { if (!n->serial) {
return -1; error_setg(errp, "serial property not set");
return;
} }
blkconf_blocksizes(&n->conf); blkconf_blocksizes(&n->conf);
blkconf_apply_backend_options(&n->conf, blk_is_read_only(n->conf.blk), if (!blkconf_apply_backend_options(&n->conf, blk_is_read_only(n->conf.blk),
false, &local_err); false, errp)) {
if (local_err) { return;
error_report_err(local_err);
return -1;
} }
pci_conf = pci_dev->config; pci_conf = pci_dev->config;
@ -1046,7 +1046,6 @@ static int nvme_init(PCIDevice *pci_dev)
cpu_to_le64(n->ns_size >> cpu_to_le64(n->ns_size >>
id_ns->lbaf[NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas)].ds); id_ns->lbaf[NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas)].ds);
} }
return 0;
} }
static void nvme_exit(PCIDevice *pci_dev) static void nvme_exit(PCIDevice *pci_dev)
@ -1081,7 +1080,7 @@ static void nvme_class_init(ObjectClass *oc, void *data)
DeviceClass *dc = DEVICE_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc);
PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
pc->init = nvme_init; pc->realize = nvme_realize;
pc->exit = nvme_exit; pc->exit = nvme_exit;
pc->class_id = PCI_CLASS_STORAGE_EXPRESS; pc->class_id = PCI_CLASS_STORAGE_EXPRESS;
pc->vendor_id = PCI_VENDOR_ID_INTEL; pc->vendor_id = PCI_VENDOR_ID_INTEL;

View file

@ -928,23 +928,34 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
error_setg(errp, "num-queues property must be larger than 0"); error_setg(errp, "num-queues property must be larger than 0");
return; return;
} }
if (!is_power_of_2(conf->queue_size) ||
conf->queue_size > VIRTQUEUE_MAX_SIZE) {
error_setg(errp, "invalid queue-size property (%" PRIu16 "), "
"must be a power of 2 (max %d)",
conf->queue_size, VIRTQUEUE_MAX_SIZE);
return;
}
blkconf_serial(&conf->conf, &conf->serial); blkconf_serial(&conf->conf, &conf->serial);
blkconf_apply_backend_options(&conf->conf, if (!blkconf_apply_backend_options(&conf->conf,
blk_is_read_only(conf->conf.blk), true, blk_is_read_only(conf->conf.blk), true,
&err); errp)) {
if (err) {
error_propagate(errp, err);
return; return;
} }
s->original_wce = blk_enable_write_cache(conf->conf.blk); s->original_wce = blk_enable_write_cache(conf->conf.blk);
blkconf_geometry(&conf->conf, NULL, 65535, 255, 255, &err); if (!blkconf_geometry(&conf->conf, NULL, 65535, 255, 255, errp)) {
if (err) {
error_propagate(errp, err);
return; return;
} }
blkconf_blocksizes(&conf->conf); blkconf_blocksizes(&conf->conf);
if (conf->conf.logical_block_size >
conf->conf.physical_block_size) {
error_setg(errp,
"logical_block_size > physical_block_size not supported");
return;
}
virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK, virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK,
sizeof(struct virtio_blk_config)); sizeof(struct virtio_blk_config));
@ -953,7 +964,7 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
s->sector_mask = (s->conf.conf.logical_block_size / BDRV_SECTOR_SIZE) - 1; s->sector_mask = (s->conf.conf.logical_block_size / BDRV_SECTOR_SIZE) - 1;
for (i = 0; i < conf->num_queues; i++) { for (i = 0; i < conf->num_queues; i++) {
virtio_add_queue(vdev, 128, virtio_blk_handle_output); virtio_add_queue(vdev, conf->queue_size, virtio_blk_handle_output);
} }
virtio_blk_data_plane_create(vdev, conf, &s->dataplane, &err); virtio_blk_data_plane_create(vdev, conf, &s->dataplane, &err);
if (err != NULL) { if (err != NULL) {
@ -1012,6 +1023,7 @@ static Property virtio_blk_properties[] = {
DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0, DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0,
true), true),
DEFINE_PROP_UINT16("num-queues", VirtIOBlock, conf.num_queues, 1), DEFINE_PROP_UINT16("num-queues", VirtIOBlock, conf.num_queues, 1),
DEFINE_PROP_UINT16("queue-size", VirtIOBlock, conf.queue_size, 128),
DEFINE_PROP_LINK("iothread", VirtIOBlock, conf.iothread, TYPE_IOTHREAD, DEFINE_PROP_LINK("iothread", VirtIOBlock, conf.iothread, TYPE_IOTHREAD,
IOThread *), IOThread *),
DEFINE_PROP_END_OF_LIST(), DEFINE_PROP_END_OF_LIST(),

View file

@ -22,6 +22,7 @@
#include "qapi/visitor.h" #include "qapi/visitor.h"
#include "chardev/char-fe.h" #include "chardev/char-fe.h"
#include "sysemu/iothread.h" #include "sysemu/iothread.h"
#include "sysemu/tpm_backend.h"
static void get_pointer(Object *obj, Visitor *v, Property *prop, static void get_pointer(Object *obj, Visitor *v, Property *prop,
char *(*print)(void *ptr), char *(*print)(void *ptr),

View file

@ -160,7 +160,6 @@ static void ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind, Error **errp)
{ {
IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus); IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus);
IDEState *s = bus->ifs + dev->unit; IDEState *s = bus->ifs + dev->unit;
Error *err = NULL;
int ret; int ret;
if (!dev->conf.blk) { if (!dev->conf.blk) {
@ -191,16 +190,13 @@ static void ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind, Error **errp)
blkconf_serial(&dev->conf, &dev->serial); blkconf_serial(&dev->conf, &dev->serial);
if (kind != IDE_CD) { if (kind != IDE_CD) {
blkconf_geometry(&dev->conf, &dev->chs_trans, 65535, 16, 255, &err); if (!blkconf_geometry(&dev->conf, &dev->chs_trans, 65535, 16, 255,
if (err) { errp)) {
error_propagate(errp, err);
return; return;
} }
} }
blkconf_apply_backend_options(&dev->conf, kind == IDE_CD, kind != IDE_CD, if (!blkconf_apply_backend_options(&dev->conf, kind == IDE_CD,
&err); kind != IDE_CD, errp)) {
if (err) {
error_propagate(errp, err);
return; return;
} }

View file

@ -2332,7 +2332,6 @@ static void scsi_disk_unit_attention_reported(SCSIDevice *dev)
static void scsi_realize(SCSIDevice *dev, Error **errp) static void scsi_realize(SCSIDevice *dev, Error **errp)
{ {
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
Error *err = NULL;
if (!s->qdev.conf.blk) { if (!s->qdev.conf.blk) {
error_setg(errp, "drive property not set"); error_setg(errp, "drive property not set");
@ -2356,17 +2355,13 @@ static void scsi_realize(SCSIDevice *dev, Error **errp)
} }
if (dev->type == TYPE_DISK) { if (dev->type == TYPE_DISK) {
blkconf_geometry(&dev->conf, NULL, 65535, 255, 255, &err); if (!blkconf_geometry(&dev->conf, NULL, 65535, 255, 255, errp)) {
if (err) {
error_propagate(errp, err);
return; return;
} }
} }
blkconf_apply_backend_options(&dev->conf, if (!blkconf_apply_backend_options(&dev->conf,
blk_is_read_only(s->qdev.conf.blk), blk_is_read_only(s->qdev.conf.blk),
dev->type == TYPE_DISK, &err); dev->type == TYPE_DISK, errp)) {
if (err) {
error_propagate(errp, err);
return; return;
} }

View file

@ -596,12 +596,11 @@ static void usb_msd_unrealize_storage(USBDevice *dev, Error **errp)
object_unref(OBJECT(&s->bus)); object_unref(OBJECT(&s->bus));
} }
static void usb_msd_realize_storage(USBDevice *dev, Error **errp) static void usb_msd_storage_realize(USBDevice *dev, Error **errp)
{ {
MSDState *s = USB_STORAGE_DEV(dev); MSDState *s = USB_STORAGE_DEV(dev);
BlockBackend *blk = s->conf.blk; BlockBackend *blk = s->conf.blk;
SCSIDevice *scsi_dev; SCSIDevice *scsi_dev;
Error *err = NULL;
if (!blk) { if (!blk) {
error_setg(errp, "drive property not set"); error_setg(errp, "drive property not set");
@ -610,9 +609,8 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp)
blkconf_serial(&s->conf, &dev->serial); blkconf_serial(&s->conf, &dev->serial);
blkconf_blocksizes(&s->conf); blkconf_blocksizes(&s->conf);
blkconf_apply_backend_options(&s->conf, blk_is_read_only(blk), true, &err); if (!blkconf_apply_backend_options(&s->conf, blk_is_read_only(blk), true,
if (err) { errp)) {
error_propagate(errp, err);
return; return;
} }
@ -636,24 +634,23 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp)
&usb_msd_scsi_info_storage, NULL); &usb_msd_scsi_info_storage, NULL);
scsi_dev = scsi_bus_legacy_add_drive(&s->bus, blk, 0, !!s->removable, scsi_dev = scsi_bus_legacy_add_drive(&s->bus, blk, 0, !!s->removable,
s->conf.bootindex, dev->serial, s->conf.bootindex, dev->serial,
&err); errp);
blk_unref(blk); blk_unref(blk);
if (!scsi_dev) { if (!scsi_dev) {
error_propagate(errp, err);
return; return;
} }
usb_msd_handle_reset(dev); usb_msd_handle_reset(dev);
s->scsi_dev = scsi_dev; s->scsi_dev = scsi_dev;
} }
static void usb_msd_unrealize_bot(USBDevice *dev, Error **errp) static void usb_msd_bot_unrealize(USBDevice *dev, Error **errp)
{ {
MSDState *s = USB_STORAGE_DEV(dev); MSDState *s = USB_STORAGE_DEV(dev);
object_unref(OBJECT(&s->bus)); object_unref(OBJECT(&s->bus));
} }
static void usb_msd_realize_bot(USBDevice *dev, Error **errp) static void usb_msd_bot_realize(USBDevice *dev, Error **errp)
{ {
MSDState *s = USB_STORAGE_DEV(dev); MSDState *s = USB_STORAGE_DEV(dev);
DeviceState *d = DEVICE(dev); DeviceState *d = DEVICE(dev);
@ -767,12 +764,12 @@ static void usb_msd_class_initfn_common(ObjectClass *klass, void *data)
dc->vmsd = &vmstate_usb_msd; dc->vmsd = &vmstate_usb_msd;
} }
static void usb_msd_class_initfn_storage(ObjectClass *klass, void *data) static void usb_msd_class_storage_initfn(ObjectClass *klass, void *data)
{ {
DeviceClass *dc = DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass);
USBDeviceClass *uc = USB_DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
uc->realize = usb_msd_realize_storage; uc->realize = usb_msd_storage_realize;
uc->unrealize = usb_msd_unrealize_storage; uc->unrealize = usb_msd_unrealize_storage;
dc->props = msd_properties; dc->props = msd_properties;
} }
@ -831,26 +828,26 @@ static void usb_msd_instance_init(Object *obj)
object_property_set_int(obj, -1, "bootindex", NULL); object_property_set_int(obj, -1, "bootindex", NULL);
} }
static void usb_msd_class_initfn_bot(ObjectClass *klass, void *data) static void usb_msd_class_bot_initfn(ObjectClass *klass, void *data)
{ {
USBDeviceClass *uc = USB_DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
uc->realize = usb_msd_realize_bot; uc->realize = usb_msd_bot_realize;
uc->unrealize = usb_msd_unrealize_bot; uc->unrealize = usb_msd_bot_unrealize;
uc->attached_settable = true; uc->attached_settable = true;
} }
static const TypeInfo msd_info = { static const TypeInfo msd_info = {
.name = "usb-storage", .name = "usb-storage",
.parent = TYPE_USB_STORAGE, .parent = TYPE_USB_STORAGE,
.class_init = usb_msd_class_initfn_storage, .class_init = usb_msd_class_storage_initfn,
.instance_init = usb_msd_instance_init, .instance_init = usb_msd_instance_init,
}; };
static const TypeInfo bot_info = { static const TypeInfo bot_info = {
.name = "usb-bot", .name = "usb-bot",
.parent = TYPE_USB_STORAGE, .parent = TYPE_USB_STORAGE,
.class_init = usb_msd_class_initfn_bot, .class_init = usb_msd_class_bot_initfn,
}; };
static void usb_msd_register_types(void) static void usb_msd_register_types(void)

View file

@ -72,11 +72,11 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf)
/* Configuration helpers */ /* Configuration helpers */
void blkconf_serial(BlockConf *conf, char **serial); void blkconf_serial(BlockConf *conf, char **serial);
void blkconf_geometry(BlockConf *conf, int *trans, bool blkconf_geometry(BlockConf *conf, int *trans,
unsigned cyls_max, unsigned heads_max, unsigned secs_max, unsigned cyls_max, unsigned heads_max, unsigned secs_max,
Error **errp); Error **errp);
void blkconf_blocksizes(BlockConf *conf); void blkconf_blocksizes(BlockConf *conf);
void blkconf_apply_backend_options(BlockConf *conf, bool readonly, bool blkconf_apply_backend_options(BlockConf *conf, bool readonly,
bool resizable, Error **errp); bool resizable, Error **errp);
/* Hard disk geometry */ /* Hard disk geometry */

View file

@ -39,6 +39,7 @@ struct VirtIOBlkConf
uint32_t config_wce; uint32_t config_wce;
uint32_t request_merging; uint32_t request_merging;
uint16_t num_queues; uint16_t num_queues;
uint16_t queue_size;
}; };
struct VirtIOBlockDataPlane; struct VirtIOBlockDataPlane;

View file

@ -261,12 +261,8 @@ void qemu_co_rwlock_unlock(CoRwlock *lock);
/** /**
* Yield the coroutine for a given duration * Yield the coroutine for a given duration
*
* Behaves similarly to co_sleep_ns(), but the sleeping coroutine will be
* resumed when using aio_poll().
*/ */
void coroutine_fn co_aio_sleep_ns(AioContext *ctx, QEMUClockType type, void coroutine_fn qemu_co_sleep_ns(QEMUClockType type, int64_t ns);
int64_t ns);
/** /**
* Yield until a file descriptor becomes readable * Yield until a file descriptor becomes readable

View file

@ -29,7 +29,8 @@ typedef struct {
GOnce once; GOnce once;
QemuMutex init_done_lock; QemuMutex init_done_lock;
QemuCond init_done_cond; /* is thread initialization done? */ QemuCond init_done_cond; /* is thread initialization done? */
bool stopping; bool stopping; /* has iothread_stop() been called? */
bool running; /* should iothread_run() continue? */
int thread_id; int thread_id;
/* AioContext poll parameters */ /* AioContext poll parameters */
@ -42,6 +43,7 @@ typedef struct {
OBJECT_CHECK(IOThread, obj, TYPE_IOTHREAD) OBJECT_CHECK(IOThread, obj, TYPE_IOTHREAD)
char *iothread_get_id(IOThread *iothread); char *iothread_get_id(IOThread *iothread);
IOThread *iothread_by_id(const char *id);
AioContext *iothread_get_aio_context(IOThread *iothread); AioContext *iothread_get_aio_context(IOThread *iothread);
void iothread_stop_all(void); void iothread_stop_all(void);
GMainContext *iothread_get_g_main_context(IOThread *iothread); GMainContext *iothread_get_g_main_context(IOThread *iothread);

View file

@ -55,7 +55,7 @@ static void *iothread_run(void *opaque)
qemu_cond_signal(&iothread->init_done_cond); qemu_cond_signal(&iothread->init_done_cond);
qemu_mutex_unlock(&iothread->init_done_lock); qemu_mutex_unlock(&iothread->init_done_lock);
while (!atomic_read(&iothread->stopping)) { while (iothread->running) {
aio_poll(iothread->ctx, true); aio_poll(iothread->ctx, true);
if (atomic_read(&iothread->worker_context)) { if (atomic_read(&iothread->worker_context)) {
@ -78,16 +78,25 @@ static void *iothread_run(void *opaque)
return NULL; return NULL;
} }
/* Runs in iothread_run() thread */
static void iothread_stop_bh(void *opaque)
{
IOThread *iothread = opaque;
iothread->running = false; /* stop iothread_run() */
if (iothread->main_loop) {
g_main_loop_quit(iothread->main_loop);
}
}
void iothread_stop(IOThread *iothread) void iothread_stop(IOThread *iothread)
{ {
if (!iothread->ctx || iothread->stopping) { if (!iothread->ctx || iothread->stopping) {
return; return;
} }
iothread->stopping = true; iothread->stopping = true;
aio_notify(iothread->ctx); aio_bh_schedule_oneshot(iothread->ctx, iothread_stop_bh, iothread);
if (atomic_read(&iothread->main_loop)) {
g_main_loop_quit(iothread->main_loop);
}
qemu_thread_join(&iothread->thread); qemu_thread_join(&iothread->thread);
} }
@ -134,6 +143,7 @@ static void iothread_complete(UserCreatable *obj, Error **errp)
char *name, *thread_name; char *name, *thread_name;
iothread->stopping = false; iothread->stopping = false;
iothread->running = true;
iothread->thread_id = -1; iothread->thread_id = -1;
iothread->ctx = aio_context_new(&local_error); iothread->ctx = aio_context_new(&local_error);
if (!iothread->ctx) { if (!iothread->ctx) {
@ -380,3 +390,10 @@ void iothread_destroy(IOThread *iothread)
{ {
object_unparent(OBJECT(iothread)); object_unparent(OBJECT(iothread));
} }
/* Lookup IOThread by its id. Only finds user-created objects, not internal
* iothread_create() objects. */
IOThread *iothread_by_id(const char *id)
{
return IOTHREAD(object_resolve_path_type(id, TYPE_IOTHREAD, NULL));
}

View file

@ -3949,3 +3949,43 @@
'data' : { 'parent': 'str', 'data' : { 'parent': 'str',
'*child': 'str', '*child': 'str',
'*node': 'str' } } '*node': 'str' } }
##
# @x-blockdev-set-iothread:
#
# Move @node and its children into the @iothread. If @iothread is null then
# move @node and its children into the main loop.
#
# The node must not be attached to a BlockBackend.
#
# @node-name: the name of the block driver node
#
# @iothread: the name of the IOThread object or null for the main loop
#
# @force: true if the node and its children should be moved when a BlockBackend
# is already attached
#
# Note: this command is experimental and intended for test cases that need
# control over IOThreads only.
#
# Since: 2.12
#
# Example:
#
# 1. Move a node into an IOThread
# -> { "execute": "x-blockdev-set-iothread",
# "arguments": { "node-name": "disk1",
# "iothread": "iothread0" } }
# <- { "return": {} }
#
# 2. Move a node into the main loop
# -> { "execute": "x-blockdev-set-iothread",
# "arguments": { "node-name": "disk1",
# "iothread": null } }
# <- { "return": {} }
#
##
{ 'command': 'x-blockdev-set-iothread',
'data' : { 'node-name': 'str',
'iothread': 'StrOrNull',
'*force': 'bool' } }

95
tests/qemu-iotests/202 Executable file
View file

@ -0,0 +1,95 @@
#!/usr/bin/env python
#
# Copyright (C) 2017 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Creator/Owner: Stefan Hajnoczi <stefanha@redhat.com>
#
# Check that QMP 'transaction' blockdev-snapshot-sync with multiple drives on a
# single IOThread completes successfully. This particular command triggered a
# hang due to recursive AioContext locking and BDRV_POLL_WHILE(). Protect
# against regressions.
import iotests
iotests.verify_image_format(supported_fmts=['qcow2'])
iotests.verify_platform(['linux'])
with iotests.FilePath('disk0.img') as disk0_img_path, \
iotests.FilePath('disk1.img') as disk1_img_path, \
iotests.FilePath('disk0-snap.img') as disk0_snap_img_path, \
iotests.FilePath('disk1-snap.img') as disk1_snap_img_path, \
iotests.VM() as vm:
img_size = '10M'
iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, disk0_img_path, img_size)
iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, disk1_img_path, img_size)
iotests.log('Launching VM...')
vm.launch()
iotests.log('Adding IOThread...')
iotests.log(vm.qmp('object-add',
qom_type='iothread',
id='iothread0'))
iotests.log('Adding blockdevs...')
iotests.log(vm.qmp('blockdev-add',
driver=iotests.imgfmt,
node_name='disk0',
file={
'driver': 'file',
'filename': disk0_img_path,
}))
iotests.log(vm.qmp('blockdev-add',
driver=iotests.imgfmt,
node_name='disk1',
file={
'driver': 'file',
'filename': disk1_img_path,
}))
iotests.log('Setting iothread...')
iotests.log(vm.qmp('x-blockdev-set-iothread',
node_name='disk0',
iothread='iothread0'))
iotests.log(vm.qmp('x-blockdev-set-iothread',
node_name='disk1',
iothread='iothread0'))
iotests.log('Creating external snapshots...')
iotests.log(vm.qmp(
'transaction',
actions=[
{
'data': {
'node-name': 'disk0',
'snapshot-file': disk0_snap_img_path,
'snapshot-node-name': 'disk0-snap',
'mode': 'absolute-paths',
'format': iotests.imgfmt,
},
'type': 'blockdev-snapshot-sync'
}, {
'data': {
'node-name': 'disk1',
'snapshot-file': disk1_snap_img_path,
'snapshot-node-name': 'disk1-snap',
'mode': 'absolute-paths',
'format': iotests.imgfmt
},
'type': 'blockdev-snapshot-sync'
}
]))

View file

@ -0,0 +1,11 @@
Launching VM...
Adding IOThread...
{u'return': {}}
Adding blockdevs...
{u'return': {}}
{u'return': {}}
Setting iothread...
{u'return': {}}
{u'return': {}}
Creating external snapshots...
{u'return': {}}

59
tests/qemu-iotests/203 Executable file
View file

@ -0,0 +1,59 @@
#!/usr/bin/env python
#
# Copyright (C) 2017 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Creator/Owner: Stefan Hajnoczi <stefanha@redhat.com>
#
# Check that QMP 'migrate' with multiple drives on a single IOThread completes
# successfully. This particular command triggered a hang in the source QEMU
# process due to recursive AioContext locking in bdrv_invalidate_all() and
# BDRV_POLL_WHILE().
import iotests
iotests.verify_image_format(supported_fmts=['qcow2'])
iotests.verify_platform(['linux'])
with iotests.FilePath('disk0.img') as disk0_img_path, \
iotests.FilePath('disk1.img') as disk1_img_path, \
iotests.VM() as vm:
img_size = '10M'
iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, disk0_img_path, img_size)
iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, disk1_img_path, img_size)
iotests.log('Launching VM...')
(vm.add_object('iothread,id=iothread0')
.add_drive(disk0_img_path, 'node-name=drive0-node', interface='none')
.add_drive(disk1_img_path, 'node-name=drive1-node', interface='none')
.launch())
iotests.log('Setting IOThreads...')
iotests.log(vm.qmp('x-blockdev-set-iothread',
node_name='drive0-node', iothread='iothread0',
force=True))
iotests.log(vm.qmp('x-blockdev-set-iothread',
node_name='drive1-node', iothread='iothread0',
force=True))
iotests.log('Starting migration...')
iotests.log(vm.qmp('migrate', uri='exec:cat >/dev/null'))
while True:
vm.get_qmp_event(wait=60.0)
result = vm.qmp('query-migrate')
status = result.get('return', {}).get('status', None)
if status == 'completed':
break

View file

@ -0,0 +1,6 @@
Launching VM...
Setting IOThreads...
{u'return': {}}
{u'return': {}}
Starting migration...
{u'return': {}}

View file

@ -197,3 +197,5 @@
197 rw auto quick 197 rw auto quick
198 rw auto 198 rw auto
200 rw auto 200 rw auto
202 rw auto quick
203 rw auto

View file

@ -197,6 +197,11 @@ class VM(qtest.QEMUQtestMachine):
socket_scm_helper=socket_scm_helper) socket_scm_helper=socket_scm_helper)
self._num_drives = 0 self._num_drives = 0
def add_object(self, opts):
self._args.append('-object')
self._args.append(opts)
return self
def add_device(self, opts): def add_device(self, opts):
self._args.append('-device') self._args.append('-device')
self._args.append(opts) self._args.append(opts)

View file

@ -31,9 +31,9 @@ static void co_sleep_cb(void *opaque)
aio_co_wake(sleep_cb->co); aio_co_wake(sleep_cb->co);
} }
void coroutine_fn co_aio_sleep_ns(AioContext *ctx, QEMUClockType type, void coroutine_fn qemu_co_sleep_ns(QEMUClockType type, int64_t ns)
int64_t ns)
{ {
AioContext *ctx = qemu_get_current_aio_context();
CoSleepCB sleep_cb = { CoSleepCB sleep_cb = {
.co = qemu_coroutine_self(), .co = qemu_coroutine_self(),
}; };