diff options
Diffstat (limited to 'kernel/tests/lib/cloner.c')
-rw-r--r-- | kernel/tests/lib/cloner.c | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/kernel/tests/lib/cloner.c b/kernel/tests/lib/cloner.c new file mode 100644 index 0000000..11401f2 --- /dev/null +++ b/kernel/tests/lib/cloner.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) International Business Machines Corp., 2009 + * Some wrappers for clone functionality. Thrown together by Serge Hallyn + * <serue@us.ibm.com> based on existing clone usage in ltp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <sched.h> +#include <stdarg.h> +#include "config.h" +#include "tst_clone.h" + +#undef clone /* we want to use clone() */ + +/* + * The ia64 port has never included a prototype for __clone2(). It was updated + * to take eight parameters in glibc commit: + * + * commit 625f22fc7f8e0d61e3e6cff2c65468b91dbad426 + * Author: Ulrich Drepper <drepper@redhat.com> + * Date: Mon Mar 3 19:53:27 2003 +0000 + * + * The first release that contained this commit was glibc-2.3.3 which is old + * enough to assume that __clone2() takes eight parameters. + */ +#if defined(__ia64__) +extern int __clone2(int (*fn) (void *arg), void *child_stack_base, + size_t child_stack_size, int flags, void *arg, + pid_t *parent_tid, void *tls, pid_t *child_tid); +#endif + +#ifndef CLONE_SUPPORTS_7_ARGS +# define clone(fn, stack, flags, arg, ptid, tls, ctid) \ + clone(fn, stack, flags, arg) +#endif + +/* + * ltp_clone: wrapper for clone to hide the architecture dependencies. + * 1. hppa takes bottom of stack and no stacksize (stack grows up) + * 2. __ia64__ takes bottom of stack and uses clone2 + * 3. all others take top of stack (stack grows down) + */ +static int +ltp_clone_(unsigned long flags, int (*fn)(void *arg), void *arg, + size_t stack_size, void *stack, pid_t *ptid, void *tls, pid_t *ctid) +{ + int ret; + +#if defined(__ia64__) + ret = __clone2(fn, stack, stack_size, flags, arg, ptid, tls, ctid); +#else +# if defined(__hppa__) || defined(__metag__) + /* + * These arches grow their stack up, so don't need to adjust the base. + * XXX: This should be made into a runtime test. + */ +# else + /* + * For archs where stack grows downwards, stack points to the topmost + * address of the memory space set up for the child stack. + */ + if (stack) + stack += stack_size; +# endif + + ret = clone(fn, stack, flags, arg, ptid, tls, ctid); +#endif + + return ret; +} + +int ltp_clone(unsigned long flags, int (*fn)(void *arg), void *arg, + size_t stack_size, void *stack) +{ + return ltp_clone_(flags, fn, arg, stack_size, stack, NULL, NULL, NULL); +} + +int ltp_clone7(unsigned long flags, int (*fn)(void *arg), void *arg, + size_t stack_size, void *stack, ...) +{ + pid_t *ptid, *ctid; + void *tls; + va_list arg_clone; + + va_start(arg_clone, stack); + ptid = va_arg(arg_clone, pid_t *); + tls = va_arg(arg_clone, void *); + ctid = va_arg(arg_clone, pid_t *); + va_end(arg_clone); + +#ifdef CLONE_SUPPORTS_7_ARGS + return ltp_clone_(flags, fn, arg, stack_size, stack, ptid, tls, ctid); +#else + errno = ENOSYS; + return -1; +#endif +} + +/* + * ltp_alloc_stack: allocate stack of size 'size', that is sufficiently + * aligned for all arches. User is responsible for freeing allocated + * memory. + * Returns pointer to new stack. On error, returns NULL with errno set. + */ +void *ltp_alloc_stack(size_t size) +{ + void *ret = NULL; + int err; + + err = posix_memalign(&ret, 64, size); + if (err) + errno = err; + + return ret; +} + +/* + * ltp_clone_alloc: also does the memory allocation for clone with a + * caller-specified size. + */ +int +ltp_clone_alloc(unsigned long clone_flags, int (*fn) (void *arg), void *arg, + size_t stack_size) +{ + void *stack; + int ret; + int saved_errno; + + stack = ltp_alloc_stack(stack_size); + if (stack == NULL) + return -1; + + ret = ltp_clone(clone_flags, fn, arg, stack_size, stack); + + if (ret == -1) { + saved_errno = errno; + free(stack); + errno = saved_errno; + } + + return ret; +} + +/* + * ltp_clone_quick: calls ltp_clone_alloc with predetermined stack size. + * Experience thus far suggests that one page is often insufficient, + * while 6*getpagesize() seems adequate. + */ +int ltp_clone_quick(unsigned long clone_flags, int (*fn) (void *arg), void *arg) +{ + size_t stack_size = getpagesize() * 6; + + return ltp_clone_alloc(clone_flags, fn, arg, stack_size); +} |