aboutsummaryrefslogtreecommitdiff
path: root/linux-user
diff options
context:
space:
mode:
authorpbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>2009-03-07 15:24:59 +0000
committerpbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>2009-03-07 15:24:59 +0000
commitc2764719914ff0c4d6c06adafea17629600f21ba (patch)
tree9e08316ce7fc53cc2a2602a377e821333a7d6170 /linux-user
parent0b8a988c5d34c73815136bb41b10f67009b42a1c (diff)
The _exit syscall is used for both thread termination in NPTL applications,
and process termination in legacy applications. Try to guess which we want based on the presence of multiple threads. Also implement locking when modifying the CPU list. Signed-off-by: Paul Brook <paul@codesourcery.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6735 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'linux-user')
-rw-r--r--linux-user/main.c20
-rw-r--r--linux-user/qemu.h5
-rw-r--r--linux-user/signal.c2
-rw-r--r--linux-user/syscall.c49
4 files changed, 69 insertions, 7 deletions
diff --git a/linux-user/main.c b/linux-user/main.c
index 6e2984c44b..2c1e4df6e5 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -143,6 +143,7 @@ int64_t cpu_get_real_ticks(void)
We don't require a full sync, only that no cpus are executing guest code.
The alternative is to map target atomic ops onto host equivalents,
which requires quite a lot of per host/target work. */
+static pthread_mutex_t cpu_list_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t exclusive_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t exclusive_cond = PTHREAD_COND_INITIALIZER;
static pthread_cond_t exclusive_resume = PTHREAD_COND_INITIALIZER;
@@ -165,6 +166,7 @@ void fork_end(int child)
thread_env->next_cpu = NULL;
pending_cpus = 0;
pthread_mutex_init(&exclusive_lock, NULL);
+ pthread_mutex_init(&cpu_list_mutex, NULL);
pthread_cond_init(&exclusive_cond, NULL);
pthread_cond_init(&exclusive_resume, NULL);
pthread_mutex_init(&tb_lock, NULL);
@@ -237,6 +239,16 @@ static inline void cpu_exec_end(CPUState *env)
exclusive_idle();
pthread_mutex_unlock(&exclusive_lock);
}
+
+void cpu_list_lock(void)
+{
+ pthread_mutex_lock(&cpu_list_mutex);
+}
+
+void cpu_list_unlock(void)
+{
+ pthread_mutex_unlock(&cpu_list_mutex);
+}
#else /* if !USE_NPTL */
/* These are no-ops because we are not threadsafe. */
static inline void cpu_exec_start(CPUState *env)
@@ -265,6 +277,14 @@ void fork_end(int child)
gdbserver_fork(thread_env);
}
}
+
+void cpu_list_lock(void)
+{
+}
+
+void cpu_list_unlock(void)
+{
+}
#endif
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index 41375677fe..94ae3338e8 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -100,6 +100,9 @@ typedef struct TaskState {
uint32_t v86flags;
uint32_t v86mask;
#endif
+#ifdef USE_NPTL
+ abi_ulong child_tidptr;
+#endif
#ifdef TARGET_M68K
int sim_syscalls;
#endif
@@ -225,6 +228,8 @@ int target_msync(abi_ulong start, abi_ulong len, int flags);
extern unsigned long last_brk;
void mmap_lock(void);
void mmap_unlock(void);
+void cpu_list_lock(void);
+void cpu_list_unlock(void);
#if defined(USE_NPTL)
void mmap_fork_start(void);
void mmap_fork_end(int child);
diff --git a/linux-user/signal.c b/linux-user/signal.c
index 4f3741e912..48640ec835 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -2691,7 +2691,7 @@ static int setup_sigcontext(struct target_sigcontext *sc,
return err;
}
-static int restore_sigcontext(struct CPUState *regs,
+static int restore_sigcontext(CPUState *regs,
struct target_sigcontext *sc)
{
unsigned int err = 0;
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 40eab4e624..226ee6ca6e 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -156,7 +156,6 @@ static type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5, \
}
-#define __NR_sys_exit __NR_exit
#define __NR_sys_uname __NR_uname
#define __NR_sys_faccessat __NR_faccessat
#define __NR_sys_fchmodat __NR_fchmodat
@@ -198,7 +197,6 @@ static int gettid(void) {
return -ENOSYS;
}
#endif
-_syscall1(int,sys_exit,int,status)
_syscall1(int,sys_uname,struct new_utsname *,buf)
#if defined(TARGET_NR_faccessat) && defined(__NR_faccessat)
_syscall4(int,sys_faccessat,int,dirfd,const char *,pathname,int,mode,int,flags)
@@ -2936,7 +2934,10 @@ static int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp,
nptl_flags = flags;
flags &= ~CLONE_NPTL_FLAGS2;
- /* TODO: Implement CLONE_CHILD_CLEARTID. */
+ if (nptl_flags & CLONE_CHILD_CLEARTID) {
+ ts->child_tidptr = child_tidptr;
+ }
+
if (nptl_flags & CLONE_SETTLS)
cpu_set_tls (new_env, newtls);
@@ -2961,6 +2962,7 @@ static int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp,
sigprocmask(SIG_BLOCK, &sigmask, &info.sigmask);
ret = pthread_create(&info.thread, &attr, clone_func, &info);
+ /* TODO: Free new CPU state if thread creation failed. */
sigprocmask(SIG_SETMASK, &info.sigmask, NULL);
pthread_attr_destroy(&attr);
@@ -3011,7 +3013,8 @@ static int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp,
ts = (TaskState *)env->opaque;
if (flags & CLONE_SETTLS)
cpu_set_tls (env, newtls);
- /* TODO: Implement CLONE_CHILD_CLEARTID. */
+ if (flags & CLONE_CHILD_CLEARTID)
+ ts->child_tidptr = child_tidptr;
#endif
} else {
fork_end(0);
@@ -3428,12 +3431,46 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
switch(num) {
case TARGET_NR_exit:
+#ifdef USE_NPTL
+ /* In old applications this may be used to implement _exit(2).
+ However in threaded applictions it is used for thread termination,
+ and _exit_group is used for application termination.
+ Do thread termination if we have more then one thread. */
+ /* FIXME: This probably breaks if a signal arrives. We should probably
+ be disabling signals. */
+ if (first_cpu->next_cpu) {
+ CPUState **lastp;
+ CPUState *p;
+
+ cpu_list_lock();
+ lastp = &first_cpu;
+ p = first_cpu;
+ while (p && p != (CPUState *)cpu_env) {
+ lastp = &p->next_cpu;
+ p = p->next_cpu;
+ }
+ /* If we didn't find the CPU for this thread then something is
+ horribly wrong. */
+ if (!p)
+ abort();
+ /* Remove the CPU from the list. */
+ *lastp = p->next_cpu;
+ cpu_list_unlock();
+ TaskState *ts = ((CPUState *)cpu_env)->opaque;
+ if (ts->child_tidptr) {
+ put_user_u32(0, ts->child_tidptr);
+ sys_futex(g2h(ts->child_tidptr), FUTEX_WAKE, INT_MAX,
+ NULL, NULL, 0);
+ }
+ /* TODO: Free CPU state. */
+ pthread_exit(NULL);
+ }
+#endif
#ifdef HAVE_GPROF
_mcleanup();
#endif
gdb_exit(cpu_env, arg1);
- /* XXX: should free thread stack and CPU env */
- sys_exit(arg1);
+ _exit(arg1);
ret = 0; /* avoid warning */
break;
case TARGET_NR_read: