mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-31 16:38:12 +00:00
A single scheduler fix plugging a race between sched_setscheduler() and
balance_push(). sched_setscheduler() spliced the balance callbacks accross a lock break which makes an interleaving schedule() observe an empty list. -----BEGIN PGP SIGNATURE----- iQJHBAABCgAxFiEEQp8+kY+LLUocC4bMphj1TA10mKEFAmKvHpETHHRnbHhAbGlu dXRyb25peC5kZQAKCRCmGPVMDXSYoUCfD/9LVsemdRGoz2Ur7LUjKMq9op7rzFmD arDTPsvb5ukbD2u+PrObo6pkAQvmSu/b/qFhLQNkGLUMWn/Ok5mq2jikwArboT3X k/BC56p7NSynJr2Xb035UcvlnLZxPn0giUo9SeGaGM4zm4rioXyrBWkmsjXmFh19 LrRTCip9Aom5F+ctCC96BOGvrsM/C3F4si7ZwEp8ZCcClmhrrZhTBEPFGKg0AuVJ qJkxgVmvuszaS7CL5ZyHiE35liyqRYx/NLldVWWFKxQcBqlGxqB/4ymB3QyecJfI VsuWr8oNZOGbjbhg7Nv/qdbDTz5dHxpEuzHx0osAFhUwQ7TSmJfh2InE1Xe0jNyU 0HXFsx2kmytLoMDuZCkzGJRaGotSsLL7ZTs2e6Ktbh4j42MGQaHYC5GbX68Doxv1 A9aoKenh658oU26kPXGI0iYo9tkToMY7U8HM56CXxaAc5+XwHKPoU2pUOUTrD0a/ eqP2Uu7QMuNzQ6M9vsD/lAm8HUlicp08h9s6wzENIwGSkJ7GdpItat5Mbh1pu7yh dL1+1q6kZCBjc9oy7THV4Oqph6Hxgg/oze9Oi9f4ocvt97GHtn1ahEOxey2i/CJR 7912p/yxbVXWOmUV6BGZ1TxjV3P9HpgWD5KPt/vOItnhMeWFU0NnxW6e0JJSV4T9 5dmPK8wH0/Nzvg== =k54w -----END PGP SIGNATURE----- Merge tag 'sched-urgent-2022-06-19' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip Pull scheduler fix from Thomas Gleixner: "A single scheduler fix plugging a race between sched_setscheduler() and balance_push(). sched_setscheduler() spliced the balance callbacks accross a lock break which makes it possible for an interleaving schedule() to observe an empty list" * tag 'sched-urgent-2022-06-19' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: sched: Fix balance_push() vs __sched_setscheduler()
This commit is contained in:
commit
727c3991df
2 changed files with 38 additions and 3 deletions
|
@ -4798,25 +4798,55 @@ static void do_balance_callbacks(struct rq *rq, struct callback_head *head)
|
|||
|
||||
static void balance_push(struct rq *rq);
|
||||
|
||||
/*
|
||||
* balance_push_callback is a right abuse of the callback interface and plays
|
||||
* by significantly different rules.
|
||||
*
|
||||
* Where the normal balance_callback's purpose is to be ran in the same context
|
||||
* that queued it (only later, when it's safe to drop rq->lock again),
|
||||
* balance_push_callback is specifically targeted at __schedule().
|
||||
*
|
||||
* This abuse is tolerated because it places all the unlikely/odd cases behind
|
||||
* a single test, namely: rq->balance_callback == NULL.
|
||||
*/
|
||||
struct callback_head balance_push_callback = {
|
||||
.next = NULL,
|
||||
.func = (void (*)(struct callback_head *))balance_push,
|
||||
};
|
||||
|
||||
static inline struct callback_head *splice_balance_callbacks(struct rq *rq)
|
||||
static inline struct callback_head *
|
||||
__splice_balance_callbacks(struct rq *rq, bool split)
|
||||
{
|
||||
struct callback_head *head = rq->balance_callback;
|
||||
|
||||
if (likely(!head))
|
||||
return NULL;
|
||||
|
||||
lockdep_assert_rq_held(rq);
|
||||
if (head)
|
||||
/*
|
||||
* Must not take balance_push_callback off the list when
|
||||
* splice_balance_callbacks() and balance_callbacks() are not
|
||||
* in the same rq->lock section.
|
||||
*
|
||||
* In that case it would be possible for __schedule() to interleave
|
||||
* and observe the list empty.
|
||||
*/
|
||||
if (split && head == &balance_push_callback)
|
||||
head = NULL;
|
||||
else
|
||||
rq->balance_callback = NULL;
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
static inline struct callback_head *splice_balance_callbacks(struct rq *rq)
|
||||
{
|
||||
return __splice_balance_callbacks(rq, true);
|
||||
}
|
||||
|
||||
static void __balance_callbacks(struct rq *rq)
|
||||
{
|
||||
do_balance_callbacks(rq, splice_balance_callbacks(rq));
|
||||
do_balance_callbacks(rq, __splice_balance_callbacks(rq, false));
|
||||
}
|
||||
|
||||
static inline void balance_callbacks(struct rq *rq, struct callback_head *head)
|
||||
|
|
|
@ -1693,6 +1693,11 @@ queue_balance_callback(struct rq *rq,
|
|||
{
|
||||
lockdep_assert_rq_held(rq);
|
||||
|
||||
/*
|
||||
* Don't (re)queue an already queued item; nor queue anything when
|
||||
* balance_push() is active, see the comment with
|
||||
* balance_push_callback.
|
||||
*/
|
||||
if (unlikely(head->next || rq->balance_callback == &balance_push_callback))
|
||||
return;
|
||||
|
||||
|
|
Loading…
Reference in a new issue