On Tue, May 30, 2017 at 09:03:39PM +0200, Peter Zijlstra wrote:
> On Tue, May 30, 2017 at 10:37:46AM -0700, Alexei Starovoitov wrote:
> > On 5/30/17 9:51 AM, Peter Zijlstra wrote:
> 
> > > I'm not entirely sure I see how that is required. Should per task not
> > > already work? The WARN that's there will only trigger if you call them
> > > on the wrong task, which is something you shouldn't do anyway.
> > 
> > The kernel WARN is considered to be a bug of bpf infra. That's the
> > reason we do all these checks at map update time and at run-time.
> > The bpf program authors should be able to do all possible experiments
> > until their scripts work. Dealing with kernel warns and reboots is not
> > something user space folks like to do.
> > Today bpf_perf_event_read() for per-task events isn't really
> > working due to event->oncpu != cpu runtime check in there.
> > If we convert warns to returns the existing scripts will continue
> > to work as-is and per-task will be possible.
> 
> Ah yes.. I always forget that side of things (not ever having seen a
> bpf thing working doesn't help either of course).
> 
> Let me consider that for a bit..

OK, do that. Something like the below should do I suppose.

It will return -EOPNOTSUPP for permanent failure (it cannot ever work)
and -EINVAL for temporary failure (could work if you call it on another
task/cpu).

---
 kernel/events/core.c | 41 +++++++++++++++++++++++++++--------------
 1 file changed, 27 insertions(+), 14 deletions(-)

diff --git a/kernel/events/core.c b/kernel/events/core.c
index 8d6acaeeea17..22b6407da374 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -3637,10 +3637,10 @@ static inline u64 perf_event_count(struct perf_event 
*event)
  *     will not be local and we cannot read them atomically
  *   - must not have a pmu::count method
  */
-u64 perf_event_read_local(struct perf_event *event)
+int perf_event_read_local(struct perf_event *event, u64 *value)
 {
        unsigned long flags;
-       u64 val;
+       int ret = 0;
 
        /*
         * Disabling interrupts avoids all counter scheduling (context
@@ -3648,25 +3648,37 @@ u64 perf_event_read_local(struct perf_event *event)
         */
        local_irq_save(flags);
 
-       /* If this is a per-task event, it must be for current */
-       WARN_ON_ONCE((event->attach_state & PERF_ATTACH_TASK) &&
-                    event->hw.target != current);
-
-       /* If this is a per-CPU event, it must be for this CPU */
-       WARN_ON_ONCE(!(event->attach_state & PERF_ATTACH_TASK) &&
-                    event->cpu != smp_processor_id());
-
        /*
         * It must not be an event with inherit set, we cannot read
         * all child counters from atomic context.
         */
-       WARN_ON_ONCE(event->attr.inherit);
+       if (event->attr.inherit) {
+               ret = -EOPNOTSUPP;
+               goto out;
+       }
 
        /*
         * It must not have a pmu::count method, those are not
         * NMI safe.
         */
-       WARN_ON_ONCE(event->pmu->count);
+       if (event->pmu->count) {
+               ret = -EOPNOTSUPP;
+               goto out;
+       }
+
+       /* If this is a per-task event, it must be for current */
+       if ((event->attach_state & PERF_ATTACH_TASK) &&
+           event->hw.target != current) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       /* If this is a per-CPU event, it must be for this CPU */
+       if (!(event->attach_state & PERF_ATTACH_TASK) &&
+           event->cpu != smp_processor_id()) {
+               ret = -EINVAL;
+               goto out;
+       }
 
        /*
         * If the event is currently on this CPU, its either a per-task event,
@@ -3676,10 +3688,11 @@ u64 perf_event_read_local(struct perf_event *event)
        if (event->oncpu == smp_processor_id())
                event->pmu->read(event);
 
-       val = local64_read(&event->count);
+       *value = local64_read(&event->count);
+out:
        local_irq_restore(flags);
 
-       return val;
+       return ret;
 }
 
 static int perf_event_read(struct perf_event *event, bool group)

Reply via email to