#include <inttypes.h>
#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
#include <ccan/ptrint/ptrint.h>
#include <ccan/compiler/compiler.h>
#include <ccan/build_assert/build_assert.h>
((char *)buf + bufsize - metasize) - 1;
#endif
- stack->magic = COROUTINE_STACK_MAGIC;
+ stack->magic = COROUTINE_STACK_MAGIC_BUF;
stack->size = size;
vg_register_stack(stack);
return stack;
}
+struct coroutine_stack *coroutine_stack_alloc(size_t totalsize, size_t metasize)
+{
+ struct coroutine_stack *stack;
+ size_t pgsz = getpagesize();
+ size_t mapsize;
+ char *map, *guard;
+ int rc;
+
+ mapsize = ((totalsize + (pgsz - 1)) & ~(pgsz - 1)) + pgsz;
+
+ map = mmap(NULL, mapsize, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (map == MAP_FAILED)
+ return NULL;
+
+#if HAVE_STACK_GROWS_UPWARDS
+ guard = map + mapsize - pgsz;
+ stack = (struct coroutine_stack *)(guard - totalsize + metasize);
+#else
+ guard = map;
+ stack = (struct coroutine_stack *)(map + pgsz + totalsize - metasize)
+ - 1;
+#endif
+
+ rc = mprotect(guard, pgsz, PROT_NONE);
+ if (rc != 0) {
+ munmap(map, mapsize);
+ return NULL;
+ }
+
+ stack->magic = COROUTINE_STACK_MAGIC_ALLOC;
+ stack->size = totalsize - sizeof(*stack) - metasize;
+
+ vg_register_stack(stack);
+
+ return stack;
+}
+
+static void coroutine_stack_free(struct coroutine_stack *stack, size_t metasize)
+{
+ void *map;
+ size_t pgsz = getpagesize();
+ size_t totalsize = stack->size + sizeof(*stack) + metasize;
+ size_t mapsize = ((totalsize + (pgsz - 1)) & ~(pgsz - 1)) + pgsz;
+
+#if HAVE_STACK_GROWS_UPWARDS
+ map = (char *)(stack + 1) + stack->size + pgsz - mapsize;
+#else
+ map = (char *)stack - stack->size - pgsz;
+#endif
+
+ munmap(map, mapsize);
+}
+
void coroutine_stack_release(struct coroutine_stack *stack, size_t metasize)
{
vg_deregister_stack(stack);
- memset(stack, 0, sizeof(*stack));
+
+ switch (stack->magic) {
+ case COROUTINE_STACK_MAGIC_BUF:
+ memset(stack, 0, sizeof(*stack));
+ break;
+
+ case COROUTINE_STACK_MAGIC_ALLOC:
+ coroutine_stack_free(stack, metasize);
+ break;
+
+ default:
+ abort();
+ }
}
struct coroutine_stack *coroutine_stack_check(struct coroutine_stack *stack,
const char *abortstr)
{
if (stack && vg_addressable(stack, sizeof(*stack))
- && (stack->magic == COROUTINE_STACK_MAGIC)
+ && ((stack->magic == COROUTINE_STACK_MAGIC_BUF)
+ || (stack->magic == COROUTINE_STACK_MAGIC_ALLOC))
&& (stack->size >= COROUTINE_MIN_STKSZ))
return stack;
#define COROUTINE_MIN_STKSZ 2048
/**
- * COROUTINE_STACK_MAGIC - Magic number for coroutine stacks
+ * COROUTINE_STACK_MAGIC_BUF - Magic number for coroutine stacks in a user
+ * supplied buffer
*/
-#define COROUTINE_STACK_MAGIC 0xc040c040574c574c
+#define COROUTINE_STACK_MAGIC_BUF 0xc040c040574cb00f
+/**
+ * COROUTINE_STACK_MAGIC_ALLOC - Magic number for coroutine stacks
+ * allocated by this module
+ */
+#define COROUTINE_STACK_MAGIC_ALLOC 0xc040c040574ca110
/**
* coroutine_stack_init - Prepare a coroutine stack in an existing buffer
struct coroutine_stack *coroutine_stack_init(void *buf, size_t bufsize,
size_t metasize);
+/**
+ * coroutine_stack_alloc - Allocate a coroutine stack
+ * @totalsize: total size to allocate
+ * @metasize: size of metadata to add to the stack (not including
+ * coroutine internal overhead)
+ *
+ * Allocates a coroutine stack of size @totalsize, including both
+ * internal overhead (COROUTINE_STK_OVERHEAD) and metadata of size
+ * @metasize. Where available this will also create a guard page, so
+ * that overruning the stack will result in an immediate crash, rather
+ * than data corruption.
+ *
+ * This will fail if the totalsize < (COROUTINE_MIN_STKSZ +
+ * COROUTINE_STK_OVERHEAD + metasize).
+ */
+struct coroutine_stack *coroutine_stack_alloc(size_t bufsize, size_t metasize);
+
/**
* coroutine_stack_release - Stop using a coroutine stack
* @stack: coroutine stack to release
#include <ccan/coroutine/coroutine.h>
#include <ccan/tap/tap.h>
+#define STKSZ (COROUTINE_MIN_STKSZ + COROUTINE_STK_OVERHEAD)
+
static int global = 0;
static void trivial_fn(void *p)
{
struct coroutine_state t, master;
+ ok1(stack != NULL);
+ ok1(coroutine_stack_check(stack, NULL) == stack);
+ ok1(coroutine_stack_size(stack) == COROUTINE_MIN_STKSZ);
+
if (!COROUTINE_AVAILABLE) {
skip(1, "Coroutines not available");
return;
}
-static char buf[COROUTINE_MIN_STKSZ + COROUTINE_STK_OVERHEAD];
+static char buf[STKSZ];
int main(void)
{
struct coroutine_stack *stack;
/* This is how many tests you plan to run */
- plan_tests(4);
+ plan_tests(2 * 4 + 1);
stack = coroutine_stack_init(buf, sizeof(buf), 0);
- ok1(stack != NULL);
- ok1(coroutine_stack_check(stack, NULL) == stack);
- ok1(coroutine_stack_size(stack) == COROUTINE_MIN_STKSZ);
-
test_trivial(stack);
+ coroutine_stack_release(stack, 0);
+ ok1(!coroutine_stack_check(stack, NULL));
+ stack = coroutine_stack_alloc(STKSZ, 0);
+ test_trivial(stack);
coroutine_stack_release(stack, 0);
/* This exits depending on whether all tests passed */
static void test1(size_t bufsz)
{
- void *buf1, *buf2;
struct coroutine_stack *stack1, *stack2;
- buf1 = malloc(bufsz);
- ok1(buf1 != NULL);
- stack1 = coroutine_stack_init(buf1, bufsz, 0);
- diag("buf1=%p stack1=%p bufsz=0x%zx overhead=0x%zx\n",
- buf1, stack1, bufsz, COROUTINE_STK_OVERHEAD);
+ stack1 = coroutine_stack_alloc(bufsz, 0);
ok1(coroutine_stack_check(stack1, NULL) == stack1);
ok1(coroutine_stack_size(stack1) == bufsz - COROUTINE_STK_OVERHEAD);
- buf2 = malloc(bufsz);
- ok1(buf2 != NULL);
- stack2 = coroutine_stack_init(buf2, bufsz, 0);
+ stack2 = coroutine_stack_alloc(bufsz, 0);
ok1(coroutine_stack_check(stack2, NULL) == stack2);
ok1(coroutine_stack_size(stack2) == bufsz - COROUTINE_STK_OVERHEAD);
ok(1, "Completed test1");
coroutine_stack_release(stack1, 0);
- ok1(coroutine_stack_check(stack1, NULL) == NULL);
- free(buf1);
coroutine_stack_release(stack2, 0);
- ok1(coroutine_stack_check(stack2, NULL) == NULL);
- free(buf2);
}
int main(void)
{
/* This is how many tests you plan to run */
- plan_tests(14);
+ plan_tests(10);
test1(8192);
{
struct metadata *meta;
+ ok1(stack != NULL);
+ ok1(coroutine_stack_check(stack, NULL) == stack);
+ ok1(coroutine_stack_size(stack)
+ == BUFSIZE - COROUTINE_STK_OVERHEAD - sizeof(struct metadata));
+
meta = coroutine_stack_to_metadata(stack, sizeof(*meta));
ok1(coroutine_stack_from_metadata(meta, sizeof(*meta)) == stack);
struct coroutine_stack *stack;
/* This is how many tests you plan to run */
- plan_tests(10);
+ plan_tests(1 + 2 * 9);
/* Fix seed so we get consistent, though pseudo-random results */
srandom(0);
+ stack = coroutine_stack_alloc(BUFSIZE, sizeof(struct metadata));
+ test_metadata(stack);
+ coroutine_stack_release(stack, sizeof(struct metadata));
+
buf = malloc(BUFSIZE);
ok1(buf != NULL);
-
stack = coroutine_stack_init(buf, BUFSIZE, sizeof(struct metadata));
- ok1(stack != NULL);
- ok1(coroutine_stack_check(stack, NULL) == stack);
- ok1(coroutine_stack_size(stack)
- == BUFSIZE - COROUTINE_STK_OVERHEAD - sizeof(struct metadata));
-
test_metadata(stack);
-
coroutine_stack_release(stack, sizeof(struct metadata));
free(buf);