summaryrefslogtreecommitdiff
path: root/sys/compat/linux/common/linux_exec.c
diff options
context:
space:
mode:
authorchs <chs@NetBSD.org>2010-07-07 01:30:32 +0000
committerchs <chs@NetBSD.org>2010-07-07 01:30:32 +0000
commit33fa5ccbbf17bc2390e2d136c34926c6f93ac5d1 (patch)
treec52fdcf81396fba59fce0a9c410d0dcc8c104091 /sys/compat/linux/common/linux_exec.c
parent56acc98394a822ba38a843f6385171e0b1aa5e9a (diff)
many changes for COMPAT_LINUX:
- update the linux syscall table for each platform. - support new-style (NPTL) linux pthreads on all platforms. clone() with CLONE_THREAD uses 1 process with many LWPs instead of separate processes. - move the contents of sys__lwp_setprivate() into a new lwp_setprivate() and use that everywhere. - update linux_release[] and linux32_release[] to "2.6.18". - adjust placement of emul fork/exec/exit hooks as needed and adjust other emul code to match. - convert all struct emul definitions to use named initializers. - change the pid allocator to allow multiple pids to refer to the same proc. - remove a few fields from struct proc that are no longer needed. - disable the non-functional "vdso" code in linux32/amd64, glibc works fine without it. - fix a race in the futex code where we could miss a wakeup after a requeue operation. - redo futex locking to be a little more efficient.
Diffstat (limited to 'sys/compat/linux/common/linux_exec.c')
-rw-r--r--sys/compat/linux/common/linux_exec.c334
1 files changed, 61 insertions, 273 deletions
diff --git a/sys/compat/linux/common/linux_exec.c b/sys/compat/linux/common/linux_exec.c
index 148f97fe878..afe9b3f54f3 100644
--- a/sys/compat/linux/common/linux_exec.c
+++ b/sys/compat/linux/common/linux_exec.c
@@ -1,4 +1,4 @@
-/* $NetBSD: linux_exec.c,v 1.113 2009/10/25 01:14:03 rmind Exp $ */
+/* $NetBSD: linux_exec.c,v 1.114 2010/07/07 01:30:35 chs Exp $ */
/*-
* Copyright (c) 1994, 1995, 1998, 2000, 2007, 2008 The NetBSD Foundation, Inc.
@@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: linux_exec.c,v 1.113 2009/10/25 01:14:03 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: linux_exec.c,v 1.114 2010/07/07 01:30:35 chs Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -74,15 +74,6 @@ extern struct sysent linux_sysent[];
extern const char * const linux_syscallnames[];
extern char linux_sigcode[], linux_esigcode[];
-static void linux_e_proc_exec(struct proc *, struct exec_package *);
-static void linux_e_proc_fork(struct proc *, struct proc *, int);
-static void linux_e_proc_exit(struct proc *);
-static void linux_e_proc_init(struct proc *, struct proc *, int);
-
-#ifdef LINUX_NPTL
-void linux_userret(void);
-#endif
-
/*
* Emulation switch.
*/
@@ -94,7 +85,7 @@ struct emul emul_linux = {
.e_path = "/emul/linux",
#ifndef __HAVE_MINIMAL_EMUL
.e_flags = 0,
- .e_errno = (const int *)native_to_linux_errno,
+ .e_errno = native_to_linux_errno,
.e_nosys = LINUX_SYS_syscall,
.e_nsysent = LINUX_SYS_NSYSENT,
#endif
@@ -110,8 +101,8 @@ struct emul emul_linux = {
.e_proc_exec = linux_e_proc_exec,
.e_proc_fork = linux_e_proc_fork,
.e_proc_exit = linux_e_proc_exit,
- .e_lwp_fork = NULL,
- .e_lwp_exit = NULL,
+ .e_lwp_fork = linux_e_lwp_fork,
+ .e_lwp_exit = linux_e_lwp_exit,
#ifdef __HAVE_SYSCALL_INTERN
.e_syscall_intern = linux_syscall_intern,
#else
@@ -126,293 +117,90 @@ struct emul emul_linux = {
.e_startlwp = NULL
};
-static void
-linux_e_proc_init(struct proc *p, struct proc *parent, int forkflags)
+void
+linux_e_proc_exec(struct proc *p, struct exec_package *epp)
{
- struct linux_emuldata *e = p->p_emuldata;
- struct linux_emuldata_shared *s;
- struct linux_emuldata *ep = NULL;
-
- if (!e) {
- /* allocate new Linux emuldata */
- e = malloc(sizeof(struct linux_emuldata),
- M_EMULDATA, M_WAITOK);
- } else {
- mutex_enter(proc_lock);
- e->s->refs--;
- if (e->s->refs == 0)
- free(e->s, M_EMULDATA);
- mutex_exit(proc_lock);
- }
-
- memset(e, '\0', sizeof(struct linux_emuldata));
-
- e->proc = p;
- e->robust_futexes = NULL;
+ struct lwp *l;
- if (parent)
- ep = parent->p_emuldata;
-
- if (forkflags & FORK_SHAREVM) {
- mutex_enter(proc_lock);
-#ifdef DIAGNOSTIC
- if (ep == NULL) {
- killproc(p, "FORK_SHAREVM while emuldata is NULL\n");
- mutex_exit(proc_lock);
- free(e, M_EMULDATA);
- return;
- }
-#endif
- s = ep->s;
- s->refs++;
+ l = LIST_FIRST(&p->p_lwps);
+ if (l->l_emuldata == NULL) {
+ l->l_emuldata = kmem_zalloc(sizeof(struct linux_emuldata), KM_SLEEP);
} else {
- struct vmspace *vm;
-
- s = malloc(sizeof(struct linux_emuldata_shared),
- M_EMULDATA, M_WAITOK);
- s->refs = 1;
-
- /*
- * Set the process idea of the break to the real value.
- * For fork, we use parent's vmspace since our's
- * is not setup at the time of this call and is going
- * to be copy of parent's anyway. For exec, just
- * use our own vmspace.
- */
- vm = (parent) ? parent->p_vmspace : p->p_vmspace;
- s->p_break = (char *)vm->vm_daddr + ctob(vm->vm_dsize);
-
- /*
- * Linux threads are emulated as NetBSD processes (not lwp)
- * We use native PID for Linux TID. The Linux TID is the
- * PID of the first process in the group. It is stored
- * here
- */
- s->group_pid = p->p_pid;
-
- /*
- * Initialize the list of threads in the group
- */
- LIST_INIT(&s->threads);
-
- s->xstat = 0;
- s->flags = 0;
- mutex_enter(proc_lock);
+ memset(l->l_emuldata, 0, sizeof (struct linux_emuldata));
}
- e->s = s;
-
- /*
- * Add this thread in the group thread list
- */
- LIST_INSERT_HEAD(&s->threads, e, threads);
-
-#ifdef LINUX_NPTL
- if (ep != NULL) {
- e->parent_tidptr = ep->parent_tidptr;
- e->child_tidptr = ep->child_tidptr;
- e->clone_flags = ep->clone_flags;
- }
-#endif /* LINUX_NPTL */
-
- mutex_exit(proc_lock);
- p->p_emuldata = e;
-}
-
-/*
- * Allocate new per-process structures. Called when executing Linux
- * process. We can reuse the old emuldata - if it's not null,
- * the executed process is of same emulation as original forked one.
- */
-static void
-linux_e_proc_exec(struct proc *p, struct exec_package *epp)
-{
-
- /* exec, use our vmspace */
- linux_e_proc_init(p, NULL, 0);
+ KASSERT(p->p_nlwps == 1);
+ l = LIST_FIRST(&p->p_lwps);
+ mutex_enter(p->p_lock);
+ l->l_lid = p->p_pid;
+ mutex_exit(p->p_lock);
}
-/*
- * Emulation per-process exit hook.
- */
-static void
+void
linux_e_proc_exit(struct proc *p)
{
- struct linux_emuldata *e = p->p_emuldata;
-
-#ifdef LINUX_NPTL
- linux_nptl_proc_exit(p);
- release_futexes(p);
-#endif
-
- /* Remove the thread for the group thread list */
- mutex_enter(proc_lock);
- LIST_REMOVE(e, threads);
+ struct lwp *l;
- /* free Linux emuldata and set the pointer to null */
- e->s->refs--;
- if (e->s->refs == 0)
- free(e->s, M_EMULDATA);
- p->p_emuldata = NULL;
- mutex_exit(proc_lock);
- free(e, M_EMULDATA);
+ KASSERT(p->p_nlwps == 1);
+ l = LIST_FIRST(&p->p_lwps);
+ linux_e_lwp_exit(l);
}
-/*
- * Emulation fork hook.
- */
-static void
-linux_e_proc_fork(struct proc *p, struct proc *parent, int forkflags)
+void
+linux_e_proc_fork(struct proc *p2, struct lwp *l1, int flags)
{
-
- /*
- * The new process might share some vmspace-related stuff
- * with parent, depending on fork flags (CLONE_VM et.al).
- * Force allocation of new base emuldata, and share the
- * VM-related parts only if necessary.
- */
- p->p_emuldata = NULL;
- linux_e_proc_init(p, parent, forkflags);
-
-#ifdef LINUX_NPTL
- linux_nptl_proc_fork(p, parent, linux_userret);
-#endif
-
- return;
+ struct linux_emuldata *led1, *led2;
+ struct lwp *l2;
+
+ KASSERT(p2->p_nlwps == 1);
+ l2 = LIST_FIRST(&p2->p_lwps);
+ l2->l_lid = p2->p_pid;
+ led1 = l1->l_emuldata;
+ led2 = l2->l_emuldata;
+ led2->led_child_tidptr = led1->led_child_tidptr;
}
-#ifdef LINUX_NPTL
void
-linux_userret(void)
+linux_e_lwp_fork(struct lwp *l1, struct lwp *l2)
{
- struct lwp *l = curlwp;
- struct proc *p = l->l_proc;
- struct linux_emuldata *led = p->p_emuldata;
- int error;
-
- /* LINUX_CLONE_CHILD_SETTID: copy child's TID to child's memory */
- if (led->clone_flags & LINUX_CLONE_CHILD_SETTID) {
- if ((error = copyout(&l->l_proc->p_pid,
- led->child_tidptr, sizeof(l->l_proc->p_pid))) != 0)
- printf("linux_userret: LINUX_CLONE_CHILD_SETTID "
- "failed (led->child_tidptr = %p, p->p_pid = %d)\n",
- led->child_tidptr, p->p_pid);
- }
-
- /* LINUX_CLONE_SETTLS: allocate a new TLS */
- if (led->clone_flags & LINUX_CLONE_SETTLS) {
- if (linux_set_newtls(l, linux_get_newtls(l)) != 0)
- printf("linux_userret: linux_set_tls failed");
- }
+ struct linux_emuldata *led2;
- return;
+ led2 = kmem_zalloc(sizeof(struct linux_emuldata), KM_SLEEP);
+ l2->l_emuldata = led2;
}
void
-linux_nptl_proc_exit(struct proc *p)
+linux_e_lwp_exit(struct lwp *l)
{
- struct linux_emuldata *e = p->p_emuldata;
-
- mutex_enter(proc_lock);
-
- /*
- * Check if we are a thread group leader victim of another
- * thread doing exit_group(). If we are, change the exit code.
- */
- if ((e->s->group_pid == p->p_pid) &&
- (e->s->flags & LINUX_LES_INEXITGROUP)) {
- p->p_xstat = e->s->xstat;
- }
-
- /*
- * Members of the thread groups others than the leader should
- * exit quietely: no zombie stage, no signal. We do that by
- * reparenting to init. init will collect us and nobody will
- * notice what happened.
- */
-#ifdef DEBUG_LINUX
- printf("%s:%d e->s->group_pid = %d, p->p_pid = %d, flags = 0x%x\n",
- __func__, __LINE__, e->s->group_pid, p->p_pid, e->s->flags);
-#endif
- if ((e->s->group_pid != p->p_pid) &&
- (e->clone_flags & LINUX_CLONE_THREAD)) {
- proc_reparent(p, initproc);
- cv_broadcast(&initproc->p_waitcv);
+ struct linux_emuldata *led;
+ struct linux_sys_futex_args cup;
+ register_t retval;
+ int error, zero = 0;
+
+ led = l->l_emuldata;
+ if (led->led_clear_tid == NULL) {
+ return;
}
- mutex_exit(proc_lock);
-
/* Emulate LINUX_CLONE_CHILD_CLEARTID */
- if (e->clear_tid != NULL) {
- int error;
- int null = 0;
- struct linux_sys_futex_args cup;
- register_t retval;
-
- error = copyout(&null, e->clear_tid, sizeof(null));
+ error = copyout(&zero, led->led_clear_tid, sizeof(zero));
#ifdef DEBUG_LINUX
- if (error != 0)
- printf("%s: cannot clear TID\n", __func__);
+ if (error != 0)
+ printf("%s: cannot clear TID\n", __func__);
#endif
- SCARG(&cup, uaddr) = e->clear_tid;
- SCARG(&cup, op) = LINUX_FUTEX_WAKE;
- SCARG(&cup, val) = 0x7fffffff; /* Awake everyone */
- SCARG(&cup, timeout) = NULL;
- SCARG(&cup, uaddr2) = NULL;
- SCARG(&cup, val3) = 0;
- if ((error = linux_sys_futex(curlwp, &cup, &retval)) != 0)
- printf("%s: linux_sys_futex failed\n", __func__);
- }
-
- return;
-}
-
-void
-linux_nptl_proc_fork(struct proc *p, struct proc *parent, void (*luserret)(void))
-{
- struct linux_emuldata *e = p->p_emuldata;
-
- /* LINUX_CLONE_CHILD_CLEARTID: clear TID in child's memory on exit() */
- if (e->clone_flags & LINUX_CLONE_CHILD_CLEARTID)
- e->clear_tid = e->child_tidptr;
-
- /* LINUX_CLONE_PARENT_SETTID: set child's TID in parent's memory */
- if (e->clone_flags & LINUX_CLONE_PARENT_SETTID) {
- if (copyout_proc(parent, &p->p_pid,
- e->parent_tidptr, sizeof(p->p_pid)) != 0)
- printf("%s: LINUX_CLONE_PARENT_SETTID "
- "failed (e->parent_tidptr = %p, "
- "parent->p_pid = %d, p->p_pid = %d)\n",
- __func__, e->parent_tidptr,
- parent->p_pid, p->p_pid);
- }
+ SCARG(&cup, uaddr) = led->led_clear_tid;
+ SCARG(&cup, op) = LINUX_FUTEX_WAKE;
+ SCARG(&cup, val) = 0x7fffffff; /* Awake everyone */
+ SCARG(&cup, timeout) = NULL;
+ SCARG(&cup, uaddr2) = NULL;
+ SCARG(&cup, val3) = 0;
+ if ((error = linux_sys_futex(curlwp, &cup, &retval)) != 0)
+ printf("%s: linux_sys_futex failed\n", __func__);
- /*
- * CLONE_CHILD_SETTID and LINUX_CLONE_SETTLS require child's VM
- * setup to be completed, we postpone them until userret time.
- */
- if (e->clone_flags &
- (LINUX_CLONE_CHILD_CLEARTID | LINUX_CLONE_SETTLS))
- p->p_userret = luserret;
-
- return;
-}
-
-void
-linux_nptl_proc_init(struct proc *p, struct proc *parent)
-{
- struct linux_emuldata *e = p->p_emuldata;
- struct linux_emuldata *ep;
-
- if ((parent != NULL) && (parent->p_emuldata != NULL)) {
- ep = parent->p_emuldata;
-
- e->parent_tidptr = ep->parent_tidptr;
- e->child_tidptr = ep->child_tidptr;
- e->clone_flags = ep->clone_flags;
- }
+ release_futexes(l);
- return;
+ led = l->l_emuldata;
+ l->l_emuldata = NULL;
+ kmem_free(led, sizeof (struct linux_emuldata));
}
-#endif /* LINUX_NPTL */