blockjob: add block_job_defer_to_main_loop()

Block jobs will run in the BlockDriverState's AioContext, which may not
always be the QEMU main loop.

There are some block layer APIs that are either not thread-safe or risk
lock ordering problems.  This includes bdrv_unref(), bdrv_close(), and
anything that calls bdrv_drain_all().

The block_job_defer_to_main_loop() API allows a block job to schedule a
function to run in the main loop with the BlockDriverState AioContext
held.

This function will be used to perform cleanup and backing chain
manipulations in block jobs.

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Message-id: 1413889440-32577-6-git-send-email-stefanha@redhat.com
This commit is contained in:
Stefan Hajnoczi 2014-10-21 12:03:54 +01:00
parent 723c5d93c5
commit dec7d421f8
2 changed files with 64 additions and 0 deletions

View file

@ -342,3 +342,48 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs,
}
return action;
}
typedef struct {
BlockJob *job;
QEMUBH *bh;
AioContext *aio_context;
BlockJobDeferToMainLoopFn *fn;
void *opaque;
} BlockJobDeferToMainLoopData;
static void block_job_defer_to_main_loop_bh(void *opaque)
{
BlockJobDeferToMainLoopData *data = opaque;
AioContext *aio_context;
qemu_bh_delete(data->bh);
/* Prevent race with block_job_defer_to_main_loop() */
aio_context_acquire(data->aio_context);
/* Fetch BDS AioContext again, in case it has changed */
aio_context = bdrv_get_aio_context(data->job->bs);
aio_context_acquire(aio_context);
data->fn(data->job, data->opaque);
aio_context_release(aio_context);
aio_context_release(data->aio_context);
g_free(data);
}
void block_job_defer_to_main_loop(BlockJob *job,
BlockJobDeferToMainLoopFn *fn,
void *opaque)
{
BlockJobDeferToMainLoopData *data = g_malloc(sizeof(*data));
data->job = job;
data->bh = qemu_bh_new(block_job_defer_to_main_loop_bh, data);
data->aio_context = bdrv_get_aio_context(job->bs);
data->fn = fn;
data->opaque = opaque;
qemu_bh_schedule(data->bh);
}

View file

@ -315,4 +315,23 @@ void block_job_iostatus_reset(BlockJob *job);
BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs,
BlockdevOnError on_err,
int is_read, int error);
typedef void BlockJobDeferToMainLoopFn(BlockJob *job, void *opaque);
/**
* block_job_defer_to_main_loop:
* @job: The job
* @fn: The function to run in the main loop
* @opaque: The opaque value that is passed to @fn
*
* Execute a given function in the main loop with the BlockDriverState
* AioContext acquired. Block jobs must call bdrv_unref(), bdrv_close(), and
* anything that uses bdrv_drain_all() in the main loop.
*
* The @job AioContext is held while @fn executes.
*/
void block_job_defer_to_main_loop(BlockJob *job,
BlockJobDeferToMainLoopFn *fn,
void *opaque);
#endif