Skip to content

Commit

Permalink
perf: Always wake the parent event
Browse files Browse the repository at this point in the history
When using per-process mode and event inheritance is set to true,
forked processes will create a new perf events via inherit_event() ->
perf_event_alloc(). But these events will not have ring buffers
assigned to them. Any call to wakeup will be dropped if it's called on
an event with no ring buffer assigned because that's the object that
holds the wakeup list.

If the child event is disabled due to a call to
perf_aux_output_begin() or perf_aux_output_end(), the wakeup is
dropped leaving userspace hanging forever on the poll.

Normally the event is explicitly re-enabled by userspace after it
wakes up to read the aux data, but in this case it does not get woken
up so the event remains disabled.

This can be reproduced when using Arm SPE and 'stress' which forks once
before running the workload. By looking at the list of aux buffers read,
it's apparent that they stop after the fork:

  perf record -e arm_spe// -vvv -- stress -c 1

With this patch applied they continue to be printed. This behaviour
doesn't happen when using systemwide or per-cpu mode.

Reported-by: Ruben Ayrapetyan <[email protected]>
Signed-off-by: James Clark <[email protected]>
Signed-off-by: Peter Zijlstra (Intel) <[email protected]>
Link: https://lkml.kernel.org/r/[email protected]
  • Loading branch information
James-A-Clark authored and Peter Zijlstra committed Jan 26, 2022
1 parent 8c16dc0 commit 961c391
Showing 1 changed file with 10 additions and 2 deletions.
12 changes: 10 additions & 2 deletions kernel/events/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -5985,6 +5985,8 @@ static void ring_buffer_attach(struct perf_event *event,
struct perf_buffer *old_rb = NULL;
unsigned long flags;

WARN_ON_ONCE(event->parent);

if (event->rb) {
/*
* Should be impossible, we set this when removing
Expand Down Expand Up @@ -6042,6 +6044,9 @@ static void ring_buffer_wakeup(struct perf_event *event)
{
struct perf_buffer *rb;

if (event->parent)
event = event->parent;

rcu_read_lock();
rb = rcu_dereference(event->rb);
if (rb) {
Expand All @@ -6055,6 +6060,9 @@ struct perf_buffer *ring_buffer_get(struct perf_event *event)
{
struct perf_buffer *rb;

if (event->parent)
event = event->parent;

rcu_read_lock();
rb = rcu_dereference(event->rb);
if (rb) {
Expand Down Expand Up @@ -6763,7 +6771,7 @@ static unsigned long perf_prepare_sample_aux(struct perf_event *event,
if (WARN_ON_ONCE(READ_ONCE(sampler->oncpu) != smp_processor_id()))
goto out;

rb = ring_buffer_get(sampler->parent ? sampler->parent : sampler);
rb = ring_buffer_get(sampler);
if (!rb)
goto out;

Expand Down Expand Up @@ -6829,7 +6837,7 @@ static void perf_aux_sample_output(struct perf_event *event,
if (WARN_ON_ONCE(!sampler || !data->aux_size))
return;

rb = ring_buffer_get(sampler->parent ? sampler->parent : sampler);
rb = ring_buffer_get(sampler);
if (!rb)
return;

Expand Down

0 comments on commit 961c391

Please sign in to comment.