mirror of https://github.com/xemu-project/xemu.git
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:
commit
f1faf2d59c
14
block.c
14
block.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
259
blockdev.c
259
blockdev.c
|
@ -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),
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
27
iothread.c
27
iothread.c
|
@ -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));
|
||||||
|
}
|
||||||
|
|
|
@ -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' } }
|
||||||
|
|
|
@ -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'
|
||||||
|
}
|
||||||
|
]))
|
|
@ -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': {}}
|
|
@ -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
|
|
@ -0,0 +1,6 @@
|
||||||
|
Launching VM...
|
||||||
|
Setting IOThreads...
|
||||||
|
{u'return': {}}
|
||||||
|
{u'return': {}}
|
||||||
|
Starting migration...
|
||||||
|
{u'return': {}}
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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(),
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue