mirror of https://github.com/xemu-project/xemu.git
coroutine-lock: add mutex argument to CoQueue APIs
All that CoQueue needs in order to become thread-safe is help from an external mutex. Add this to the API. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Reviewed-by: Fam Zheng <famz@redhat.com> Message-id: 20170213181244.16297-6-pbonzini@redhat.com Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
parent
f8c6e1cbc3
commit
1ace7ceac5
|
@ -64,7 +64,7 @@ static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
|
||||||
retry = false;
|
retry = false;
|
||||||
QLIST_FOREACH(req, &job->inflight_reqs, list) {
|
QLIST_FOREACH(req, &job->inflight_reqs, list) {
|
||||||
if (end > req->start && start < req->end) {
|
if (end > req->start && start < req->end) {
|
||||||
qemu_co_queue_wait(&req->wait_queue);
|
qemu_co_queue_wait(&req->wait_queue, NULL);
|
||||||
retry = true;
|
retry = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -539,7 +539,7 @@ static bool coroutine_fn wait_serialising_requests(BdrvTrackedRequest *self)
|
||||||
* (instead of producing a deadlock in the former case). */
|
* (instead of producing a deadlock in the former case). */
|
||||||
if (!req->waiting_for) {
|
if (!req->waiting_for) {
|
||||||
self->waiting_for = req;
|
self->waiting_for = req;
|
||||||
qemu_co_queue_wait(&req->wait_queue);
|
qemu_co_queue_wait(&req->wait_queue, NULL);
|
||||||
self->waiting_for = NULL;
|
self->waiting_for = NULL;
|
||||||
retry = true;
|
retry = true;
|
||||||
waited = true;
|
waited = true;
|
||||||
|
@ -2275,7 +2275,7 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
|
||||||
|
|
||||||
/* Wait until any previous flushes are completed */
|
/* Wait until any previous flushes are completed */
|
||||||
while (bs->active_flush_req) {
|
while (bs->active_flush_req) {
|
||||||
qemu_co_queue_wait(&bs->flush_queue);
|
qemu_co_queue_wait(&bs->flush_queue, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
bs->active_flush_req = true;
|
bs->active_flush_req = true;
|
||||||
|
|
|
@ -182,7 +182,7 @@ static void nbd_coroutine_start(NBDClientSession *s,
|
||||||
/* Poor man semaphore. The free_sema is locked when no other request
|
/* Poor man semaphore. The free_sema is locked when no other request
|
||||||
* can be accepted, and unlocked after receiving one reply. */
|
* can be accepted, and unlocked after receiving one reply. */
|
||||||
if (s->in_flight == MAX_NBD_REQUESTS) {
|
if (s->in_flight == MAX_NBD_REQUESTS) {
|
||||||
qemu_co_queue_wait(&s->free_sema);
|
qemu_co_queue_wait(&s->free_sema, NULL);
|
||||||
assert(s->in_flight < MAX_NBD_REQUESTS);
|
assert(s->in_flight < MAX_NBD_REQUESTS);
|
||||||
}
|
}
|
||||||
s->in_flight++;
|
s->in_flight++;
|
||||||
|
|
|
@ -932,9 +932,7 @@ static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset,
|
||||||
if (bytes == 0) {
|
if (bytes == 0) {
|
||||||
/* Wait for the dependency to complete. We need to recheck
|
/* Wait for the dependency to complete. We need to recheck
|
||||||
* the free/allocated clusters when we continue. */
|
* the free/allocated clusters when we continue. */
|
||||||
qemu_co_mutex_unlock(&s->lock);
|
qemu_co_queue_wait(&old_alloc->dependent_requests, &s->lock);
|
||||||
qemu_co_queue_wait(&old_alloc->dependent_requests);
|
|
||||||
qemu_co_mutex_lock(&s->lock);
|
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -486,7 +486,7 @@ static void wait_for_overlapping_aiocb(BDRVSheepdogState *s, SheepdogAIOCB *acb)
|
||||||
retry:
|
retry:
|
||||||
QLIST_FOREACH(cb, &s->inflight_aiocb_head, aiocb_siblings) {
|
QLIST_FOREACH(cb, &s->inflight_aiocb_head, aiocb_siblings) {
|
||||||
if (AIOCBOverlapping(acb, cb)) {
|
if (AIOCBOverlapping(acb, cb)) {
|
||||||
qemu_co_queue_wait(&s->overlapping_queue);
|
qemu_co_queue_wait(&s->overlapping_queue, NULL);
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -326,7 +326,7 @@ void coroutine_fn throttle_group_co_io_limits_intercept(BlockBackend *blk,
|
||||||
if (must_wait || blkp->pending_reqs[is_write]) {
|
if (must_wait || blkp->pending_reqs[is_write]) {
|
||||||
blkp->pending_reqs[is_write]++;
|
blkp->pending_reqs[is_write]++;
|
||||||
qemu_mutex_unlock(&tg->lock);
|
qemu_mutex_unlock(&tg->lock);
|
||||||
qemu_co_queue_wait(&blkp->throttled_reqs[is_write]);
|
qemu_co_queue_wait(&blkp->throttled_reqs[is_write], NULL);
|
||||||
qemu_mutex_lock(&tg->lock);
|
qemu_mutex_lock(&tg->lock);
|
||||||
blkp->pending_reqs[is_write]--;
|
blkp->pending_reqs[is_write]--;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2374,7 +2374,7 @@ static void coroutine_fn v9fs_flush(void *opaque)
|
||||||
/*
|
/*
|
||||||
* Wait for pdu to complete.
|
* Wait for pdu to complete.
|
||||||
*/
|
*/
|
||||||
qemu_co_queue_wait(&cancel_pdu->complete);
|
qemu_co_queue_wait(&cancel_pdu->complete, NULL);
|
||||||
cancel_pdu->cancelled = 0;
|
cancel_pdu->cancelled = 0;
|
||||||
pdu_free(cancel_pdu);
|
pdu_free(cancel_pdu);
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,7 +160,8 @@ void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CoQueues are a mechanism to queue coroutines in order to continue executing
|
* CoQueues are a mechanism to queue coroutines in order to continue executing
|
||||||
* them later.
|
* them later. They are similar to condition variables, but they need help
|
||||||
|
* from an external mutex in order to maintain thread-safety.
|
||||||
*/
|
*/
|
||||||
typedef struct CoQueue {
|
typedef struct CoQueue {
|
||||||
QSIMPLEQ_HEAD(, Coroutine) entries;
|
QSIMPLEQ_HEAD(, Coroutine) entries;
|
||||||
|
@ -174,9 +175,10 @@ void qemu_co_queue_init(CoQueue *queue);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the current coroutine to the CoQueue and transfers control to the
|
* Adds the current coroutine to the CoQueue and transfers control to the
|
||||||
* caller of the coroutine.
|
* caller of the coroutine. The mutex is unlocked during the wait and
|
||||||
|
* locked again afterwards.
|
||||||
*/
|
*/
|
||||||
void coroutine_fn qemu_co_queue_wait(CoQueue *queue);
|
void coroutine_fn qemu_co_queue_wait(CoQueue *queue, CoMutex *mutex);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restarts the next coroutine in the CoQueue and removes it from the queue.
|
* Restarts the next coroutine in the CoQueue and removes it from the queue.
|
||||||
|
|
|
@ -40,12 +40,30 @@ void qemu_co_queue_init(CoQueue *queue)
|
||||||
QSIMPLEQ_INIT(&queue->entries);
|
QSIMPLEQ_INIT(&queue->entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
void coroutine_fn qemu_co_queue_wait(CoQueue *queue)
|
void coroutine_fn qemu_co_queue_wait(CoQueue *queue, CoMutex *mutex)
|
||||||
{
|
{
|
||||||
Coroutine *self = qemu_coroutine_self();
|
Coroutine *self = qemu_coroutine_self();
|
||||||
QSIMPLEQ_INSERT_TAIL(&queue->entries, self, co_queue_next);
|
QSIMPLEQ_INSERT_TAIL(&queue->entries, self, co_queue_next);
|
||||||
|
|
||||||
|
if (mutex) {
|
||||||
|
qemu_co_mutex_unlock(mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* There is no race condition here. Other threads will call
|
||||||
|
* aio_co_schedule on our AioContext, which can reenter this
|
||||||
|
* coroutine but only after this yield and after the main loop
|
||||||
|
* has gone through the next iteration.
|
||||||
|
*/
|
||||||
qemu_coroutine_yield();
|
qemu_coroutine_yield();
|
||||||
assert(qemu_in_coroutine());
|
assert(qemu_in_coroutine());
|
||||||
|
|
||||||
|
/* TODO: OSv implements wait morphing here, where the wakeup
|
||||||
|
* primitive automatically places the woken coroutine on the
|
||||||
|
* mutex's queue. This avoids the thundering herd effect.
|
||||||
|
*/
|
||||||
|
if (mutex) {
|
||||||
|
qemu_co_mutex_lock(mutex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -335,7 +353,7 @@ void qemu_co_rwlock_rdlock(CoRwlock *lock)
|
||||||
Coroutine *self = qemu_coroutine_self();
|
Coroutine *self = qemu_coroutine_self();
|
||||||
|
|
||||||
while (lock->writer) {
|
while (lock->writer) {
|
||||||
qemu_co_queue_wait(&lock->queue);
|
qemu_co_queue_wait(&lock->queue, NULL);
|
||||||
}
|
}
|
||||||
lock->reader++;
|
lock->reader++;
|
||||||
self->locks_held++;
|
self->locks_held++;
|
||||||
|
@ -365,7 +383,7 @@ void qemu_co_rwlock_wrlock(CoRwlock *lock)
|
||||||
Coroutine *self = qemu_coroutine_self();
|
Coroutine *self = qemu_coroutine_self();
|
||||||
|
|
||||||
while (lock->writer || lock->reader) {
|
while (lock->writer || lock->reader) {
|
||||||
qemu_co_queue_wait(&lock->queue);
|
qemu_co_queue_wait(&lock->queue, NULL);
|
||||||
}
|
}
|
||||||
lock->writer = true;
|
lock->writer = true;
|
||||||
self->locks_held++;
|
self->locks_held++;
|
||||||
|
|
Loading…
Reference in New Issue