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: Add snapshot refcount

When a ring-buffer is memory mapped by user-space, no trace or
ring-buffer swap is possible. This means the snapshot feature is
mutually exclusive with the memory mapping. Having a refcount on
snapshot users will help to know if a mapping is possible or not.

Instead of relying on the global trace_types_lock, a new spinlock is
introduced to serialize accesses to trace_array->snapshot. This intends
to allow access to that variable in a context where the mmap lock is
already held.

Link: https://lore.kernel.org/linux-trace-kernel/20240220202310.2489614-4-vdonnefort@google.com

Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>

authored by

Vincent Donnefort and committed by
Steven Rostedt (Google)
180e4e39 b70f2938

+129 -36
+82 -17
kernel/trace/trace.c
··· 1300 1300 tr->allocated_snapshot = false; 1301 1301 } 1302 1302 1303 + static int tracing_arm_snapshot_locked(struct trace_array *tr) 1304 + { 1305 + int ret; 1306 + 1307 + lockdep_assert_held(&trace_types_lock); 1308 + 1309 + spin_lock(&tr->snapshot_trigger_lock); 1310 + if (tr->snapshot == UINT_MAX) { 1311 + spin_unlock(&tr->snapshot_trigger_lock); 1312 + return -EBUSY; 1313 + } 1314 + 1315 + tr->snapshot++; 1316 + spin_unlock(&tr->snapshot_trigger_lock); 1317 + 1318 + ret = tracing_alloc_snapshot_instance(tr); 1319 + if (ret) { 1320 + spin_lock(&tr->snapshot_trigger_lock); 1321 + tr->snapshot--; 1322 + spin_unlock(&tr->snapshot_trigger_lock); 1323 + } 1324 + 1325 + return ret; 1326 + } 1327 + 1328 + int tracing_arm_snapshot(struct trace_array *tr) 1329 + { 1330 + int ret; 1331 + 1332 + mutex_lock(&trace_types_lock); 1333 + ret = tracing_arm_snapshot_locked(tr); 1334 + mutex_unlock(&trace_types_lock); 1335 + 1336 + return ret; 1337 + } 1338 + 1339 + void tracing_disarm_snapshot(struct trace_array *tr) 1340 + { 1341 + spin_lock(&tr->snapshot_trigger_lock); 1342 + if (!WARN_ON(!tr->snapshot)) 1343 + tr->snapshot--; 1344 + spin_unlock(&tr->snapshot_trigger_lock); 1345 + } 1346 + 1303 1347 /** 1304 1348 * tracing_alloc_snapshot - allocate snapshot buffer. 1305 1349 * ··· 1417 1373 1418 1374 mutex_lock(&trace_types_lock); 1419 1375 1420 - ret = tracing_alloc_snapshot_instance(tr); 1421 - if (ret) 1422 - goto fail_unlock; 1423 - 1424 1376 if (tr->current_trace->use_max_tr) { 1425 1377 ret = -EBUSY; 1426 1378 goto fail_unlock; ··· 1434 1394 ret = -EBUSY; 1435 1395 goto fail_unlock; 1436 1396 } 1397 + 1398 + ret = tracing_arm_snapshot_locked(tr); 1399 + if (ret) 1400 + goto fail_unlock; 1437 1401 1438 1402 local_irq_disable(); 1439 1403 arch_spin_lock(&tr->max_lock); ··· 1483 1439 arch_spin_unlock(&tr->max_lock); 1484 1440 local_irq_enable(); 1485 1441 1442 + tracing_disarm_snapshot(tr); 1443 + 1486 1444 return ret; 1487 1445 } 1488 1446 EXPORT_SYMBOL_GPL(tracing_snapshot_cond_disable); ··· 1527 1481 } 1528 1482 EXPORT_SYMBOL_GPL(tracing_snapshot_cond_disable); 1529 1483 #define free_snapshot(tr) do { } while (0) 1484 + #define tracing_arm_snapshot_locked(tr) ({ -EBUSY; }) 1530 1485 #endif /* CONFIG_TRACER_SNAPSHOT */ 1531 1486 1532 1487 void tracer_tracing_off(struct trace_array *tr) ··· 6159 6112 */ 6160 6113 synchronize_rcu(); 6161 6114 free_snapshot(tr); 6115 + tracing_disarm_snapshot(tr); 6162 6116 } 6163 6117 6164 - if (t->use_max_tr && !tr->allocated_snapshot) { 6165 - ret = tracing_alloc_snapshot_instance(tr); 6166 - if (ret < 0) 6118 + if (t->use_max_tr) { 6119 + ret = tracing_arm_snapshot_locked(tr); 6120 + if (ret) 6167 6121 goto out; 6168 6122 } 6169 6123 #else ··· 6173 6125 6174 6126 if (t->init) { 6175 6127 ret = tracer_init(t, tr); 6176 - if (ret) 6128 + if (ret) { 6129 + #ifdef CONFIG_TRACER_MAX_TRACE 6130 + if (t->use_max_tr) 6131 + tracing_disarm_snapshot(tr); 6132 + #endif 6177 6133 goto out; 6134 + } 6178 6135 } 6179 6136 6180 6137 tr->current_trace = t; ··· 7281 7228 if (tr->allocated_snapshot) 7282 7229 ret = resize_buffer_duplicate_size(&tr->max_buffer, 7283 7230 &tr->array_buffer, iter->cpu_file); 7284 - else 7285 - ret = tracing_alloc_snapshot_instance(tr); 7286 - if (ret < 0) 7231 + 7232 + ret = tracing_arm_snapshot_locked(tr); 7233 + if (ret) 7287 7234 break; 7235 + 7288 7236 /* Now, we're going to swap */ 7289 7237 if (iter->cpu_file == RING_BUFFER_ALL_CPUS) { 7290 7238 local_irq_disable(); ··· 7295 7241 smp_call_function_single(iter->cpu_file, tracing_swap_cpu_buffer, 7296 7242 (void *)tr, 1); 7297 7243 } 7244 + tracing_disarm_snapshot(tr); 7298 7245 break; 7299 7246 default: 7300 7247 if (tr->allocated_snapshot) { ··· 8427 8372 8428 8373 ops = param ? &snapshot_count_probe_ops : &snapshot_probe_ops; 8429 8374 8430 - if (glob[0] == '!') 8431 - return unregister_ftrace_function_probe_func(glob+1, tr, ops); 8375 + if (glob[0] == '!') { 8376 + ret = unregister_ftrace_function_probe_func(glob+1, tr, ops); 8377 + if (!ret) 8378 + tracing_disarm_snapshot(tr); 8379 + 8380 + return ret; 8381 + } 8432 8382 8433 8383 if (!param) 8434 8384 goto out_reg; ··· 8452 8392 return ret; 8453 8393 8454 8394 out_reg: 8455 - ret = tracing_alloc_snapshot_instance(tr); 8395 + ret = tracing_arm_snapshot(tr); 8456 8396 if (ret < 0) 8457 8397 goto out; 8458 8398 8459 8399 ret = register_ftrace_function_probe(glob, tr, ops, count); 8460 - 8400 + if (ret < 0) 8401 + tracing_disarm_snapshot(tr); 8461 8402 out: 8462 8403 return ret < 0 ? ret : 0; 8463 8404 } ··· 9265 9204 raw_spin_lock_init(&tr->start_lock); 9266 9205 9267 9206 tr->max_lock = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED; 9268 - 9207 + #ifdef CONFIG_TRACER_MAX_TRACE 9208 + spin_lock_init(&tr->snapshot_trigger_lock); 9209 + #endif 9269 9210 tr->current_trace = &nop_trace; 9270 9211 9271 9212 INIT_LIST_HEAD(&tr->systems); ··· 10237 10174 global_trace.current_trace = &nop_trace; 10238 10175 10239 10176 global_trace.max_lock = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED; 10240 - 10177 + #ifdef CONFIG_TRACER_MAX_TRACE 10178 + spin_lock_init(&global_trace.snapshot_trigger_lock); 10179 + #endif 10241 10180 ftrace_init_global_array_ops(&global_trace); 10242 10181 10243 10182 init_trace_flags_index(&global_trace);
+6 -2
kernel/trace/trace.h
··· 334 334 */ 335 335 struct array_buffer max_buffer; 336 336 bool allocated_snapshot; 337 - #endif 338 - #ifdef CONFIG_TRACER_MAX_TRACE 337 + spinlock_t snapshot_trigger_lock; 338 + unsigned int snapshot; 339 339 unsigned long max_latency; 340 340 #ifdef CONFIG_FSNOTIFY 341 341 struct dentry *d_max_latency; ··· 1983 1983 #ifdef CONFIG_TRACER_SNAPSHOT 1984 1984 void tracing_snapshot_instance(struct trace_array *tr); 1985 1985 int tracing_alloc_snapshot_instance(struct trace_array *tr); 1986 + int tracing_arm_snapshot(struct trace_array *tr); 1987 + void tracing_disarm_snapshot(struct trace_array *tr); 1986 1988 #else 1987 1989 static inline void tracing_snapshot_instance(struct trace_array *tr) { } 1988 1990 static inline int tracing_alloc_snapshot_instance(struct trace_array *tr) 1989 1991 { 1990 1992 return 0; 1991 1993 } 1994 + static inline int tracing_arm_snapshot(struct trace_array *tr) { return 0; } 1995 + static inline void tracing_disarm_snapshot(struct trace_array *tr) { } 1992 1996 #endif 1993 1997 1994 1998 #ifdef CONFIG_PREEMPT_TRACER
+41 -17
kernel/trace/trace_events_trigger.c
··· 597 597 return ret; 598 598 } 599 599 600 - /** 601 - * unregister_trigger - Generic event_command @unreg implementation 602 - * @glob: The raw string used to register the trigger 603 - * @test: Trigger-specific data used to find the trigger to remove 604 - * @file: The trace_event_file associated with the event 605 - * 606 - * Common implementation for event trigger unregistration. 607 - * 608 - * Usually used directly as the @unreg method in event command 609 - * implementations. 600 + /* 601 + * True if the trigger was found and unregistered, else false. 610 602 */ 611 - static void unregister_trigger(char *glob, 612 - struct event_trigger_data *test, 613 - struct trace_event_file *file) 603 + static bool try_unregister_trigger(char *glob, 604 + struct event_trigger_data *test, 605 + struct trace_event_file *file) 614 606 { 615 607 struct event_trigger_data *data = NULL, *iter; 616 608 ··· 618 626 } 619 627 } 620 628 621 - if (data && data->ops->free) 622 - data->ops->free(data); 629 + if (data) { 630 + if (data->ops->free) 631 + data->ops->free(data); 632 + 633 + return true; 634 + } 635 + 636 + return false; 637 + } 638 + 639 + /** 640 + * unregister_trigger - Generic event_command @unreg implementation 641 + * @glob: The raw string used to register the trigger 642 + * @test: Trigger-specific data used to find the trigger to remove 643 + * @file: The trace_event_file associated with the event 644 + * 645 + * Common implementation for event trigger unregistration. 646 + * 647 + * Usually used directly as the @unreg method in event command 648 + * implementations. 649 + */ 650 + static void unregister_trigger(char *glob, 651 + struct event_trigger_data *test, 652 + struct trace_event_file *file) 653 + { 654 + try_unregister_trigger(glob, test, file); 623 655 } 624 656 625 657 /* ··· 1486 1470 struct event_trigger_data *data, 1487 1471 struct trace_event_file *file) 1488 1472 { 1489 - int ret = tracing_alloc_snapshot_instance(file->tr); 1473 + int ret = tracing_arm_snapshot(file->tr); 1490 1474 1491 1475 if (ret < 0) 1492 1476 return ret; 1493 1477 1494 1478 return register_trigger(glob, data, file); 1479 + } 1480 + 1481 + static void unregister_snapshot_trigger(char *glob, 1482 + struct event_trigger_data *data, 1483 + struct trace_event_file *file) 1484 + { 1485 + if (try_unregister_trigger(glob, data, file)) 1486 + tracing_disarm_snapshot(file->tr); 1495 1487 } 1496 1488 1497 1489 static int ··· 1534 1510 .trigger_type = ETT_SNAPSHOT, 1535 1511 .parse = event_trigger_parse, 1536 1512 .reg = register_snapshot_trigger, 1537 - .unreg = unregister_trigger, 1513 + .unreg = unregister_snapshot_trigger, 1538 1514 .get_trigger_ops = snapshot_get_trigger_ops, 1539 1515 .set_filter = set_trigger_filter, 1540 1516 };