diff options
author | Christiano F. Haesbaert <haesbaert@cvs.openbsd.org> | 2013-06-06 13:09:38 +0000 |
---|---|---|
committer | Christiano F. Haesbaert <haesbaert@cvs.openbsd.org> | 2013-06-06 13:09:38 +0000 |
commit | 9cd8289020fd6919ba717e9b96c76a8535245ac1 (patch) | |
tree | 667b010b4abe9a3ec3a4be558d375c0836df6658 /sys | |
parent | 2424e6ec93c811776df91db1be5a139e5965dba8 (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.c | 15 | ||||
-rw-r--r-- | sys/kern/kern_sched.c | 15 | ||||
-rw-r--r-- | sys/sys/proc.h | 3 |
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 |