Re: [PATCH + QUESTION] bpf: use cond_resched_tasks_rcu_qs in bpf_fd_array_map_clear() loop

From: Rik van Riel

Date: Thu Jul 16 2026 - 22:05:43 EST


On Wed, 2026-07-15 at 21:53 -0400, Rik van Riel wrote:
> syzkaller creates a PROG_ARRAY with huge max_entries and triggers
> perf
> tracepoint open close in parallel. The hung task detector reports
> "INFO: task hung in perf_tp_event_init" with event_mutex held waiting
> for synchronize_rcu_tasks().
>
OK, this is more interesting than it looked at first,
and this patch does not look like the right approach,
but I'm also not sure what other approach would work :(

The actual error being thrown is a hung task:



The syzkaller kernel failure is a tragedy in 3 parts.

First, we have perf_event_open() waiting on the event_mutex:

syz.7.35551 (pid 31354, state D): perf_event_open 
-> perf_tp_event_init
-> perf_trace_init
-> __mutex_lock
-> blocked on event_mutex.


Second, we have perf_trace_destroy() indirectly
waiting in synchronize_rcu_tasks(), with the
event_mutex held.

syz.5.35491 (state:I) do_exit 
-> task_work_run 
-> __fput 
-> perf_release
-> perf_event_release_kernel
-> __free_event
-> perf_trace_destroy
-> perf_trace_event_close
-> reg(TRACE_REG_PERF_CLOSE)
-> perf_ftrace_event_register
-> perf_ftrace_function_unregister
-> unregister_ftrace_function 
-> ftrace_shutdown
-> synchronize_rcu_tasks() <-- BLOCKS HERE, forever,
holding event_mutex

Tasks RCU waits for every task to have gone through
a voluntary reschedule, before the grace period can
be advanced. A preemption does not count.


Third, we have a kworker looping for a very long time:
 
kworker/0:0 (pid 9, TASK_RUNNING, on CPU0): process_one_work
-> prog_array_map_clear_deferred
-> bpf_fd_array_map_clear
-> __fd_array_map_delete_elem. Looping over a huge PROG_ARRAY
max_entries.

With CONFIG_PREEMPT_LAZY=y this gets preempted,
but since involuntary preemptions do not count
for tasks RCU, that does nothnig to help
ftrace_shutdown() get unstuck.


How do we solve this?

Do we actually need to have cond_resched() points
quiesce the task RCU state when running with lazy
preempt, since those are points where we could
preempt voluntarily?

Do we need to prohibit calling synchronize_rcu_tasks()
while holding a mutex?

Do we need to do something else?

What is the best way forward here?

--
All Rights Reversed.