diff options
author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2009-11-27 11:32:46 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-12-09 04:03:05 -0500 |
commit | cd29fe6f2637cc2ccbda5ac65f5332d6bf5fa3c6 (patch) | |
tree | b4206012d424a8c0bce1f260d042c678db0602a2 | |
parent | ab19cb23313733c55e0517607844b86720b35f5f (diff) |
sched: Sanitize fork() handling
Currently we try to do task placement in wake_up_new_task() after we do
the load-balance pass in sched_fork(). This yields complicated semantics
in that we have to deal with tasks on different RQs and the
set_task_cpu() calls in copy_process() and sched_fork()
Rename ->task_new() to ->task_fork() and call it from sched_fork()
before the balancing, this gives the policy a clear point to place the
task.
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r-- | include/linux/sched.h | 2 | ||||
-rw-r--r-- | kernel/sched.c | 47 | ||||
-rw-r--r-- | kernel/sched_fair.c | 28 |
3 files changed, 34 insertions, 43 deletions
diff --git a/include/linux/sched.h b/include/linux/sched.h index ca72ed42ac34..31d9dec78675 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h | |||
@@ -1102,7 +1102,7 @@ struct sched_class { | |||
1102 | 1102 | ||
1103 | void (*set_curr_task) (struct rq *rq); | 1103 | void (*set_curr_task) (struct rq *rq); |
1104 | void (*task_tick) (struct rq *rq, struct task_struct *p, int queued); | 1104 | void (*task_tick) (struct rq *rq, struct task_struct *p, int queued); |
1105 | void (*task_new) (struct rq *rq, struct task_struct *p); | 1105 | void (*task_fork) (struct task_struct *p); |
1106 | 1106 | ||
1107 | void (*switched_from) (struct rq *this_rq, struct task_struct *task, | 1107 | void (*switched_from) (struct rq *this_rq, struct task_struct *task, |
1108 | int running); | 1108 | int running); |
diff --git a/kernel/sched.c b/kernel/sched.c index c92670f8e097..33c903573132 100644 --- a/kernel/sched.c +++ b/kernel/sched.c | |||
@@ -1811,6 +1811,20 @@ static void cfs_rq_set_shares(struct cfs_rq *cfs_rq, unsigned long shares) | |||
1811 | 1811 | ||
1812 | static void calc_load_account_active(struct rq *this_rq); | 1812 | static void calc_load_account_active(struct rq *this_rq); |
1813 | 1813 | ||
1814 | static inline void __set_task_cpu(struct task_struct *p, unsigned int cpu) | ||
1815 | { | ||
1816 | set_task_rq(p, cpu); | ||
1817 | #ifdef CONFIG_SMP | ||
1818 | /* | ||
1819 | * After ->cpu is set up to a new value, task_rq_lock(p, ...) can be | ||
1820 | * successfuly executed on another CPU. We must ensure that updates of | ||
1821 | * per-task data have been completed by this moment. | ||
1822 | */ | ||
1823 | smp_wmb(); | ||
1824 | task_thread_info(p)->cpu = cpu; | ||
1825 | #endif | ||
1826 | } | ||
1827 | |||
1814 | #include "sched_stats.h" | 1828 | #include "sched_stats.h" |
1815 | #include "sched_idletask.c" | 1829 | #include "sched_idletask.c" |
1816 | #include "sched_fair.c" | 1830 | #include "sched_fair.c" |
@@ -1967,20 +1981,6 @@ inline int task_curr(const struct task_struct *p) | |||
1967 | return cpu_curr(task_cpu(p)) == p; | 1981 | return cpu_curr(task_cpu(p)) == p; |
1968 | } | 1982 | } |
1969 | 1983 | ||
1970 | static inline void __set_task_cpu(struct task_struct *p, unsigned int cpu) | ||
1971 | { | ||
1972 | set_task_rq(p, cpu); | ||
1973 | #ifdef CONFIG_SMP | ||
1974 | /* | ||
1975 | * After ->cpu is set up to a new value, task_rq_lock(p, ...) can be | ||
1976 | * successfuly executed on another CPU. We must ensure that updates of | ||
1977 | * per-task data have been completed by this moment. | ||
1978 | */ | ||
1979 | smp_wmb(); | ||
1980 | task_thread_info(p)->cpu = cpu; | ||
1981 | #endif | ||
1982 | } | ||
1983 | |||
1984 | static inline void check_class_changed(struct rq *rq, struct task_struct *p, | 1984 | static inline void check_class_changed(struct rq *rq, struct task_struct *p, |
1985 | const struct sched_class *prev_class, | 1985 | const struct sched_class *prev_class, |
1986 | int oldprio, int running) | 1986 | int oldprio, int running) |
@@ -2552,7 +2552,6 @@ static void __sched_fork(struct task_struct *p) | |||
2552 | void sched_fork(struct task_struct *p, int clone_flags) | 2552 | void sched_fork(struct task_struct *p, int clone_flags) |
2553 | { | 2553 | { |
2554 | int cpu = get_cpu(); | 2554 | int cpu = get_cpu(); |
2555 | unsigned long flags; | ||
2556 | 2555 | ||
2557 | __sched_fork(p); | 2556 | __sched_fork(p); |
2558 | 2557 | ||
@@ -2586,13 +2585,13 @@ void sched_fork(struct task_struct *p, int clone_flags) | |||
2586 | if (!rt_prio(p->prio)) | 2585 | if (!rt_prio(p->prio)) |
2587 | p->sched_class = &fair_sched_class; | 2586 | p->sched_class = &fair_sched_class; |
2588 | 2587 | ||
2588 | if (p->sched_class->task_fork) | ||
2589 | p->sched_class->task_fork(p); | ||
2590 | |||
2589 | #ifdef CONFIG_SMP | 2591 | #ifdef CONFIG_SMP |
2590 | cpu = select_task_rq(p, SD_BALANCE_FORK, 0); | 2592 | cpu = select_task_rq(p, SD_BALANCE_FORK, 0); |
2591 | #endif | 2593 | #endif |
2592 | local_irq_save(flags); | ||
2593 | update_rq_clock(cpu_rq(cpu)); | ||
2594 | set_task_cpu(p, cpu); | 2594 | set_task_cpu(p, cpu); |
2595 | local_irq_restore(flags); | ||
2596 | 2595 | ||
2597 | #if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT) | 2596 | #if defined(CONFIG_SCHEDSTATS) || defined(CONFIG_TASK_DELAY_ACCT) |
2598 | if (likely(sched_info_on())) | 2597 | if (likely(sched_info_on())) |
@@ -2625,17 +2624,7 @@ void wake_up_new_task(struct task_struct *p, unsigned long clone_flags) | |||
2625 | rq = task_rq_lock(p, &flags); | 2624 | rq = task_rq_lock(p, &flags); |
2626 | BUG_ON(p->state != TASK_RUNNING); | 2625 | BUG_ON(p->state != TASK_RUNNING); |
2627 | update_rq_clock(rq); | 2626 | update_rq_clock(rq); |
2628 | 2627 | activate_task(rq, p, 0); | |
2629 | if (!p->sched_class->task_new || !current->se.on_rq) { | ||
2630 | activate_task(rq, p, 0); | ||
2631 | } else { | ||
2632 | /* | ||
2633 | * Let the scheduling class do new task startup | ||
2634 | * management (if any): | ||
2635 | */ | ||
2636 | p->sched_class->task_new(rq, p); | ||
2637 | inc_nr_running(rq); | ||
2638 | } | ||
2639 | trace_sched_wakeup_new(rq, p, 1); | 2628 | trace_sched_wakeup_new(rq, p, 1); |
2640 | check_preempt_curr(rq, p, WF_FORK); | 2629 | check_preempt_curr(rq, p, WF_FORK); |
2641 | #ifdef CONFIG_SMP | 2630 | #ifdef CONFIG_SMP |
diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c index 613c1c749677..44ec80ccfa85 100644 --- a/kernel/sched_fair.c +++ b/kernel/sched_fair.c | |||
@@ -1922,28 +1922,30 @@ static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued) | |||
1922 | } | 1922 | } |
1923 | 1923 | ||
1924 | /* | 1924 | /* |
1925 | * Share the fairness runtime between parent and child, thus the | 1925 | * called on fork with the child task as argument from the parent's context |
1926 | * total amount of pressure for CPU stays equal - new tasks | 1926 | * - child not yet on the tasklist |
1927 | * get a chance to run but frequent forkers are not allowed to | 1927 | * - preemption disabled |
1928 | * monopolize the CPU. Note: the parent runqueue is locked, | ||
1929 | * the child is not running yet. | ||
1930 | */ | 1928 | */ |
1931 | static void task_new_fair(struct rq *rq, struct task_struct *p) | 1929 | static void task_fork_fair(struct task_struct *p) |
1932 | { | 1930 | { |
1933 | struct cfs_rq *cfs_rq = task_cfs_rq(p); | 1931 | struct cfs_rq *cfs_rq = task_cfs_rq(current); |
1934 | struct sched_entity *se = &p->se, *curr = cfs_rq->curr; | 1932 | struct sched_entity *se = &p->se, *curr = cfs_rq->curr; |
1935 | int this_cpu = smp_processor_id(); | 1933 | int this_cpu = smp_processor_id(); |
1934 | struct rq *rq = this_rq(); | ||
1935 | unsigned long flags; | ||
1936 | |||
1937 | spin_lock_irqsave(&rq->lock, flags); | ||
1936 | 1938 | ||
1937 | sched_info_queued(p); | 1939 | if (unlikely(task_cpu(p) != this_cpu)) |
1940 | __set_task_cpu(p, this_cpu); | ||
1938 | 1941 | ||
1939 | update_curr(cfs_rq); | 1942 | update_curr(cfs_rq); |
1943 | |||
1940 | if (curr) | 1944 | if (curr) |
1941 | se->vruntime = curr->vruntime; | 1945 | se->vruntime = curr->vruntime; |
1942 | place_entity(cfs_rq, se, 1); | 1946 | place_entity(cfs_rq, se, 1); |
1943 | 1947 | ||
1944 | /* 'curr' will be NULL if the child belongs to a different group */ | 1948 | if (sysctl_sched_child_runs_first && curr && entity_before(curr, se)) { |
1945 | if (sysctl_sched_child_runs_first && this_cpu == task_cpu(p) && | ||
1946 | curr && entity_before(curr, se)) { | ||
1947 | /* | 1949 | /* |
1948 | * Upon rescheduling, sched_class::put_prev_task() will place | 1950 | * Upon rescheduling, sched_class::put_prev_task() will place |
1949 | * 'current' within the tree based on its new key value. | 1951 | * 'current' within the tree based on its new key value. |
@@ -1952,7 +1954,7 @@ static void task_new_fair(struct rq *rq, struct task_struct *p) | |||
1952 | resched_task(rq->curr); | 1954 | resched_task(rq->curr); |
1953 | } | 1955 | } |
1954 | 1956 | ||
1955 | enqueue_task_fair(rq, p, 0); | 1957 | spin_unlock_irqrestore(&rq->lock, flags); |
1956 | } | 1958 | } |
1957 | 1959 | ||
1958 | /* | 1960 | /* |
@@ -2052,7 +2054,7 @@ static const struct sched_class fair_sched_class = { | |||
2052 | 2054 | ||
2053 | .set_curr_task = set_curr_task_fair, | 2055 | .set_curr_task = set_curr_task_fair, |
2054 | .task_tick = task_tick_fair, | 2056 | .task_tick = task_tick_fair, |
2055 | .task_new = task_new_fair, | 2057 | .task_fork = task_fork_fair, |
2056 | 2058 | ||
2057 | .prio_changed = prio_changed_fair, | 2059 | .prio_changed = prio_changed_fair, |
2058 | .switched_to = switched_to_fair, | 2060 | .switched_to = switched_to_fair, |