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->bs) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    aio_context = bdrv_get_aio_context(state->bs);
 | 
				
			||||||
 | 
					    aio_context_acquire(aio_context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (state->aio_context) {
 | 
					 | 
				
			||||||
        if (state->bs) {
 | 
					 | 
				
			||||||
    bdrv_drained_end(state->bs);
 | 
					    bdrv_drained_end(state->bs);
 | 
				
			||||||
        }
 | 
					
 | 
				
			||||||
        aio_context_release(state->aio_context);
 | 
					    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' } }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										95
									
								
								tests/qemu-iotests/202
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										95
									
								
								tests/qemu-iotests/202
									
									
									
									
									
										Executable 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'
 | 
				
			||||||
 | 
					                      }
 | 
				
			||||||
 | 
					                  ]))
 | 
				
			||||||
							
								
								
									
										11
									
								
								tests/qemu-iotests/202.out
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								tests/qemu-iotests/202.out
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										59
									
								
								tests/qemu-iotests/203
									
									
									
									
									
										Executable 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
 | 
				
			||||||
							
								
								
									
										6
									
								
								tests/qemu-iotests/203.out
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								tests/qemu-iotests/203.out
									
									
									
									
									
										Normal file
									
								
							@ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user