summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/kern/kern_exec.c15
-rw-r--r--sys/kern/kern_exit.c61
-rw-r--r--sys/kern/kern_sig.c166
-rw-r--r--sys/kern/kern_synch.c7
-rw-r--r--sys/sys/proc.h28
5 files changed, 231 insertions, 46 deletions
diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c
index 1c5380c40f9..ff53d3feb9a 100644
--- a/sys/kern/kern_exec.c
+++ b/sys/kern/kern_exec.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_exec.c,v 1.120 2011/10/16 05:29:51 guenther Exp $ */
+/* $OpenBSD: kern_exec.c,v 1.121 2011/12/11 19:42:28 guenther Exp $ */
/* $NetBSD: kern_exec.c,v 1.75 1996/02/09 18:59:28 christos Exp $ */
/*-
@@ -267,6 +267,10 @@ sys_execve(struct proc *p, void *v, register_t *retval)
#endif
char *pathbuf = NULL;
+ /* get other threads to stop */
+ if ((error = single_thread_set(p, SINGLE_UNWIND, 1)))
+ goto bad;
+
/*
* Cheap solution to complicated problems.
* Mark this process as "leave me alone, I'm execing".
@@ -410,6 +414,13 @@ sys_execve(struct proc *p, void *v, register_t *retval)
pack.ep_ssize = len; /* maybe should go elsewhere, but... */
/*
+ * we're committed: any further errors will kill the process, so
+ * kill the other threads now.
+ * XXX wait until threads are reaped to make uvmspace_exec() cheaper?
+ */
+ single_thread_set(p, SINGLE_EXIT, 0);
+
+ /*
* Prepare vmspace for remapping. Note that uvmspace_exec can replace
* p_vmspace!
*/
@@ -667,6 +678,7 @@ sys_execve(struct proc *p, void *v, register_t *retval)
#endif
atomic_clearbits_int(&p->p_flag, P_INEXEC);
+ single_thread_clear(p);
#if NSYSTRACE > 0
if (ISSET(p->p_flag, P_SYSTRACE) &&
@@ -702,6 +714,7 @@ bad:
clrflag:
#endif
atomic_clearbits_int(&p->p_flag, P_INEXEC);
+ single_thread_clear(p);
if (pathbuf != NULL)
pool_put(&namei_pool, pathbuf);
diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c
index dc4e7a16281..3e029725a0c 100644
--- a/sys/kern/kern_exit.c
+++ b/sys/kern/kern_exit.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_exit.c,v 1.103 2011/07/25 19:38:53 tedu Exp $ */
+/* $OpenBSD: kern_exit.c,v 1.104 2011/12/11 19:42:28 guenther Exp $ */
/* $NetBSD: kern_exit.c,v 1.39 1996/04/22 01:38:25 christos Exp $ */
/*
@@ -119,7 +119,6 @@ sys_threxit(struct proc *p, void *v, register_t *retval)
void
exit1(struct proc *p, int rv, int flags)
{
- struct proc *q, *nq;
struct process *pr, *qr, *nqr;
if (p->p_pid == 1)
@@ -128,39 +127,21 @@ exit1(struct proc *p, int rv, int flags)
atomic_setbits_int(&p->p_flag, P_WEXIT);
- /* unlink ourselves from the active threads */
pr = p->p_p;
- TAILQ_REMOVE(&pr->ps_threads, p, p_thr_link);
- if (TAILQ_EMPTY(&pr->ps_threads))
- wakeup(&pr->ps_threads);
- /*
- * if one thread calls exit, we take down everybody.
- * we have to be careful not to get recursively caught.
- * this is kinda sick.
- */
- if (flags == EXIT_NORMAL && (p->p_flag & P_THREAD) &&
- (pr->ps_mainproc->p_flag & P_WEXIT) == 0) {
- /*
- * we are one of the threads. we SIGKILL the parent,
- * it will wake us up again, then we proceed.
- */
- atomic_setbits_int(&pr->ps_mainproc->p_flag, P_IGNEXITRV);
+
+ /* single-threaded? */
+ if (TAILQ_FIRST(&pr->ps_threads) == p &&
+ TAILQ_NEXT(p, p_thr_link) == NULL)
+ flags = EXIT_NORMAL;
+ else {
+ /* nope, multi-threaded */
+ if (flags == EXIT_NORMAL)
+ single_thread_set(p, SINGLE_EXIT, 0);
+ }
+
+ if (flags == EXIT_NORMAL) {
pr->ps_mainproc->p_xstat = rv;
- ptsignal(pr->ps_mainproc, SIGKILL, SPROPAGATED);
- tsleep(pr, PUSER, "thrdying", 0);
- } else if ((p->p_flag & P_THREAD) == 0) {
- if (flags == EXIT_NORMAL) {
- q = TAILQ_FIRST(&pr->ps_threads);
- for (; q != NULL; q = nq) {
- nq = TAILQ_NEXT(q, p_thr_link);
- atomic_setbits_int(&q->p_flag, P_IGNEXITRV);
- q->p_xstat = rv;
- ptsignal(q, SIGKILL, SPROPAGATED);
- }
- }
- wakeup(pr);
- while (!TAILQ_EMPTY(&pr->ps_threads))
- tsleep(&pr->ps_threads, PUSER, "thrdeath", 0);
+
/*
* If parent is waiting for us to exit or exec, PS_PPWAIT
* is set; we wake up the parent early to avoid deadlock.
@@ -173,6 +154,15 @@ exit1(struct proc *p, int rv, int flags)
}
}
+ /* unlink ourselves from the active threads */
+ TAILQ_REMOVE(&pr->ps_threads, p, p_thr_link);
+ if ((p->p_flag & P_THREAD) == 0) {
+ /* main thread gotta wait because it has the pid, et al */
+ while (! TAILQ_EMPTY(&pr->ps_threads))
+ tsleep(&pr->ps_threads, PUSER, "thrdeath", 0);
+ } else if (TAILQ_EMPTY(&pr->ps_threads))
+ wakeup(&pr->ps_threads);
+
if (p->p_flag & P_PROFIL)
stopprofclock(p);
p->p_ru = pool_get(&rusage_pool, PR_WAITOK);
@@ -287,8 +277,6 @@ exit1(struct proc *p, int rv, int flags)
* Save exit status and final rusage info, adding in child rusage
* info and self times.
*/
- if (!(p->p_flag & P_IGNEXITRV))
- p->p_xstat = rv;
*p->p_ru = p->p_stats->p_ru;
calcru(p, &p->p_ru->ru_utime, &p->p_ru->ru_stime, NULL);
ruadd(p->p_ru, &p->p_stats->p_cru);
@@ -488,7 +476,8 @@ loop:
proc_finish_wait(q, p);
return (0);
}
- if (p->p_stat == SSTOP && (p->p_flag & P_WAITED) == 0 &&
+ if (p->p_stat == SSTOP &&
+ (p->p_flag & (P_WAITED|P_SUSPSINGLE)) == 0 &&
(p->p_flag & P_TRACED || SCARG(uap, options) & WUNTRACED)) {
atomic_setbits_int(&p->p_flag, P_WAITED);
retval[0] = p->p_pid;
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
index 9d1c5d845d5..0775a1edf52 100644
--- a/sys/kern/kern_sig.c
+++ b/sys/kern/kern_sig.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_sig.c,v 1.130 2011/11/22 23:20:19 joshe Exp $ */
+/* $OpenBSD: kern_sig.c,v 1.131 2011/12/11 19:42:28 guenther Exp $ */
/* $NetBSD: kern_sig.c,v 1.54 1996/04/22 01:38:32 christos Exp $ */
/*
@@ -1158,7 +1158,7 @@ proc_stop(struct proc *p, int sw)
p->p_stat = SSTOP;
atomic_clearbits_int(&p->p_flag, P_WAITED);
- atomic_setbits_int(&p->p_flag, P_STOPPED);
+ atomic_setbits_int(&p->p_flag, P_STOPPED|P_SUSPSIG);
if (!timeout_pending(&proc_stop_to)) {
timeout_add(&proc_stop_to, 0);
/*
@@ -1313,6 +1313,12 @@ sigexit(struct proc *p, int signum)
p->p_acflag |= AXSIG;
if (sigprop[signum] & SA_CORE) {
p->p_sisig = signum;
+
+ /* if there are other threads, pause them */
+ if (TAILQ_FIRST(&p->p_p->ps_threads) != p ||
+ TAILQ_NEXT(p, p_thr_link) != NULL)
+ single_thread_set(p, SINGLE_SUSPEND, 0);
+
if (coredump(p) == 0)
signum |= WCOREFLAG;
}
@@ -1640,5 +1646,161 @@ userret(struct proc *p)
while ((sig = CURSIG(p)) != 0)
postsig(sig);
+ if (p->p_flag & P_SUSPSINGLE) {
+ KERNEL_LOCK();
+ single_thread_check(p, 0);
+ KERNEL_UNLOCK();
+ }
+
p->p_cpu->ci_schedstate.spc_curpriority = p->p_priority = p->p_usrpri;
}
+
+int
+single_thread_check(struct proc *p, int deep)
+{
+ struct process *pr = p->p_p;
+
+ if (pr->ps_single != NULL && pr->ps_single != p) {
+ do {
+ int s;
+
+ /* if we're in deep, we need to unwind to the edge */
+ if (deep) {
+ if (pr->ps_flags & PS_SINGLEUNWIND)
+ return (ERESTART);
+ if (pr->ps_flags & PS_SINGLEEXIT)
+ return (EINTR);
+ }
+
+ if (--pr->ps_singlecount == 0)
+ wakeup(&pr->ps_singlecount);
+ if (pr->ps_flags & PS_SINGLEEXIT)
+ exit1(p, 0, EXIT_THREAD);
+
+ /* not exiting and don't need to unwind, so suspend */
+ SCHED_LOCK(s);
+ p->p_stat = SSTOP;
+ mi_switch();
+ SCHED_UNLOCK(s);
+ } while (pr->ps_single != NULL);
+ }
+
+ return (0);
+}
+
+/*
+ * Stop other threads in the process. The mode controls how and
+ * where the other threads should stop:
+ * - SINGLE_SUSPEND: stop wherever they are, will later either be told to exit
+ * (by setting to SINGLE_EXIT) or be released (via single_thread_clear())
+ * - SINGLE_UNWIND: just unwind to kernel boundary, will be told to exit
+ * or released as with SINGLE_SUSPEND
+ * - SINGLE_EXIT: unwind to kernel boundary and exit
+ */
+int
+single_thread_set(struct proc *p, enum single_thread_mode mode, int deep)
+{
+ struct process *pr = p->p_p;
+ struct proc *q;
+ int error;
+
+ if ((error = single_thread_check(p, deep)))
+ return error;
+
+ switch (mode) {
+ case SINGLE_SUSPEND:
+ break;
+ case SINGLE_UNWIND:
+ atomic_setbits_int(&pr->ps_flags, PS_SINGLEUNWIND);
+ break;
+ case SINGLE_EXIT:
+ atomic_setbits_int(&pr->ps_flags, PS_SINGLEEXIT);
+ atomic_clearbits_int(&pr->ps_flags, PS_SINGLEUNWIND);
+ break;
+#ifdef DIAGNOSTIC
+ default:
+ panic("single_thread_mode = %d", mode);
+#endif
+ }
+ pr->ps_single = p;
+ pr->ps_singlecount = 0;
+ TAILQ_FOREACH(q, &pr->ps_threads, p_thr_link) {
+ int s;
+
+ if (q == p)
+ continue;
+ SCHED_LOCK(s);
+ atomic_setbits_int(&q->p_flag, P_SUSPSINGLE);
+ switch (q->p_stat) {
+ case SIDL:
+ case SRUN:
+ pr->ps_singlecount++;
+ break;
+ case SSLEEP:
+ /* if it's not interruptible, then just have to wait */
+ if (q->p_flag & P_SINTR) {
+ /* merely need to suspend? just stop it */
+ if (mode == SINGLE_SUSPEND) {
+ q->p_stat = SSTOP;
+ break;
+ }
+ /* need to unwind or exit, so wake it */
+ setrunnable(q);
+ }
+ pr->ps_singlecount++;
+ break;
+ case SSTOP:
+ if (mode == SINGLE_EXIT) {
+ setrunnable(q);
+ pr->ps_singlecount++;
+ }
+ break;
+ case SZOMB:
+ case SDEAD:
+ break;
+ case SONPROC:
+ pr->ps_singlecount++;
+ signotify(q);
+ break;
+ }
+ SCHED_UNLOCK(s);
+ }
+
+ /* wait until they're all suspended */
+ while (pr->ps_singlecount > 0)
+ tsleep(&pr->ps_singlecount, PUSER, "suspend", 0);
+ return 0;
+}
+
+void
+single_thread_clear(struct proc *p)
+{
+ struct process *pr = p->p_p;
+ struct proc *q;
+
+ KASSERT(pr->ps_single == p);
+
+ pr->ps_single = NULL;
+ atomic_clearbits_int(&pr->ps_flags, PS_SINGLEUNWIND | PS_SINGLEEXIT);
+ TAILQ_FOREACH(q, &pr->ps_threads, p_thr_link) {
+ int s;
+
+ if (q == p || (q->p_flag & P_SUSPSINGLE) == 0)
+ continue;
+ atomic_clearbits_int(&q->p_flag, P_SUSPSINGLE);
+
+ /*
+ * if the thread was only stopped for single threading
+ * then clearing that either makes it runnable or puts
+ * it back into some sleep queue
+ */
+ SCHED_LOCK(s);
+ if (q->p_stat == SSTOP && (q->p_flag & P_SUSPSIG) == 0) {
+ if (q->p_wchan == 0)
+ setrunnable(q);
+ else
+ q->p_stat = SSLEEP;
+ }
+ SCHED_UNLOCK(s);
+ }
+}
diff --git a/sys/kern/kern_synch.c b/sys/kern/kern_synch.c
index aebdfcb3c14..4b711711c02 100644
--- a/sys/kern/kern_synch.c
+++ b/sys/kern/kern_synch.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_synch.c,v 1.97 2011/07/07 18:00:33 guenther Exp $ */
+/* $OpenBSD: kern_synch.c,v 1.98 2011/12/11 19:42:28 guenther Exp $ */
/* $NetBSD: kern_synch.c,v 1.37 1996/04/22 01:38:37 christos Exp $ */
/*
@@ -282,7 +282,7 @@ sleep_setup_signal(struct sleep_state *sls, int prio)
* stopped, p->p_wchan will be 0 upon return from CURSIG.
*/
atomic_setbits_int(&p->p_flag, P_SINTR);
- if ((sls->sls_sig = CURSIG(p)) != 0) {
+ if (p->p_p->ps_single != NULL || (sls->sls_sig = CURSIG(p)) != 0) {
if (p->p_wchan)
unsleep(p);
p->p_stat = SONPROC;
@@ -297,8 +297,11 @@ int
sleep_finish_signal(struct sleep_state *sls)
{
struct proc *p = curproc;
+ int error;
if (sls->sls_catch != 0) {
+ if ((error = single_thread_check(p, 1)))
+ return (error);
if (sls->sls_sig != 0 || (sls->sls_sig = CURSIG(p)) != 0) {
if (p->p_sigacts->ps_sigintr & sigmask(sls->sls_sig))
return (EINTR);
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
index 40533dcd063..75f6ed3d04f 100644
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: proc.h,v 1.146 2011/11/22 23:20:19 joshe Exp $ */
+/* $OpenBSD: proc.h,v 1.147 2011/12/11 19:42:28 guenther Exp $ */
/* $NetBSD: proc.h,v 1.44 1996/04/22 01:23:21 christos Exp $ */
/*-
@@ -155,6 +155,9 @@ struct process {
struct klist ps_klist; /* knotes attached to this process */
int ps_flags; /* PS_* flags. */
+ struct proc *ps_single; /* Single threading to this thread. */
+ int ps_singlecount; /* Not yet suspended threads. */
+
/* End area that is zeroed on creation. */
#define ps_endzero ps_startcopy
@@ -199,6 +202,8 @@ struct process {
#define PS_SYSTRACE _P_SYSTRACE
#define PS_CONTINUED _P_CONTINUED
#define PS_STOPPED _P_STOPPED
+#define PS_SINGLEEXIT _P_SINGLEEXIT
+#define PS_SINGLEUNWIND _P_SINGLEUNWIND
struct proc {
TAILQ_ENTRY(proc) p_runq;
@@ -348,14 +353,17 @@ struct proc {
#define P_SSTEP 0x020000 /* proc needs single-step fixup ??? */
#define _P_SUGIDEXEC 0x040000 /* last execve() was set[ug]id */
+#define P_SUSPSINGLE 0x080000 /* Need to stop for single threading. */
#define P_NOZOMBIE 0x100000 /* Pid 1 waits for me instead of dad */
#define P_INEXEC 0x200000 /* Process is doing an exec right now */
#define P_SYSTRACE 0x400000 /* Process system call tracing active*/
#define P_CONTINUED 0x800000 /* Proc has continued from a stopped state. */
+#define _P_SINGLEEXIT 0x1000000 /* Other threads must die. */
+#define _P_SINGLEUNWIND 0x2000000 /* Other threads must unwind. */
#define P_THREAD 0x4000000 /* Only a thread, not a real process */
-#define P_IGNEXITRV 0x8000000 /* For thread kills */
+#define P_SUSPSIG 0x8000000 /* Stopped from signal. */
#define P_SOFTDEP 0x10000000 /* Stuck processing softdep worklist */
-#define P_STOPPED 0x20000000 /* Just stopped. */
+#define P_STOPPED 0x20000000 /* Just stopped, need sig to parent. */
#define P_CPUPEG 0x40000000 /* Do not move to another cpu. */
#ifndef _KERNEL
@@ -369,9 +377,10 @@ struct proc {
#define P_BITS \
("\20\02CONTROLT\03INMEM\04SIGPAUSE\05PPWAIT\06PROFIL\07SELECT" \
"\010SINTR\011SUGID\012SYSTEM\013TIMEOUT\014TRACED\015WAITED\016WEXIT" \
- "\017EXEC\020PWEUPC\021ISPWAIT\022SSTEP\023SUGIDEXEC" \
+ "\017EXEC\020PWEUPC\021ISPWAIT\022SSTEP\023SUGIDEXEC\024SUSPSINGLE" \
"\025NOZOMBIE\026INEXEC\027SYSTRACE\030CONTINUED" \
- "\033THREAD\034IGNEXITRV\035SOFTDEP\036STOPPED\037CPUPEG")
+ "\031SINGLEEXIT\032SINGLEUNWIND" \
+ "\033THREAD\034SUSPSIG\035SOFTDEP\036STOPPED\037CPUPEG")
/* Macro to compute the exit signal to be delivered. */
#define P_EXITSIG(p) \
@@ -493,6 +502,15 @@ int fork1(struct proc *, int, int, void *, pid_t *, void (*)(void *),
void *, register_t *, struct proc **);
int groupmember(gid_t, struct ucred *);
+enum single_thread_mode {
+ SINGLE_SUSPEND, /* other threads to stop wherever they are */
+ SINGLE_UNWIND, /* other threads to unwind and stop */
+ SINGLE_EXIT /* other threads to unwind and then exit */
+};
+int single_thread_set(struct proc *, enum single_thread_mode, int);
+void single_thread_clear(struct proc *);
+int single_thread_check(struct proc *, int);
+
void child_return(void *);
int proc_cansugid(struct proc *);