aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorPeter Zijlstra <a.p.zijlstra@chello.nl>2011-12-07 09:07:31 -0500
committerIngo Molnar <mingo@elte.hu>2011-12-21 04:34:44 -0500
commit518cd62341786aa4e3839810832af2fbc0de1ea4 (patch)
treebd28f7fce47887e9c5d33bd772c8380255ef3065 /kernel
parent612ef28a045efadb3a98d4492ead7806a146485d (diff)
sched: Only queue remote wakeups when crossing cache boundaries
Mike reported a 13% drop in netperf TCP_RR performance due to the new remote wakeup code. Suresh too noticed some performance issues with it. Reducing the IPIs to only cross cache domains solves the observed performance issues. Reported-by: Suresh Siddha <suresh.b.siddha@intel.com> Reported-by: Mike Galbraith <efault@gmx.de> Acked-by: Suresh Siddha <suresh.b.siddha@intel.com> Acked-by: Mike Galbraith <efault@gmx.de> Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Chris Mason <chris.mason@oracle.com> Cc: Dave Kleikamp <dave.kleikamp@oracle.com> Link: http://lkml.kernel.org/r/1323338531.17673.7.camel@twins Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/sched/core.c34
-rw-r--r--kernel/sched/fair.c24
-rw-r--r--kernel/sched/sched.h42
3 files changed, 70 insertions, 30 deletions
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index cdf51a2adc26..dba878c73a08 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -1511,6 +1511,11 @@ static int ttwu_activate_remote(struct task_struct *p, int wake_flags)
1511 1511
1512} 1512}
1513#endif /* __ARCH_WANT_INTERRUPTS_ON_CTXSW */ 1513#endif /* __ARCH_WANT_INTERRUPTS_ON_CTXSW */
1514
1515static inline int ttwu_share_cache(int this_cpu, int that_cpu)
1516{
1517 return per_cpu(sd_llc_id, this_cpu) == per_cpu(sd_llc_id, that_cpu);
1518}
1514#endif /* CONFIG_SMP */ 1519#endif /* CONFIG_SMP */
1515 1520
1516static void ttwu_queue(struct task_struct *p, int cpu) 1521static void ttwu_queue(struct task_struct *p, int cpu)
@@ -1518,7 +1523,7 @@ static void ttwu_queue(struct task_struct *p, int cpu)
1518 struct rq *rq = cpu_rq(cpu); 1523 struct rq *rq = cpu_rq(cpu);
1519 1524
1520#if defined(CONFIG_SMP) 1525#if defined(CONFIG_SMP)
1521 if (sched_feat(TTWU_QUEUE) && cpu != smp_processor_id()) { 1526 if (sched_feat(TTWU_QUEUE) && !ttwu_share_cache(smp_processor_id(), cpu)) {
1522 sched_clock_cpu(cpu); /* sync clocks x-cpu */ 1527 sched_clock_cpu(cpu); /* sync clocks x-cpu */
1523 ttwu_queue_remote(p, cpu); 1528 ttwu_queue_remote(p, cpu);
1524 return; 1529 return;
@@ -5744,6 +5749,31 @@ static void destroy_sched_domains(struct sched_domain *sd, int cpu)
5744} 5749}
5745 5750
5746/* 5751/*
5752 * Keep a special pointer to the highest sched_domain that has
5753 * SD_SHARE_PKG_RESOURCE set (Last Level Cache Domain) for this
5754 * allows us to avoid some pointer chasing select_idle_sibling().
5755 *
5756 * Also keep a unique ID per domain (we use the first cpu number in
5757 * the cpumask of the domain), this allows us to quickly tell if
5758 * two cpus are in the same cache domain, see ttwu_share_cache().
5759 */
5760DEFINE_PER_CPU(struct sched_domain *, sd_llc);
5761DEFINE_PER_CPU(int, sd_llc_id);
5762
5763static void update_top_cache_domain(int cpu)
5764{
5765 struct sched_domain *sd;
5766 int id = cpu;
5767
5768 sd = highest_flag_domain(cpu, SD_SHARE_PKG_RESOURCES);
5769 if (sd)
5770 id = cpumask_first(sched_domain_span(sd));
5771
5772 rcu_assign_pointer(per_cpu(sd_llc, cpu), sd);
5773 per_cpu(sd_llc_id, cpu) = id;
5774}
5775
5776/*
5747 * Attach the domain 'sd' to 'cpu' as its base domain. Callers must 5777 * Attach the domain 'sd' to 'cpu' as its base domain. Callers must
5748 * hold the hotplug lock. 5778 * hold the hotplug lock.
5749 */ 5779 */
@@ -5782,6 +5812,8 @@ cpu_attach_domain(struct sched_domain *sd, struct root_domain *rd, int cpu)
5782 tmp = rq->sd; 5812 tmp = rq->sd;
5783 rcu_assign_pointer(rq->sd, sd); 5813 rcu_assign_pointer(rq->sd, sd);
5784 destroy_sched_domains(tmp, cpu); 5814 destroy_sched_domains(tmp, cpu);
5815
5816 update_top_cache_domain(cpu);
5785} 5817}
5786 5818
5787/* cpus with isolated domains */ 5819/* cpus with isolated domains */
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index a4d2b7abc3cd..2237ffefdbce 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -2644,28 +2644,6 @@ find_idlest_cpu(struct sched_group *group, struct task_struct *p, int this_cpu)
2644 return idlest; 2644 return idlest;
2645} 2645}
2646 2646
2647/**
2648 * highest_flag_domain - Return highest sched_domain containing flag.
2649 * @cpu: The cpu whose highest level of sched domain is to
2650 * be returned.
2651 * @flag: The flag to check for the highest sched_domain
2652 * for the given cpu.
2653 *
2654 * Returns the highest sched_domain of a cpu which contains the given flag.
2655 */
2656static inline struct sched_domain *highest_flag_domain(int cpu, int flag)
2657{
2658 struct sched_domain *sd, *hsd = NULL;
2659
2660 for_each_domain(cpu, sd) {
2661 if (!(sd->flags & flag))
2662 break;
2663 hsd = sd;
2664 }
2665
2666 return hsd;
2667}
2668
2669/* 2647/*
2670 * Try and locate an idle CPU in the sched_domain. 2648 * Try and locate an idle CPU in the sched_domain.
2671 */ 2649 */
@@ -2696,7 +2674,7 @@ static int select_idle_sibling(struct task_struct *p, int target)
2696 */ 2674 */
2697 rcu_read_lock(); 2675 rcu_read_lock();
2698 2676
2699 sd = highest_flag_domain(target, SD_SHARE_PKG_RESOURCES); 2677 sd = rcu_dereference(per_cpu(sd_llc, target));
2700 for_each_lower_domain(sd) { 2678 for_each_lower_domain(sd) {
2701 sg = sd->groups; 2679 sg = sd->groups;
2702 do { 2680 do {
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index d8d3613a4055..98c0c2623db8 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -487,6 +487,14 @@ static inline int cpu_of(struct rq *rq)
487 487
488DECLARE_PER_CPU(struct rq, runqueues); 488DECLARE_PER_CPU(struct rq, runqueues);
489 489
490#define cpu_rq(cpu) (&per_cpu(runqueues, (cpu)))
491#define this_rq() (&__get_cpu_var(runqueues))
492#define task_rq(p) cpu_rq(task_cpu(p))
493#define cpu_curr(cpu) (cpu_rq(cpu)->curr)
494#define raw_rq() (&__raw_get_cpu_var(runqueues))
495
496#ifdef CONFIG_SMP
497
490#define rcu_dereference_check_sched_domain(p) \ 498#define rcu_dereference_check_sched_domain(p) \
491 rcu_dereference_check((p), \ 499 rcu_dereference_check((p), \
492 lockdep_is_held(&sched_domains_mutex)) 500 lockdep_is_held(&sched_domains_mutex))
@@ -499,15 +507,37 @@ DECLARE_PER_CPU(struct rq, runqueues);
499 * preempt-disabled sections. 507 * preempt-disabled sections.
500 */ 508 */
501#define for_each_domain(cpu, __sd) \ 509#define for_each_domain(cpu, __sd) \
502 for (__sd = rcu_dereference_check_sched_domain(cpu_rq(cpu)->sd); __sd; __sd = __sd->parent) 510 for (__sd = rcu_dereference_check_sched_domain(cpu_rq(cpu)->sd); \
511 __sd; __sd = __sd->parent)
503 512
504#define for_each_lower_domain(sd) for (; sd; sd = sd->child) 513#define for_each_lower_domain(sd) for (; sd; sd = sd->child)
505 514
506#define cpu_rq(cpu) (&per_cpu(runqueues, (cpu))) 515/**
507#define this_rq() (&__get_cpu_var(runqueues)) 516 * highest_flag_domain - Return highest sched_domain containing flag.
508#define task_rq(p) cpu_rq(task_cpu(p)) 517 * @cpu: The cpu whose highest level of sched domain is to
509#define cpu_curr(cpu) (cpu_rq(cpu)->curr) 518 * be returned.
510#define raw_rq() (&__raw_get_cpu_var(runqueues)) 519 * @flag: The flag to check for the highest sched_domain
520 * for the given cpu.
521 *
522 * Returns the highest sched_domain of a cpu which contains the given flag.
523 */
524static inline struct sched_domain *highest_flag_domain(int cpu, int flag)
525{
526 struct sched_domain *sd, *hsd = NULL;
527
528 for_each_domain(cpu, sd) {
529 if (!(sd->flags & flag))
530 break;
531 hsd = sd;
532 }
533
534 return hsd;
535}
536
537DECLARE_PER_CPU(struct sched_domain *, sd_llc);
538DECLARE_PER_CPU(int, sd_llc_id);
539
540#endif /* CONFIG_SMP */
511 541
512#include "stats.h" 542#include "stats.h"
513#include "auto_group.h" 543#include "auto_group.h"