Re: [PATCH RFC 3/4] perf/core: Add support for SIGTRAP on perf events

From: Dmitry Vyukov
Date: Tue Feb 23 2021 - 13:15:01 EST


On Tue, Feb 23, 2021 at 3:34 PM Marco Elver <elver@xxxxxxxxxx> wrote:
>
> 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. To distinguish perf events and allow user space to decode
> si_perf (if set), the event type is set in si_errno.
>
> The primary motivation is to support synchronous signals on perf events
> in the task where an event (such as breakpoints) triggered.
>
> Link: https://lore.kernel.org/lkml/YBv3rAT566k+6zjg@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/
> Suggested-by: Peter Zijlstra <peterz@xxxxxxxxxxxxx>
> Signed-off-by: Marco Elver <elver@xxxxxxxxxx>
> ---
> include/uapi/linux/perf_event.h | 3 ++-
> kernel/events/core.c | 21 +++++++++++++++++++++
> 2 files changed, 23 insertions(+), 1 deletion(-)
>
> diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h
> index ad15e40d7f5d..b9cc6829a40c 100644
> --- a/include/uapi/linux/perf_event.h
> +++ b/include/uapi/linux/perf_event.h
> @@ -389,7 +389,8 @@ struct perf_event_attr {
> cgroup : 1, /* include cgroup events */
> text_poke : 1, /* include text poke events */
> build_id : 1, /* use build id in mmap2 events */
> - __reserved_1 : 29;
> + sigtrap : 1, /* send synchronous SIGTRAP on event */
> + __reserved_1 : 28;
>
> union {
> __u32 wakeup_events; /* wakeup every n events */
> diff --git a/kernel/events/core.c b/kernel/events/core.c
> index 37a8297be164..8718763045fd 100644
> --- a/kernel/events/core.c
> +++ b/kernel/events/core.c
> @@ -6288,6 +6288,17 @@ void perf_event_wakeup(struct perf_event *event)
> }
> }
>
> +static void perf_sigtrap(struct perf_event *event)
> +{
> + struct kernel_siginfo info;
> +
> + clear_siginfo(&info);
> + info.si_signo = SIGTRAP;
> + info.si_code = TRAP_PERF;
> + info.si_errno = event->attr.type;
> + force_sig_info(&info);
> +}
> +
> static void perf_pending_event_disable(struct perf_event *event)
> {
> int cpu = READ_ONCE(event->pending_disable);
> @@ -6297,6 +6308,13 @@ static void perf_pending_event_disable(struct perf_event *event)
>
> if (cpu == smp_processor_id()) {
> WRITE_ONCE(event->pending_disable, -1);
> +
> + if (event->attr.sigtrap) {
> + atomic_inc(&event->event_limit); /* rearm event */

We send the signal to the current task. Can this fire outside of the
current task context? E.g. in interrupt/softirq/etc? And then we will
send the signal to the current task. Watchpoint can be set to
userspace address and then something asynchronous (some IO completion)
that does not belong to this task access the userspace address (is
this possible?). But watchpoints can also be set to kernel addresses,
then another context can definitely access it.
(1) can this happen? maybe perf context is somehow disabled when !in_task()?
(2) if yes, what is the desired behavior?




> + perf_sigtrap(event);
> + return;
> + }
> +
> perf_event_disable_local(event);
> return;
> }
> @@ -11325,6 +11343,9 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
>
> event->state = PERF_EVENT_STATE_INACTIVE;
>
> + if (event->attr.sigtrap)
> + atomic_set(&event->event_limit, 1);
> +
> if (task) {
> event->attach_state = PERF_ATTACH_TASK;
> /*
> --
> 2.30.0.617.g56c4b15f3c-goog
>