[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH 4/6] coroutine-lock: reimplement CoRwlock to fix downgrade bu
From: |
Stefan Hajnoczi |
Subject: |
Re: [PATCH 4/6] coroutine-lock: reimplement CoRwlock to fix downgrade bug |
Date: |
Wed, 24 Mar 2021 16:15:23 +0000 |
On Wed, Mar 17, 2021 at 07:00:11PM +0100, Paolo Bonzini wrote:
> +static void qemu_co_rwlock_maybe_wake_one(CoRwlock *lock)
> +{
> + CoRwTicket *tkt = QSIMPLEQ_FIRST(&lock->tickets);
> + Coroutine *co = NULL;
> +
> + /*
> + * Setting lock->owners here prevents rdlock and wrlock from
> + * sneaking in between unlock and wake.
> + */
> +
> + if (tkt) {
> + if (tkt->read) {
> + if (lock->owners >= 0) {
> + lock->owners++;
> + co = tkt->co;
> + }
> + } else {
> + if (lock->owners == 0) {
> + lock->owners = -1;
> + co = tkt->co;
> + }
> + }
> + }
> +
> + if (co) {
> + QSIMPLEQ_REMOVE_HEAD(&lock->tickets, next);
> + qemu_co_mutex_unlock(&lock->mutex);
> + aio_co_wake(co);
I find it hard to reason about QSIMPLEQ_EMPTY(&lock->tickets) callers
that execute before co is entered. They see an empty queue even though a
coroutine is about to run. Updating owners above ensures that the code
correctly tracks the state of the rwlock, but I'm not 100% confident
about this aspect of the code.
> void qemu_co_rwlock_upgrade(CoRwlock *lock)
> {
> - Coroutine *self = qemu_coroutine_self();
> -
> qemu_co_mutex_lock(&lock->mutex);
> - assert(lock->reader > 0);
> - lock->reader--;
> - lock->pending_writer++;
> - while (lock->reader) {
> - qemu_co_queue_wait(&lock->queue, &lock->mutex);
> - }
> - lock->pending_writer--;
> + assert(lock->owners > 0);
> + /* For fairness, wait if a writer is in line. */
> + if (lock->owners == 1 && QSIMPLEQ_EMPTY(&lock->tickets)) {
> + lock->owners = -1;
> + qemu_co_mutex_unlock(&lock->mutex);
> + } else {
> + CoRwTicket my_ticket = { false, qemu_coroutine_self() };
>
> - /* The rest of the write-side critical section is run with
> - * the mutex taken, similar to qemu_co_rwlock_wrlock. Do
> - * not account for the lock twice in self->locks_held.
> - */
> - self->locks_held--;
> + lock->owners--;
> + QSIMPLEQ_INSERT_TAIL(&lock->tickets, &my_ticket, next);
> + qemu_co_rwlock_maybe_wake_one(lock);
> + qemu_coroutine_yield();
> + assert(lock->owners == -1);
lock->owners is read outside lock->mutex here. Not sure if this can
cause problems.
> + }
> }
locks_held is kept unchanged across qemu_coroutine_yield() even though
the read lock has been released. rdlock() and wrlock() only increment
locks_held after acquiring the rwlock.
In practice I don't think it matters, but it seems inconsistent. If
locks_held is supposed to track tickets (not just coroutines currently
holding a lock), then rdlock() and wrlock() should increment before
yielding.
signature.asc
Description: PGP signature
- [PATCH v5 0/6] coroutine rwlock downgrade fix, minor VDI changes, Paolo Bonzini, 2021/03/17
- [PATCH 3/6] coroutine/mutex: Store the coroutine in the CoWaitRecord only once, Paolo Bonzini, 2021/03/17
- [PATCH 5/6] test-coroutine: add rwlock upgrade test, Paolo Bonzini, 2021/03/17
- [PATCH 2/6] block/vdi: Don't assume that blocks are larger than VdiHeader, Paolo Bonzini, 2021/03/17
- [PATCH 1/6] block/vdi: When writing new bmap entry fails, don't leak the buffer, Paolo Bonzini, 2021/03/17
- [PATCH 6/6] test-coroutine: Add rwlock downgrade test, Paolo Bonzini, 2021/03/17
- [PATCH 4/6] coroutine-lock: reimplement CoRwlock to fix downgrade bug, Paolo Bonzini, 2021/03/17
- Re: [PATCH 4/6] coroutine-lock: reimplement CoRwlock to fix downgrade bug,
Stefan Hajnoczi <=
- Re: [PATCH v5 0/6] coroutine rwlock downgrade fix, minor VDI changes, Max Reitz, 2021/03/24
- Re: [PATCH v5 0/6] coroutine rwlock downgrade fix, minor VDI changes, Stefan Hajnoczi, 2021/03/24