Skip to content

Commit

Permalink
blockdev: acquire AioContext in QMP 'transaction' actions
Browse files Browse the repository at this point in the history
The transaction QMP command performs operations atomically on a group of
drives.  This command needs to acquire AioContext in order to work
safely when virtio-blk dataplane IOThreads are accessing drives.

The transactional nature of the command means that actions are split
into prepare, commit, abort, and clean functions.  Acquire the
AioContext in prepare and don't release it until one of the other
functions is called.  This prevents the IOThread from running the
AioContext before the transaction has completed.

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Message-id: 1416566940-4430-4-git-send-email-stefanha@redhat.com
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
  • Loading branch information
stefanhaRH authored and kevmw committed Dec 10, 2014
1 parent 73f1f75 commit 5d6e96e
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 1 deletion.
52 changes: 51 additions & 1 deletion blockdev.c
Expand Up @@ -1207,6 +1207,7 @@ struct BlkTransactionState {
typedef struct InternalSnapshotState {
BlkTransactionState common;
BlockDriverState *bs;
AioContext *aio_context;
QEMUSnapshotInfo sn;
} InternalSnapshotState;

Expand Down Expand Up @@ -1240,6 +1241,10 @@ static void internal_snapshot_prepare(BlkTransactionState *common,
return;
}

/* AioContext is released in .clean() */
state->aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(state->aio_context);

if (!bdrv_is_inserted(bs)) {
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
return;
Expand Down Expand Up @@ -1317,11 +1322,22 @@ static void internal_snapshot_abort(BlkTransactionState *common)
}
}

static void internal_snapshot_clean(BlkTransactionState *common)
{
InternalSnapshotState *state = DO_UPCAST(InternalSnapshotState,
common, common);

if (state->aio_context) {
aio_context_release(state->aio_context);
}
}

/* external snapshot private data */
typedef struct ExternalSnapshotState {
BlkTransactionState common;
BlockDriverState *old_bs;
BlockDriverState *new_bs;
AioContext *aio_context;
} ExternalSnapshotState;

static void external_snapshot_prepare(BlkTransactionState *common,
Expand Down Expand Up @@ -1388,6 +1404,10 @@ static void external_snapshot_prepare(BlkTransactionState *common,
return;
}

/* Acquire AioContext now so any threads operating on old_bs stop */
state->aio_context = bdrv_get_aio_context(state->old_bs);
aio_context_acquire(state->aio_context);

if (!bdrv_is_inserted(state->old_bs)) {
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
return;
Expand Down Expand Up @@ -1446,13 +1466,17 @@ static void external_snapshot_commit(BlkTransactionState *common)
ExternalSnapshotState *state =
DO_UPCAST(ExternalSnapshotState, common, common);

bdrv_set_aio_context(state->new_bs, state->aio_context);

/* This removes our old bs and adds the new bs */
bdrv_append(state->new_bs, state->old_bs);
/* We don't need (or want) to use the transactional
* bdrv_reopen_multiple() across all the entries at once, because we
* don't want to abort all of them if one of them fails the reopen */
bdrv_reopen(state->new_bs, state->new_bs->open_flags & ~BDRV_O_RDWR,
NULL);

aio_context_release(state->aio_context);
}

static void external_snapshot_abort(BlkTransactionState *common)
Expand All @@ -1462,23 +1486,38 @@ static void external_snapshot_abort(BlkTransactionState *common)
if (state->new_bs) {
bdrv_unref(state->new_bs);
}
if (state->aio_context) {
aio_context_release(state->aio_context);
}
}

typedef struct DriveBackupState {
BlkTransactionState common;
BlockDriverState *bs;
AioContext *aio_context;
BlockJob *job;
} DriveBackupState;

static void drive_backup_prepare(BlkTransactionState *common, Error **errp)
{
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
BlockDriverState *bs;
DriveBackup *backup;
Error *local_err = NULL;

assert(common->action->kind == TRANSACTION_ACTION_KIND_DRIVE_BACKUP);
backup = common->action->drive_backup;

bs = bdrv_find(backup->device);
if (!bs) {
error_set(errp, QERR_DEVICE_NOT_FOUND, backup->device);
return;
}

/* AioContext is released in .clean() */
state->aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(state->aio_context);

qmp_drive_backup(backup->device, backup->target,
backup->has_format, backup->format,
backup->sync,
Expand All @@ -1492,7 +1531,7 @@ static void drive_backup_prepare(BlkTransactionState *common, Error **errp)
return;
}

state->bs = bdrv_find(backup->device);
state->bs = bs;
state->job = state->bs->job;
}

Expand All @@ -1507,6 +1546,15 @@ static void drive_backup_abort(BlkTransactionState *common)
}
}

static void drive_backup_clean(BlkTransactionState *common)
{
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);

if (state->aio_context) {
aio_context_release(state->aio_context);
}
}

static void abort_prepare(BlkTransactionState *common, Error **errp)
{
error_setg(errp, "Transaction aborted using Abort action");
Expand All @@ -1528,6 +1576,7 @@ static const BdrvActionOps actions[] = {
.instance_size = sizeof(DriveBackupState),
.prepare = drive_backup_prepare,
.abort = drive_backup_abort,
.clean = drive_backup_clean,
},
[TRANSACTION_ACTION_KIND_ABORT] = {
.instance_size = sizeof(BlkTransactionState),
Expand All @@ -1538,6 +1587,7 @@ static const BdrvActionOps actions[] = {
.instance_size = sizeof(InternalSnapshotState),
.prepare = internal_snapshot_prepare,
.abort = internal_snapshot_abort,
.clean = internal_snapshot_clean,
},
};

Expand Down
2 changes: 2 additions & 0 deletions hw/block/dataplane/virtio-blk.c
Expand Up @@ -200,6 +200,8 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_CHANGE, s->blocker);
blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_COMMIT, s->blocker);
blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_EJECT, s->blocker);
blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, s->blocker);
blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, s->blocker);
blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE,
s->blocker);
blk_op_unblock(conf->conf.blk, BLOCK_OP_TYPE_MIRROR, s->blocker);
Expand Down

0 comments on commit 5d6e96e

Please sign in to comment.