/* uctx-setjmp.h - setjmp based user-space context switching * * Copyright (C) 2009 Timo Teräs * All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 or later as * published by the Free Software Foundation. * * See http://www.gnu.org/ for details. */ #include #include #include #include #ifdef VALGRIND #include #endif #define STACK_GUARD 0xbad57ac4 struct tf_uctx { int *stack_guard; void *alloc; jmp_buf jmpbuf; #ifdef VALGRIND unsigned int stack_id; #endif struct tf_fiber fiber; }; #define set_stack(ptr) ({ __asm__("movl %0, %%esp" : : "r"(ptr)); }) #define get_stack() ({ void *ptr; __asm__("movl %%esp, %0" : "=r"(ptr)); ptr; }) attribute_never_inline static void *tf_uctx_start(tf_fiber_proc fiber_main, size_t private_size) { struct tf_uctx *uctx; uctx = alloca(private_size); if (setjmp(uctx->jmpbuf) != 0) { fiber_main(uctx->fiber.data); tf_exit(); } return uctx; } static inline struct tf_fiber *tf_uctx_create(tf_fiber_proc fiber_main, int private_size) { size_t size = TF_STACK_SIZE; struct tf_uctx *uctx; void *stack, *old_stack, *stack_head, *stack_tail; #ifdef VALGRIND int stack_id; #endif stack = malloc(size); if (stack == NULL) return NULL; #ifdef VALGRIND stack_id = VALGRIND_STACK_REGISTER(stack, size); #endif /* Assumes stack grows up */ stack_head = stack + size - 8; stack_tail = stack; old_stack = get_stack(); set_stack(stack_head); uctx = tf_uctx_start(fiber_main, private_size + sizeof(struct tf_uctx)); set_stack(old_stack); #ifdef VALGRIND uctx->stack_id = stack_id; #endif uctx->alloc = stack; uctx->stack_guard = stack_tail; *uctx->stack_guard = STACK_GUARD; return &uctx->fiber; } static inline void tf_uctx_destroy(struct tf_fiber *fiber) { struct tf_uctx *uctx = container_of(fiber, struct tf_uctx, fiber); #ifdef VALGRIND VALGRIND_STACK_DEREGISTER(uctx->stack_id); #endif free(uctx->alloc); } static inline int tf_uctx_transfer(struct tf_fiber *from, struct tf_fiber *to, int value) { struct tf_uctx *ufrom = container_of(from, struct tf_uctx, fiber); struct tf_uctx *uto = container_of(to, struct tf_uctx, fiber); int r; TF_BUG_ON(unlikely(*ufrom->stack_guard != STACK_GUARD)); r = setjmp(ufrom->jmpbuf); if (r == 0) longjmp(uto->jmpbuf, value); return r; }