summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorChristiano F. Haesbaert <haesbaert@cvs.openbsd.org>2013-06-06 13:09:38 +0000
committerChristiano F. Haesbaert <haesbaert@cvs.openbsd.org>2013-06-06 13:09:38 +0000
commit9cd8289020fd6919ba717e9b96c76a8535245ac1 (patch)
tree667b010b4abe9a3ec3a4be558d375c0836df6658 /sys
parent2424e6ec93c811776df91db1be5a139e5965dba8 (diff)
Prevent idle thread from being stolen on startup.
There is a race condition which might trigger a case where two cpus try to run the same idle thread. The problem arises when one cpu steals the idle proc of another cpu and this other cpu ends up running the idle thread via spc->spc_idleproc, resulting in two cpus trying to cpu_switchto(idleX). On startup, idle procs are scaterred around different runqueues, the decision for scheduling is: 1 look at my runqueue. 2 if empty, look at other dudes runqueue. 3 if empty, select idle proc via spc->spc_idleproc. The problem is that cpu0's idle0 might be running on cpu1 due to step 1 or 2 and cpu0 hits step 3. So cpu0 will select idle0, while cpu1 is in fact running it already. The solution is to never place idle on a runqueue, therefore being only selectable through spc->spc_idleproc. This race can be more easily triggered on a HT cpu on virtualized environments, where the guest more often than not doesn't have the cpu for itself, so timing gets shuffled. ok tedu@ guenther@ go ahead after t2k13 deraadt@
Diffstat (limited to 'sys')
-rw-r--r--sys/kern/kern_fork.c15
-rw-r--r--sys/kern/kern_sched.c15
-rw-r--r--sys/sys/proc.h3
3 files changed, 24 insertions, 9 deletions
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
index 8cb56608824..4eeb65c83ed 100644
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_fork.c,v 1.150 2013/06/05 00:53:26 tedu Exp $ */
+/* $OpenBSD: kern_fork.c,v 1.151 2013/06/06 13:09:37 haesbaert Exp $ */
/* $NetBSD: kern_fork.c,v 1.29 1996/02/09 18:59:34 christos Exp $ */
/*
@@ -495,11 +495,14 @@ fork1(struct proc *curp, int exitsig, int flags, void *stack, pid_t *tidptr,
/*
* Make child runnable and add to run queue.
*/
- SCHED_LOCK(s);
- p->p_stat = SRUN;
- p->p_cpu = sched_choosecpu_fork(curp, flags);
- setrunqueue(p);
- SCHED_UNLOCK(s);
+ if ((flags & FORK_IDLE) == 0) {
+ SCHED_LOCK(s);
+ p->p_stat = SRUN;
+ p->p_cpu = sched_choosecpu_fork(curp, flags);
+ setrunqueue(p);
+ SCHED_UNLOCK(s);
+ } else
+ p->p_cpu = arg;
if (newptstat)
free(newptstat, M_SUBPROC);
diff --git a/sys/kern/kern_sched.c b/sys/kern/kern_sched.c
index dad6291081e..431345baba1 100644
--- a/sys/kern/kern_sched.c
+++ b/sys/kern/kern_sched.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_sched.c,v 1.29 2013/06/03 16:55:22 guenther Exp $ */
+/* $OpenBSD: kern_sched.c,v 1.30 2013/06/06 13:09:37 haesbaert Exp $ */
/*
* Copyright (c) 2007, 2008 Artur Grabowski <art@openbsd.org>
*
@@ -105,9 +105,20 @@ sched_kthreads_create(void *v)
struct schedstate_percpu *spc = &ci->ci_schedstate;
static int num;
- if (kthread_create(sched_idle, ci, &spc->spc_idleproc, "idle%d", num))
+ if (fork1(&proc0, 0, FORK_SHAREVM|FORK_SHAREFILES|FORK_NOZOMBIE|
+ FORK_SIGHAND|FORK_IDLE, NULL, 0, sched_idle, ci, NULL,
+ &spc->spc_idleproc))
panic("fork idle");
+ /*
+ * Mark it as a system process.
+ */
+ atomic_setbits_int(&spc->spc_idleproc->p_flag, P_SYSTEM);
+
+ /* Name it as specified. */
+ snprintf(spc->spc_idleproc->p_comm, sizeof(spc->spc_idleproc->p_comm),
+ "idle%d", num);
+
num++;
}
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
index 9a8a00f4e27..03c034ae1c6 100644
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: proc.h,v 1.167 2013/06/05 00:53:27 tedu Exp $ */
+/* $OpenBSD: proc.h,v 1.168 2013/06/06 13:09:37 haesbaert Exp $ */
/* $NetBSD: proc.h,v 1.44 1996/04/22 01:23:21 christos Exp $ */
/*-
@@ -471,6 +471,7 @@ struct uidinfo *uid_find(uid_t);
*/
#define FORK_FORK 0x00000001
#define FORK_VFORK 0x00000002
+#define FORK_IDLE 0x00000004
#define FORK_PPWAIT 0x00000008
#define FORK_SHAREFILES 0x00000010
#define FORK_NOZOMBIE 0x00000040