blockjob: Wake up BDS when job becomes idle
In the context of draining a BDS, the .drained_poll callback of block jobs is called. If this returns true (i.e. there is still some activity pending), the drain operation may call aio_poll() with blocking=true to wait for completion. As soon as the pending activity is completed and the job finally arrives in a quiescent state (i.e. its coroutine either yields with busy=false or terminates), the block job must notify the aio_poll() loop to wake up, otherwise we get a deadlock if both are running in different threads. Signed-off-by: Kevin Wolf <kwolf@redhat.com> Reviewed-by: Fam Zheng <famz@redhat.com> Reviewed-by: Max Reitz <mreitz@redhat.com>
This commit is contained in:
parent
d1756c780b
commit
34dc97b9a0
18
blockjob.c
18
blockjob.c
@ -221,6 +221,22 @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void block_job_wakeup_all_bdrv(BlockJob *job)
|
||||||
|
{
|
||||||
|
GSList *l;
|
||||||
|
|
||||||
|
for (l = job->nodes; l; l = l->next) {
|
||||||
|
BdrvChild *c = l->data;
|
||||||
|
bdrv_wakeup(c->bs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void block_job_on_idle(Notifier *n, void *opaque)
|
||||||
|
{
|
||||||
|
BlockJob *job = opaque;
|
||||||
|
block_job_wakeup_all_bdrv(job);
|
||||||
|
}
|
||||||
|
|
||||||
bool block_job_is_internal(BlockJob *job)
|
bool block_job_is_internal(BlockJob *job)
|
||||||
{
|
{
|
||||||
return (job->job.id == NULL);
|
return (job->job.id == NULL);
|
||||||
@ -416,6 +432,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
|
|||||||
job->finalize_completed_notifier.notify = block_job_event_completed;
|
job->finalize_completed_notifier.notify = block_job_event_completed;
|
||||||
job->pending_notifier.notify = block_job_event_pending;
|
job->pending_notifier.notify = block_job_event_pending;
|
||||||
job->ready_notifier.notify = block_job_event_ready;
|
job->ready_notifier.notify = block_job_event_ready;
|
||||||
|
job->idle_notifier.notify = block_job_on_idle;
|
||||||
|
|
||||||
notifier_list_add(&job->job.on_finalize_cancelled,
|
notifier_list_add(&job->job.on_finalize_cancelled,
|
||||||
&job->finalize_cancelled_notifier);
|
&job->finalize_cancelled_notifier);
|
||||||
@ -423,6 +440,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
|
|||||||
&job->finalize_completed_notifier);
|
&job->finalize_completed_notifier);
|
||||||
notifier_list_add(&job->job.on_pending, &job->pending_notifier);
|
notifier_list_add(&job->job.on_pending, &job->pending_notifier);
|
||||||
notifier_list_add(&job->job.on_ready, &job->ready_notifier);
|
notifier_list_add(&job->job.on_ready, &job->ready_notifier);
|
||||||
|
notifier_list_add(&job->job.on_idle, &job->idle_notifier);
|
||||||
|
|
||||||
error_setg(&job->blocker, "block device is in use by block job: %s",
|
error_setg(&job->blocker, "block device is in use by block job: %s",
|
||||||
job_type_str(&job->job));
|
job_type_str(&job->job));
|
||||||
|
@ -70,6 +70,9 @@ typedef struct BlockJob {
|
|||||||
/** Called when the job transitions to READY */
|
/** Called when the job transitions to READY */
|
||||||
Notifier ready_notifier;
|
Notifier ready_notifier;
|
||||||
|
|
||||||
|
/** Called when the job coroutine yields or terminates */
|
||||||
|
Notifier idle_notifier;
|
||||||
|
|
||||||
/** BlockDriverStates that are involved in this block job */
|
/** BlockDriverStates that are involved in this block job */
|
||||||
GSList *nodes;
|
GSList *nodes;
|
||||||
} BlockJob;
|
} BlockJob;
|
||||||
@ -118,6 +121,16 @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs,
|
|||||||
*/
|
*/
|
||||||
void block_job_remove_all_bdrv(BlockJob *job);
|
void block_job_remove_all_bdrv(BlockJob *job);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* block_job_wakeup_all_bdrv:
|
||||||
|
* @job: The block job
|
||||||
|
*
|
||||||
|
* Calls bdrv_wakeup() for all BlockDriverStates that have been added to the
|
||||||
|
* job. This function is to be called whenever child_job_drained_poll() would
|
||||||
|
* go from true to false to notify waiting drain requests.
|
||||||
|
*/
|
||||||
|
void block_job_wakeup_all_bdrv(BlockJob *job);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* block_job_set_speed:
|
* block_job_set_speed:
|
||||||
* @job: The job to set the speed for.
|
* @job: The job to set the speed for.
|
||||||
|
@ -156,6 +156,9 @@ typedef struct Job {
|
|||||||
/** Notifiers called when the job transitions to READY */
|
/** Notifiers called when the job transitions to READY */
|
||||||
NotifierList on_ready;
|
NotifierList on_ready;
|
||||||
|
|
||||||
|
/** Notifiers called when the job coroutine yields or terminates */
|
||||||
|
NotifierList on_idle;
|
||||||
|
|
||||||
/** Element of the list of jobs */
|
/** Element of the list of jobs */
|
||||||
QLIST_ENTRY(Job) job_list;
|
QLIST_ENTRY(Job) job_list;
|
||||||
|
|
||||||
|
7
job.c
7
job.c
@ -402,6 +402,11 @@ static void job_event_ready(Job *job)
|
|||||||
notifier_list_notify(&job->on_ready, job);
|
notifier_list_notify(&job->on_ready, job);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void job_event_idle(Job *job)
|
||||||
|
{
|
||||||
|
notifier_list_notify(&job->on_idle, job);
|
||||||
|
}
|
||||||
|
|
||||||
void job_enter_cond(Job *job, bool(*fn)(Job *job))
|
void job_enter_cond(Job *job, bool(*fn)(Job *job))
|
||||||
{
|
{
|
||||||
if (!job_started(job)) {
|
if (!job_started(job)) {
|
||||||
@ -447,6 +452,7 @@ static void coroutine_fn job_do_yield(Job *job, uint64_t ns)
|
|||||||
timer_mod(&job->sleep_timer, ns);
|
timer_mod(&job->sleep_timer, ns);
|
||||||
}
|
}
|
||||||
job->busy = false;
|
job->busy = false;
|
||||||
|
job_event_idle(job);
|
||||||
job_unlock();
|
job_unlock();
|
||||||
qemu_coroutine_yield();
|
qemu_coroutine_yield();
|
||||||
|
|
||||||
@ -865,6 +871,7 @@ static void coroutine_fn job_co_entry(void *opaque)
|
|||||||
assert(job && job->driver && job->driver->run);
|
assert(job && job->driver && job->driver->run);
|
||||||
job_pause_point(job);
|
job_pause_point(job);
|
||||||
job->ret = job->driver->run(job, &job->err);
|
job->ret = job->driver->run(job, &job->err);
|
||||||
|
job_event_idle(job);
|
||||||
job->deferred_to_main_loop = true;
|
job->deferred_to_main_loop = true;
|
||||||
aio_bh_schedule_oneshot(qemu_get_aio_context(), job_exit, job);
|
aio_bh_schedule_oneshot(qemu_get_aio_context(), job_exit, job);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user