Skip to content

Commit 73ab05a

Browse files
Waiman-LongPeter Zijlstra
authored andcommitted
sched/core: Disable page allocation in task_tick_mm_cid()
With KASAN and PREEMPT_RT enabled, calling task_work_add() in task_tick_mm_cid() may cause the following splat. [ 63.696416] BUG: sleeping function called from invalid context at kernel/locking/spinlock_rt.c:48 [ 63.696416] in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 610, name: modprobe [ 63.696416] preempt_count: 10001, expected: 0 [ 63.696416] RCU nest depth: 1, expected: 1 This problem is caused by the following call trace. sched_tick() [ acquire rq->__lock ] -> task_tick_mm_cid() -> task_work_add() -> __kasan_record_aux_stack() -> kasan_save_stack() -> stack_depot_save_flags() -> alloc_pages_mpol_noprof() -> __alloc_pages_noprof() -> get_page_from_freelist() -> rmqueue() -> rmqueue_pcplist() -> __rmqueue_pcplist() -> rmqueue_bulk() -> rt_spin_lock() The rq lock is a raw_spinlock_t. We can't sleep while holding it. IOW, we can't call alloc_pages() in stack_depot_save_flags(). The task_tick_mm_cid() function with its task_work_add() call was introduced by commit 223baf9 ("sched: Fix performance regression introduced by mm_cid") in v6.4 kernel. Fortunately, there is a kasan_record_aux_stack_noalloc() variant that calls stack_depot_save_flags() while not allowing it to allocate new pages. To allow task_tick_mm_cid() to use task_work without page allocation, a new TWAF_NO_ALLOC flag is added to enable calling kasan_record_aux_stack_noalloc() instead of kasan_record_aux_stack() if set. The task_tick_mm_cid() function is modified to add this new flag. The possible downside is the missing stack trace in a KASAN report due to new page allocation required when task_work_add_noallloc() is called which should be rare. Fixes: 223baf9 ("sched: Fix performance regression introduced by mm_cid") Signed-off-by: Waiman Long <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Link: https://lkml.kernel.org/r/[email protected]
1 parent d16b7eb commit 73ab05a

File tree

3 files changed

+20
-4
lines changed

3 files changed

+20
-4
lines changed

include/linux/task_work.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,14 @@ init_task_work(struct callback_head *twork, task_work_func_t func)
1414
}
1515

1616
enum task_work_notify_mode {
17-
TWA_NONE,
17+
TWA_NONE = 0,
1818
TWA_RESUME,
1919
TWA_SIGNAL,
2020
TWA_SIGNAL_NO_IPI,
2121
TWA_NMI_CURRENT,
22+
23+
TWA_FLAGS = 0xff00,
24+
TWAF_NO_ALLOC = 0x0100,
2225
};
2326

2427
static inline bool task_work_pending(struct task_struct *task)

kernel/sched/core.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10458,7 +10458,9 @@ void task_tick_mm_cid(struct rq *rq, struct task_struct *curr)
1045810458
return;
1045910459
if (time_before(now, READ_ONCE(curr->mm->mm_cid_next_scan)))
1046010460
return;
10461-
task_work_add(curr, work, TWA_RESUME);
10461+
10462+
/* No page allocation under rq lock */
10463+
task_work_add(curr, work, TWA_RESUME | TWAF_NO_ALLOC);
1046210464
}
1046310465

1046410466
void sched_mm_cid_exit_signals(struct task_struct *t)

kernel/task_work.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,26 @@ int task_work_add(struct task_struct *task, struct callback_head *work,
5555
enum task_work_notify_mode notify)
5656
{
5757
struct callback_head *head;
58+
int flags = notify & TWA_FLAGS;
5859

60+
notify &= ~TWA_FLAGS;
5961
if (notify == TWA_NMI_CURRENT) {
6062
if (WARN_ON_ONCE(task != current))
6163
return -EINVAL;
6264
if (!IS_ENABLED(CONFIG_IRQ_WORK))
6365
return -EINVAL;
6466
} else {
65-
/* record the work call stack in order to print it in KASAN reports */
66-
kasan_record_aux_stack(work);
67+
/*
68+
* Record the work call stack in order to print it in KASAN
69+
* reports.
70+
*
71+
* Note that stack allocation can fail if TWAF_NO_ALLOC flag
72+
* is set and new page is needed to expand the stack buffer.
73+
*/
74+
if (flags & TWAF_NO_ALLOC)
75+
kasan_record_aux_stack_noalloc(work);
76+
else
77+
kasan_record_aux_stack(work);
6778
}
6879

6980
head = READ_ONCE(task->task_works);

0 commit comments

Comments
 (0)