lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <b35c4271e3028bccf10b4af3bc5a0c5e5c889fdc.camel@arm.com>
Date: Mon, 13 May 2024 13:23:16 +0000
From: Ben Gainey <Ben.Gainey@....com>
To: "namhyung@...nel.org" <namhyung@...nel.org>
CC: "alexander.shishkin@...ux.intel.com" <alexander.shishkin@...ux.intel.com>,
	"peterz@...radead.org" <peterz@...radead.org>, "acme@...nel.org"
	<acme@...nel.org>, "mingo@...hat.com" <mingo@...hat.com>, James Clark
	<James.Clark@....com>, "adrian.hunter@...el.com" <adrian.hunter@...el.com>,
	"irogers@...gle.com" <irogers@...gle.com>, "jolsa@...nel.org"
	<jolsa@...nel.org>, "linux-kernel@...r.kernel.org"
	<linux-kernel@...r.kernel.org>, "linux-perf-users@...r.kernel.org"
	<linux-perf-users@...r.kernel.org>, Mark Rutland <Mark.Rutland@....com>
Subject: Re: [PATCH v5 1/4] perf: Support PERF_SAMPLE_READ with inherit

On Thu, 2024-05-09 at 16:40 -0700, Namhyung Kim wrote:
> Hello,
>
> On Mon, Apr 15, 2024 at 1:15 AM Ben Gainey <ben.gainey@....com>
> wrote:
> >
> > This change allows events to use PERF_SAMPLE READ with inherit
> > so long as PERF_SAMPLE_TID is also set.
> >
> > In this configuration, an event will be inherited into any
> > child processes / threads, allowing convenient profiling of a
> > multiprocess or multithreaded application, whilst allowing
> > profiling tools to collect per-thread samples, in particular
> > of groups of counters.
> >
> > The read_format field of both PERF_RECORD_READ and
> > PERF_RECORD_SAMPLE
> > are changed by this new configuration, but calls to `read()` on the
> > same
> > event file descriptor are unaffected and continue to return the
> > cumulative total.
> >
> > Signed-off-by: Ben Gainey <ben.gainey@....com>
>
> Looks ok to me now, some nitpicks below.
>

Thanks. I've a couple of replies below, otherwise I'll sort this out
and rebase onto v6.9 unless there is a better tag/branch to target?

Ben


>
> > ---
> >  include/linux/perf_event.h |  1 +
> >  kernel/events/core.c       | 82 ++++++++++++++++++++++++++++------
> > ----
> >  2 files changed, 62 insertions(+), 21 deletions(-)
> >
> > diff --git a/include/linux/perf_event.h
> > b/include/linux/perf_event.h
> > index d2a15c0c6f8a..e7eed33c50f1 100644
> > --- a/include/linux/perf_event.h
> > +++ b/include/linux/perf_event.h
> > @@ -932,6 +932,7 @@ struct perf_event_context {
> >
> >         int                             nr_task_data;
> >         int                             nr_stat;
> > +       int                             nr_inherit_read;
> >         int                             nr_freq;
> >         int                             rotate_disable;
> >
> > diff --git a/kernel/events/core.c b/kernel/events/core.c
> > index 724e6d7e128f..bf0639a2e2b1 100644
> > --- a/kernel/events/core.c
> > +++ b/kernel/events/core.c
> > @@ -1767,6 +1767,18 @@ perf_event_groups_next(struct perf_event
> > *event, struct pmu *pmu)
> >                 event = rb_entry_safe(rb_next(&event-
> > >group_node),      \
> >                                 typeof(*event), group_node))
> >
> > +/*
> > + * Does the event attribute request inherit with PERF_SAMPLE_READ
> > + */
> > +#define
> > perf_attr_has_inherit_and_sample_read(attr)                    \
>
> Looks somewhat verbose.  Can it be just
> has_inherit_sample_read() ?  Also you can make it static inline.
>
>
> > +       ((attr)->inherit && ((attr)->sample_type &
> > PERF_SAMPLE_READ))
> > +
> > +/*
> > + * Does the event request an attribte that requests inherit with
> > PERF_SAMPLE_READ
>
> typo: attribte
>
>
> > + */
> > +#define
> > perf_event_has_inherit_and_sample_read(event)                  \
> > +       perf_attr_has_inherit_and_sample_read(&((event)->attr))
> > +
> >  /*
> >   * Add an event from the lists for its context.
> >   * Must be called with ctx->mutex and ctx->lock held.
> > @@ -1797,6 +1809,8 @@ list_add_event(struct perf_event *event,
> > struct perf_event_context *ctx)
> >                 ctx->nr_user++;
> >         if (event->attr.inherit_stat)
> >                 ctx->nr_stat++;
> > +       if (perf_event_has_inherit_and_sample_read(event))
> > +               ctx->nr_inherit_read++;
> >
> >         if (event->state > PERF_EVENT_STATE_OFF)
> >                 perf_cgroup_event_enable(event, ctx);
> > @@ -2021,6 +2035,8 @@ list_del_event(struct perf_event *event,
> > struct perf_event_context *ctx)
> >                 ctx->nr_user--;
> >         if (event->attr.inherit_stat)
> >                 ctx->nr_stat--;
> > +       if (perf_event_has_inherit_and_sample_read(event))
> > +               ctx->nr_inherit_read--;
> >
> >         list_del_rcu(&event->event_entry);
> >
> > @@ -3529,11 +3545,18 @@ perf_event_context_sched_out(struct
> > task_struct *task, struct task_struct *next)
> >                         perf_ctx_disable(ctx, false);
> >
> >                         /* PMIs are disabled; ctx->nr_pending is
> > stable. */
> > -                       if (local_read(&ctx->nr_pending) ||
> > +                       if (ctx->nr_inherit_read ||
> > +                           next_ctx->nr_inherit_read ||
> > +                           local_read(&ctx->nr_pending) ||
> >                             local_read(&next_ctx->nr_pending)) {
> >                                 /*
> >                                  * Must not swap out ctx when
> > there's pending
> >                                  * events that rely on the ctx-
> > >task relation.
> > +                                *
> > +                                * Likewise, when a context
> > contains inherit +
> > +                                * SAMPLE_READ events they should
> > be switched
> > +                                * out using the slow path so that
> > they are
> > +                                * treated as if they were distinct
> > contexts.
> >                                  */
> >                                 raw_spin_unlock(&next_ctx->lock);
> >                                 rcu_read_unlock();
> > @@ -4533,11 +4556,19 @@ static void __perf_event_read(void *info)
> >         raw_spin_unlock(&ctx->lock);
> >  }
> >
> > -static inline u64 perf_event_count(struct perf_event *event)
> > +static inline u64 perf_event_count_cumulative(struct perf_event
> > *event)
> >  {
> >         return local64_read(&event->count) + atomic64_read(&event-
> > >child_count);
> >  }
>
> Maybe it's better to leave it as is and add a new wrapper below.
> At least it'd create a smaller diff. :)

I can do that, but the reason I did it this way was to avoid some
future easy-to-make-mistake because someone picks up the
`perf_event_count(struct perf_event*)` when they should have used
something else. With this change any future refactor / work requires
some developer to pick between `perf_event_count_cumulative` and
`perf_event_count(..., bool self_value_only)` which feels marginally
less error prone since. Please let me know if you'd still like this one
changed.


>
> >
> > +static inline u64 perf_event_count(struct perf_event *event, bool
> > self_value_only)
> > +{
> > +       if (self_value_only &&
> > perf_event_has_inherit_and_sample_read(event))
> > +               return local64_read(&event->count);
> > +
> > +       return perf_event_count_cumulative(event);
> > +}
> > +
> >  static void calc_timer_values(struct perf_event *event,
> >                                 u64 *now,
> >                                 u64 *enabled,
> > @@ -5454,7 +5485,7 @@ static u64 __perf_event_read_value(struct
> > perf_event *event, u64 *enabled, u64 *
> >         mutex_lock(&event->child_mutex);
> >
> >         (void)perf_event_read(event, false);
> > -       total += perf_event_count(event);
> > +       total += perf_event_count_cumulative(event);
> >
> >         *enabled += event->total_time_enabled +
> >                         atomic64_read(&event-
> > >child_total_time_enabled);
> > @@ -5463,7 +5494,7 @@ static u64 __perf_event_read_value(struct
> > perf_event *event, u64 *enabled, u64 *
> >
> >         list_for_each_entry(child, &event->child_list, child_list)
> > {
> >                 (void)perf_event_read(child, false);
> > -               total += perf_event_count(child);
> > +               total += perf_event_count_cumulative(child);
> >                 *enabled += child->total_time_enabled;
> >                 *running += child->total_time_running;
> >         }
> > @@ -5545,14 +5576,14 @@ static int __perf_read_group_add(struct
> > perf_event *leader,
> >         /*
> >          * Write {count,id} tuples for every sibling.
> >          */
> > -       values[n++] += perf_event_count(leader);
> > +       values[n++] += perf_event_count_cumulative(leader);
> >         if (read_format & PERF_FORMAT_ID)
> >                 values[n++] = primary_event_id(leader);
> >         if (read_format & PERF_FORMAT_LOST)
> >                 values[n++] = atomic64_read(&leader->lost_samples);
> >
> >         for_each_sibling_event(sub, leader) {
> > -               values[n++] += perf_event_count(sub);
> > +               values[n++] += perf_event_count_cumulative(sub);
> >                 if (read_format & PERF_FORMAT_ID)
> >                         values[n++] = primary_event_id(sub);
> >                 if (read_format & PERF_FORMAT_LOST)
> > @@ -6132,7 +6163,7 @@ void perf_event_update_userpage(struct
> > perf_event *event)
> >         ++userpg->lock;
> >         barrier();
> >         userpg->index = perf_event_index(event);
> > -       userpg->offset = perf_event_count(event);
> > +       userpg->offset = perf_event_count_cumulative(event);
> >         if (userpg->index)
> >                 userpg->offset -= local64_read(&event-
> > >hw.prev_count);
> >
> > @@ -7194,13 +7225,14 @@ void perf_event__output_id_sample(struct
> > perf_event *event,
> >
> >  static void perf_output_read_one(struct perf_output_handle
> > *handle,
> >                                  struct perf_event *event,
> > -                                u64 enabled, u64 running)
> > +                                u64 enabled, u64 running,
> > +                                bool from_sample)
> >  {
> >         u64 read_format = event->attr.read_format;
> >         u64 values[5];
> >         int n = 0;
> >
> > -       values[n++] = perf_event_count(event);
> > +       values[n++] = perf_event_count(event, from_sample);
> >         if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) {
> >                 values[n++] = enabled +
> >                         atomic64_read(&event-
> > >child_total_time_enabled);
> > @@ -7218,8 +7250,9 @@ static void perf_output_read_one(struct
> > perf_output_handle *handle,
> >  }
> >
> >  static void perf_output_read_group(struct perf_output_handle
> > *handle,
> > -                           struct perf_event *event,
> > -                           u64 enabled, u64 running)
> > +                                  struct perf_event *event,
> > +                                  u64 enabled, u64 running,
> > +                                  bool from_sample)
> >  {
> >         struct perf_event *leader = event->group_leader, *sub;
> >         u64 read_format = event->attr.read_format;
> > @@ -7245,7 +7278,7 @@ static void perf_output_read_group(struct
> > perf_output_handle *handle,
> >             (leader->state == PERF_EVENT_STATE_ACTIVE))
> >                 leader->pmu->read(leader);
> >
> > -       values[n++] = perf_event_count(leader);
> > +       values[n++] = perf_event_count(leader, from_sample);
> >         if (read_format & PERF_FORMAT_ID)
> >                 values[n++] = primary_event_id(leader);
> >         if (read_format & PERF_FORMAT_LOST)
> > @@ -7260,7 +7293,7 @@ static void perf_output_read_group(struct
> > perf_output_handle *handle,
> >                     (sub->state == PERF_EVENT_STATE_ACTIVE))
> >                         sub->pmu->read(sub);
> >
> > -               values[n++] = perf_event_count(sub);
> > +               values[n++] = perf_event_count(sub, from_sample);
> >                 if (read_format & PERF_FORMAT_ID)
> >                         values[n++] = primary_event_id(sub);
> >                 if (read_format & PERF_FORMAT_LOST)
> > @@ -7281,9 +7314,14 @@ static void perf_output_read_group(struct
> > perf_output_handle *handle,
> >   * The problem is that its both hard and excessively expensive to
> > iterate the
> >   * child list, not to mention that its impossible to IPI the
> > children running
> >   * on another CPU, from interrupt/NMI context.
> > + *
> > + * Instead the combination of PERF_SAMPLE_READ and inherit will
> > track per-thread
> > + * counts rather than attempting to accumulate some value across
> > all children on
> > + * all cores.
> >   */
> >  static void perf_output_read(struct perf_output_handle *handle,
> > -                            struct perf_event *event)
> > +                            struct perf_event *event,
> > +                            bool from_sample)
> >  {
> >         u64 enabled = 0, running = 0, now;
> >         u64 read_format = event->attr.read_format;
> > @@ -7301,9 +7339,9 @@ static void perf_output_read(struct
> > perf_output_handle *handle,
> >                 calc_timer_values(event, &now, &enabled, &running);
> >
> >         if (event->attr.read_format & PERF_FORMAT_GROUP)
> > -               perf_output_read_group(handle, event, enabled,
> > running);
> > +               perf_output_read_group(handle, event, enabled,
> > running, from_sample);
> >         else
> > -               perf_output_read_one(handle, event, enabled,
> > running);
> > +               perf_output_read_one(handle, event, enabled,
> > running, from_sample);
> >  }
> >
> >  void perf_output_sample(struct perf_output_handle *handle,
> > @@ -7343,7 +7381,7 @@ void perf_output_sample(struct
> > perf_output_handle *handle,
> >                 perf_output_put(handle, data->period);
> >
> >         if (sample_type & PERF_SAMPLE_READ)
> > -               perf_output_read(handle, event);
> > +               perf_output_read(handle, event, true);
> >
> >         if (sample_type & PERF_SAMPLE_CALLCHAIN) {
> >                 int size = 1;
> > @@ -7944,7 +7982,7 @@ perf_event_read_event(struct perf_event
> > *event,
> >                 return;
> >
> >         perf_output_put(&handle, read_event);
> > -       perf_output_read(&handle, event);
> > +       perf_output_read(&handle, event, false);
> >         perf_event__output_id_sample(event, &handle, &sample);
> >
> >         perf_output_end(&handle);
> > @@ -12006,10 +12044,12 @@ perf_event_alloc(struct perf_event_attr
> > *attr, int cpu,
> >         local64_set(&hwc->period_left, hwc->sample_period);
> >
> >         /*
> > -        * We currently do not support PERF_SAMPLE_READ on
> > inherited events.
> > +        * We do not support PERF_SAMPLE_READ on inherited events
> > unless
> > +        * PERF_SAMPLE_TID is also selected, which allows inherited
> > events to
> > +        * collect per-thread samples.
> >          * See perf_output_read().
> >          */
> > -       if (attr->inherit && (attr->sample_type &
> > PERF_SAMPLE_READ))
> > +       if (perf_attr_has_inherit_and_sample_read(attr) && !(attr-
> > >sample_type & PERF_SAMPLE_TID))
>
> If you leave the original condition and just add a check
> for _TID, you can get rid of the perf_attr_ function.

True, though `perf_event_has_inherit_and_sample_read` is defined in
terms of `perf_attr_has_inherit_and_sample_read`, and whilst this is
the only other use of `perf_attr_has_inherit_and_sample_read`, it the
_event_ version is used in several places, so this mostly just keeps
the two consistent. Please let me know if you'd still like this one
changed.



>
> Thanks,
> Namhyung
>
>
> >                 goto err_ns;
> >
> >         if (!has_branch_stack(event))
> > @@ -13033,7 +13073,7 @@ static void sync_child_event(struct
> > perf_event *child_event)
> >                         perf_event_read_event(child_event, task);
> >         }
> >
> > -       child_val = perf_event_count(child_event);
> > +       child_val = perf_event_count_cumulative(child_event);
> >
> >         /*
> >          * Add back the child's count to the parent's count:
> > --
> > 2.44.0
> >


IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ