From 9ae441e469e0cb7fc84998958a3eed9ac1499eb5 Mon Sep 17 00:00:00 2001 From: Alexander Boettcher Date: Fri, 14 Feb 2014 12:24:01 +0100 Subject: [PATCH] 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 --- base-codezero/lib/mk/base-common.mk | 1 + base-nova/src/base/lock/lock_helper.h | 3 - base-nova/src/base/lock/spin_lock.h | 103 ++++++++++++++++++++++++++ base-okl4/lib/mk/base-common.mk | 1 + base-pistachio/lib/mk/base-common.mk | 1 + base/src/base/lock/lock.cc | 2 +- 6 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 base-nova/src/base/lock/spin_lock.h diff --git a/base-codezero/lib/mk/base-common.mk b/base-codezero/lib/mk/base-common.mk index 511b31112..a284b7669 100644 --- a/base-codezero/lib/mk/base-common.mk +++ b/base-codezero/lib/mk/base-common.mk @@ -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 diff --git a/base-nova/src/base/lock/lock_helper.h b/base-nova/src/base/lock/lock_helper.h index 779f38bf2..a205215ec 100644 --- a/base-nova/src/base/lock/lock_helper.h +++ b/base-nova/src/base/lock/lock_helper.h @@ -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 ? diff --git a/base-nova/src/base/lock/spin_lock.h b/base-nova/src/base/lock/spin_lock.h new file mode 100644 index 000000000..eec3b3dd7 --- /dev/null +++ b/base-nova/src/base/lock/spin_lock.h @@ -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 +#include + +/* local includes */ +#include + + +enum State { + SPINLOCK_LOCKED = 0, SPINLOCK_UNLOCKED = 1, SPINLOCK_CONTENDED = 2, +}; + +enum { RESERVED_BITS = 12, COUNTER_MASK = 0xFFC }; + +template +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(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 +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(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_ */ diff --git a/base-okl4/lib/mk/base-common.mk b/base-okl4/lib/mk/base-common.mk index eaaa37078..a44169aad 100644 --- a/base-okl4/lib/mk/base-common.mk +++ b/base-okl4/lib/mk/base-common.mk @@ -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 diff --git a/base-pistachio/lib/mk/base-common.mk b/base-pistachio/lib/mk/base-common.mk index 31e529137..de836659d 100644 --- a/base-pistachio/lib/mk/base-common.mk +++ b/base-pistachio/lib/mk/base-common.mk @@ -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 diff --git a/base/src/base/lock/lock.cc b/base/src/base/lock/lock.cc index c82ccafc8..673c31b8d 100644 --- a/base/src/base/lock/lock.cc +++ b/base/src/base/lock/lock.cc @@ -15,7 +15,7 @@ #include /* local includes */ -#include "spin_lock.h" +#include using namespace Genode;