summaryrefslogtreecommitdiffstats
path: root/lib/libkse
diff options
context:
space:
mode:
authorjasone <jasone@FreeBSD.org>2001-07-20 04:23:11 +0000
committerjasone <jasone@FreeBSD.org>2001-07-20 04:23:11 +0000
commita9a7a5e9d63f8a71f1d3dfe0e1ed1a28ab589215 (patch)
tree656e85a738aa3a12d8926a342cea87c2eae35e1d /lib/libkse
parentf7019990adf13ef4a3ccbd680c5c5ba8292d5f7f (diff)
downloadFreeBSD-src-a9a7a5e9d63f8a71f1d3dfe0e1ed1a28ab589215.zip
FreeBSD-src-a9a7a5e9d63f8a71f1d3dfe0e1ed1a28ab589215.tar.gz
Implement pthread_attr_[gs]etguardsize(). Non-default-size stacks used to
be malloc()ed, but they are now allocated using mmap(), just as the default-size stacks are. A separate cache of stacks is kept for non-default-size stacks. Collaboration with: deischen
Diffstat (limited to 'lib/libkse')
-rw-r--r--lib/libkse/test/Makefile4
-rw-r--r--lib/libkse/test/guard_b.c150
-rw-r--r--lib/libkse/test/guard_b.exp3
-rw-r--r--lib/libkse/test/guard_s.pl69
-rw-r--r--lib/libkse/thread/Makefile.inc3
-rw-r--r--lib/libkse/thread/thr_attr_getguardsize.c52
-rw-r--r--lib/libkse/thread/thr_attr_setguardsize.c57
-rw-r--r--lib/libkse/thread/thr_create.c69
-rw-r--r--lib/libkse/thread/thr_fork.c27
-rw-r--r--lib/libkse/thread/thr_init.c5
-rw-r--r--lib/libkse/thread/thr_private.h50
-rw-r--r--lib/libkse/thread/thr_stack.c235
12 files changed, 599 insertions, 125 deletions
diff --git a/lib/libkse/test/Makefile b/lib/libkse/test/Makefile
index ef0090e..0eb530c 100644
--- a/lib/libkse/test/Makefile
+++ b/lib/libkse/test/Makefile
@@ -12,10 +12,10 @@ CTESTS := hello_d.c hello_s.c join_leak_d.c mutex_d.c sem_d.c sigsuspend_d.c \
# C programs that are used internally by the tests. The build system merely
# compiles these.
-BTESTS := hello_b.c
+BTESTS := guard_b.c hello_b.c
# Tests written in perl.
-PTESTS := propagate_s.pl
+PTESTS := guard_s.pl propagate_s.pl
# Munge the file lists to their final executable names (strip the .c).
CTESTS := $(CTESTS:R)
diff --git a/lib/libkse/test/guard_b.c b/lib/libkse/test/guard_b.c
new file mode 100644
index 0000000..e2f1a3a
--- /dev/null
+++ b/lib/libkse/test/guard_b.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2001 Jason Evans <jasone@freebsd.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice(s), this list of conditions and the following disclaimer
+ * unmodified other than the allowable addition of one or more
+ * copyright notices.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice(s), this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ * Test thread stack guard functionality.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <pthread.h>
+
+#define FRAME_SIZE 1024
+#define FRAME_OVERHEAD 40
+
+struct args
+{
+ void *top; /* Top of thread's initial stack frame. */
+ int cur; /* Recursion depth. */
+ int max; /* Maximum recursion depth. */
+};
+
+void *
+recurse(void *args)
+{
+ int top;
+ struct args *parms = (struct args *)args;
+ char filler[FRAME_SIZE - FRAME_OVERHEAD];
+
+ /* Touch the memory in this stack frame. */
+ top = 0xa5;
+ memset(filler, 0xa5, sizeof(filler));
+
+ if (parms->top == NULL) {
+ /* Initial stack frame. */
+ parms->top = (void*)&top;
+ }
+
+ /*
+ * Make sure frame size is what we expect. Getting this right involves
+ * hand tweaking, so just print a warning rather than aborting.
+ */
+ if (parms->top - (void *)&top != FRAME_SIZE * parms->cur) {
+ fprintf(stderr, "Stack size (%d) != expected (%d), frame %d\n",
+ parms->top - (void *)&top, FRAME_SIZE * parms->cur,
+ parms->cur);
+ }
+
+ parms->cur++;
+ if (parms->cur < parms->max)
+ recurse(args);
+
+ return NULL;
+}
+
+
+int
+main(int argc, char **argv)
+{
+ size_t def_stacksize, def_guardsize;
+ size_t stacksize, guardsize;
+ pthread_t thread;
+ pthread_attr_t attr;
+ struct args args;
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: guard_b <stacksize> <guardsize>\n");
+ exit(1);
+ }
+ fprintf(stderr, "Test begin\n");
+
+ stacksize = strtoul(argv[1], NULL, 10);
+ guardsize = strtoul(argv[2], NULL, 10);
+
+ assert(pthread_attr_init(&attr) == 0);
+ /*
+ * Exercise the attribute APIs more thoroughly than is strictly
+ * necessary for the meat of this test program.
+ */
+ assert(pthread_attr_getstacksize(&attr, &def_stacksize) == 0);
+ assert(pthread_attr_getguardsize(&attr, &def_guardsize) == 0);
+ if (def_stacksize != stacksize) {
+ assert(pthread_attr_setstacksize(&attr, stacksize) == 0);
+ assert(pthread_attr_getstacksize(&attr, &def_stacksize) == 0);
+ assert(def_stacksize == stacksize);
+ }
+ if (def_guardsize != guardsize) {
+ assert(pthread_attr_setguardsize(&attr, guardsize) == 0);
+ assert(pthread_attr_getguardsize(&attr, &def_guardsize) == 0);
+ assert(def_guardsize >= guardsize);
+ }
+
+ /*
+ * Create a thread that will come just short of overflowing the thread
+ * stack. We need to leave a bit of breathing room in case the thread
+ * is context switched, and we also have to take care not to call any
+ * functions in the deepest stack frame.
+ */
+ args.top = NULL;
+ args.cur = 0;
+ args.max = (stacksize / FRAME_SIZE) - 1;
+ fprintf(stderr, "No overflow:\n");
+ assert(pthread_create(&thread, &attr, recurse, &args) == 0);
+ assert(pthread_join(thread, NULL) == 0);
+
+ /*
+ * Create a thread that will barely of overflow the thread stack. This
+ * should cause a segfault.
+ */
+ args.top = NULL;
+ args.cur = 0;
+ args.max = (stacksize / FRAME_SIZE) + 1;
+ fprintf(stderr, "Overflow:\n");
+ assert(pthread_create(&thread, &attr, recurse, &args) == 0);
+ assert(pthread_join(thread, NULL) == 0);
+
+ /* Not reached. */
+ fprintf(stderr, "Unexpected success\n");
+ abort();
+
+ return 0;
+}
diff --git a/lib/libkse/test/guard_b.exp b/lib/libkse/test/guard_b.exp
new file mode 100644
index 0000000..8e5b9e4
--- /dev/null
+++ b/lib/libkse/test/guard_b.exp
@@ -0,0 +1,3 @@
+Test begin
+No overflow:
+Overflow:
diff --git a/lib/libkse/test/guard_s.pl b/lib/libkse/test/guard_s.pl
new file mode 100644
index 0000000..7802ff3
--- /dev/null
+++ b/lib/libkse/test/guard_s.pl
@@ -0,0 +1,69 @@
+#!/usr/bin/perl -w
+#
+# Copyright (C) 2001 Jason Evans <jasone@freebsd.org>.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice(s), this list of conditions and the following disclaimer
+# unmodified other than the allowable addition of one or more
+# copyright notices.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice(s), this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+# Test thread stack guard functionality. The C test program needs to be driven
+# by this script because it segfaults when the stack guard is hit.
+#
+
+print "1..30\n";
+
+$i = 0;
+# Iterates 10 times.
+for ($stacksize = 65536; $stacksize < 131072; $stacksize += 7168)
+{
+ # Iterates 3 times (1024, 4096, 7168).
+ for ($guardsize = 1024; $guardsize < 8192; $guardsize += 3072)
+ {
+ $i++;
+
+ print "stacksize: $stacksize, guardsize: $guardsize\n";
+
+ `./guard_b $stacksize $guardsize >guard_b.out 2>&1`;
+
+ if (! -f "./guard_b.out")
+ {
+ print "not ok $i\n";
+ }
+ else
+ {
+ `diff guard_b.exp guard_b.out >guard_b.diff 2>&1`;
+ if ($?)
+ {
+ # diff returns non-zero if there is a difference.
+ print "not ok $i\n";
+ }
+ else
+ {
+ print "ok $i\n";
+ }
+ }
+ }
+}
diff --git a/lib/libkse/thread/Makefile.inc b/lib/libkse/thread/Makefile.inc
index 8c1e567..0eb1d59 100644
--- a/lib/libkse/thread/Makefile.inc
+++ b/lib/libkse/thread/Makefile.inc
@@ -13,6 +13,7 @@ SRCS+= \
uthread_attr_destroy.c \
uthread_attr_init.c \
uthread_attr_getdetachstate.c \
+ uthread_attr_getguardsize.c \
uthread_attr_getinheritsched.c \
uthread_attr_getschedparam.c \
uthread_attr_getschedpolicy.c \
@@ -21,6 +22,7 @@ SRCS+= \
uthread_attr_getstacksize.c \
uthread_attr_setcreatesuspend_np.c \
uthread_attr_setdetachstate.c \
+ uthread_attr_setguardsize.c \
uthread_attr_setinheritsched.c \
uthread_attr_setschedparam.c \
uthread_attr_setschedpolicy.c \
@@ -124,6 +126,7 @@ SRCS+= \
uthread_socketpair.c \
uthread_spec.c \
uthread_spinlock.c \
+ uthread_stack.c \
uthread_suspend_np.c \
uthread_switch_np.c \
uthread_system.c \
diff --git a/lib/libkse/thread/thr_attr_getguardsize.c b/lib/libkse/thread/thr_attr_getguardsize.c
new file mode 100644
index 0000000..849bf27
--- /dev/null
+++ b/lib/libkse/thread/thr_attr_getguardsize.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2001 Jason Evans <jasone@freebsd.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice(s), this list of conditions and the following disclaimer
+ * unmodified other than the allowable addition of one or more
+ * copyright notices.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice(s), this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <errno.h>
+#include <pthread.h>
+#include "pthread_private.h"
+
+__weak_reference(_pthread_attr_getguardsize, pthread_attr_getguardsize);
+
+int
+_pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize)
+{
+ int ret;
+
+ /* Check for invalid arguments: */
+ if (attr == NULL || *attr == NULL || guardsize == NULL)
+ ret = EINVAL;
+ else {
+ /* Return the guard size: */
+ *guardsize = (*attr)->guardsize_attr;
+ ret = 0;
+ }
+ return(ret);
+}
diff --git a/lib/libkse/thread/thr_attr_setguardsize.c b/lib/libkse/thread/thr_attr_setguardsize.c
new file mode 100644
index 0000000..0c3de14
--- /dev/null
+++ b/lib/libkse/thread/thr_attr_setguardsize.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2001 Jason Evans <jasone@freebsd.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice(s), this list of conditions and the following disclaimer
+ * unmodified other than the allowable addition of one or more
+ * copyright notices.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice(s), this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <errno.h>
+#include <pthread.h>
+#include "pthread_private.h"
+
+__weak_reference(_pthread_attr_setguardsize, pthread_attr_setguardsize);
+
+int
+_pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize)
+{
+ int ret;
+
+ /* Check for invalid arguments. */
+ if (attr == NULL || *attr == NULL)
+ ret = EINVAL;
+ else {
+ /* Round guardsize up to the nearest multiple of PAGE_SIZE. */
+ if (guardsize % PAGE_SIZE != 0)
+ guardsize = ((guardsize / PAGE_SIZE) + 1) * PAGE_SIZE;
+
+ /* Save the stack size. */
+ (*attr)->guardsize_attr = guardsize;
+ ret = 0;
+ }
+ return(ret);
+}
diff --git a/lib/libkse/thread/thr_create.c b/lib/libkse/thread/thr_create.c
index 40f5364..7c52b4b 100644
--- a/lib/libkse/thread/thr_create.c
+++ b/lib/libkse/thread/thr_create.c
@@ -38,8 +38,6 @@
#include <unistd.h>
#include <stddef.h>
#include <sys/time.h>
-#include <sys/param.h>
-#include <sys/mman.h>
#include <machine/reg.h>
#include <pthread.h>
#include "pthread_private.h"
@@ -99,68 +97,15 @@ _pthread_create(pthread_t * thread, const pthread_attr_t * attr,
/* Check if a stack was specified in the thread attributes: */
if ((stack = pattr->stackaddr_attr) != NULL) {
}
- /* Allocate memory for a default-size stack: */
- else if (pattr->stacksize_attr == PTHREAD_STACK_DEFAULT) {
- struct stack *spare_stack;
-
- /* Allocate or re-use a default-size stack. */
-
- /*
- * Use the garbage collector mutex for synchronization
- * of the spare stack list.
- */
- if (pthread_mutex_lock(&_gc_mutex) != 0)
- PANIC("Cannot lock gc mutex");
-
- if ((spare_stack = SLIST_FIRST(&_stackq)) != NULL) {
- /* Use the spare stack. */
- SLIST_REMOVE_HEAD(&_stackq, qe);
-
- /* Unlock the garbage collector mutex. */
- if (pthread_mutex_unlock(&_gc_mutex) != 0)
- PANIC("Cannot unlock gc mutex");
-
- stack = sizeof(struct stack)
- + (void *) spare_stack
- - PTHREAD_STACK_DEFAULT;
- } else {
- /* Allocate a new stack. */
- stack = _next_stack + PTHREAD_STACK_GUARD;
-
- /*
- * Even if stack allocation fails, we don't want
- * to try to use this location again, so
- * unconditionally decrement _next_stack. Under
- * normal operating conditions, the most likely
- * reason for an mmap() error is a stack
- * overflow of the adjacent thread stack.
- */
- _next_stack -= (PTHREAD_STACK_DEFAULT
- + PTHREAD_STACK_GUARD);
-
- /* Unlock the garbage collector mutex. */
- if (pthread_mutex_unlock(&_gc_mutex) != 0)
- PANIC("Cannot unlock gc mutex");
-
- /* Stack: */
- if (mmap(stack, PTHREAD_STACK_DEFAULT,
- PROT_READ | PROT_WRITE, MAP_STACK,
- -1, 0) == MAP_FAILED) {
- ret = EAGAIN;
- free(new_thread);
- }
+ /* Allocate a stack: */
+ else {
+ stack = _thread_stack_alloc(pattr->stacksize_attr,
+ pattr->guardsize_attr);
+ if (stack == NULL) {
+ ret = EAGAIN;
+ free(new_thread);
}
}
- /*
- * The user wants a stack of a particular size. Lets hope they
- * really know what they want, and simply malloc the stack.
- */
- else if ((stack = (void *) malloc(pattr->stacksize_attr))
- == NULL) {
- /* Insufficient memory to create a thread: */
- ret = EAGAIN;
- free(new_thread);
- }
/* Check for errors: */
if (ret != 0) {
diff --git a/lib/libkse/thread/thr_fork.c b/lib/libkse/thread/thr_fork.c
index 4437d88..9d9a647 100644
--- a/lib/libkse/thread/thr_fork.c
+++ b/lib/libkse/thread/thr_fork.c
@@ -31,6 +31,7 @@
*
* $FreeBSD$
*/
+#include <sys/param.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
@@ -220,28 +221,16 @@ _fork(void)
static void
free_thread_resources(struct pthread *thread)
{
- struct stack *spare_stack;
/* Check to see if the threads library allocated the stack. */
if ((thread->attr.stackaddr_attr == NULL) && (thread->stack != NULL)) {
- if (thread->attr.stacksize_attr != PTHREAD_STACK_DEFAULT) {
- /*
- * The threads library malloc()'d the stack;
- * just free() it.
- */
- free(thread->stack);
- } else {
- /*
- * This stack was allocated from the main threads
- * stack; cache it for future use. Since this is
- * being called from fork, we are currently single
- * threaded so there is no need to protect the
- * queue insertion.
- */
- spare_stack = (thread->stack + PTHREAD_STACK_DEFAULT -
- sizeof(struct stack));
- SLIST_INSERT_HEAD(&_stackq, spare_stack, qe);
- }
+ /*
+ * Since this is being called from fork, we are currently single
+ * threaded so there is no need to protect the call to
+ * _thread_stack_free() with _gc_mutex.
+ */
+ _thread_stack_free(thread->stack, thread->attr.stacksize_attr,
+ thread->attr.guardsize_attr);
}
if (thread->specific_data != NULL)
diff --git a/lib/libkse/thread/thr_init.c b/lib/libkse/thread/thr_init.c
index b63a111..9f10f74 100644
--- a/lib/libkse/thread/thr_init.c
+++ b/lib/libkse/thread/thr_init.c
@@ -268,9 +268,6 @@ _thread_init(void)
memcpy((void *) &_thread_initial->attr, &pthread_attr_default,
sizeof(struct pthread_attr));
- /* Initialize the thread stack cache: */
- SLIST_INIT(&_stackq);
-
/*
* Create a red zone below the main stack. All other stacks are
* constrained to a maximum size by the paramters passed to
@@ -279,7 +276,7 @@ _thread_init(void)
* thread stack that is just beyond.
*/
if (mmap((void *) USRSTACK - PTHREAD_STACK_INITIAL -
- PTHREAD_STACK_GUARD, PTHREAD_STACK_GUARD, 0, MAP_ANON,
+ PTHREAD_GUARD_DEFAULT, PTHREAD_GUARD_DEFAULT, 0, MAP_ANON,
-1, 0) == MAP_FAILED)
PANIC("Cannot allocate red zone for initial thread");
diff --git a/lib/libkse/thread/thr_private.h b/lib/libkse/thread/thr_private.h
index 6528c13..2bfac78 100644
--- a/lib/libkse/thread/thr_private.h
+++ b/lib/libkse/thread/thr_private.h
@@ -389,6 +389,7 @@ struct pthread_attr {
void (*cleanup_attr) ();
void *stackaddr_attr;
size_t stacksize_attr;
+ size_t guardsize_attr;
};
/*
@@ -414,13 +415,13 @@ enum pthread_susp {
*/
#define PTHREAD_STACK_DEFAULT 65536
/*
- * Size of red zone at the end of each stack. In actuality, this "red zone" is
- * merely an unmapped region, except in the case of the initial stack. Since
- * mmap() makes it possible to specify the maximum growth of a MAP_STACK region,
- * an unmapped gap between thread stacks achieves the same effect as explicitly
- * mapped red zones.
+ * Size of default red zone at the end of each stack. In actuality, this "red
+ * zone" is merely an unmapped region, except in the case of the initial stack.
+ * Since mmap() makes it possible to specify the maximum growth of a MAP_STACK
+ * region, an unmapped gap between thread stacks achieves the same effect as
+ * explicitly mapped red zones.
*/
-#define PTHREAD_STACK_GUARD PAGE_SIZE
+#define PTHREAD_GUARD_DEFAULT PAGE_SIZE
/*
* Maximum size of initial thread's stack. This perhaps deserves to be larger
@@ -875,11 +876,6 @@ struct pthread {
int lineno; /* Source line number. */
};
-/* Spare thread stack. */
-struct stack {
- SLIST_ENTRY(stack) qe; /* Queue entry for this stack. */
-};
-
/*
* Global variables for the uthread kernel.
*/
@@ -992,8 +988,9 @@ SCLASS struct pthread *_thread_initial
/* Default thread attributes: */
SCLASS struct pthread_attr pthread_attr_default
#ifdef GLOBAL_PTHREAD_PRIVATE
-= { SCHED_RR, 0, TIMESLICE_USEC, PTHREAD_DEFAULT_PRIORITY, PTHREAD_CREATE_RUNNING,
- PTHREAD_CREATE_JOINABLE, NULL, NULL, NULL, PTHREAD_STACK_DEFAULT };
+= { SCHED_RR, 0, TIMESLICE_USEC, PTHREAD_DEFAULT_PRIORITY,
+ PTHREAD_CREATE_RUNNING, PTHREAD_CREATE_JOINABLE, NULL, NULL, NULL,
+ PTHREAD_STACK_DEFAULT, PTHREAD_GUARD_DEFAULT };
#else
;
#endif
@@ -1142,31 +1139,6 @@ SCLASS pthread_switch_routine_t _sched_switch_hook
;
/*
- * Spare stack queue. Stacks of default size are cached in order to reduce
- * thread creation time. Spare stacks are used in LIFO order to increase cache
- * locality.
- */
-SCLASS SLIST_HEAD(, stack) _stackq;
-
-/*
- * Base address of next unallocated default-size {stack, red zone}. Stacks are
- * allocated contiguously, starting below the bottom of the main stack. When a
- * new stack is created, a red zone is created (actually, the red zone is simply
- * left unmapped) below the bottom of the stack, such that the stack will not be
- * able to grow all the way to the top of the next stack. This isn't
- * fool-proof. It is possible for a stack to grow by a large amount, such that
- * it grows into the next stack, and as long as the memory within the red zone
- * is never accessed, nothing will prevent one thread stack from trouncing all
- * over the next.
- */
-SCLASS void * _next_stack
-#ifdef GLOBAL_PTHREAD_PRIVATE
-/* main stack top - main stack size - stack size - (red zone + main stack red zone) */
-= (void *) USRSTACK - PTHREAD_STACK_INITIAL - PTHREAD_STACK_DEFAULT - (2 * PTHREAD_STACK_GUARD)
-#endif
-;
-
-/*
* Declare the kernel scheduler jump buffer and stack:
*/
SCLASS jmp_buf _thread_kern_sched_jb;
@@ -1210,6 +1182,8 @@ void _fd_lock_backout(pthread_t);
int _find_thread(pthread_t);
struct pthread *_get_curthread(void);
void _set_curthread(struct pthread *);
+void *_thread_stack_alloc(size_t, size_t);
+void _thread_stack_free(void *, size_t, size_t);
int _thread_create(pthread_t *,const pthread_attr_t *,void *(*start_routine)(void *),void *,pthread_t);
int _thread_fd_lock(int, int, struct timespec *);
int _thread_fd_lock_debug(int, int, struct timespec *,char *fname,int lineno);
diff --git a/lib/libkse/thread/thr_stack.c b/lib/libkse/thread/thr_stack.c
new file mode 100644
index 0000000..055af07
--- /dev/null
+++ b/lib/libkse/thread/thr_stack.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2001 Daniel Eischen <deischen@freebsd.org>
+ * Copyright (c) 2000-2001 Jason Evans <jasone@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/user.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include "pthread_private.h"
+
+/* Spare thread stack. */
+struct stack {
+ LIST_ENTRY(stack) qe; /* Stack queue linkage. */
+ size_t stacksize; /* Stack size (rounded up). */
+ size_t guardsize; /* Guard size. */
+ void *stackaddr; /* Stack address. */
+};
+
+/*
+ * Default sized (stack and guard) spare stack queue. Stacks are cached to
+ * avoid additional complexity managing mmap()ed stack regions. Spare stacks
+ * are used in LIFO order to increase cache locality.
+ */
+static LIST_HEAD(, stack) _dstackq = LIST_HEAD_INITIALIZER(_dstackq);
+
+/*
+ * Miscellaneous sized (non-default stack and/or guard) spare stack queue.
+ * Stacks are cached to avoid additional complexity managing mmap()ed stack
+ * regions. This list is unordered, since ordering on both stack size and guard
+ * size would be more trouble than it's worth. Stacks are allocated from this
+ * cache on a first size match basis.
+ */
+static LIST_HEAD(, stack) _mstackq = LIST_HEAD_INITIALIZER(_mstackq);
+
+/**
+ * Base address of the last stack allocated (including its red zone, if there is
+ * one). Stacks are allocated contiguously, starting beyond the top of the main
+ * stack. When a new stack is created, a red zone is typically created
+ * (actually, the red zone is simply left unmapped) above the top of the stack,
+ * such that the stack will not be able to grow all the way to the bottom of the
+ * next stack. This isn't fool-proof. It is possible for a stack to grow by a
+ * large amount, such that it grows into the next stack, and as long as the
+ * memory within the red zone is never accessed, nothing will prevent one thread
+ * stack from trouncing all over the next.
+ *
+ * low memory
+ * . . . . . . . . . . . . . . . . . .
+ * | |
+ * | stack 3 | start of 3rd thread stack
+ * +-----------------------------------+
+ * | |
+ * | Red Zone (guard page) | red zone for 2nd thread
+ * | |
+ * +-----------------------------------+
+ * | stack 2 - PTHREAD_STACK_DEFAULT | top of 2nd thread stack
+ * | |
+ * | |
+ * | |
+ * | |
+ * | stack 2 |
+ * +-----------------------------------+ <-- start of 2nd thread stack
+ * | |
+ * | Red Zone | red zone for 1st thread
+ * | |
+ * +-----------------------------------+
+ * | stack 1 - PTHREAD_STACK_DEFAULT | top of 1st thread stack
+ * | |
+ * | |
+ * | |
+ * | |
+ * | stack 1 |
+ * +-----------------------------------+ <-- start of 1st thread stack
+ * | | (initial value of last_stack)
+ * | Red Zone |
+ * | | red zone for main thread
+ * +-----------------------------------+
+ * | USRSTACK - PTHREAD_STACK_INITIAL | top of main thread stack
+ * | | ^
+ * | | |
+ * | | |
+ * | | | stack growth
+ * | |
+ * +-----------------------------------+ <-- start of main thread stack
+ * (USRSTACK)
+ * high memory
+ *
+ */
+static void * last_stack = (void *) USRSTACK - PTHREAD_STACK_INITIAL
+ - PTHREAD_GUARD_DEFAULT;
+
+void *
+_thread_stack_alloc(size_t stacksize, size_t guardsize)
+{
+ void *stack = NULL;
+ struct stack *spare_stack;
+ size_t stack_size;
+
+ /*
+ * Round up stack size to nearest multiple of PAGE_SIZE, so that mmap()
+ * will work. If the stack size is not an even multiple, we end up
+ * initializing things such that there is unused space above the
+ * beginning of the stack, so the stack sits snugly against its guard.
+ */
+ if (stacksize % PAGE_SIZE != 0)
+ stack_size = ((stacksize / PAGE_SIZE) + 1) * PAGE_SIZE;
+ else
+ stack_size = stacksize;
+
+ /*
+ * If the stack and guard sizes are default, try to allocate a stack
+ * from the default-size stack cache:
+ */
+ if (stack_size == PTHREAD_STACK_DEFAULT &&
+ guardsize == PTHREAD_GUARD_DEFAULT) {
+ /*
+ * Use the garbage collector mutex for synchronization of the
+ * spare stack list.
+ */
+ if (pthread_mutex_lock(&_gc_mutex) != 0)
+ PANIC("Cannot lock gc mutex");
+
+ if ((spare_stack = LIST_FIRST(&_dstackq)) != NULL) {
+ /* Use the spare stack. */
+ LIST_REMOVE(spare_stack, qe);
+ stack = spare_stack->stackaddr;
+ }
+
+ /* Unlock the garbage collector mutex. */
+ if (pthread_mutex_unlock(&_gc_mutex) != 0)
+ PANIC("Cannot unlock gc mutex");
+ }
+ /*
+ * The user specified a non-default stack and/or guard size, so try to
+ * allocate a stack from the non-default size stack cache, using the
+ * rounded up stack size (stack_size) in the search:
+ */
+ else {
+ /*
+ * Use the garbage collector mutex for synchronization of the
+ * spare stack list.
+ */
+ if (pthread_mutex_lock(&_gc_mutex) != 0)
+ PANIC("Cannot lock gc mutex");
+
+ LIST_FOREACH(spare_stack, &_mstackq, qe) {
+ if (spare_stack->stacksize == stack_size &&
+ spare_stack->guardsize == guardsize) {
+ LIST_REMOVE(spare_stack, qe);
+ stack = spare_stack->stackaddr;
+ break;
+ }
+ }
+
+ /* Unlock the garbage collector mutex. */
+ if (pthread_mutex_unlock(&_gc_mutex) != 0)
+ PANIC("Cannot unlock gc mutex");
+ }
+
+ /* Check if a stack was not allocated from a stack cache: */
+ if (stack == NULL) {
+
+ /* Allocate a new stack. */
+
+ stack = last_stack - stack_size;
+
+ /*
+ * Even if stack allocation fails, we don't want to try to use
+ * this location again, so unconditionally decrement
+ * last_stack. Under normal operating conditions, the most
+ * likely reason for an mmap() error is a stack overflow of the
+ * adjacent thread stack.
+ */
+ last_stack -= (stack_size + guardsize);
+
+ /* Stack: */
+ if (mmap(stack, stack_size, PROT_READ | PROT_WRITE, MAP_STACK,
+ -1, 0) == MAP_FAILED)
+ stack = NULL;
+ }
+
+ return (stack);
+}
+
+/* This function must be called with _gc_mutex held. */
+void
+_thread_stack_free(void *stack, size_t stacksize, size_t guardsize)
+{
+ struct stack *spare_stack;
+
+ spare_stack = (stack + stacksize - sizeof(struct stack));
+ /* Round stacksize up to nearest multiple of PAGE_SIZE. */
+ if (stacksize % PAGE_SIZE != 0) {
+ spare_stack->stacksize = ((stacksize / PAGE_SIZE) + 1) *
+ PAGE_SIZE;
+ } else
+ spare_stack->stacksize = stacksize;
+ spare_stack->guardsize = guardsize;
+ spare_stack->stackaddr = stack;
+
+ if (spare_stack->stacksize == PTHREAD_STACK_DEFAULT &&
+ spare_stack->guardsize == PTHREAD_GUARD_DEFAULT) {
+ /* Default stack/guard size. */
+ LIST_INSERT_HEAD(&_dstackq, spare_stack, qe);
+ } else {
+ /* Non-default stack/guard size. */
+ LIST_INSERT_HEAD(&_mstackq, spare_stack, qe);
+ }
+}
OpenPOWER on IntegriCloud