Skip to content

Commit

Permalink
Merge pull request torvalds#225 from tavip/syscall-threads-autocleanup
Browse files Browse the repository at this point in the history
Syscall threads autocleanup
  • Loading branch information
tavip authored Sep 20, 2016
2 parents 81d90fa + 149d3f6 commit 0c98882
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 24 deletions.
8 changes: 5 additions & 3 deletions arch/lkl/include/uapi/asm/host_ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,12 @@ typedef unsigned long lkl_thread_t;
* @thread_join - wait for the given thread to terminate. Returns 0
* for success, -1 otherwise
*
* @tls_alloc - allocate a thread local storage key; returns 0 if succesful
* @tls_alloc - allocate a thread local storage key; returns 0 if successful; if
* destructor is not NULL it will be called when a thread terminates with its
* argument set to the current thread local storage value
* @tls_free - frees a thread local storage key; returns 0 if succesful
* @tls_set - associate data to the thread local storage key; returns 0 if
* succesful
* successful
* @tls_get - return data associated with the thread local storage key or NULL
* on error
*
Expand Down Expand Up @@ -85,7 +87,7 @@ struct lkl_host_operations {
void (*thread_exit)(void);
int (*thread_join)(lkl_thread_t tid);

int (*tls_alloc)(unsigned int *key);
int (*tls_alloc)(unsigned int *key, void (*destructor)(void *));
int (*tls_free)(unsigned int key);
int (*tls_set)(unsigned int key, void *data);
void *(*tls_get)(unsigned int key);
Expand Down
87 changes: 75 additions & 12 deletions arch/lkl/kernel/syscalls.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ static irqreturn_t syscall_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}

static int __lkl_stop_syscall_thread(struct syscall_thread_data *data,
bool host);
static void cleanup_syscall_threads(void);

int syscall_thread(void *_data)
{
struct syscall_thread_data *data;
Expand Down Expand Up @@ -131,15 +131,8 @@ int syscall_thread(void *_data)
lkl_ops->sem_up(data->completion);
}

if (data == &default_syscall_thread_data) {
struct syscall_thread_data *i = NULL, *aux;

list_for_each_entry_safe(i, aux, &syscall_threads, list) {
if (i == &default_syscall_thread_data)
continue;
__lkl_stop_syscall_thread(i, false);
}
}
if (data == &default_syscall_thread_data)
cleanup_syscall_threads();

pr_info("lkl: exiting syscall thread %s\n", current->comm);

Expand Down Expand Up @@ -331,12 +324,82 @@ sys_create_syscall_thread(struct syscall_thread_data *data)
return 0;
}


/*
* A synchronization algorithm between cleanup_syscall_threads (which terminates
* all remaining syscall threads) and destructors functions (which frees a
* syscall thread as soon as the associated host thread terminates) is required
* since destructor functions run in host context and is not subject to kernel
* scheduling.
*
* An atomic counter is used to count the number of running destructor functions
* and allows the cleanup function to wait for the running destructor functions
* to complete.
*
* The cleanup functions adds MAX_SYSCALL_THREADS to this counter and this
* allows the destructor functions to check if the cleanup process has started
* and abort execution. This prevents "late" destructors from trying to free the
* syscall threads.
*
* This algorithm assumes that we never have more the MAX_SYSCALL_THREADS
* running.
*/
#define MAX_SYSCALL_THREADS 1000000
static unsigned int destrs;

/*
* This is called when the host thread terminates if auto_syscall_threads is
* enabled. We use it to remove the associated kernel syscall thread since it is
* not going to be used anymore.
*
* Note that this run in host context, not kernel context.
*
* To avoid races between the destructor and lkl_sys_halt we announce that a
* destructor is running and also check to see if lkl_sys_halt is running, in
* which case we bail out - the kernel thread is going to be / has been stopped
* by lkl_sys_halt.
*/
static void syscall_thread_destructor(void *_data)
{
struct syscall_thread_data *data = _data;

if (!data)
return;

if (__sync_fetch_and_add(&destrs, 1) < MAX_SYSCALL_THREADS)
__lkl_stop_syscall_thread(data, true);
__sync_fetch_and_sub(&destrs, 1);
}

static void cleanup_syscall_threads(void)
{
struct syscall_thread_data *i = NULL, *aux;

/* announce destructors that we are stopping */
__sync_fetch_and_add(&destrs, MAX_SYSCALL_THREADS);

/* wait for any pending destructors to complete */
while (__sync_fetch_and_add(&destrs, 0) > MAX_SYSCALL_THREADS)
schedule_timeout(1);

/* no more destructors, we can safely remove the remaining threads */
list_for_each_entry_safe(i, aux, &syscall_threads, list) {
if (i == &default_syscall_thread_data)
continue;
__lkl_stop_syscall_thread(i, false);
}
}

int initial_syscall_thread(void *sem)
{
void (*destr)(void *) = NULL;
int ret = 0;

if (auto_syscall_threads)
destr = syscall_thread_destructor;

if (lkl_ops->tls_alloc)
ret = lkl_ops->tls_alloc(&syscall_thread_data_key);
ret = lkl_ops->tls_alloc(&syscall_thread_data_key, destr);
if (ret)
return ret;

Expand Down
1 change: 1 addition & 0 deletions tools/lkl/lib/Build
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
CFLAGS_posix-host.o += -D_FILE_OFFSET_BITS=64
CFLAGS_virtio_net_vde.o += $(shell pkg-config --cflags vdeplug)
CFLAGS_nt-host.o += -D_WIN32_WINNT=0x0600

lkl-y += fs.o
lkl-y += iomem.o
Expand Down
13 changes: 8 additions & 5 deletions tools/lkl/lib/nt-host.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,25 +88,28 @@ static int thread_join(lkl_thread_t tid)
return 0;
}

static int tls_alloc(unsigned int *key)
static int tls_alloc(unsigned int *key, void (*destructor)(void *))
{
*key = TlsAlloc();
*key = FlsAlloc((PFLS_CALLBACK_FUNCTION)destructor);
return *key == TLS_OUT_OF_INDEXES ? -1 : 0;
}

static int tls_free(unsigned int key)
{
return TlsFree(key) ? 0 : -1;
/* setting to NULL first to prevent the callback from being called */
if (!FlsSetValue(key, NULL))
return -1;
return FlsFree(key) ? 0 : -1;
}

static int tls_set(unsigned int key, void *data)
{
return TlsSetValue(key, data) ? 0 : -1;
return FlsSetValue(key, data) ? 0 : -1;
}

static void *tls_get(unsigned int key)
{
return TlsGetValue(key);
return FlsGetValue(key);
}


Expand Down
4 changes: 2 additions & 2 deletions tools/lkl/lib/posix-host.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,9 @@ static int thread_join(lkl_thread_t tid)
return 0;
}

static int tls_alloc(unsigned int *key)
static int tls_alloc(unsigned int *key, void (*destructor)(void *))
{
return pthread_key_create((pthread_key_t*)key, NULL);
return pthread_key_create((pthread_key_t *)key, destructor);
}

static int tls_free(unsigned int key)
Expand Down
40 changes: 38 additions & 2 deletions tools/lkl/tests/boot.c
Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,6 @@ static void test_thread(void *data)
fprintf(stderr, "%s: %s\n", __func__, lkl_strerror(ret));
}

lkl_stop_syscall_thread();
}

static int test_syscall_thread(char *str, int len)
Expand Down Expand Up @@ -775,7 +774,36 @@ static int test_syscall_thread(char *str, int len)
return TEST_SUCCESS;
}

void thread_quit_immediately(void *unused)
#ifndef __MINGW32__
static void thread_get_pid(void *unused)
{
lkl_sys_getpid();
}

static int test_many_syscall_threads(char *str, int len)
{
lkl_thread_t tid;
int count = 65, ret;

while (--count > 0) {
tid = lkl_host_ops.thread_create(thread_get_pid, NULL);
if (!tid) {
snprintf(str, len, "failed to create thread");
return TEST_FAILURE;
}

ret = lkl_host_ops.thread_join(tid);
if (ret) {
snprintf(str, len, "failed to join thread");
return TEST_FAILURE;
}
}

return TEST_SUCCESS;
}
#endif

static void thread_quit_immediately(void *unused)
{
}

Expand Down Expand Up @@ -911,6 +939,14 @@ int main(int argc, char **argv)
TEST(gettid);
TEST(syscall_thread);
TEST(join);
/*
* Wine has an issue where the FlsCallback is not called when the thread
* terminates which makes testing the automatic syscall threads cleanup
* impossible under wine.
*/
#ifndef __MINGW32__
TEST(many_syscall_threads);
#endif

lkl_sys_halt();

Expand Down

0 comments on commit 0c98882

Please sign in to comment.