* 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:
@@ -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 */
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 */
|
||||
@@ -10,6 +10,7 @@ SubDir HAIKU_TOP src system kernel ;
|
||||
SubDirC++Flags $(defines) ;
|
||||
}
|
||||
|
||||
UsePrivateHeaders libroot ;
|
||||
UsePrivateHeaders shared ;
|
||||
UsePrivateHeaders runtime_loader ;
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user