diff options
author | Artur Grabowski <art@cvs.openbsd.org> | 2009-04-14 09:13:26 +0000 |
---|---|---|
committer | Artur Grabowski <art@cvs.openbsd.org> | 2009-04-14 09:13:26 +0000 |
commit | 422bb19d43b00d556ea2128df60a4911470b1791 (patch) | |
tree | 0e026fc7bc28241faacf4b813b0642ab40dae32f /sys | |
parent | 56e38014feacef00c9f7ee671e9e5c6090dbf763 (diff) |
Some tweaks to the cpu affinity code.
- Split up choosing of cpu between fork and "normal" cases. Fork is
very different and should be treated as such.
- Instead of implicitly choosing a cpu in setrunqueue, do it outside
where it actually makes sense.
- Just because a cpu is marked as idle doesn't mean it will be soon.
There could be a thundering herd effect if we call wakeup from an
interrupt handler, so subtract cpus with queued processes when
deciding which cpu is actually idle.
- some simplifications allowed by the above.
kettenis@ ok (except one bugfix that was not in the intial diff)
Diffstat (limited to 'sys')
-rw-r--r-- | sys/kern/kern_fork.c | 3 | ||||
-rw-r--r-- | sys/kern/kern_sched.c | 159 | ||||
-rw-r--r-- | sys/kern/kern_synch.c | 13 | ||||
-rw-r--r-- | sys/kern/sched_bsd.c | 4 | ||||
-rw-r--r-- | sys/sys/proc.h | 7 | ||||
-rw-r--r-- | sys/sys/sched.h | 5 |
6 files changed, 118 insertions, 73 deletions
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index 10dd9a5f8eb..b2947bd4421 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_fork.c,v 1.102 2009/03/23 13:25:11 art Exp $ */ +/* $OpenBSD: kern_fork.c,v 1.103 2009/04/14 09:13:25 art Exp $ */ /* $NetBSD: kern_fork.c,v 1.29 1996/02/09 18:59:34 christos Exp $ */ /* @@ -432,6 +432,7 @@ fork1(struct proc *p1, int exitsig, int flags, void *stack, size_t stacksize, getmicrotime(&p2->p_stats->p_start); p2->p_acflag = AFORK; p2->p_stat = SRUN; + p2->p_cpu = sched_choosecpu_fork(p1, flags); setrunqueue(p2); SCHED_UNLOCK(s); diff --git a/sys/kern/kern_sched.c b/sys/kern/kern_sched.c index 29dd5793efe..7205832b79f 100644 --- a/sys/kern/kern_sched.c +++ b/sys/kern/kern_sched.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_sched.c,v 1.10 2009/04/03 09:29:15 art Exp $ */ +/* $OpenBSD: kern_sched.c,v 1.11 2009/04/14 09:13:25 art Exp $ */ /* * Copyright (c) 2007, 2008 Artur Grabowski <art@openbsd.org> * @@ -207,7 +207,6 @@ setrunqueue(struct proc *p) int queue = p->p_priority >> 2; SCHED_ASSERT_LOCKED(); - sched_choosecpu(p); spc = &p->p_cpu->ci_schedstate; spc->spc_nrun++; @@ -215,7 +214,7 @@ setrunqueue(struct proc *p) spc->spc_whichqs |= (1 << queue); cpuset_add(&sched_queued_cpus, p->p_cpu); - if (p->p_cpu != curcpu()) + if (cpuset_isset(&sched_idle_cpus, p->p_cpu)) cpu_unidle(p->p_cpu); } @@ -283,7 +282,58 @@ uint64_t sched_choose; uint64_t sched_wasidle; uint64_t sched_nomigrations; -void +struct cpu_info * +sched_choosecpu_fork(struct proc *parent, int flags) +{ + struct cpu_info *choice = NULL; + fixpt_t load, best_load = ~0; + int run, best_run = INT_MAX; + struct cpu_info *ci; + struct cpuset set; + +#if 0 + /* + * XXX + * Don't do this until we have a painless way to move the cpu in exec. + * Preferably when nuking the old pmap and getting a new one on a + * new cpu. + */ + /* + * PPWAIT forks are simple. We know that the parent will not + * run until we exec and choose another cpu, so we just steal its + * cpu. + */ + if (flags & FORK_PPWAIT) + return (parent->p_cpu); +#endif + + /* + * Look at all cpus that are currently idle and have nothing queued. + * If there are none, pick the one with least queued procs first, + * then the one with lowest load average. + */ + cpuset_complement(&set, &sched_queued_cpus, &sched_idle_cpus); + if (cpuset_first(&set) == NULL) + cpuset_add_all(&set); + + while ((ci = cpuset_first(&set)) != NULL) { + cpuset_del(&set, ci); + + load = ci->ci_schedstate.spc_ldavg; + run = ci->ci_schedstate.spc_nrun; + + if (choice == NULL || run < best_run || + (run == best_run &&load < best_load)) { + choice = ci; + best_load = load; + best_run = run; + } + } + + return (choice); +} + +struct cpu_info * sched_choosecpu(struct proc *p) { struct cpu_info *choice = NULL; @@ -295,41 +345,34 @@ sched_choosecpu(struct proc *p) * If pegged to a cpu, don't allow it to move. */ if (p->p_flag & P_CPUPEG) - return; + return (p->p_cpu); sched_choose++; /* - * The simplest case. Our cpu of choice was idle. This happens - * when we were sleeping and something woke us up. - * - * We also need to check sched_queued_cpus to make sure that - * we're not thundering herding one cpu that hasn't managed to - * get out of the idle loop yet. + * Look at all cpus that are currently idle and have nothing queued. + * If there are none, pick the cheapest of those. + * (idle + queued could mean that the cpu is handling an interrupt + * at this moment and haven't had time to leave idle yet). */ - if (p->p_cpu && cpuset_isset(&sched_idle_cpus, p->p_cpu) && - !cpuset_isset(&sched_queued_cpus, p->p_cpu)) { - sched_wasidle++; - return; - } - -#if 0 + cpuset_complement(&set, &sched_queued_cpus, &sched_idle_cpus); - /* Most likely, this is broken. don't do it. */ /* - * Second case. (shouldn't be necessary in the future) - * If our cpu is not idle, but has nothing else queued (which - * means that we are curproc and roundrobin asks us to reschedule). + * First, just check if our current cpu is in that set, if it is, + * this is simple. + * Also, our cpu might not be idle, but if it's the current cpu + * and it has nothing else queued and we're curproc, take it. */ - if (p->p_cpu && p->p_cpu->ci_schedstate.spc_nrun == 0) - return; -#endif + if (cpuset_isset(&set, p->p_cpu) || + (p->p_cpu == curcpu() && p->p_cpu->ci_schedstate.spc_nrun == 0 && + curproc == p)) { + sched_wasidle++; + return (p->p_cpu); + } + + if (cpuset_first(&set) == NULL) + cpuset_add_all(&set); - /* - * Look at all cpus that are currently idle. Pick the cheapest of - * those. - */ - cpuset_copy(&set, &sched_idle_cpus); while ((ci = cpuset_first(&set)) != NULL) { int cost = sched_proc_to_cpu_cost(ci, p); @@ -340,35 +383,12 @@ sched_choosecpu(struct proc *p) cpuset_del(&set, ci); } - /* - * All cpus are busy. Pick one. - */ - if (choice == NULL) { - CPU_INFO_ITERATOR cii; - - sched_noidle++; - - /* - * Not curproc, pick the cpu with the lowest cost to switch to. - */ - CPU_INFO_FOREACH(cii, ci) { - int cost = sched_proc_to_cpu_cost(ci, p); - - if (choice == NULL || cost < last_cost) { - choice = ci; - last_cost = cost; - } - } - } - - KASSERT(choice); - - if (p->p_cpu && p->p_cpu != choice) + if (p->p_cpu != choice) sched_nmigrations++; - else if (p->p_cpu != NULL) + else sched_nomigrations++; - p->p_cpu = choice; + return (choice); } /* @@ -577,3 +597,30 @@ cpuset_first(struct cpuset *cs) return (NULL); } + +void +cpuset_union(struct cpuset *to, struct cpuset *a, struct cpuset *b) +{ + int i; + + for (i = 0; i < CPUSET_ASIZE(ncpus); i++) + to->cs_set[i] = a->cs_set[i] | b->cs_set[i]; +} + +void +cpuset_intersection(struct cpuset *to, struct cpuset *a, struct cpuset *b) +{ + int i; + + for (i = 0; i < CPUSET_ASIZE(ncpus); i++) + to->cs_set[i] = a->cs_set[i] & b->cs_set[i]; +} + +void +cpuset_complement(struct cpuset *to, struct cpuset *a, struct cpuset *b) +{ + int i; + + for (i = 0; i < CPUSET_ASIZE(ncpus); i++) + to->cs_set[i] = b->cs_set[i] & ~a->cs_set[i]; +} diff --git a/sys/kern/kern_synch.c b/sys/kern/kern_synch.c index 449f93c7b5a..5d15d0e23a2 100644 --- a/sys/kern/kern_synch.c +++ b/sys/kern/kern_synch.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_synch.c,v 1.88 2009/03/23 13:25:11 art Exp $ */ +/* $OpenBSD: kern_synch.c,v 1.89 2009/04/14 09:13:25 art Exp $ */ /* $NetBSD: kern_synch.c,v 1.37 1996/04/22 01:38:37 christos Exp $ */ /* @@ -370,16 +370,7 @@ wakeup_n(void *ident, int n) updatepri(p); p->p_slptime = 0; p->p_stat = SRUN; - - /* - * Since curpriority is a user priority, - * p->p_priority is always better than - * curpriority on the last CPU on - * which it ran. - * - * XXXSMP See affinity comment in - * resched_proc(). - */ + p->p_cpu = sched_choosecpu(p); setrunqueue(p); need_resched(p->p_cpu); /* END INLINE EXPANSION */ diff --git a/sys/kern/sched_bsd.c b/sys/kern/sched_bsd.c index 53c0902dc49..2c4e7c6caa0 100644 --- a/sys/kern/sched_bsd.c +++ b/sys/kern/sched_bsd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sched_bsd.c,v 1.20 2009/03/23 13:25:11 art Exp $ */ +/* $OpenBSD: sched_bsd.c,v 1.21 2009/04/14 09:13:25 art Exp $ */ /* $NetBSD: kern_synch.c,v 1.37 1996/04/22 01:38:37 christos Exp $ */ /*- @@ -337,6 +337,7 @@ preempt(struct proc *newp) SCHED_LOCK(s); p->p_priority = p->p_usrpri; p->p_stat = SRUN; + p->p_cpu = sched_choosecpu(p); setrunqueue(p); p->p_stats->p_ru.ru_nivcsw++; mi_switch(); @@ -516,6 +517,7 @@ setrunnable(struct proc *p) break; } p->p_stat = SRUN; + p->p_cpu = sched_choosecpu(p); setrunqueue(p); if (p->p_slptime > 1) updatepri(p); diff --git a/sys/sys/proc.h b/sys/sys/proc.h index 2bfc6e308e5..a2c5b4869a4 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: proc.h,v 1.115 2009/04/03 09:29:15 art Exp $ */ +/* $OpenBSD: proc.h,v 1.116 2009/04/14 09:13:25 art Exp $ */ /* $NetBSD: proc.h,v 1.44 1996/04/22 01:23:21 christos Exp $ */ /*- @@ -485,7 +485,10 @@ void cpuset_add(struct cpuset *, struct cpu_info *); void cpuset_del(struct cpuset *, struct cpu_info *); int cpuset_isset(struct cpuset *, struct cpu_info *); void cpuset_add_all(struct cpuset *); -void cpuset_copy(struct cpuset *to, struct cpuset *from); +void cpuset_copy(struct cpuset *, struct cpuset *); +void cpuset_union(struct cpuset *, struct cpuset *, struct cpuset *); +void cpuset_intersection(struct cpuset *t, struct cpuset *, struct cpuset *); +void cpuset_complement(struct cpuset *, struct cpuset *, struct cpuset *); struct cpu_info *cpuset_first(struct cpuset *); #endif /* _KERNEL */ diff --git a/sys/sys/sched.h b/sys/sys/sched.h index 7766f4da093..8211961b815 100644 --- a/sys/sys/sched.h +++ b/sys/sys/sched.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sched.h,v 1.21 2009/04/03 09:29:15 art Exp $ */ +/* $OpenBSD: sched.h,v 1.22 2009/04/14 09:13:25 art Exp $ */ /* $NetBSD: sched.h,v 1.2 1999/02/28 18:14:58 ross Exp $ */ /*- @@ -140,7 +140,8 @@ void sched_exit(struct proc *); void mi_switch(void); void cpu_switchto(struct proc *, struct proc *); struct proc *sched_chooseproc(void); -void sched_choosecpu(struct proc *); +struct cpu_info *sched_choosecpu(struct proc *); +struct cpu_info *sched_choosecpu_fork(struct proc *parent, int); void cpu_idle_enter(void); void cpu_idle_cycle(void); void cpu_idle_leave(void); |