History

sched: Fix balance_callback()

The intent of balance_callback() has always been to delay executing balancing operations until the end of the current rq->lock section. This is because balance operations must often drop rq->lock, and that isn't safe in general. However, as noted by Scott, there were a few holes in that scheme; balance_callback() was called after rq->lock was dropped, which means another CPU can interleave and touch the callback list. Rework code to call the balance callbacks before dropping rq->lock where possible, and otherwise splice the balance list onto a local stack. This guarantees that the balance list must be empty when we take rq->lock. IOW, we'll only ever run our own balance callbacks.

Peter Zijlstra committed 6 years ago

Commit 565790d1 parent a8b62fd2 files · +78 −44Browse files

Unified · Split

2 files
  1. kernel/sched/core.c +75 −44
  2. kernel/sched/sched.h +3 −0
kernel/sched/core.c+75−44
@@ -3485,6 +3485,69 @@ static inline void finish_task(struct task_struct *prev) · 3484 unmodified lines
34853485 #endif
34863486 }
34873487
3488+#ifdef CONFIG_SMP
3489+
3490+static void do_balance_callbacks(struct rq *rq, struct callback_head *head)
3491+{
3492+ void (*func)(struct rq *rq);
3493+ struct callback_head *next;
3494+
3495+ lockdep_assert_held(&rq->lock);
3496+
3497+ while (head) {
3498+ func = (void (*)(struct rq *))head->func;
3499+ next = head->next;
3500+ head->next = NULL;
3501+ head = next;
3502+
3503+ func(rq);
3504+ }
3505+}
3506+
3507+static inline struct callback_head *splice_balance_callbacks(struct rq *rq)
3508+{
3509+ struct callback_head *head = rq->balance_callback;
3510+
3511+ lockdep_assert_held(&rq->lock);
3512+ if (head)
3513+ rq->balance_callback = NULL;
3514+
3515+ return head;
3516+}
3517+
3518+static void __balance_callbacks(struct rq *rq)
3519+{
3520+ do_balance_callbacks(rq, splice_balance_callbacks(rq));
3521+}
3522+
3523+static inline void balance_callbacks(struct rq *rq, struct callback_head *head)
3524+{
3525+ unsigned long flags;
3526+
3527+ if (unlikely(head)) {
3528+ raw_spin_lock_irqsave(&rq->lock, flags);
3529+ do_balance_callbacks(rq, head);
3530+ raw_spin_unlock_irqrestore(&rq->lock, flags);
3531+ }
3532+}
3533+
3534+#else
3535+
3536+static inline void __balance_callbacks(struct rq *rq)
3537+{
3538+}
3539+
3540+static inline struct callback_head *splice_balance_callbacks(struct rq *rq)
3541+{
3542+ return NULL;
3543+}
3544+
3545+static inline void balance_callbacks(struct rq *rq, struct callback_head *head)
3546+{
3547+}
3548+
3549+#endif
3550+
34883551 static inline void
34893552 prepare_lock_switch(struct rq *rq, struct task_struct *next, struct rq_flags *rf)
34903553 {
@@ -3510,6 +3573,7 @@ static inline void finish_lock_switch(struct rq *rq) · 19 unmodified lines
35103573 * prev into current:
35113574 */
35123575 spin_acquire(&rq->lock.dep_map, 0, 0, _THIS_IP_);
3576+ __balance_callbacks(rq);
35133577 raw_spin_unlock_irq(&rq->lock);
35143578 }
35153579
@@ -3651,43 +3715,6 @@ static struct rq *finish_task_switch(struct task_struct *prev) · 135 unmodified lines
36513715 return rq;
36523716 }
36533717
3654−#ifdef CONFIG_SMP
3655−
3656−/* rq->lock is NOT held, but preemption is disabled */
3657−static void __balance_callback(struct rq *rq)
3658−{
3659− struct callback_head *head, *next;
3660− void (*func)(struct rq *rq);
3661− unsigned long flags;
3662−
3663− raw_spin_lock_irqsave(&rq->lock, flags);
3664− head = rq->balance_callback;
3665− rq->balance_callback = NULL;
3666− while (head) {
3667− func = (void (*)(struct rq *))head->func;
3668− next = head->next;
3669− head->next = NULL;
3670− head = next;
3671−
3672− func(rq);
3673− }
3674− raw_spin_unlock_irqrestore(&rq->lock, flags);
3675−}
3676−
3677−static inline void balance_callback(struct rq *rq)
3678−{
3679− if (unlikely(rq->balance_callback))
3680− __balance_callback(rq);
3681−}
3682−
3683−#else
3684−
3685−static inline void balance_callback(struct rq *rq)
3686−{
3687−}
3688−
3689−#endif
3690−
36913718 /**
36923719 * schedule_tail - first thing a freshly forked thread must call.
36933720 * @prev: the thread we just switched away from.
@@ -3707,7 +3734,6 @@ asmlinkage __visible void schedule_tail(struct task_struct *prev) · 13 unmodified lines
37073734 */
37083735
37093736 rq = finish_task_switch(prev);
3710− balance_callback(rq);
37113737 preempt_enable();
37123738
37133739 if (current->set_child_tid)
@@ -4523,10 +4549,11 @@ static void __sched notrace __schedule(bool preempt) · 809 unmodified lines
45234549 rq = context_switch(rq, prev, next, &rf);
45244550 } else {
45254551 rq->clock_update_flags &= ~(RQCF_ACT_SKIP|RQCF_REQ_SKIP);
4526− rq_unlock_irq(rq, &rf);
4527− }
45284552
4529− balance_callback(rq);
4553+ rq_unpin_lock(rq, &rf);
4554+ __balance_callbacks(rq);
4555+ raw_spin_unlock_irq(&rq->lock);
4556+ }
45304557 }
45314558
45324559 void __noreturn do_task_dead(void)
@@ -4937,9 +4964,11 @@ void rt_mutex_setprio(struct task_struct *p, struct task_struct *pi_task) · 404 unmodified lines
49374964 out_unlock:
49384965 /* Avoid rq from going away on us: */
49394966 preempt_disable();
4940− __task_rq_unlock(rq, &rf);
49414967
4942− balance_callback(rq);
4968+ rq_unpin_lock(rq, &rf);
4969+ __balance_callbacks(rq);
4970+ raw_spin_unlock(&rq->lock);
4971+
49434972 preempt_enable();
49444973 }
49454974 #else
@@ -5213,6 +5242,7 @@ static int __sched_setscheduler(struct task_struct *p, · 267 unmodified lines
52135242 int retval, oldprio, oldpolicy = -1, queued, running;
52145243 int new_effective_prio, policy = attr->sched_policy;
52155244 const struct sched_class *prev_class;
5245+ struct callback_head *head;
52165246 struct rq_flags rf;
52175247 int reset_on_fork;
52185248 int queue_flags = DEQUEUE_SAVE | DEQUEUE_MOVE | DEQUEUE_NOCLOCK;
@@ -5451,6 +5481,7 @@ change: · 232 unmodified lines
54515481
54525482 /* Avoid rq from going away on us: */
54535483 preempt_disable();
5484+ head = splice_balance_callbacks(rq);
54545485 task_rq_unlock(rq, p, &rf);
54555486
54565487 if (pi) {
@@ -5459,7 +5490,7 @@ change: · 2 unmodified lines
54595490 }
54605491
54615492 /* Run balance callbacks after we've adjusted the PI chain: */
5462− balance_callback(rq);
5493+ balance_callbacks(rq, head);
54635494 preempt_enable();
54645495
54655496 return 0;
kernel/sched/sched.h+3−0
@@ -1221,6 +1221,9 @@ static inline void rq_pin_lock(struct rq *rq, struct rq_flags *rf) · 1220 unmodified lines
12211221 rq->clock_update_flags &= (RQCF_REQ_SKIP|RQCF_ACT_SKIP);
12221222 rf->clock_update_flags = 0;
12231223 #endif
1224+#ifdef CONFIG_SMP
1225+ SCHED_WARN_ON(rq->balance_callback);
1226+#endif
12241227 }
12251228
12261229 static inline void rq_unpin_lock(struct rq *rq, struct rq_flags *rf)

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t