Subject: proposal for Linux exit_group emulation
To: None <tech-kern@netbsd.org>
From: Emmanuel Dreyfus <manu@netbsd.org>
List: tech-kern
Date: 12/01/2005 22:26:13
--fdj2RfSjLxBAspz7
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Hello

Please find attached a proposal for emulating Linux exit_group(). I'm not
sur it follows The Right Way.

exit_group is supposed to exit all threads of a group. We emulate Linux
threads as processes with shared memory.

In order to exit all the processes in a group:
- I awake sleeping processes
- I tag processes as doomed by exit_grouo
- a test in syscall and userret checks the tag and call exit1 if nescessary.

I'm not sure the test in syscall is a good idea.
-- 
Emmanuel Dreyfus
manu@netbsd.org

--fdj2RfSjLxBAspz7
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="exit_group.diff"

Index: arch/amd64/amd64/syscall.c
===================================================================
RCS file: /cvsroot/src/sys/arch/amd64/amd64/syscall.c,v
retrieving revision 1.12
diff -U2 -r1.12 syscall.c
--- arch/amd64/amd64/syscall.c	13 Nov 2005 00:14:57 -0000	1.12
+++ arch/amd64/amd64/syscall.c	1 Dec 2005 22:14:42 -0000
@@ -128,4 +128,11 @@
 	size_t argsize, argoff;
 	register_t code, args[9], rval[2], *argp;
+#ifdef COMPAT_LINUX
+	struct linux_emuldata *led;
+
+	led = l->l_proc->p_emuldata;
+	if (led->exit_group)
+		return exit1(l, 0);
+#endif
 
 	uvmexp.syscalls++;
@@ -235,4 +244,11 @@
 	size_t argsize, argoff;
 	register_t code, args[9], rval[2], *argp;
+#ifdef COMPAT_LINUX
+	struct linux_emuldata *led;
+
+	led = l->l_proc->p_emuldata;
+	if (led->exit_group)
+		return exit1(l, 0);
+#endif
 
 	uvmexp.syscalls++;
Index: compat/linux/common/linux_emuldata.h
===================================================================
RCS file: /cvsroot/src/sys/compat/linux/common/linux_emuldata.h,v
retrieving revision 1.9
diff -U2 -r1.9 linux_emuldata.h
--- compat/linux/common/linux_emuldata.h	5 Nov 2005 00:47:26 -0000	1.9
+++ compat/linux/common/linux_emuldata.h	1 Dec 2005 22:14:42 -0000
@@ -65,4 +65,5 @@
 	int *clear_tid;		/* Own TID to clear on exit */
 	unsigned long set_tls;	/* New TLS in child if not 0 */
+	int exit_group;		/* Set to 1 if exit_group in operation */
 #endif
 };
Index: compat/linux/common/linux_exec.c
===================================================================
RCS file: /cvsroot/src/sys/compat/linux/common/linux_exec.c,v
retrieving revision 1.82
diff -U2 -r1.82 linux_exec.c
--- compat/linux/common/linux_exec.c	23 Nov 2005 22:38:46 -0000	1.82
+++ compat/linux/common/linux_exec.c	1 Dec 2005 22:14:43 -0000
@@ -64,6 +64,6 @@
 #include <compat/linux/common/linux_signal.h>
 #include <compat/linux/common/linux_util.h>
-#include <compat/linux/common/linux_exec.h>
 #include <compat/linux/common/linux_machdep.h>
+#include <compat/linux/common/linux_exec.h>
 #include <compat/linux/common/linux_futex.h>
 
@@ -83,8 +83,4 @@
 static void linux_e_proc_init __P((struct proc *, struct proc *, int));
 
-#ifdef LINUX_NPTL
-static void linux_userret __P((struct lwp *, void *));
-#endif
-
 /*
  * Execve(2). Just check the alternate emulation path, and pass it on
@@ -226,4 +222,5 @@
 	e->child_clear_tid = NULL;
 	e->child_set_tid = NULL;
+	e->exit_group = 0;
 	if (ep != NULL) {
 		e->clear_tid = ep->child_clear_tid;
@@ -336,5 +336,5 @@
 
 #ifdef LINUX_NPTL
-static void
+void
 linux_userret(l, arg)
 	struct lwp *l;
@@ -347,4 +347,8 @@
 	p->p_userret = NULL;
 
+	/* Emulate exit_group() */
+	if (led->exit_group)
+		return exit1(l, 0);
+
 	/* Emulate LINUX_CLONE_CHILD_SETTID  */
 	if (led->set_tid != NULL) {
Index: compat/linux/common/linux_exec.h
===================================================================
RCS file: /cvsroot/src/sys/compat/linux/common/linux_exec.h,v
retrieving revision 1.35
diff -U2 -r1.35 linux_exec.h
--- compat/linux/common/linux_exec.h	20 May 2005 12:48:27 -0000	1.35
+++ compat/linux/common/linux_exec.h	1 Dec 2005 22:14:43 -0000
@@ -146,4 +146,8 @@
 #endif
 __END_DECLS
+
+#ifdef LINUX_NPTL
+void linux_userret __P((struct lwp *, void *));
+#endif
 #endif /* !_KERNEL */
 
Index: compat/linux/common/linux_sched.c
===================================================================
RCS file: /cvsroot/src/sys/compat/linux/common/linux_sched.c,v
retrieving revision 1.29
diff -U2 -r1.29 linux_sched.c
--- compat/linux/common/linux_sched.c	29 Nov 2005 22:31:59 -0000	1.29
+++ compat/linux/common/linux_sched.c	1 Dec 2005 22:14:43 -0000
@@ -60,4 +60,5 @@
 #include <compat/linux/common/linux_signal.h>
 #include <compat/linux/common/linux_machdep.h> /* For LINUX_NPTL */
+#include <compat/linux/common/linux_exec.h>
 #include <compat/linux/common/linux_emuldata.h>
 
@@ -407,13 +408,45 @@
 		syscallarg(int) error_code;
 	} */ *uap = v;
+#ifdef LINUX_NPTL
+	struct proc *p;
+	struct linux_emuldata *led;
+	pid_t group_pid;
 
 	/*
-	 * XXX The calling thread is supposed to kill all threads
+	 * The calling thread is supposed to kill all threads
 	 * in the same thread group (i.e. all threads created
-	 * via clone(2) with CLONE_THREAD flag set). This appears
-	 * to not be used yet, so the thread group handling
-	 * is currently not implemented.
+	 * via clone(2) with CLONE_THREAD flag set).
 	 */
+	led = l->l_proc->p_emuldata;
+	group_pid = led->s->group_pid;
+
+	PROCLIST_FOREACH(p, &allproc) {
+		if (p->p_emul != &emul_linux)
+			continue;
+
+		if (p == l->l_proc)
+			continue;
+
+		led = p->p_emuldata;
+		if (led->s->group_pid == group_pid) {
+			struct lwp *cl;
+
+			/* There is only one LWP in Linux processes... */
+			cl = proc_representative_lwp(p);
+
+			printf("exit with pid %d\n", p->p_pid);
+			if (cl->l_stat == LSSLEEP) {
+				printf("   awake pid %d wchan %s\n", 
+				    p->p_pid, l->l_wmesg);
+				wakeup(cl->l_wchan);
+			}
+
+			/* Tag as a process to exit */
+			led->exit_group = 1;
+			p->p_userret = (*linux_userret);
+		}
+	}
 
+#endif /* LINUX_NPTL */	
 	exit1(l, W_EXITCODE(SCARG(uap, error_code), 0));
 	/* NOTREACHED */

--fdj2RfSjLxBAspz7--