mirror of
https://gitlab.com/qemu-project/qemu.git
synced 2026-10-28 14:04:39 +08:00
job: keep job paused across overlapping pause requests
job_pause_point_locked() sets job->paused before yielding and clears it
unconditionally on wake, before re-checking whether a pause is still
pending. job_pause() re-enters a parked job only while it is not yet
paused, so the wake that resumes one comes from a drain *ending*
(job_resume() -> job_enter_cond()). If the next drain begins before that
wake runs, the woken coroutine clears job->paused while pause_count is
already > 0 again:
AioContext change (BQL thread) job coroutine (iothread)
----------------------------- ------------------------
parked in job_pause_point():
paused=1, pause_count=1, yielded
drain ends -> job_resume():
pause_count = 0
job_enter_cond(): queue wake ..> (wake pending)
bdrv_try_change_aio_context():
bdrv_drain_all_begin():
job_pause() per node
pause_count = N (> 0)
wake runs, leaves job_do_yield():
paused = 0 (pause_count == N)
tran_commit -> job_set_aio_context():
assert(paused || completed) --> abort: paused == 0
bdrv_try_change_aio_context() drains precisely to quiesce the job before
changing its AioContext, but that brief paused==0 window trips the
assertion. It is guest-triggerable: a virtio-blk reset
(virtio_blk_stop_ioeventfd() -> blk_set_aio_context()) racing a running
mirror/blockCopy job hits it, as do x-blockdev-set-iothread, blockdev
hot-plug/unplug and job completion.
Keep job->paused set while a pause is still pending: loop the yield
until job_should_pause_locked() is false (or the job is cancelled), and
only then clear job->paused. Drained-state consumers then never observe
a pending-pause job as unpaused.
Signed-off-by: Denis V. Lunev <den@openvz.org>
Message-ID: <20260623152406.1180235-2-den@openvz.org>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
This commit is contained in:
1 parent
9696bf5dc5
commit
2667133c27
1 file changed
+8
-1
@@ -629,7 +629,14 @@ static void coroutine_fn job_pause_point_locked(Job *job)
|
|||||||
? JOB_STATUS_STANDBY
|
? JOB_STATUS_STANDBY
|
||||||
: JOB_STATUS_PAUSED);
|
: JOB_STATUS_PAUSED);
|
||||||
job->paused = true;
|
job->paused = true;
|
||||||
job_do_yield_locked(job, -1);
|
/*
|
||||||
|
* Stay paused across back-to-back pause requests: a transient
|
||||||
|
* paused == false while pause_count > 0 would be observed as
|
||||||
|
* "not paused" by job_set_aio_context() and other drain consumers.
|
||||||
|
*/
|
||||||
|
do {
|
||||||
|
job_do_yield_locked(job, -1);
|
||||||
|
} while (job_should_pause_locked(job) && !job_is_cancelled_locked(job));
|
||||||
job->paused = false;
|
job->paused = false;
|
||||||
job_state_transition_locked(job, status);
|
job_state_transition_locked(job, status);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user