nova: replacement of generic Genode spinlock

Turn Genode user-level spinlock into a user-level "helpinglock". This requires
support by the kernel introduced with kernel branch r5.

The commit avoids live-locks when multiple threads with SCs on different
priority levels compete for the same user-level Genode "spinlock".

Issue #986
This commit is contained in:
Alexander Boettcher 2014-02-14 12:24:01 +01:00 committed by Christian Helmuth
parent bfa3053e62
commit 9ae441e469
6 changed files with 107 additions and 4 deletions

View File

@ -25,6 +25,7 @@ SRC_CC += thread/context_allocator.cc env/utcb.cc
SRC_CC += lock/cmpxchg.cc
INC_DIR += $(REP_DIR)/src/base/lock
INC_DIR += $(BASE_DIR)/src/base/lock
INC_DIR += $(BASE_DIR)/src/base/thread
INC_DIR += $(REP_DIR)/include/codezero/dummies

View File

@ -41,9 +41,6 @@ Genode::Thread_base * __attribute__((weak)) Genode::Thread_base::myself()
}
static inline void thread_yield() { Nova::ec_ctrl(Nova::EC_YIELD); }
static inline bool thread_check_stopped_and_restart(Genode::Thread_base *thread_base)
{
Genode::addr_t sem = thread_base ?

View File

@ -0,0 +1,103 @@
/*
* \brief Nova specific user land "Spin lock" implementation
* \author Alexander Boettcher
* \date 2014-02-07
*/
/*
* Copyright (C) 2014-2014 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__BASE__LOCK__SPIN_H_
#define _INCLUDE__BASE__LOCK__SPIN_H_
/* Genode includes */
#include <cpu/atomic.h>
#include <base/thread.h>
/* local includes */
#include <lock_helper.h>
enum State {
SPINLOCK_LOCKED = 0, SPINLOCK_UNLOCKED = 1, SPINLOCK_CONTENDED = 2,
};
enum { RESERVED_BITS = 12, COUNTER_MASK = 0xFFC };
template <typename T>
static inline void spinlock_lock(volatile T *lock_variable)
{
using Genode::cmpxchg;
Genode::Thread_base * myself = Genode::Thread_base::myself();
T const tid = myself ? myself->tid().ec_sel : Nova::PT_SEL_MAIN_EC;
unsigned help_counter = 0;
/* sanity check that ec_sel fits into the lock_variable */
if (tid >= (1 << (sizeof(*lock_variable) * 8 - RESERVED_BITS)))
nova_die();
if (myself) {
Nova::Utcb * utcb = reinterpret_cast<Nova::Utcb *>(myself->utcb());
help_counter = utcb->tls & COUNTER_MASK;
}
/* try to get lock */
do {
T raw = *lock_variable;
if (raw != SPINLOCK_UNLOCKED) {
if (!(raw & SPINLOCK_CONTENDED))
/* if it fails - just re-read and retry */
if (!Genode::cmpxchg(lock_variable, raw, raw | SPINLOCK_CONTENDED))
continue;
/*
* Donate remaining time slice to help the spinlock holder to
* pass the critical section.
*/
unsigned long const ec = raw >> RESERVED_BITS;
unsigned long const tls = raw & COUNTER_MASK;
Nova::ec_ctrl(Nova::EC_DONATE_SC, ec, tls);
continue;
}
} while (!cmpxchg(lock_variable, (T)SPINLOCK_UNLOCKED,
(tid << RESERVED_BITS) | help_counter | SPINLOCK_LOCKED));
}
template <typename T>
static inline void spinlock_unlock(volatile T *lock_variable)
{
using Nova::Utcb;
Genode::Thread_base * myself = Genode::Thread_base::myself();
Utcb * utcb = myself ? reinterpret_cast<Utcb *>(myself->utcb()) : 0;
/* unlock */
T old;
do {
old = *lock_variable;
} while (!Genode::cmpxchg(lock_variable, old, (T)SPINLOCK_UNLOCKED));
/* de-flag time donation help request and set new counter */
if (utcb) {
utcb->tls = (((utcb->tls & COUNTER_MASK) + 4) % 4096) & COUNTER_MASK;
/* take care that compiler generates code that writes tls to memory */
asm volatile ("":::"memory");
}
/*
* If anybody donated time, request kernel for a re-schedule in order that
* the helper can get its time donation (SC) back.
*/
if (old & SPINLOCK_CONTENDED)
Nova::ec_ctrl(Nova::EC_RESCHEDULE);
}
#endif /* _INCLUDE__BASE__LOCK__SPIN_H_ */

View File

@ -24,6 +24,7 @@ SRC_CC += thread/thread.cc thread/thread_bootstrap.cc thread/trace.cc
SRC_CC += thread/context_allocator.cc
INC_DIR += $(REP_DIR)/src/base/lock
INC_DIR += $(BASE_DIR)/src/base/lock
INC_DIR += $(BASE_DIR)/src/base/thread
vpath cap_copy.cc $(BASE_DIR)/src/platform

View File

@ -24,6 +24,7 @@ SRC_CC += thread/thread.cc thread/trace.cc thread/thread_bootstrap.cc
SRC_CC += thread/context_allocator.cc
INC_DIR += $(REP_DIR)/src/base/lock
INC_DIR += $(BASE_DIR)/src/base/lock
INC_DIR += $(BASE_DIR)/src/base/thread
vpath cap_copy.cc $(BASE_DIR)/src/platform

View File

@ -15,7 +15,7 @@
#include <base/cancelable_lock.h>
/* local includes */
#include "spin_lock.h"
#include <spin_lock.h>
using namespace Genode;