thread: rearrange thread context management

Use a bit allocator for the allocation management of thread contexts,
instead of holding allocation information within the Thread_base objects,
which lead to race conditions in the past.

Moreover, extend the Thread_base class interface with the ability to
to add additional stacks to a thread, and associate the context they're
located in with the corresponding Thread_base object. Additional stacks
can be used to do user-level scheduling with stack switching, without breaking
Genode's API.

Fixes #1024
Fixes #1036
This commit is contained in:
Stefan Kalkowski 2014-01-24 12:06:21 +01:00 committed by Christian Helmuth
parent 66c5887bd3
commit 5447c406e5
25 changed files with 259 additions and 179 deletions

View File

@ -95,13 +95,15 @@ namespace Genode {
/**
* Thread-context area configuration.
*/
static addr_t context_area_virtual_base() { return 0x40000000UL; }
static addr_t context_area_virtual_size() { return 0x10000000UL; }
static constexpr addr_t context_area_virtual_base() {
return 0x40000000UL; }
static constexpr addr_t context_area_virtual_size() {
return 0x10000000UL; }
/**
* Size of virtual address region holding the context of one thread
*/
static addr_t context_virtual_size() { return 0x00100000UL; }
static constexpr addr_t context_virtual_size() { return 0x00100000UL; }
};
struct Native_pd_args { };

View File

@ -78,13 +78,15 @@ namespace Genode {
/**
* Thread-context area configuration.
*/
static addr_t context_area_virtual_base() { return 0x40000000UL; }
static addr_t context_area_virtual_size() { return 0x10000000UL; }
static constexpr addr_t context_area_virtual_base() {
return 0x40000000UL; }
static constexpr addr_t context_area_virtual_size() {
return 0x10000000UL; }
/**
* Size of virtual address region holding the context of one thread
*/
static addr_t context_virtual_size() { return 0x00100000UL; }
static constexpr addr_t context_virtual_size() { return 0x00100000UL; }
};
struct Native_pd_args { };

View File

@ -0,0 +1,38 @@
/*
* \brief Platform-specific context area definitions
* \author Stefan Kalkowski
* \date 2014-01-24
*/
/*
* Copyright (C) 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__NATIVE_CONFIG_H_
#define _INCLUDE__BASE__NATIVE_CONFIG_H_
#include <base/stdint.h>
namespace Genode {
struct Native_config
{
/**
* Thread-context area configuration
*/
static constexpr addr_t context_area_virtual_base() {
return 0x20000000UL; }
static constexpr addr_t context_area_virtual_size() {
return 0x10000000UL; }
/**
* Size of virtual address region holding the context of one thread
*/
static constexpr addr_t context_virtual_size() { return 0x00100000UL; }
};
}
#endif /* _INCLUDE__BASE__NATIVE_CONFIG_H_ */

View File

@ -1,8 +1,8 @@
#ifndef _INCLUDE__BASE__NATIVE_TYPES_H_
#define _INCLUDE__BASE__NATIVE_TYPES_H_
#include <base/native_config.h>
#include <base/cap_map.h>
#include <base/stdint.h>
namespace Fiasco {
#include <l4/sys/consts.h>
@ -178,20 +178,6 @@ namespace Genode {
typedef int Native_connection_state;
struct Native_config
{
/**
* Thread-context area configuration
*/
static addr_t context_area_virtual_base();
static addr_t context_area_virtual_size() { return 0x10000000UL; }
/**
* Size of virtual address region holding the context of one thread
*/
static addr_t context_virtual_size() { return 0x00100000UL; }
};
struct Native_pd_args { };
}

View File

@ -0,0 +1,38 @@
/*
* \brief Platform-specific context area definitions
* \author Stefan Kalkowski
* \date 2014-01-24
*/
/*
* Copyright (C) 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__NATIVE_CONFIG_H_
#define _INCLUDE__BASE__NATIVE_CONFIG_H_
#include <base/stdint.h>
namespace Genode {
struct Native_config
{
/**
* Thread-context area configuration
*/
static constexpr addr_t context_area_virtual_base() {
return 0x40000000UL; }
static constexpr addr_t context_area_virtual_size() {
return 0x10000000UL; }
/**
* Size of virtual address region holding the context of one thread
*/
static constexpr addr_t context_virtual_size() { return 0x00100000UL; }
};
}
#endif /* _INCLUDE__BASE__NATIVE_CONFIG_H_ */

View File

@ -1,3 +0,0 @@
include $(REP_DIR)/lib/mk/base.inc
SRC_CC += thread/thread_context_area.cc

View File

@ -1,3 +0,0 @@
include $(REP_DIR)/lib/mk/base.inc
SRC_CC += thread/arndale/thread_context_area.cc

View File

@ -1,3 +0,0 @@
include $(REP_DIR)/lib/mk/base.inc
SRC_CC += thread/thread_context_area.cc

View File

@ -1,19 +0,0 @@
/*
* \brief Arndale specific definition of the context area location
* \author Sebastian Sumpf
* \date 2013-02-12
*
* We need to place the context area within core outside the physical memory.
* Sigma0 maps physical to core-local memory always 1:1 when using
* SIGMA0_REQ_FPAGE_ANY. Those mappings would interfere with the context area.
*
* Because the UTCB area of a task resides at the end of the context area and
* its address gets calculated by core, the context area in other tasks needs
* to be at the same address as in core.
*/
#include <base/native_types.h>
using namespace Genode;
addr_t Native_config::context_area_virtual_base() { return 0x20000000UL; }

View File

@ -17,10 +17,6 @@
#include <util/string.h>
#include <util/misc_math.h>
namespace Fiasco {
#include <l4/sys/utcb.h>
}
using namespace Genode;
@ -41,7 +37,8 @@ namespace Genode {
Thread_base::Context *Thread_base::Context_allocator::base_to_context(addr_t base)
{
addr_t result = base + Native_config::context_virtual_size() - sizeof(Context);
addr_t result = base + Native_config::context_virtual_size()
- sizeof(Context);
return reinterpret_cast<Context *>(result);
}
@ -52,14 +49,17 @@ addr_t Thread_base::Context_allocator::addr_to_base(void *addr)
}
bool Thread_base::Context_allocator::_is_in_use(addr_t base)
size_t Thread_base::Context_allocator::base_to_idx(addr_t base)
{
List_element<Thread_base> *le = _threads.first();
for (; le; le = le->next())
if (base_to_context(base) == le->object()->_context)
return true;
return (base - Native_config::context_area_virtual_base()) /
Native_config::context_virtual_size();
}
return false;
addr_t Thread_base::Context_allocator::idx_to_base(size_t idx)
{
return Native_config::context_area_virtual_base() +
idx * Native_config::context_virtual_size();
}
@ -67,30 +67,19 @@ Thread_base::Context *Thread_base::Context_allocator::alloc(Thread_base *thread_
{
Lock::Guard _lock_guard(_threads_lock);
/*
* Find slot in context area for the new context
*/
addr_t base = Native_config::context_area_virtual_base();
for (; _is_in_use(base); base += Native_config::context_virtual_size()) {
/* check upper bound of context area */
if (base >= Native_config::context_area_virtual_base() + Native_config::context_area_virtual_size())
return 0;
try {
return base_to_context(idx_to_base(_alloc.alloc()));
} catch(Bit_allocator<MAX_THREADS>::Out_of_indices) {
return 0;
}
_threads.insert(&thread_base->_list_element);
return base_to_context(base);
}
void Thread_base::Context_allocator::free(Thread_base *thread_base)
void Thread_base::Context_allocator::free(Context *context)
{
Lock::Guard _lock_guard(_threads_lock);
_threads.remove(&thread_base->_list_element);
thread_base->_context->~Context();
_alloc.free(base_to_idx(addr_to_base(context)));
}
@ -124,7 +113,8 @@ Thread_base::Context *Thread_base::_alloc_context(size_t stack_size)
enum { PAGE_SIZE_LOG2 = 12 };
size_t ds_size = align_addr(stack_size, PAGE_SIZE_LOG2);
if (stack_size >= Native_config::context_virtual_size() - sizeof(Native_utcb) - (1 << PAGE_SIZE_LOG2))
if (stack_size >= Native_config::context_virtual_size() -
sizeof(Native_utcb) - (1UL << PAGE_SIZE_LOG2))
throw Stack_too_large();
/*
@ -132,8 +122,9 @@ Thread_base::Context *Thread_base::_alloc_context(size_t stack_size)
*
* The stack is always located at the top of the context.
*/
addr_t ds_addr = Context_allocator::addr_to_base(context) + Native_config::context_virtual_size()
- ds_size;
addr_t ds_addr = Context_allocator::addr_to_base(context) +
Native_config::context_virtual_size() -
ds_size;
/* add padding for UTCB if defined for the platform */
if (sizeof(Native_utcb) >= (1 << PAGE_SIZE_LOG2))
@ -144,41 +135,47 @@ Thread_base::Context *Thread_base::_alloc_context(size_t stack_size)
try {
ds_cap = env_context_area_ram_session()->alloc(ds_size);
addr_t attach_addr = ds_addr - Native_config::context_area_virtual_base();
env_context_area_rm_session()->attach_at(ds_cap, attach_addr, ds_size);
} catch (Ram_session::Alloc_failed) {
throw Stack_alloc_failed();
if (attach_addr != (addr_t)env_context_area_rm_session()->attach_at(ds_cap, attach_addr, ds_size))
throw Stack_alloc_failed();
}
catch (Ram_session::Alloc_failed) { throw Stack_alloc_failed(); }
/*
* Now the thread context is backed by memory, so it is safe to access its
* members.
*
* We need to initalize the context object's memory with zeroes,
* We need to initialize the context object's memory with zeroes,
* otherwise the ds_cap isn't invalid. That would cause trouble
* when the assignment operator of Native_capability is used.
*/
memset(context, 0, sizeof(Context));
memset(context, 0, sizeof(Context) - sizeof(Context::utcb));
context->thread_base = this;
context->stack_base = ds_addr;
context->ds_cap = ds_cap;
return context;
}
void Thread_base::_free_context()
void Thread_base::_free_context(Context* context)
{
addr_t ds_addr = _context->stack_base - Native_config::context_area_virtual_base();
Ram_dataspace_capability ds_cap = _context->ds_cap;
_context_allocator()->free(this);
addr_t ds_addr = context->stack_base - Native_config::context_area_virtual_base();
Ram_dataspace_capability ds_cap = context->ds_cap;
/* call de-constructor explicitly before memory gets detached */
context->~Context();
Genode::env_context_area_rm_session()->detach((void *)ds_addr);
Genode::env_context_area_ram_session()->free(ds_cap);
/* context area ready for reuse */
_context_allocator()->free(context);
}
void Thread_base::name(char *dst, size_t dst_len)
{
snprintf(dst, min(dst_len, (size_t)Context::NAME_LEN), _context->name);
snprintf(dst, min(dst_len, (size_t)Context::NAME_LEN), "%s", _context->name);
}
@ -196,9 +193,23 @@ void Thread_base::join()
}
void* Thread_base::alloc_secondary_stack(char const *name, size_t stack_size)
{
Context *context = _alloc_context(stack_size);
strncpy(context->name, name, sizeof(context->name));
return (void *)context->stack_top();
}
void Thread_base::free_secondary_stack(void* stack_addr)
{
addr_t base = Context_allocator::addr_to_base(stack_addr);
_free_context(Context_allocator::base_to_context(base));
}
Thread_base::Thread_base(const char *name, size_t stack_size)
:
_list_element(this),
_context(_alloc_context(stack_size)),
_join_lock(Lock::LOCKED)
{
@ -210,5 +221,5 @@ Thread_base::Thread_base(const char *name, size_t stack_size)
Thread_base::~Thread_base()
{
_deinit_platform_thread();
_free_context();
_free_context(_context);
}

View File

@ -1,11 +0,0 @@
/*
* \brief Generic definitions for the location of the thread-context area
* \author Sebastian Sumpf
* \date 2013-02-12
*/
#include <base/native_types.h>
using namespace Genode;
addr_t Native_config::context_area_virtual_base() { return 0x40000000UL; }

View File

@ -1,6 +1,3 @@
# override default location of thread context area within core
vpath thread_context_area.cc $(REP_DIR)/src/base/thread/arndale
include $(PRG_DIR)/../target.inc
LD_TEXT_ADDR = 0x80100000

View File

@ -33,7 +33,6 @@ SRC_CC = cap_session_component.cc \
signal_source_component.cc \
trace_session_component.cc \
thread_start.cc \
thread_context_area.cc \
core_printf.cc
INC_DIR += $(REP_DIR)/src/core/include \

View File

@ -37,13 +37,15 @@ namespace Genode {
/**
* Thread-context area configuration.
*/
static addr_t context_area_virtual_base() { return 0x40000000UL; }
static addr_t context_area_virtual_size() { return 0x10000000UL; }
static constexpr addr_t context_area_virtual_base() {
return 0x40000000UL; }
static constexpr addr_t context_area_virtual_size() {
return 0x10000000UL; }
/**
* Size of virtual address region holding the context of one thread
*/
static addr_t context_virtual_size() { return 0x00100000UL; }
static constexpr addr_t context_virtual_size() { return 0x00100000UL; }
};
struct Native_pd_args { };

View File

@ -106,13 +106,15 @@ namespace Genode
/**
* Thread-context area configuration.
*/
static addr_t context_area_virtual_base() { return 0x40000000UL; }
static addr_t context_area_virtual_size() { return 0x10000000UL; }
static constexpr addr_t context_area_virtual_base() {
return 0x40000000UL; }
static constexpr addr_t context_area_virtual_size() {
return 0x10000000UL; }
/**
* Size of virtual address region holding the context of one thread
*/
static addr_t context_virtual_size() { return 0x00100000UL; }
static constexpr addr_t context_virtual_size() { return 0x00100000UL; }
};
struct Native_pd_args { };

View File

@ -31,7 +31,6 @@ Native_utcb * Thread_base::utcb()
{
if (this) { return _tid.platform_thread->utcb_virt(); }
return _main_thread_utcb;
}
@ -55,8 +54,6 @@ void Thread_base::_thread_start()
Thread_base::Thread_base(const char * const label, size_t const stack_size)
:
_list_element(this)
{
_tid.platform_thread = new (platform()->core_mem_alloc())
Platform_thread(stack_size, Kernel::core_id(), label);

View File

@ -131,13 +131,15 @@ namespace Genode {
* Please update platform-specific files after changing these
* values, e.g., 'base-linux/src/platform/context_area.*.ld'.
*/
static addr_t context_area_virtual_base() { return 0x40000000UL; }
static addr_t context_area_virtual_size() { return 0x10000000UL; }
static constexpr addr_t context_area_virtual_base() {
return 0x40000000UL; }
static constexpr addr_t context_area_virtual_size() {
return 0x10000000UL; }
/**
* Size of virtual address region holding the context of one thread
*/
static addr_t context_virtual_size() { return 0x00100000UL; }
static constexpr addr_t context_virtual_size() { return 0x00100000UL; }
};
class Native_pd_args

View File

@ -402,8 +402,6 @@ void Thread_base::join()
Thread_base::Thread_base(const char *name, size_t stack_size)
:
_list_element(this)
{
_tid.meta_data = new (env()->heap()) Thread_meta_data_created(this);

View File

@ -257,13 +257,15 @@ namespace Genode {
/**
* Thread-context area configuration.
*/
static addr_t context_area_virtual_base() { return 0xa0000000UL; }
static addr_t context_area_virtual_size() { return 0x10000000UL; }
static constexpr addr_t context_area_virtual_base() {
return 0xa0000000UL; }
static constexpr addr_t context_area_virtual_size() {
return 0x10000000UL; }
/**
* Size of virtual address region holding the context of one thread
*/
static addr_t context_virtual_size() { return 0x00100000UL; }
static constexpr addr_t context_virtual_size() { return 0x00100000UL; }
};
struct Native_pd_args { };

View File

@ -95,13 +95,15 @@ namespace Genode {
/**
* Thread-context area configuration.
*/
static addr_t context_area_virtual_base() { return 0x40000000UL; }
static addr_t context_area_virtual_size() { return 0x10000000UL; }
static constexpr addr_t context_area_virtual_base() {
return 0x40000000UL; }
static constexpr addr_t context_area_virtual_size() {
return 0x10000000UL; }
/**
* Size of virtual address region holding the context of one thread
*/
static addr_t context_virtual_size() { return 0x00100000UL; }
static constexpr addr_t context_virtual_size() { return 0x00100000UL; }
};
struct Native_pd_args { };

View File

@ -79,13 +79,15 @@ namespace Genode {
/**
* Thread-context area configuration.
*/
static addr_t context_area_virtual_base() { return 0x40000000UL; }
static addr_t context_area_virtual_size() { return 0x10000000UL; }
static constexpr addr_t context_area_virtual_base() {
return 0x40000000UL; }
static constexpr addr_t context_area_virtual_size() {
return 0x10000000UL; }
/**
* Size of virtual address region holding the context of one thread
*/
static addr_t context_virtual_size() { return 0x00100000UL; }
static constexpr addr_t context_virtual_size() { return 0x00100000UL; }
};
struct Native_pd_args { };

View File

@ -39,6 +39,9 @@
* additional context members. Note that this memory is allocated from the RAM
* session of the process environment and not accounted for when using the
* 'sizeof()' operand on a 'Thread_base' object.
*
* A thread may be associated with more than one stack. Additional secondary
* stacks can be associated with a thread, and used for user level scheduling.
*/
/*
@ -57,7 +60,7 @@
#include <base/native_types.h>
#include <base/trace/logger.h>
#include <util/string.h>
#include <util/list.h>
#include <util/bit_allocator.h>
#include <ram_session/ram_session.h> /* for 'Ram_dataspace_capability' type */
#include <cpu_session/cpu_session.h> /* for 'Thread_capability' type */
@ -80,15 +83,6 @@ namespace Genode {
class Stack_too_large : public Exception { };
class Stack_alloc_failed : public Exception { };
private:
/**
* List-element helper to enable inserting threads in a list
*/
List_element<Thread_base> _list_element;
public:
/**
* Thread context located within the thread-context area
*
@ -169,8 +163,12 @@ namespace Genode {
{
private:
List<List_element<Thread_base> > _threads;
Lock _threads_lock;
static constexpr size_t MAX_THREADS =
Native_config::context_area_virtual_size() /
Native_config::context_virtual_size();
Bit_allocator<MAX_THREADS> _alloc;
Lock _threads_lock;
/**
* Detect if a context already exists at the specified address
@ -191,7 +189,7 @@ namespace Genode {
/**
* Release thread context
*/
void free(Thread_base *thread);
void free(Context *thread);
/**
* Return 'Context' object for a given base address
@ -202,6 +200,16 @@ namespace Genode {
* Return base address of context containing the specified address
*/
static addr_t addr_to_base(void *addr);
/**
* Return index in context area for a given base address
*/
static size_t base_to_idx(addr_t base);
/**
* Return base address of context given index in context area
*/
static addr_t idx_to_base(size_t idx);
};
/**
@ -217,7 +225,7 @@ namespace Genode {
/**
* Detach and release thread context of the thread
*/
void _free_context();
void _free_context(Context *context);
/**
* Platform-specific thread-startup code
@ -258,7 +266,7 @@ namespace Genode {
Genode::Pager_capability _pager_cap;
/**
* Pointer to corresponding thread context
* Pointer to primary thread context
*/
Context *_context;
@ -326,6 +334,28 @@ namespace Genode {
*/
void name(char *dst, size_t dst_len);
/**
* Add an additional stack to the thread
*
* \throw Stack_too_large
* \throw Stack_alloc_failed
* \throw Context_alloc_failed
*
* The stack for the new thread will be allocated from the RAM
* session of the process environment. A small portion of the
* stack size is internally used by the framework for storing
* thread-context information such as the thread's name (see
* 'struct Context').
*
* \return pointer to the new stack's top
*/
void* alloc_secondary_stack(char const *name, size_t stack_size);
/**
* Remove a secondary stack from the thread
*/
void free_secondary_stack(void* stack_addr);
/**
* Request capability of thread
*/

View File

@ -49,14 +49,17 @@ addr_t Thread_base::Context_allocator::addr_to_base(void *addr)
}
bool Thread_base::Context_allocator::_is_in_use(addr_t base)
size_t Thread_base::Context_allocator::base_to_idx(addr_t base)
{
List_element<Thread_base> *le = _threads.first();
for (; le; le = le->next())
if (base_to_context(base) == le->object()->_context)
return true;
return (base - Native_config::context_area_virtual_base()) /
Native_config::context_virtual_size();
}
return false;
addr_t Thread_base::Context_allocator::idx_to_base(size_t idx)
{
return Native_config::context_area_virtual_base() +
idx * Native_config::context_virtual_size();
}
@ -64,29 +67,19 @@ Thread_base::Context *Thread_base::Context_allocator::alloc(Thread_base *thread_
{
Lock::Guard _lock_guard(_threads_lock);
/*
* Find slot in context area for the new context
*/
addr_t base = Native_config::context_area_virtual_base();
for (; _is_in_use(base); base += Native_config::context_virtual_size()) {
/* check upper bound of context area */
if (base >= Native_config::context_area_virtual_base() +
Native_config::context_area_virtual_size())
return 0;
try {
return base_to_context(idx_to_base(_alloc.alloc()));
} catch(Bit_allocator<MAX_THREADS>::Out_of_indices) {
return 0;
}
_threads.insert(&thread_base->_list_element);
return base_to_context(base);
}
void Thread_base::Context_allocator::free(Thread_base *thread_base)
void Thread_base::Context_allocator::free(Context *context)
{
Lock::Guard _lock_guard(_threads_lock);
_threads.remove(&thread_base->_list_element);
_alloc.free(base_to_idx(addr_to_base(context)));
}
@ -159,23 +152,24 @@ Thread_base::Context *Thread_base::_alloc_context(size_t stack_size)
context->thread_base = this;
context->stack_base = ds_addr;
context->ds_cap = ds_cap;
return context;
}
void Thread_base::_free_context()
void Thread_base::_free_context(Context* context)
{
addr_t ds_addr = _context->stack_base - Native_config::context_area_virtual_base();
Ram_dataspace_capability ds_cap = _context->ds_cap;
addr_t ds_addr = context->stack_base - Native_config::context_area_virtual_base();
Ram_dataspace_capability ds_cap = context->ds_cap;
/* call de-constructor explicitly before memory gets detached */
_context->~Context();
context->~Context();
Genode::env_context_area_rm_session()->detach((void *)ds_addr);
Genode::env_context_area_ram_session()->free(ds_cap);
/* context area ready for reuse */
_context_allocator()->free(this);
_context_allocator()->free(context);
}
@ -211,9 +205,23 @@ void Thread_base::join()
}
void* Thread_base::alloc_secondary_stack(char const *name, size_t stack_size)
{
Context *context = _alloc_context(stack_size);
strncpy(context->name, name, sizeof(context->name));
return (void *)context->stack_top();
}
void Thread_base::free_secondary_stack(void* stack_addr)
{
addr_t base = Context_allocator::addr_to_base(stack_addr);
_free_context(Context_allocator::base_to_context(base));
}
Thread_base::Thread_base(const char *name, size_t stack_size)
:
_list_element(this),
_context(_alloc_context(stack_size)),
_join_lock(Lock::LOCKED)
{
@ -225,5 +233,5 @@ Thread_base::Thread_base(const char *name, size_t stack_size)
Thread_base::~Thread_base()
{
_deinit_platform_thread();
_free_context();
_free_context(_context);
}

View File

@ -64,15 +64,16 @@ class Routine : public Genode::List<Routine>::Element
/* will never return */
if (!_started) {
_started = true;
_stack = (char *)dde_kit_simple_malloc(STACK_SIZE);
Genode::Thread_base *th = Genode::Thread_base::myself();
_stack = (char *) th->alloc_secondary_stack(_name, STACK_SIZE);
if (verbose)
PDBG("Start func %s (%p) sp: %p", _name, _func, (_stack + STACK_SIZE));
PDBG("Start func %s (%p) sp: %p", _name, _func, _stack);
/* XXX move to platform code */
/* switch stack and call '_func(_arg)' */
platform_execute((void *)(_stack + STACK_SIZE), (void *)_func, _arg);
platform_execute((void *)(_stack), (void *)_func, _arg);
}
/* restore old state */
@ -122,7 +123,7 @@ class Routine : public Genode::List<Routine>::Element
~Routine()
{
if (_stack)
dde_kit_simple_free(_stack);
Genode::Thread_base::myself()->free_secondary_stack(_stack);
}
/**