Skip to content

Commit 97ba62b

Browse files
melverPeter Zijlstra
authored andcommitted
perf: Add support for SIGTRAP on perf events
Adds bit perf_event_attr::sigtrap, which can be set to cause events to send SIGTRAP (with si_code TRAP_PERF) to the task where the event occurred. The primary motivation is to support synchronous signals on perf events in the task where an event (such as breakpoints) triggered. To distinguish perf events based on the event type, the type is set in si_errno. For events that are associated with an address, si_addr is copied from perf_sample_data. The new field perf_event_attr::sig_data is copied to si_perf, which allows user space to disambiguate which event (of the same type) triggered the signal. For example, user space could encode the relevant information it cares about in sig_data. We note that the choice of an opaque u64 provides the simplest and most flexible option. Alternatives where a reference to some user space data is passed back suffer from the problem that modification of referenced data (be it the event fd, or the perf_event_attr) can race with the signal being delivered (of course, the same caveat applies if user space decides to store a pointer in sig_data, but the ABI explicitly avoids prescribing such a design). Suggested-by: Peter Zijlstra <[email protected]> Signed-off-by: Marco Elver <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Acked-by: Dmitry Vyukov <[email protected]> Link: https://lore.kernel.org/lkml/[email protected]/
1 parent fb6cc12 commit 97ba62b

File tree

3 files changed

+58
-2
lines changed

3 files changed

+58
-2
lines changed

include/linux/perf_event.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,7 @@ struct perf_event {
735735
int pending_wakeup;
736736
int pending_kill;
737737
int pending_disable;
738+
unsigned long pending_addr; /* SIGTRAP */
738739
struct irq_work pending;
739740

740741
atomic_t event_limit;

include/uapi/linux/perf_event.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ enum perf_event_read_format {
311311
#define PERF_ATTR_SIZE_VER4 104 /* add: sample_regs_intr */
312312
#define PERF_ATTR_SIZE_VER5 112 /* add: aux_watermark */
313313
#define PERF_ATTR_SIZE_VER6 120 /* add: aux_sample_size */
314+
#define PERF_ATTR_SIZE_VER7 128 /* add: sig_data */
314315

315316
/*
316317
* Hardware event_id to monitor via a performance monitoring event:
@@ -391,7 +392,8 @@ struct perf_event_attr {
391392
build_id : 1, /* use build id in mmap2 events */
392393
inherit_thread : 1, /* children only inherit if cloned with CLONE_THREAD */
393394
remove_on_exec : 1, /* event is removed from task on exec */
394-
__reserved_1 : 27;
395+
sigtrap : 1, /* send synchronous SIGTRAP on event */
396+
__reserved_1 : 26;
395397

396398
union {
397399
__u32 wakeup_events; /* wakeup every n events */
@@ -443,6 +445,12 @@ struct perf_event_attr {
443445
__u16 __reserved_2;
444446
__u32 aux_sample_size;
445447
__u32 __reserved_3;
448+
449+
/*
450+
* User provided data if sigtrap=1, passed back to user via
451+
* siginfo_t::si_perf, e.g. to permit user to identify the event.
452+
*/
453+
__u64 sig_data;
446454
};
447455

448456
/*

kernel/events/core.c

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6392,6 +6392,33 @@ void perf_event_wakeup(struct perf_event *event)
63926392
}
63936393
}
63946394

6395+
static void perf_sigtrap(struct perf_event *event)
6396+
{
6397+
struct kernel_siginfo info;
6398+
6399+
/*
6400+
* We'd expect this to only occur if the irq_work is delayed and either
6401+
* ctx->task or current has changed in the meantime. This can be the
6402+
* case on architectures that do not implement arch_irq_work_raise().
6403+
*/
6404+
if (WARN_ON_ONCE(event->ctx->task != current))
6405+
return;
6406+
6407+
/*
6408+
* perf_pending_event() can race with the task exiting.
6409+
*/
6410+
if (current->flags & PF_EXITING)
6411+
return;
6412+
6413+
clear_siginfo(&info);
6414+
info.si_signo = SIGTRAP;
6415+
info.si_code = TRAP_PERF;
6416+
info.si_errno = event->attr.type;
6417+
info.si_perf = event->attr.sig_data;
6418+
info.si_addr = (void __user *)event->pending_addr;
6419+
force_sig_info(&info);
6420+
}
6421+
63956422
static void perf_pending_event_disable(struct perf_event *event)
63966423
{
63976424
int cpu = READ_ONCE(event->pending_disable);
@@ -6401,6 +6428,13 @@ static void perf_pending_event_disable(struct perf_event *event)
64016428

64026429
if (cpu == smp_processor_id()) {
64036430
WRITE_ONCE(event->pending_disable, -1);
6431+
6432+
if (event->attr.sigtrap) {
6433+
perf_sigtrap(event);
6434+
atomic_set_release(&event->event_limit, 1); /* rearm event */
6435+
return;
6436+
}
6437+
64046438
perf_event_disable_local(event);
64056439
return;
64066440
}
@@ -9103,6 +9137,7 @@ static int __perf_event_overflow(struct perf_event *event,
91039137
if (events && atomic_dec_and_test(&event->event_limit)) {
91049138
ret = 1;
91059139
event->pending_kill = POLL_HUP;
9140+
event->pending_addr = data->addr;
91069141

91079142
perf_event_disable_inatomic(event);
91089143
}
@@ -11384,6 +11419,10 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
1138411419
if (!task || cpu != -1)
1138511420
return ERR_PTR(-EINVAL);
1138611421
}
11422+
if (attr->sigtrap && !task) {
11423+
/* Requires a task: avoid signalling random tasks. */
11424+
return ERR_PTR(-EINVAL);
11425+
}
1138711426

1138811427
node = (cpu >= 0) ? cpu_to_node(cpu) : -1;
1138911428
event = kmem_cache_alloc_node(perf_event_cache, GFP_KERNEL | __GFP_ZERO,
@@ -11432,6 +11471,9 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
1143211471

1143311472
event->state = PERF_EVENT_STATE_INACTIVE;
1143411473

11474+
if (event->attr.sigtrap)
11475+
atomic_set(&event->event_limit, 1);
11476+
1143511477
if (task) {
1143611478
event->attach_state = PERF_ATTACH_TASK;
1143711479
/*
@@ -11710,6 +11752,9 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,
1171011752
if (attr->remove_on_exec && attr->enable_on_exec)
1171111753
return -EINVAL;
1171211754

11755+
if (attr->sigtrap && !attr->remove_on_exec)
11756+
return -EINVAL;
11757+
1171311758
out:
1171411759
return ret;
1171511760

@@ -12936,7 +12981,9 @@ inherit_task_group(struct perf_event *event, struct task_struct *parent,
1293612981
struct perf_event_context *child_ctx;
1293712982

1293812983
if (!event->attr.inherit ||
12939-
(event->attr.inherit_thread && !(clone_flags & CLONE_THREAD))) {
12984+
(event->attr.inherit_thread && !(clone_flags & CLONE_THREAD)) ||
12985+
/* Do not inherit if sigtrap and signal handlers were cleared. */
12986+
(event->attr.sigtrap && (clone_flags & CLONE_CLEAR_SIGHAND))) {
1294012987
*inherited_all = 0;
1294112988
return 0;
1294212989
}

0 commit comments

Comments
 (0)