sched: fix cpupri hotplug support
The RT folks over at RedHat found an issue w.r.t. hotplug support which was traced to problems with the cpupri infrastructure in the scheduler: https://bugzilla.redhat.com/show_bug.cgi?id=449676 This bug affects 23-rt12+, 24-rtX, 25-rtX, and sched-devel. This patch applies to 25.4-rt4, though it should trivially apply to most cpupri enabled kernels mentioned above. It turned out that the issue was that offline cpus could get inadvertently registered with cpupri so that they were erroneously selected during migration decisions. The end result would be an OOPS as the offline cpu had tasks routed to it. This patch generalizes the old join/leave domain interface into an online/offline interface, and adjusts the root-domain/hotplug code to utilize it. I was able to easily reproduce the issue prior to this patch, and am no longer able to reproduce it after this patch. I can offline cpus indefinately and everything seems to be in working order. Thanks to Arnaldo (acme), Thomas, and Peter for doing the legwork to point me in the right direction. Also thank you to Peter for reviewing the early iterations of this patch. Signed-off-by: Gregory Haskins <ghaskins@novell.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Ingo Molnar <mingo@elte.hu> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
This commit is contained in:
parent
099f98c8a1
commit
1f11eb6a8b
3 changed files with 60 additions and 22 deletions
|
@ -903,8 +903,8 @@ struct sched_class {
|
||||||
void (*set_cpus_allowed)(struct task_struct *p,
|
void (*set_cpus_allowed)(struct task_struct *p,
|
||||||
const cpumask_t *newmask);
|
const cpumask_t *newmask);
|
||||||
|
|
||||||
void (*join_domain)(struct rq *rq);
|
void (*rq_online)(struct rq *rq);
|
||||||
void (*leave_domain)(struct rq *rq);
|
void (*rq_offline)(struct rq *rq);
|
||||||
|
|
||||||
void (*switched_from) (struct rq *this_rq, struct task_struct *task,
|
void (*switched_from) (struct rq *this_rq, struct task_struct *task,
|
||||||
int running);
|
int running);
|
||||||
|
|
|
@ -529,6 +529,7 @@ struct rq {
|
||||||
int push_cpu;
|
int push_cpu;
|
||||||
/* cpu of this runqueue: */
|
/* cpu of this runqueue: */
|
||||||
int cpu;
|
int cpu;
|
||||||
|
int online;
|
||||||
|
|
||||||
struct task_struct *migration_thread;
|
struct task_struct *migration_thread;
|
||||||
struct list_head migration_queue;
|
struct list_head migration_queue;
|
||||||
|
@ -1498,6 +1499,8 @@ static void cfs_rq_set_shares(struct cfs_rq *cfs_rq, unsigned long shares)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define sched_class_highest (&rt_sched_class)
|
#define sched_class_highest (&rt_sched_class)
|
||||||
|
#define for_each_class(class) \
|
||||||
|
for (class = sched_class_highest; class; class = class->next)
|
||||||
|
|
||||||
static inline void inc_load(struct rq *rq, const struct task_struct *p)
|
static inline void inc_load(struct rq *rq, const struct task_struct *p)
|
||||||
{
|
{
|
||||||
|
@ -6065,6 +6068,36 @@ static void unregister_sched_domain_sysctl(void)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void set_rq_online(struct rq *rq)
|
||||||
|
{
|
||||||
|
if (!rq->online) {
|
||||||
|
const struct sched_class *class;
|
||||||
|
|
||||||
|
cpu_set(rq->cpu, rq->rd->online);
|
||||||
|
rq->online = 1;
|
||||||
|
|
||||||
|
for_each_class(class) {
|
||||||
|
if (class->rq_online)
|
||||||
|
class->rq_online(rq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_rq_offline(struct rq *rq)
|
||||||
|
{
|
||||||
|
if (rq->online) {
|
||||||
|
const struct sched_class *class;
|
||||||
|
|
||||||
|
for_each_class(class) {
|
||||||
|
if (class->rq_offline)
|
||||||
|
class->rq_offline(rq);
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu_clear(rq->cpu, rq->rd->online);
|
||||||
|
rq->online = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* migration_call - callback that gets triggered when a CPU is added.
|
* migration_call - callback that gets triggered when a CPU is added.
|
||||||
* Here we can start up the necessary migration thread for the new CPU.
|
* Here we can start up the necessary migration thread for the new CPU.
|
||||||
|
@ -6102,7 +6135,8 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu)
|
||||||
spin_lock_irqsave(&rq->lock, flags);
|
spin_lock_irqsave(&rq->lock, flags);
|
||||||
if (rq->rd) {
|
if (rq->rd) {
|
||||||
BUG_ON(!cpu_isset(cpu, rq->rd->span));
|
BUG_ON(!cpu_isset(cpu, rq->rd->span));
|
||||||
cpu_set(cpu, rq->rd->online);
|
|
||||||
|
set_rq_online(rq);
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&rq->lock, flags);
|
spin_unlock_irqrestore(&rq->lock, flags);
|
||||||
break;
|
break;
|
||||||
|
@ -6163,7 +6197,7 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu)
|
||||||
spin_lock_irqsave(&rq->lock, flags);
|
spin_lock_irqsave(&rq->lock, flags);
|
||||||
if (rq->rd) {
|
if (rq->rd) {
|
||||||
BUG_ON(!cpu_isset(cpu, rq->rd->span));
|
BUG_ON(!cpu_isset(cpu, rq->rd->span));
|
||||||
cpu_clear(cpu, rq->rd->online);
|
set_rq_offline(rq);
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&rq->lock, flags);
|
spin_unlock_irqrestore(&rq->lock, flags);
|
||||||
break;
|
break;
|
||||||
|
@ -6385,20 +6419,16 @@ sd_parent_degenerate(struct sched_domain *sd, struct sched_domain *parent)
|
||||||
static void rq_attach_root(struct rq *rq, struct root_domain *rd)
|
static void rq_attach_root(struct rq *rq, struct root_domain *rd)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
const struct sched_class *class;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&rq->lock, flags);
|
spin_lock_irqsave(&rq->lock, flags);
|
||||||
|
|
||||||
if (rq->rd) {
|
if (rq->rd) {
|
||||||
struct root_domain *old_rd = rq->rd;
|
struct root_domain *old_rd = rq->rd;
|
||||||
|
|
||||||
for (class = sched_class_highest; class; class = class->next) {
|
if (cpu_isset(rq->cpu, old_rd->online))
|
||||||
if (class->leave_domain)
|
set_rq_offline(rq);
|
||||||
class->leave_domain(rq);
|
|
||||||
}
|
|
||||||
|
|
||||||
cpu_clear(rq->cpu, old_rd->span);
|
cpu_clear(rq->cpu, old_rd->span);
|
||||||
cpu_clear(rq->cpu, old_rd->online);
|
|
||||||
|
|
||||||
if (atomic_dec_and_test(&old_rd->refcount))
|
if (atomic_dec_and_test(&old_rd->refcount))
|
||||||
kfree(old_rd);
|
kfree(old_rd);
|
||||||
|
@ -6409,12 +6439,7 @@ static void rq_attach_root(struct rq *rq, struct root_domain *rd)
|
||||||
|
|
||||||
cpu_set(rq->cpu, rd->span);
|
cpu_set(rq->cpu, rd->span);
|
||||||
if (cpu_isset(rq->cpu, cpu_online_map))
|
if (cpu_isset(rq->cpu, cpu_online_map))
|
||||||
cpu_set(rq->cpu, rd->online);
|
set_rq_online(rq);
|
||||||
|
|
||||||
for (class = sched_class_highest; class; class = class->next) {
|
|
||||||
if (class->join_domain)
|
|
||||||
class->join_domain(rq);
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&rq->lock, flags);
|
spin_unlock_irqrestore(&rq->lock, flags);
|
||||||
}
|
}
|
||||||
|
@ -7824,6 +7849,7 @@ void __init sched_init(void)
|
||||||
rq->next_balance = jiffies;
|
rq->next_balance = jiffies;
|
||||||
rq->push_cpu = 0;
|
rq->push_cpu = 0;
|
||||||
rq->cpu = i;
|
rq->cpu = i;
|
||||||
|
rq->online = 0;
|
||||||
rq->migration_thread = NULL;
|
rq->migration_thread = NULL;
|
||||||
INIT_LIST_HEAD(&rq->migration_queue);
|
INIT_LIST_HEAD(&rq->migration_queue);
|
||||||
rq_attach_root(rq, &def_root_domain);
|
rq_attach_root(rq, &def_root_domain);
|
||||||
|
|
|
@ -12,6 +12,9 @@ static inline int rt_overloaded(struct rq *rq)
|
||||||
|
|
||||||
static inline void rt_set_overload(struct rq *rq)
|
static inline void rt_set_overload(struct rq *rq)
|
||||||
{
|
{
|
||||||
|
if (!rq->online)
|
||||||
|
return;
|
||||||
|
|
||||||
cpu_set(rq->cpu, rq->rd->rto_mask);
|
cpu_set(rq->cpu, rq->rd->rto_mask);
|
||||||
/*
|
/*
|
||||||
* Make sure the mask is visible before we set
|
* Make sure the mask is visible before we set
|
||||||
|
@ -26,6 +29,9 @@ static inline void rt_set_overload(struct rq *rq)
|
||||||
|
|
||||||
static inline void rt_clear_overload(struct rq *rq)
|
static inline void rt_clear_overload(struct rq *rq)
|
||||||
{
|
{
|
||||||
|
if (!rq->online)
|
||||||
|
return;
|
||||||
|
|
||||||
/* the order here really doesn't matter */
|
/* the order here really doesn't matter */
|
||||||
atomic_dec(&rq->rd->rto_count);
|
atomic_dec(&rq->rd->rto_count);
|
||||||
cpu_clear(rq->cpu, rq->rd->rto_mask);
|
cpu_clear(rq->cpu, rq->rd->rto_mask);
|
||||||
|
@ -394,7 +400,10 @@ void inc_rt_tasks(struct sched_rt_entity *rt_se, struct rt_rq *rt_rq)
|
||||||
if (rt_se_prio(rt_se) < rt_rq->highest_prio) {
|
if (rt_se_prio(rt_se) < rt_rq->highest_prio) {
|
||||||
struct rq *rq = rq_of_rt_rq(rt_rq);
|
struct rq *rq = rq_of_rt_rq(rt_rq);
|
||||||
rt_rq->highest_prio = rt_se_prio(rt_se);
|
rt_rq->highest_prio = rt_se_prio(rt_se);
|
||||||
cpupri_set(&rq->rd->cpupri, rq->cpu, rt_se_prio(rt_se));
|
|
||||||
|
if (rq->online)
|
||||||
|
cpupri_set(&rq->rd->cpupri, rq->cpu,
|
||||||
|
rt_se_prio(rt_se));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_SMP
|
#ifdef CONFIG_SMP
|
||||||
|
@ -448,7 +457,10 @@ void dec_rt_tasks(struct sched_rt_entity *rt_se, struct rt_rq *rt_rq)
|
||||||
|
|
||||||
if (rt_rq->highest_prio != highest_prio) {
|
if (rt_rq->highest_prio != highest_prio) {
|
||||||
struct rq *rq = rq_of_rt_rq(rt_rq);
|
struct rq *rq = rq_of_rt_rq(rt_rq);
|
||||||
cpupri_set(&rq->rd->cpupri, rq->cpu, rt_rq->highest_prio);
|
|
||||||
|
if (rq->online)
|
||||||
|
cpupri_set(&rq->rd->cpupri, rq->cpu,
|
||||||
|
rt_rq->highest_prio);
|
||||||
}
|
}
|
||||||
|
|
||||||
update_rt_migration(rq_of_rt_rq(rt_rq));
|
update_rt_migration(rq_of_rt_rq(rt_rq));
|
||||||
|
@ -1154,7 +1166,7 @@ static void set_cpus_allowed_rt(struct task_struct *p,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Assumes rq->lock is held */
|
/* Assumes rq->lock is held */
|
||||||
static void join_domain_rt(struct rq *rq)
|
static void rq_online_rt(struct rq *rq)
|
||||||
{
|
{
|
||||||
if (rq->rt.overloaded)
|
if (rq->rt.overloaded)
|
||||||
rt_set_overload(rq);
|
rt_set_overload(rq);
|
||||||
|
@ -1163,7 +1175,7 @@ static void join_domain_rt(struct rq *rq)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Assumes rq->lock is held */
|
/* Assumes rq->lock is held */
|
||||||
static void leave_domain_rt(struct rq *rq)
|
static void rq_offline_rt(struct rq *rq)
|
||||||
{
|
{
|
||||||
if (rq->rt.overloaded)
|
if (rq->rt.overloaded)
|
||||||
rt_clear_overload(rq);
|
rt_clear_overload(rq);
|
||||||
|
@ -1331,8 +1343,8 @@ static const struct sched_class rt_sched_class = {
|
||||||
.load_balance = load_balance_rt,
|
.load_balance = load_balance_rt,
|
||||||
.move_one_task = move_one_task_rt,
|
.move_one_task = move_one_task_rt,
|
||||||
.set_cpus_allowed = set_cpus_allowed_rt,
|
.set_cpus_allowed = set_cpus_allowed_rt,
|
||||||
.join_domain = join_domain_rt,
|
.rq_online = rq_online_rt,
|
||||||
.leave_domain = leave_domain_rt,
|
.rq_offline = rq_offline_rt,
|
||||||
.pre_schedule = pre_schedule_rt,
|
.pre_schedule = pre_schedule_rt,
|
||||||
.post_schedule = post_schedule_rt,
|
.post_schedule = post_schedule_rt,
|
||||||
.task_wake_up = task_wake_up_rt,
|
.task_wake_up = task_wake_up_rt,
|
||||||
|
|
Loading…
Reference in a new issue