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 | 74 | |
| 75 | 75 | #if defined(USE_NPTL) |
| 76 | 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 | 82 | #endif |
| 78 | 83 | |
| 79 | 84 | //#define DEBUG |
| ... | ... | @@ -2706,6 +2711,48 @@ abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr) |
| 2706 | 2711 | |
| 2707 | 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 | 2756 | /* this stack is the equivalent of the kernel stack associated with a |
| 2710 | 2757 | thread/process */ |
| 2711 | 2758 | #define NEW_STACK_SIZE 8192 |
| ... | ... | @@ -2717,24 +2764,27 @@ static int clone_func(void *arg) |
| 2717 | 2764 | /* never exits */ |
| 2718 | 2765 | return 0; |
| 2719 | 2766 | } |
| 2767 | +#endif | |
| 2720 | 2768 | |
| 2721 | 2769 | /* do_fork() Must return host values and target errnos (unlike most |
| 2722 | 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 | 2775 | int ret; |
| 2726 | 2776 | TaskState *ts; |
| 2727 | 2777 | uint8_t *new_stack; |
| 2728 | 2778 | CPUState *new_env; |
| 2779 | +#if defined(USE_NPTL) | |
| 2780 | + unsigned int nptl_flags; | |
| 2781 | + sigset_t sigmask; | |
| 2782 | +#endif | |
| 2729 | 2783 | |
| 2730 | 2784 | if (flags & CLONE_VM) { |
| 2731 | 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 | 2788 | #endif |
| 2739 | 2789 | ts = malloc(sizeof(TaskState) + NEW_STACK_SIZE); |
| 2740 | 2790 | init_task_state(ts); |
| ... | ... | @@ -2744,19 +2794,94 @@ int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp) |
| 2744 | 2794 | /* Init regs that differ from the parent. */ |
| 2745 | 2795 | cpu_clone_regs(new_env, newsp); |
| 2746 | 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 | 2846 | #ifdef __ia64__ |
| 2748 | 2847 | ret = __clone2(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env); |
| 2749 | 2848 | #else |
| 2750 | 2849 | ret = clone(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env); |
| 2751 | 2850 | #endif |
| 2851 | +#endif | |
| 2752 | 2852 | } else { |
| 2753 | 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 | 2855 | return -EINVAL; |
| 2856 | + fork_start(); | |
| 2756 | 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 | 2881 | if (ret == 0) { |
| 2758 | 2882 | cpu_clone_regs(env, newsp); |
| 2759 | 2883 | } |
| 2884 | +#endif | |
| 2760 | 2885 | } |
| 2761 | 2886 | return ret; |
| 2762 | 2887 | } |
| ... | ... | @@ -3153,7 +3278,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, |
| 3153 | 3278 | ret = do_brk(arg1); |
| 3154 | 3279 | break; |
| 3155 | 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 | 3282 | break; |
| 3158 | 3283 | #ifdef TARGET_NR_waitpid |
| 3159 | 3284 | case TARGET_NR_waitpid: |
| ... | ... | @@ -4531,7 +4656,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, |
| 4531 | 4656 | ret = get_errno(fsync(arg1)); |
| 4532 | 4657 | break; |
| 4533 | 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 | 4660 | break; |
| 4536 | 4661 | #ifdef __NR_exit_group |
| 4537 | 4662 | /* new thread calls */ |
| ... | ... | @@ -4967,7 +5092,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, |
| 4967 | 5092 | #endif |
| 4968 | 5093 | #ifdef TARGET_NR_vfork |
| 4969 | 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 | 5097 | break; |
| 4972 | 5098 | #endif |
| 4973 | 5099 | #ifdef TARGET_NR_ugetrlimit | ... | ... |