dde_rump: fix race condition in rumpuser_mtx

The new mutex is a binary semaphore based on the implementation of
Genode::Semaphore using an applicant FIFO.
This commit is contained in:
Christian Helmuth 2015-11-12 12:25:56 +01:00
parent 60fa8ade1a
commit a01b1793eb

View File

@ -15,6 +15,8 @@
extern "C" { extern "C" {
#include <sys/cdefs.h> #include <sys/cdefs.h>
} }
#include <base/lock.h>
#include <util/fifo.h>
#include <os/timed_semaphore.h> #include <os/timed_semaphore.h>
#include "sched.h" #include "sched.h"
@ -23,72 +25,81 @@ extern "C" {
** Mutexes ** ** Mutexes **
*************/ *************/
/**
* Mutex with support for try_enter()
*
* The mutex is a binary semaphore based on the implementation of
* Genode::Semaphore using an applicant FIFO.
*/
struct rumpuser_mtx struct rumpuser_mtx
{ {
Genode::Semaphore sem; struct Applicant : Genode::Fifo<Applicant>::Element
Genode::Lock counter_lock; {
struct lwp *owner; Genode::Lock lock { Genode::Lock::LOCKED };
void block() { lock.lock(); }
void wake_up() { lock.unlock(); }
};
Genode::Fifo<Applicant> fifo;
bool occupied = false;
Genode::Lock meta_lock;
struct lwp *owner = nullptr;
int flags; int flags;
rumpuser_mtx(int flags) : sem(1), owner(0), flags(flags) { } rumpuser_mtx(int flags) : flags(flags) { }
bool _enter(bool try_enter)
bool down(bool try_lock = false)
{ {
Genode::Lock::Guard guard(counter_lock); while (true) {
/*
* We need a freshly constructed applicant instance on each loop
* iteration to potentially add ourself to the applicant FIFO
* again.
*/
Applicant applicant;
{
Genode::Lock::Guard guard(meta_lock);
if (sem.cnt() > 1) if (!occupied) {
PERR("%p: SEM cnt > 1 (%d) %p", rumpuser_curlwp(), sem.cnt(), this); occupied = true;
bool locked = sem.cnt() <= 0; if (flags & RUMPUSER_MTX_KMUTEX) {
if (owner != nullptr)
if (locked && try_lock) PERR("OWNER already set on KMUTEX enter");
return false; owner = rumpuser_curlwp();
}
if (!try_lock)
counter_lock.unlock();
sem.down();
set_owner();
return true; return true;
} }
void up() if (try_enter)
{ return false;
Genode::Lock::Guard guard(counter_lock);
if (sem.cnt() >= 1) fifo.enqueue(&applicant);
return; }
applicant.block();
clear_owner(); }
sem.up();
} }
bool try_lock() bool enter() { return _enter(false); }
{ bool try_enter() { return _enter(true); }
return down(true);
}
void set_owner() void exit()
{ {
Genode::Lock::Guard guard(meta_lock);
occupied = false;
if (flags & RUMPUSER_MTX_KMUTEX) { if (flags & RUMPUSER_MTX_KMUTEX) {
if(owner != 0) { if (owner == nullptr)
PERR("OWNER != 0 %d", sem.cnt()); PERR("OWNER not set on KMUTEX exit");
} owner = nullptr;
owner = rumpuser_curlwp();
}
} }
void clear_owner() if (Applicant *applicant = fifo.dequeue())
{ applicant->wake_up();
if (flags & RUMPUSER_MTX_KMUTEX) {
if(owner == 0) {
PERR("OWNER 0");
}
owner = 0;
}
} }
}; };
@ -101,13 +112,14 @@ void rumpuser_mutex_init(struct rumpuser_mtx **mtxp, int flags)
void rumpuser_mutex_owner(struct rumpuser_mtx *mtx, struct lwp **lp) void rumpuser_mutex_owner(struct rumpuser_mtx *mtx, struct lwp **lp)
{ {
/* XXX we set the owner in KMUTEX only */
*lp = mtx->owner; *lp = mtx->owner;
} }
void rumpuser_mutex_enter_nowrap(struct rumpuser_mtx *mtx) void rumpuser_mutex_enter_nowrap(struct rumpuser_mtx *mtx)
{ {
mtx->down(); mtx->enter();
} }
@ -118,10 +130,10 @@ void rumpuser_mutex_enter(struct rumpuser_mtx *mtx)
return; return;
} }
if (!mtx->try_lock()) { if (!mtx->try_enter()) {
int nlocks; int nlocks;
rumpkern_unsched(&nlocks, 0); rumpkern_unsched(&nlocks, 0);
mtx->down(); mtx->enter();
rumpkern_sched(nlocks, 0); rumpkern_sched(nlocks, 0);
} }
} }
@ -129,13 +141,13 @@ void rumpuser_mutex_enter(struct rumpuser_mtx *mtx)
int rumpuser_mutex_tryenter(struct rumpuser_mtx *mtx) int rumpuser_mutex_tryenter(struct rumpuser_mtx *mtx)
{ {
return mtx->try_lock() ? 0 : 1; return mtx->try_enter() ? 0 : 1;
} }
void rumpuser_mutex_exit(struct rumpuser_mtx *mtx) void rumpuser_mutex_exit(struct rumpuser_mtx *mtx)
{ {
mtx->up(); mtx->exit();
} }
@ -182,7 +194,7 @@ struct Cond
num_waiters++; num_waiters++;
counter_lock.unlock(); counter_lock.unlock();
mutex->up(); mutex->exit();
if (!abstime) if (!abstime)
signal_sem.down(); signal_sem.down();
@ -213,7 +225,7 @@ struct Cond
num_waiters--; num_waiters--;
counter_lock.unlock(); counter_lock.unlock();
mutex->down(); mutex->enter();
return result == -2 ? ETIMEDOUT : 0; return result == -2 ? ETIMEDOUT : 0;
} }
@ -298,7 +310,7 @@ static void cv_reschedule(struct rumpuser_mtx *mtx, int nlocks)
*/ */
if ((mtx->flags & (RUMPUSER_MTX_SPIN | RUMPUSER_MTX_KMUTEX)) == if ((mtx->flags & (RUMPUSER_MTX_SPIN | RUMPUSER_MTX_KMUTEX)) ==
(RUMPUSER_MTX_SPIN | RUMPUSER_MTX_KMUTEX)) { (RUMPUSER_MTX_SPIN | RUMPUSER_MTX_KMUTEX)) {
mtx->up(); mtx->exit();
rumpkern_sched(nlocks, mtx); rumpkern_sched(nlocks, mtx);
rumpuser_mutex_enter_nowrap(mtx); rumpuser_mutex_enter_nowrap(mtx);
} else { } else {