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

From: Paul E. McKenney

Date: Fri Jul 17 2026 - 00:21:44 EST


On Thu, Jul 16, 2026 at 10:04:47PM -0400, Rik van Riel wrote:
> 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?

The traditional approach is cond_resched_tasks_rcu_qs(), which is to
synchronize_rcu_tasks() as cond_resched() is to synchronize_rcu().

But if we can avoid holding mutexes across synchronize_rcu_tasks(), that
would be a very good thing. The synchronize_rcu_tasks() latency can be
many minutes, which isn't usually what you want when blocking on a mutex.

Or maybe we want a way of sleeping on a mutex at TASK_IDLE.

> What is the best way forward here?

Excellent question!!!

Thanx, Paul