Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

tracing: Use queue_rcu_work() to free filters

Freeing of filters requires to wait for both an RCU grace period as well as
a RCU task trace wait period after they have been detached from their
lists. The trace task period can be quite large so the freeing of the
filters was moved to use the call_rcu*() routines. The problem with that is
that the callback functions of call_rcu*() is done from a soft irq and can
cause latencies if the callback takes a bit of time.

The filters are freed per event in a system and the syscalls system
contains an event per system call, which can be over 700 events. Freeing 700
filters in a bottom half is undesirable.

Instead, move the freeing to use queue_rcu_work() which is done in task
context.

Link: https://lore.kernel.org/all/9a2f0cd0-1561-4206-8966-f93ccd25927f@paulmck-laptop/

Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Link: https://lore.kernel.org/20250609131732.04fd303b@gandalf.local.home
Fixes: a9d0aab5eb33 ("tracing: Fix regression of filter waiting a long time on RCU synchronization")
Suggested-by: "Paul E. McKenney" <paulmck@kernel.org>
Reviewed-by: Paul E. McKenney <paulmck@kernel.org>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>

+20 -8
+20 -8
kernel/trace/trace_events_filter.c
··· 1344 1344 1345 1345 struct filter_head { 1346 1346 struct list_head list; 1347 - struct rcu_head rcu; 1347 + union { 1348 + struct rcu_head rcu; 1349 + struct rcu_work rwork; 1350 + }; 1348 1351 }; 1349 1352 1350 - 1351 - static void free_filter_list(struct rcu_head *rhp) 1353 + static void free_filter_list(struct filter_head *filter_list) 1352 1354 { 1353 - struct filter_head *filter_list = container_of(rhp, struct filter_head, rcu); 1354 1355 struct filter_list *filter_item, *tmp; 1355 1356 1356 1357 list_for_each_entry_safe(filter_item, tmp, &filter_list->list, list) { ··· 1362 1361 kfree(filter_list); 1363 1362 } 1364 1363 1364 + static void free_filter_list_work(struct work_struct *work) 1365 + { 1366 + struct filter_head *filter_list; 1367 + 1368 + filter_list = container_of(to_rcu_work(work), struct filter_head, rwork); 1369 + free_filter_list(filter_list); 1370 + } 1371 + 1365 1372 static void free_filter_list_tasks(struct rcu_head *rhp) 1366 1373 { 1367 - call_rcu(rhp, free_filter_list); 1374 + struct filter_head *filter_list = container_of(rhp, struct filter_head, rcu); 1375 + 1376 + INIT_RCU_WORK(&filter_list->rwork, free_filter_list_work); 1377 + queue_rcu_work(system_wq, &filter_list->rwork); 1368 1378 } 1369 1379 1370 1380 /* ··· 1472 1460 tracepoint_synchronize_unregister(); 1473 1461 1474 1462 if (head) 1475 - free_filter_list(&head->rcu); 1463 + free_filter_list(head); 1476 1464 1477 1465 list_for_each_entry(file, &tr->events, list) { 1478 1466 if (file->system != dir || !file->filter) ··· 2317 2305 return 0; 2318 2306 fail: 2319 2307 /* No call succeeded */ 2320 - free_filter_list(&filter_list->rcu); 2308 + free_filter_list(filter_list); 2321 2309 parse_error(pe, FILT_ERR_BAD_SUBSYS_FILTER, 0); 2322 2310 return -EINVAL; 2323 2311 fail_mem: ··· 2327 2315 if (!fail) 2328 2316 delay_free_filter(filter_list); 2329 2317 else 2330 - free_filter_list(&filter_list->rcu); 2318 + free_filter_list(filter_list); 2331 2319 2332 2320 return -ENOMEM; 2333 2321 }