diff options
Diffstat (limited to 'kernel/sched.c')
| -rw-r--r-- | kernel/sched.c | 129 |
1 files changed, 127 insertions, 2 deletions
diff --git a/kernel/sched.c b/kernel/sched.c index 7c9098d186e6..9ae80bec1c1e 100644 --- a/kernel/sched.c +++ b/kernel/sched.c | |||
| @@ -7024,6 +7024,11 @@ fail: | |||
| 7024 | return ret; | 7024 | return ret; |
| 7025 | } | 7025 | } |
| 7026 | 7026 | ||
| 7027 | #define RCU_MIGRATION_IDLE 0 | ||
| 7028 | #define RCU_MIGRATION_NEED_QS 1 | ||
| 7029 | #define RCU_MIGRATION_GOT_QS 2 | ||
| 7030 | #define RCU_MIGRATION_MUST_SYNC 3 | ||
| 7031 | |||
| 7027 | /* | 7032 | /* |
| 7028 | * migration_thread - this is a highprio system thread that performs | 7033 | * migration_thread - this is a highprio system thread that performs |
| 7029 | * thread migration by bumping thread off CPU then 'pushing' onto | 7034 | * thread migration by bumping thread off CPU then 'pushing' onto |
| @@ -7031,6 +7036,7 @@ fail: | |||
| 7031 | */ | 7036 | */ |
| 7032 | static int migration_thread(void *data) | 7037 | static int migration_thread(void *data) |
| 7033 | { | 7038 | { |
| 7039 | int badcpu; | ||
| 7034 | int cpu = (long)data; | 7040 | int cpu = (long)data; |
| 7035 | struct rq *rq; | 7041 | struct rq *rq; |
| 7036 | 7042 | ||
| @@ -7065,8 +7071,17 @@ static int migration_thread(void *data) | |||
| 7065 | req = list_entry(head->next, struct migration_req, list); | 7071 | req = list_entry(head->next, struct migration_req, list); |
| 7066 | list_del_init(head->next); | 7072 | list_del_init(head->next); |
| 7067 | 7073 | ||
| 7068 | spin_unlock(&rq->lock); | 7074 | if (req->task != NULL) { |
| 7069 | __migrate_task(req->task, cpu, req->dest_cpu); | 7075 | spin_unlock(&rq->lock); |
| 7076 | __migrate_task(req->task, cpu, req->dest_cpu); | ||
| 7077 | } else if (likely(cpu == (badcpu = smp_processor_id()))) { | ||
| 7078 | req->dest_cpu = RCU_MIGRATION_GOT_QS; | ||
| 7079 | spin_unlock(&rq->lock); | ||
| 7080 | } else { | ||
| 7081 | req->dest_cpu = RCU_MIGRATION_MUST_SYNC; | ||
| 7082 | spin_unlock(&rq->lock); | ||
| 7083 | WARN_ONCE(1, "migration_thread() on CPU %d, expected %d\n", badcpu, cpu); | ||
| 7084 | } | ||
| 7070 | local_irq_enable(); | 7085 | local_irq_enable(); |
| 7071 | 7086 | ||
| 7072 | complete(&req->done); | 7087 | complete(&req->done); |
| @@ -10554,3 +10569,113 @@ struct cgroup_subsys cpuacct_subsys = { | |||
| 10554 | .subsys_id = cpuacct_subsys_id, | 10569 | .subsys_id = cpuacct_subsys_id, |
| 10555 | }; | 10570 | }; |
| 10556 | #endif /* CONFIG_CGROUP_CPUACCT */ | 10571 | #endif /* CONFIG_CGROUP_CPUACCT */ |
| 10572 | |||
| 10573 | #ifndef CONFIG_SMP | ||
| 10574 | |||
| 10575 | int rcu_expedited_torture_stats(char *page) | ||
| 10576 | { | ||
| 10577 | return 0; | ||
| 10578 | } | ||
| 10579 | EXPORT_SYMBOL_GPL(rcu_expedited_torture_stats); | ||
| 10580 | |||
| 10581 | void synchronize_sched_expedited(void) | ||
| 10582 | { | ||
| 10583 | } | ||
| 10584 | EXPORT_SYMBOL_GPL(synchronize_sched_expedited); | ||
| 10585 | |||
| 10586 | #else /* #ifndef CONFIG_SMP */ | ||
| 10587 | |||
| 10588 | static DEFINE_PER_CPU(struct migration_req, rcu_migration_req); | ||
| 10589 | static DEFINE_MUTEX(rcu_sched_expedited_mutex); | ||
| 10590 | |||
| 10591 | #define RCU_EXPEDITED_STATE_POST -2 | ||
| 10592 | #define RCU_EXPEDITED_STATE_IDLE -1 | ||
| 10593 | |||
| 10594 | static int rcu_expedited_state = RCU_EXPEDITED_STATE_IDLE; | ||
| 10595 | |||
| 10596 | int rcu_expedited_torture_stats(char *page) | ||
| 10597 | { | ||
| 10598 | int cnt = 0; | ||
| 10599 | int cpu; | ||
| 10600 | |||
| 10601 | cnt += sprintf(&page[cnt], "state: %d /", rcu_expedited_state); | ||
| 10602 | for_each_online_cpu(cpu) { | ||
| 10603 | cnt += sprintf(&page[cnt], " %d:%d", | ||
| 10604 | cpu, per_cpu(rcu_migration_req, cpu).dest_cpu); | ||
| 10605 | } | ||
| 10606 | cnt += sprintf(&page[cnt], "\n"); | ||
| 10607 | return cnt; | ||
| 10608 | } | ||
| 10609 | EXPORT_SYMBOL_GPL(rcu_expedited_torture_stats); | ||
| 10610 | |||
| 10611 | static long synchronize_sched_expedited_count; | ||
| 10612 | |||
| 10613 | /* | ||
| 10614 | * Wait for an rcu-sched grace period to elapse, but use "big hammer" | ||
| 10615 | * approach to force grace period to end quickly. This consumes | ||
| 10616 | * significant time on all CPUs, and is thus not recommended for | ||
| 10617 | * any sort of common-case code. | ||
| 10618 | * | ||
| 10619 | * Note that it is illegal to call this function while holding any | ||
| 10620 | * lock that is acquired by a CPU-hotplug notifier. Failing to | ||
| 10621 | * observe this restriction will result in deadlock. | ||
| 10622 | */ | ||
| 10623 | void synchronize_sched_expedited(void) | ||
| 10624 | { | ||
| 10625 | int cpu; | ||
| 10626 | unsigned long flags; | ||
| 10627 | bool need_full_sync = 0; | ||
| 10628 | struct rq *rq; | ||
| 10629 | struct migration_req *req; | ||
| 10630 | long snap; | ||
| 10631 | int trycount = 0; | ||
| 10632 | |||
| 10633 | smp_mb(); /* ensure prior mod happens before capturing snap. */ | ||
| 10634 | snap = ACCESS_ONCE(synchronize_sched_expedited_count) + 1; | ||
| 10635 | get_online_cpus(); | ||
| 10636 | while (!mutex_trylock(&rcu_sched_expedited_mutex)) { | ||
| 10637 | put_online_cpus(); | ||
| 10638 | if (trycount++ < 10) | ||
| 10639 | udelay(trycount * num_online_cpus()); | ||
| 10640 | else { | ||
| 10641 | synchronize_sched(); | ||
| 10642 | return; | ||
| 10643 | } | ||
| 10644 | if (ACCESS_ONCE(synchronize_sched_expedited_count) - snap > 0) { | ||
| 10645 | smp_mb(); /* ensure test happens before caller kfree */ | ||
| 10646 | return; | ||
| 10647 | } | ||
| 10648 | get_online_cpus(); | ||
| 10649 | } | ||
| 10650 | rcu_expedited_state = RCU_EXPEDITED_STATE_POST; | ||
| 10651 | for_each_online_cpu(cpu) { | ||
| 10652 | rq = cpu_rq(cpu); | ||
| 10653 | req = &per_cpu(rcu_migration_req, cpu); | ||
| 10654 | init_completion(&req->done); | ||
| 10655 | req->task = NULL; | ||
| 10656 | req->dest_cpu = RCU_MIGRATION_NEED_QS; | ||
| 10657 | spin_lock_irqsave(&rq->lock, flags); | ||
| 10658 | list_add(&req->list, &rq->migration_queue); | ||
| 10659 | spin_unlock_irqrestore(&rq->lock, flags); | ||
| 10660 | wake_up_process(rq->migration_thread); | ||
| 10661 | } | ||
| 10662 | for_each_online_cpu(cpu) { | ||
| 10663 | rcu_expedited_state = cpu; | ||
| 10664 | req = &per_cpu(rcu_migration_req, cpu); | ||
| 10665 | rq = cpu_rq(cpu); | ||
| 10666 | wait_for_completion(&req->done); | ||
| 10667 | spin_lock_irqsave(&rq->lock, flags); | ||
| 10668 | if (unlikely(req->dest_cpu == RCU_MIGRATION_MUST_SYNC)) | ||
| 10669 | need_full_sync = 1; | ||
| 10670 | req->dest_cpu = RCU_MIGRATION_IDLE; | ||
| 10671 | spin_unlock_irqrestore(&rq->lock, flags); | ||
| 10672 | } | ||
| 10673 | rcu_expedited_state = RCU_EXPEDITED_STATE_IDLE; | ||
| 10674 | mutex_unlock(&rcu_sched_expedited_mutex); | ||
| 10675 | put_online_cpus(); | ||
| 10676 | if (need_full_sync) | ||
| 10677 | synchronize_sched(); | ||
| 10678 | } | ||
| 10679 | EXPORT_SYMBOL_GPL(synchronize_sched_expedited); | ||
| 10680 | |||
| 10681 | #endif /* #else #ifndef CONFIG_SMP */ | ||
