Commit d865bab55244797cce4f67a2a8196cbb0ceabc47
1 parent
d5975363
Implement thread creation.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4693 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
137 additions
and
11 deletions
linux-user/syscall.c
@@ -74,6 +74,11 @@ | @@ -74,6 +74,11 @@ | ||
74 | 74 | ||
75 | #if defined(USE_NPTL) | 75 | #if defined(USE_NPTL) |
76 | #include <linux/futex.h> | 76 | #include <linux/futex.h> |
77 | +#define CLONE_NPTL_FLAGS2 (CLONE_SETTLS | \ | ||
78 | + CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID) | ||
79 | +#else | ||
80 | +/* XXX: Hardcode the above values. */ | ||
81 | +#define CLONE_NPTL_FLAGS2 0 | ||
77 | #endif | 82 | #endif |
78 | 83 | ||
79 | //#define DEBUG | 84 | //#define DEBUG |
@@ -2706,6 +2711,48 @@ abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr) | @@ -2706,6 +2711,48 @@ abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr) | ||
2706 | 2711 | ||
2707 | #endif /* defined(TARGET_I386) */ | 2712 | #endif /* defined(TARGET_I386) */ |
2708 | 2713 | ||
2714 | +#if defined(USE_NPTL) | ||
2715 | + | ||
2716 | +#define NEW_STACK_SIZE PTHREAD_STACK_MIN | ||
2717 | + | ||
2718 | +static pthread_mutex_t clone_lock = PTHREAD_MUTEX_INITIALIZER; | ||
2719 | +typedef struct { | ||
2720 | + CPUState *env; | ||
2721 | + pthread_mutex_t mutex; | ||
2722 | + pthread_cond_t cond; | ||
2723 | + pthread_t thread; | ||
2724 | + uint32_t tid; | ||
2725 | + abi_ulong child_tidptr; | ||
2726 | + abi_ulong parent_tidptr; | ||
2727 | + sigset_t sigmask; | ||
2728 | +} new_thread_info; | ||
2729 | + | ||
2730 | +static void *clone_func(void *arg) | ||
2731 | +{ | ||
2732 | + new_thread_info *info = arg; | ||
2733 | + CPUState *env; | ||
2734 | + | ||
2735 | + env = info->env; | ||
2736 | + thread_env = env; | ||
2737 | + info->tid = gettid(); | ||
2738 | + if (info->child_tidptr) | ||
2739 | + put_user_u32(info->tid, info->child_tidptr); | ||
2740 | + if (info->parent_tidptr) | ||
2741 | + put_user_u32(info->tid, info->parent_tidptr); | ||
2742 | + /* Enable signals. */ | ||
2743 | + sigprocmask(SIG_SETMASK, &info->sigmask, NULL); | ||
2744 | + /* Signal to the parent that we're ready. */ | ||
2745 | + pthread_mutex_lock(&info->mutex); | ||
2746 | + pthread_cond_broadcast(&info->cond); | ||
2747 | + pthread_mutex_unlock(&info->mutex); | ||
2748 | + /* Wait until the parent has finshed initializing the tls state. */ | ||
2749 | + pthread_mutex_lock(&clone_lock); | ||
2750 | + pthread_mutex_unlock(&clone_lock); | ||
2751 | + cpu_loop(env); | ||
2752 | + /* never exits */ | ||
2753 | + return NULL; | ||
2754 | +} | ||
2755 | +#else | ||
2709 | /* this stack is the equivalent of the kernel stack associated with a | 2756 | /* this stack is the equivalent of the kernel stack associated with a |
2710 | thread/process */ | 2757 | thread/process */ |
2711 | #define NEW_STACK_SIZE 8192 | 2758 | #define NEW_STACK_SIZE 8192 |
@@ -2717,24 +2764,27 @@ static int clone_func(void *arg) | @@ -2717,24 +2764,27 @@ static int clone_func(void *arg) | ||
2717 | /* never exits */ | 2764 | /* never exits */ |
2718 | return 0; | 2765 | return 0; |
2719 | } | 2766 | } |
2767 | +#endif | ||
2720 | 2768 | ||
2721 | /* do_fork() Must return host values and target errnos (unlike most | 2769 | /* do_fork() Must return host values and target errnos (unlike most |
2722 | do_*() functions). */ | 2770 | do_*() functions). */ |
2723 | -int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp) | 2771 | +static int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp, |
2772 | + abi_ulong parent_tidptr, target_ulong newtls, | ||
2773 | + abi_ulong child_tidptr) | ||
2724 | { | 2774 | { |
2725 | int ret; | 2775 | int ret; |
2726 | TaskState *ts; | 2776 | TaskState *ts; |
2727 | uint8_t *new_stack; | 2777 | uint8_t *new_stack; |
2728 | CPUState *new_env; | 2778 | CPUState *new_env; |
2779 | +#if defined(USE_NPTL) | ||
2780 | + unsigned int nptl_flags; | ||
2781 | + sigset_t sigmask; | ||
2782 | +#endif | ||
2729 | 2783 | ||
2730 | if (flags & CLONE_VM) { | 2784 | if (flags & CLONE_VM) { |
2731 | #if defined(USE_NPTL) | 2785 | #if defined(USE_NPTL) |
2732 | - /* qemu is not threadsafe. Bail out immediately if application | ||
2733 | - tries to create a thread. */ | ||
2734 | - if (!(flags & CLONE_VFORK)) { | ||
2735 | - gemu_log ("clone(CLONE_VM) not supported\n"); | ||
2736 | - return -EINVAL; | ||
2737 | - } | 2786 | + new_thread_info info; |
2787 | + pthread_attr_t attr; | ||
2738 | #endif | 2788 | #endif |
2739 | ts = malloc(sizeof(TaskState) + NEW_STACK_SIZE); | 2789 | ts = malloc(sizeof(TaskState) + NEW_STACK_SIZE); |
2740 | init_task_state(ts); | 2790 | init_task_state(ts); |
@@ -2744,19 +2794,94 @@ int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp) | @@ -2744,19 +2794,94 @@ int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp) | ||
2744 | /* Init regs that differ from the parent. */ | 2794 | /* Init regs that differ from the parent. */ |
2745 | cpu_clone_regs(new_env, newsp); | 2795 | cpu_clone_regs(new_env, newsp); |
2746 | new_env->opaque = ts; | 2796 | new_env->opaque = ts; |
2797 | +#if defined(USE_NPTL) | ||
2798 | + nptl_flags = flags; | ||
2799 | + flags &= ~CLONE_NPTL_FLAGS2; | ||
2800 | + | ||
2801 | + /* TODO: Implement CLONE_CHILD_CLEARTID. */ | ||
2802 | + if (nptl_flags & CLONE_SETTLS) | ||
2803 | + cpu_set_tls (new_env, newtls); | ||
2804 | + | ||
2805 | + /* Grab a mutex so that thread setup appears atomic. */ | ||
2806 | + pthread_mutex_lock(&clone_lock); | ||
2807 | + | ||
2808 | + memset(&info, 0, sizeof(info)); | ||
2809 | + pthread_mutex_init(&info.mutex, NULL); | ||
2810 | + pthread_mutex_lock(&info.mutex); | ||
2811 | + pthread_cond_init(&info.cond, NULL); | ||
2812 | + info.env = new_env; | ||
2813 | + if (nptl_flags & CLONE_CHILD_SETTID) | ||
2814 | + info.child_tidptr = child_tidptr; | ||
2815 | + if (nptl_flags & CLONE_PARENT_SETTID) | ||
2816 | + info.parent_tidptr = parent_tidptr; | ||
2817 | + | ||
2818 | + ret = pthread_attr_init(&attr); | ||
2819 | + ret = pthread_attr_setstack(&attr, new_stack, NEW_STACK_SIZE); | ||
2820 | + /* It is not safe to deliver signals until the child has finished | ||
2821 | + initializing, so temporarily block all signals. */ | ||
2822 | + sigfillset(&sigmask); | ||
2823 | + sigprocmask(SIG_BLOCK, &sigmask, &info.sigmask); | ||
2824 | + | ||
2825 | + ret = pthread_create(&info.thread, &attr, clone_func, &info); | ||
2826 | + | ||
2827 | + sigprocmask(SIG_SETMASK, &info.sigmask, NULL); | ||
2828 | + pthread_attr_destroy(&attr); | ||
2829 | + if (ret == 0) { | ||
2830 | + /* Wait for the child to initialize. */ | ||
2831 | + pthread_cond_wait(&info.cond, &info.mutex); | ||
2832 | + ret = info.tid; | ||
2833 | + if (flags & CLONE_PARENT_SETTID) | ||
2834 | + put_user_u32(ret, parent_tidptr); | ||
2835 | + } else { | ||
2836 | + ret = -1; | ||
2837 | + } | ||
2838 | + pthread_mutex_unlock(&info.mutex); | ||
2839 | + pthread_cond_destroy(&info.cond); | ||
2840 | + pthread_mutex_destroy(&info.mutex); | ||
2841 | + pthread_mutex_unlock(&clone_lock); | ||
2842 | +#else | ||
2843 | + if (flags & CLONE_NPTL_FLAGS2) | ||
2844 | + return -EINVAL; | ||
2845 | + /* This is probably going to die very quickly, but do it anyway. */ | ||
2747 | #ifdef __ia64__ | 2846 | #ifdef __ia64__ |
2748 | ret = __clone2(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env); | 2847 | ret = __clone2(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env); |
2749 | #else | 2848 | #else |
2750 | ret = clone(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env); | 2849 | ret = clone(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env); |
2751 | #endif | 2850 | #endif |
2851 | +#endif | ||
2752 | } else { | 2852 | } else { |
2753 | /* if no CLONE_VM, we consider it is a fork */ | 2853 | /* if no CLONE_VM, we consider it is a fork */ |
2754 | - if ((flags & ~CSIGNAL) != 0) | 2854 | + if ((flags & ~(CSIGNAL | CLONE_NPTL_FLAGS2)) != 0) |
2755 | return -EINVAL; | 2855 | return -EINVAL; |
2856 | + fork_start(); | ||
2756 | ret = fork(); | 2857 | ret = fork(); |
2858 | +#if defined(USE_NPTL) | ||
2859 | + /* There is a race condition here. The parent process could | ||
2860 | + theoretically read the TID in the child process before the child | ||
2861 | + tid is set. This would require using either ptrace | ||
2862 | + (not implemented) or having *_tidptr to point at a shared memory | ||
2863 | + mapping. We can't repeat the spinlock hack used above because | ||
2864 | + the child process gets its own copy of the lock. */ | ||
2865 | + if (ret == 0) { | ||
2866 | + cpu_clone_regs(env, newsp); | ||
2867 | + fork_end(1); | ||
2868 | + /* Child Process. */ | ||
2869 | + if (flags & CLONE_CHILD_SETTID) | ||
2870 | + put_user_u32(gettid(), child_tidptr); | ||
2871 | + if (flags & CLONE_PARENT_SETTID) | ||
2872 | + put_user_u32(gettid(), parent_tidptr); | ||
2873 | + ts = (TaskState *)env->opaque; | ||
2874 | + if (flags & CLONE_SETTLS) | ||
2875 | + cpu_set_tls (env, newtls); | ||
2876 | + /* TODO: Implement CLONE_CHILD_CLEARTID. */ | ||
2877 | + } else { | ||
2878 | + fork_end(0); | ||
2879 | + } | ||
2880 | +#else | ||
2757 | if (ret == 0) { | 2881 | if (ret == 0) { |
2758 | cpu_clone_regs(env, newsp); | 2882 | cpu_clone_regs(env, newsp); |
2759 | } | 2883 | } |
2884 | +#endif | ||
2760 | } | 2885 | } |
2761 | return ret; | 2886 | return ret; |
2762 | } | 2887 | } |
@@ -3153,7 +3278,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, | @@ -3153,7 +3278,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, | ||
3153 | ret = do_brk(arg1); | 3278 | ret = do_brk(arg1); |
3154 | break; | 3279 | break; |
3155 | case TARGET_NR_fork: | 3280 | case TARGET_NR_fork: |
3156 | - ret = get_errno(do_fork(cpu_env, SIGCHLD, 0)); | 3281 | + ret = get_errno(do_fork(cpu_env, SIGCHLD, 0, 0, 0, 0)); |
3157 | break; | 3282 | break; |
3158 | #ifdef TARGET_NR_waitpid | 3283 | #ifdef TARGET_NR_waitpid |
3159 | case TARGET_NR_waitpid: | 3284 | case TARGET_NR_waitpid: |
@@ -4531,7 +4656,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, | @@ -4531,7 +4656,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, | ||
4531 | ret = get_errno(fsync(arg1)); | 4656 | ret = get_errno(fsync(arg1)); |
4532 | break; | 4657 | break; |
4533 | case TARGET_NR_clone: | 4658 | case TARGET_NR_clone: |
4534 | - ret = get_errno(do_fork(cpu_env, arg1, arg2)); | 4659 | + ret = get_errno(do_fork(cpu_env, arg1, arg2, arg3, arg4, arg5)); |
4535 | break; | 4660 | break; |
4536 | #ifdef __NR_exit_group | 4661 | #ifdef __NR_exit_group |
4537 | /* new thread calls */ | 4662 | /* new thread calls */ |
@@ -4967,7 +5092,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, | @@ -4967,7 +5092,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, | ||
4967 | #endif | 5092 | #endif |
4968 | #ifdef TARGET_NR_vfork | 5093 | #ifdef TARGET_NR_vfork |
4969 | case TARGET_NR_vfork: | 5094 | case TARGET_NR_vfork: |
4970 | - ret = get_errno(do_fork(cpu_env, CLONE_VFORK | CLONE_VM | SIGCHLD, 0)); | 5095 | + ret = get_errno(do_fork(cpu_env, CLONE_VFORK | CLONE_VM | SIGCHLD, |
5096 | + 0, 0, 0, 0)); | ||
4971 | break; | 5097 | break; |
4972 | #endif | 5098 | #endif |
4973 | #ifdef TARGET_NR_ugetrlimit | 5099 | #ifdef TARGET_NR_ugetrlimit |