Re: [PATCH v2 24/24] perf: Make perf_pmu_unregister() useable

From: Peter Zijlstra
Date: Thu Feb 13 2025 - 08:07:21 EST


On Mon, Feb 10, 2025 at 12:29:21PM +0530, Ravi Bangoria wrote:
> On 05-Feb-25 3:51 PM, Peter Zijlstra wrote:
> > Previously it was only safe to call perf_pmu_unregister() if there
> > were no active events of that pmu around -- which was impossible to
> > guarantee since it races all sorts against perf_init_event().
> >
> > Rework the whole thing by:
> >
> > - keeping track of all events for a given pmu
> >
> > - 'hiding' the pmu from perf_init_event()
> >
> > - waiting for the appropriate (s)rcu grace periods such that all
> > prior references to the PMU will be completed
> >
> > - detaching all still existing events of that pmu (see first point)
> > and moving them to a new REVOKED state.
> >
> > - actually freeing the pmu data.
> >
> > Where notably the new REVOKED state must inhibit all event actions
> > from reaching code that wants to use event->pmu.
> >
> > Signed-off-by: Peter Zijlstra (Intel) <peterz@xxxxxxxxxxxxx>
>
> Another race between perf_event_init_task() failure path and
> perf_pmu_unregister():
>
> CPU 1 CPU 2
>
> perf_event_init_task()
> perf_event_free_task()
> perf_free_event(event1)
> /* event1->refcount is 1 */
> perf_pmu_unregister()
> pmu_detach_events()
> pmu_get_event(pmu) /* Picks event1 */
> atomic_long_inc_not_zero(&event1->refcount) /* event1 */
> /* event1->refcount became 2 (by CPU 2) */
> free_event(event1)
> WARN()

I've unified perf_event_free_task() and perf_event_exit_task(). This
makes both use perf_event_exit_event() and as such should no longer have
this free_event().