* For each userland team the kernel creates an area in the userland

address space that is fully locked and marked B_KERNEL_AREA. It can
  thus be accessed by the kernel without additional checks.
* For each userland thread we do create a user_thread structure in that
  area. The structure is accessible from userland via TLS, using the
  private get_user_thread() function.
* Introduced private userland functions [un]defer_signals(). They can be
  used to cheaply disable/re-enable signal delivery. They use the
  user_thread::defer_signals/pending_signals fields which are
  checked/updated by the kernel.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25451 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2008-05-11 16:25:35 +00:00
parent 58148e2e02
commit d648afb8d7
10 changed files with 256 additions and 13 deletions
@@ -32,7 +32,8 @@
#define USER_SIZE (KERNEL_BASE - 0x10000)
#define USER_TOP (USER_BASE + USER_SIZE)
#define USER_STACK_REGION 0x70000000
#define USER_STACK_REGION_SIZE (USER_TOP - USER_STACK_REGION)
#define KERNEL_USER_DATA_BASE 0x6fff0000
#define USER_STACK_REGION 0x70000000
#define USER_STACK_REGION_SIZE (USER_TOP - USER_STACK_REGION)
#endif /* _KERNEL_ARCH_x86_KERNEL_H */
+3
View File
@@ -47,6 +47,9 @@ status_t start_watching_team(team_id team, void (*hook)(team_id, void *),
status_t stop_watching_team(team_id team, void (*hook)(team_id, void *),
void *data);
struct user_thread* team_allocate_user_thread(struct team* team);
void team_free_user_thread(struct thread* thread);
// used in syscalls.c
thread_id _user_load_image(int32 argCount, const char **args, int32 envCount,
const char **env, int32 priority, uint32 flags,
+17 -3
View File
@@ -71,6 +71,7 @@ enum {
struct image; // defined in image.c
struct realtime_sem_context; // defined in realtime_sem.cpp
struct select_info;
struct user_thread; // defined in libroot/user_thread.h
struct death_entry {
struct list_link link;
@@ -165,6 +166,11 @@ struct team_dead_children : team_job_control_children {
#endif // __cplusplus
struct free_user_thread {
struct free_user_thread* next;
struct user_thread* thread;
};
struct team {
struct team *next; // next in hash
struct team *siblings_next;
@@ -199,6 +205,12 @@ struct team {
struct list watcher_list;
struct arch_team arch_info;
addr_t user_data;
area_id user_data_area;
size_t user_data_size;
size_t used_user_data;
struct free_user_thread* free_user_threads;
struct team_debug_info debug_info;
bigtime_t dead_threads_kernel_time;
@@ -239,13 +251,15 @@ struct thread {
size_t signal_stack_size;
bool signal_stack_enabled;
bool in_kernel;
bool was_yielded;
struct user_thread* user_thread;
struct {
uint8 parameters[32];
} syscall_restart;
bool in_kernel;
bool was_yielded;
struct {
status_t status; // current wait status
uint32 flags; // interrupable flags
+1
View File
@@ -17,6 +17,7 @@ enum {
TLS_THREAD_ID_SLOT,
TLS_ERRNO_SLOT,
TLS_ON_EXIT_THREAD_SLOT,
TLS_USER_THREAD_SLOT,
// Note: these entries can safely be changed between
// releases; 3rd party code always calls tls_allocate()
+48
View File
@@ -0,0 +1,48 @@
/*
* Copyright 2008, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef _LIBROOT_USER_THREAD_H
#define _LIBROOT_USER_THREAD_H
#include <OS.h>
#include <TLS.h>
#include <tls.h> /* kernel header */
struct user_thread {
int32 defer_signals; // counter; 0 == signals allowed
uint32 pending_signals; // signals that are pending, when
// signals are deferred
status_t wait_status;
};
static inline user_thread*
get_user_thread()
{
return (user_thread*)tls_get(TLS_USER_THREAD_SLOT);
}
static void inline
defer_signals()
{
get_user_thread()->defer_signals++;
}
static void inline
undefer_signals()
{
user_thread* thread = get_user_thread();
if (--thread->defer_signals == 0 && thread->pending_signals != 0) {
// signals shall no longer be deferred -- call a dummy syscall to handle
// the pending ones
is_computer_on();
}
}
#endif /* _LIBROOT_USER_THREAD_H */
+1
View File
@@ -10,6 +10,7 @@ SubDir HAIKU_TOP src system kernel ;
SubDirC++Flags $(defines) ;
}
UsePrivateHeaders libroot ;
UsePrivateHeaders shared ;
UsePrivateHeaders runtime_loader ;
+3 -1
View File
@@ -309,15 +309,17 @@ arch_thread_init_kthread_stack(struct thread *t, int (*start_func)(void),
status_t
arch_thread_init_tls(struct thread *thread)
{
uint32 tls[TLS_THREAD_ID_SLOT + 1];
uint32 tls[TLS_USER_THREAD_SLOT + 1];
int32 i;
thread->user_local_storage = thread->user_stack_base
+ thread->user_stack_size;
// initialize default TLS fields
memset(tls, 0, sizeof(tls));
tls[TLS_BASE_ADDRESS_SLOT] = thread->user_local_storage;
tls[TLS_THREAD_ID_SLOT] = thread->id;
tls[TLS_USER_THREAD_SLOT] = (addr_t)thread->user_thread;
return user_memcpy((void *)thread->user_local_storage, tls, sizeof(tls));
}
+14
View File
@@ -24,6 +24,7 @@
#include <thread.h>
#include <tracing.h>
#include <user_debugger.h>
#include <user_thread.h>
#include <util/AutoLock.h>
@@ -42,6 +43,11 @@
#define DEFAULT_IGNORE_SIGNALS \
(SIGNAL_TO_MASK(SIGCHLD) | SIGNAL_TO_MASK(SIGWINCH) \
| SIGNAL_TO_MASK(SIGCONT))
#define NON_DEFERRABLE_SIGNALS \
(KILL_SIGNALS \
| SIGNAL_TO_MASK(SIGILL) \
| SIGNAL_TO_MASK(SIGFPE) \
| SIGNAL_TO_MASK(SIGSEGV))
const char * const sigstr[NSIG] = {
@@ -270,6 +276,14 @@ handle_signals(struct thread *thread)
if (signalMask == 0)
return 0;
if (thread->user_thread->defer_signals > 0
&& (signalMask & NON_DEFERRABLE_SIGNALS) == 0) {
thread->user_thread->pending_signals = signalMask;
return 0;
}
thread->user_thread->pending_signals = 0;
bool restart = (atomic_and(&thread->flags,
~THREAD_FLAGS_DONT_RESTART_SYSCALL)
& THREAD_FLAGS_DONT_RESTART_SYSCALL) == 0;
+152 -2
View File
@@ -1,4 +1,5 @@
/*
* Copyright 2008, Ingo Weinhold, [email protected].
* Copyright 2002-2008, Axel Dörfler, [email protected].
* Distributed under the terms of the MIT License.
*
@@ -35,6 +36,7 @@
#include <tls.h>
#include <tracing.h>
#include <user_runtime.h>
#include <user_thread.h>
#include <usergroup.h>
#include <vfs.h>
#include <vm.h>
@@ -69,6 +71,7 @@ struct fork_arg {
size_t user_stack_size;
addr_t user_local_storage;
sigset_t sig_block_mask;
struct user_thread* user_thread;
struct arch_fork_arg arch_info;
};
@@ -830,6 +833,11 @@ create_team_struct(const char *name, bool kernel)
team->state = TEAM_STATE_BIRTH;
team->flags = 0;
team->death_sem = -1;
team->user_data_area = -1;
team->user_data = 0;
team->used_user_data = 0;
team->user_data_size = 0;
team->free_user_threads = NULL;
team->supplementary_groups = NULL;
team->supplementary_group_count = 0;
@@ -911,6 +919,11 @@ delete_team_struct(struct team *team)
while (job_control_entry* entry = team->dead_children->entries.RemoveHead())
delete entry;
while (free_user_thread* entry = team->free_user_threads) {
team->free_user_threads = entry->next;
free(entry);
}
malloc_referenced_release(team->supplementary_groups);
delete team->job_control_entry;
@@ -935,6 +948,42 @@ get_arguments_data_size(char **args, int32 argc)
}
static status_t
create_team_user_data(struct team* team)
{
void* address = (void*)KERNEL_USER_DATA_BASE;
size_t size = 4 * B_PAGE_SIZE;
team->user_data_area = create_area_etc(team, "user area", &address,
B_BASE_ADDRESS, size, B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA);
if (team->user_data_area < 0)
return team->user_data_area;
team->user_data = (addr_t)address;
team->used_user_data = 0;
team->user_data_size = size;
team->free_user_threads = NULL;
return B_OK;
}
static void
delete_team_user_data(struct team* team)
{
if (team->user_data_area >= 0) {
delete_area_etc(team, team->user_data_area);
team->user_data = 0;
team->used_user_data = 0;
team->user_data_size = 0;
team->user_data_area = -1;
while (free_user_thread* entry = team->free_user_threads) {
team->free_user_threads = entry->next;
free(entry);
}
}
}
static void
free_team_arg(struct team_arg *teamArg)
{
@@ -1004,6 +1053,9 @@ team_create_thread_start(void *args)
TRACE(("team_create_thread_start: entry thread %ld\n", t->id));
// get a user thread for the main thread
t->user_thread = team_allocate_user_thread(team);
// create an initial primary stack area
// Main stack area layout is currently as follows (starting from 0):
@@ -1216,13 +1268,18 @@ load_image_etc(int32 argCount, char * const *args, int32 envCount,
else
threadName = args[0];
// create the user data area
status = create_team_user_data(team);
if (status != B_OK)
goto err4;
// Create a kernel thread, but under the context of the new team
// The new thread will take over ownership of teamArgs
thread = spawn_kernel_thread_etc(team_create_thread_start, threadName,
B_NORMAL_PRIORITY, teamArgs, team->id, team->id);
if (thread < 0) {
status = thread;
goto err4;
goto err5;
}
// wait for the loader of the new team to finish its work
@@ -1264,6 +1321,8 @@ load_image_etc(int32 argCount, char * const *args, int32 envCount,
return thread;
err5:
delete_team_user_data(team);
err4:
vm_put_address_space(team->address_space);
err3:
@@ -1362,6 +1421,7 @@ exec_team(const char *path, int32 argCount, char * const *args,
user_debug_prepare_for_exec();
delete_team_user_data(team);
vm_delete_areas(team->address_space);
delete_owned_ports(team->id);
sem_delete_owned_sems(team->id);
@@ -1370,6 +1430,14 @@ exec_team(const char *path, int32 argCount, char * const *args,
delete_realtime_sem_context(team->realtime_sem_context);
team->realtime_sem_context = NULL;
status = create_team_user_data(team);
if (status != B_OK) {
// creating the user data failed -- we're toast
// TODO: We should better keep the old user area in the first place.
exit_thread(status);
return status;
}
user_debug_finish_after_exec();
// rename the team
@@ -1422,6 +1490,7 @@ fork_team_thread_start(void *_args)
thread->user_stack_size = forkArgs->user_stack_size;
thread->user_local_storage = forkArgs->user_local_storage;
thread->sig_block_mask = forkArgs->sig_block_mask;
thread->user_thread = forkArgs->user_thread;
arch_thread_init_tls(thread);
@@ -1509,6 +1578,8 @@ fork_team(void)
// ToDo: should be able to handle stack areas differently (ie. don't have them copy-on-write)
// ToDo: all stacks of other threads than the current one could be left out
forkArgs->user_thread = NULL;
cookie = 0;
while (get_next_area_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) {
void *address;
@@ -1519,13 +1590,30 @@ fork_team(void)
break;
}
if (info.area == parentThread->user_stack_area)
if (info.area == parentThread->user_stack_area) {
forkArgs->user_stack_area = area;
} else if (info.area == parentTeam->user_data_area) {
team->user_data = (addr_t)address;
team->used_user_data = 0;
team->user_data_size = info.size;
team->user_data_area = area;
team->free_user_threads = NULL;
forkArgs->user_thread = team_allocate_user_thread(team);
}
}
if (status < B_OK)
goto err4;
if (forkArgs->user_thread == NULL) {
#if KDEBUG
panic("user data area not found, parent area is %ld",
parentTeam->user_data_area);
#endif
status = B_ERROR;
goto err4;
}
forkArgs->user_stack_base = parentThread->user_stack_base;
forkArgs->user_stack_size = parentThread->user_stack_size;
forkArgs->user_local_storage = parentThread->user_local_storage;
@@ -2558,6 +2646,68 @@ stop_watching_team(team_id teamID, void (*hook)(team_id, void *), void *data)
}
/*! The team lock must be held or the team must still be single threaded.
*/
struct user_thread*
team_allocate_user_thread(struct team* team)
{
if (team->user_data == 0)
return NULL;
user_thread* thread = NULL;
// take an entry from the free list, if any
if (struct free_user_thread* entry = team->free_user_threads) {
thread = entry->thread;
team->free_user_threads = entry->next;
deferred_free(entry);
return thread;
} else {
// enough space left?
size_t needed = _ALIGN(sizeof(user_thread));
if (team->user_data_size - team->used_user_data < needed)
return NULL;
// TODO: This imposes a per team thread limit! We should resize the
// area, if necessary. That's problematic at this point, though, since
// we've got the team lock.
thread = (user_thread*)(team->user_data + team->used_user_data);
team->used_user_data += needed;
}
thread->defer_signals = 0;
thread->pending_signals = 0;
thread->wait_status = B_OK;
return thread;
}
/*! The team lock must not be held. \a thread must be the current thread.
*/
void
team_free_user_thread(struct thread* thread)
{
user_thread* userThread = thread->user_thread;
if (userThread == NULL)
return;
// create a free list entry
free_user_thread* entry
= (free_user_thread*)malloc(sizeof(free_user_thread));
if (entry == NULL) {
// we have to leak the user thread :-/
return;
}
InterruptsSpinLocker _(team_spinlock);
entry->thread = userThread;
entry->next = thread->team->free_user_threads;
thread->team->free_user_threads = entry;
}
// #pragma mark - Public kernel API
+14 -5
View File
@@ -444,7 +444,16 @@ create_thread(thread_creation_attributes& attributes, bool kernel)
GRAB_TEAM_LOCK();
// look at the team, make sure it's not being deleted
team = team_get_team_struct_locked(attributes.team);
if (team != NULL && team->state != TEAM_STATE_DEATH) {
if (team == NULL || team->state == TEAM_STATE_DEATH)
abort = true;
if (!abort && !kernel) {
thread->user_thread = team_allocate_user_thread(team);
abort = thread->user_thread == NULL;
}
if (!abort) {
// Debug the new thread, if the parent thread required that (see above),
// or the respective global team debug flag is set. But only, if a
// debugger is installed for the team.
@@ -457,8 +466,7 @@ create_thread(thread_creation_attributes& attributes, bool kernel)
}
insert_thread_into_team(team, thread);
} else
abort = true;
}
RELEASE_TEAM_LOCK();
if (abort) {
@@ -1407,8 +1415,10 @@ thread_exit(void)
if (team->main_thread == thread) {
// this was the main thread in this team, so we will delete that as well
deleteTeam = true;
} else
} else {
threadDeathEntry = (death_entry*)malloc(sizeof(death_entry));
team_free_user_thread(thread);
}
// remove this thread from the current team and add it to the kernel
// put the thread into the kernel team until it dies
@@ -2937,4 +2947,3 @@ _user_setrlimit(int resource, const struct rlimit *userResourceLimit)
return common_setrlimit(resource, &resourceLimit);
}