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

From: Peter Zijlstra
Date: Tue Feb 11 2025 - 10:46:34 EST


On Mon, Feb 10, 2025 at 12:09:18PM +0530, Ravi Bangoria wrote:
> Hi Peter,
>
> > @@ -1737,7 +1742,7 @@ static inline bool needs_branch_stack(st
> >
> > static inline bool has_aux(struct perf_event *event)
> > {
> > - return event->pmu->setup_aux;
> > + return event->pmu && event->pmu->setup_aux;
> > }
>
> this ...

Bah, I'll try and trace that.

> > @@ -6412,7 +6444,7 @@ static void perf_mmap_open(struct vm_are
> > if (vma->vm_pgoff)
> > atomic_inc(&event->rb->aux_mmap_count);
> >
> > - if (event->pmu->event_mapped)
> > + if (event->pmu && event->pmu->event_mapped)
> > event->pmu->event_mapped(event, vma->vm_mm);
> > }
> >
> > @@ -6435,7 +6467,8 @@ static void perf_mmap_close(struct vm_ar
> > unsigned long size = perf_data_size(rb);
> > bool detach_rest = false;
> >
> > - if (event->pmu->event_unmapped)
> > + /* FIXIES vs perf_pmu_unregister() */
> > + if (event->pmu && event->pmu->event_unmapped)
> > event->pmu->event_unmapped(event, vma->vm_mm);
>
> these two ...
>
> > @@ -6837,7 +6880,7 @@ static int perf_mmap(struct file *file,
> > if (!ret)
> > ret = map_range(rb, vma);
> >
> > - if (!ret && event->pmu->event_mapped)
> > + if (!ret && event->pmu && event->pmu->event_mapped)
> > event->pmu->event_mapped(event, vma->vm_mm);
>
> ... and this one.

These are relatively simple to fix, something like so. This relies on
the fact that if there's mapped functions the whole unregister thing
won't happen.

---
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -6432,9 +6432,22 @@ void ring_buffer_put(struct perf_buffer
call_rcu(&rb->rcu_head, rb_free_rcu);
}

+typedef void (*mapped_f)(struct perf_event *event, struct mm_struct *mm);
+
+#define get_mapped(event, func) \
+({ struct pmu *pmu; \
+ mapped_f f = NULL; \
+ guard(rcu)(); \
+ pmu = READ_ONCE(event->pmu); \
+ if (pmu) \
+ f = pmu->func; \
+ f; \
+})
+
static void perf_mmap_open(struct vm_area_struct *vma)
{
struct perf_event *event = vma->vm_file->private_data;
+ mapped_f mapped = get_mapped(event, event_mapped);

atomic_inc(&event->mmap_count);
atomic_inc(&event->rb->mmap_count);
@@ -6442,8 +6455,8 @@ static void perf_mmap_open(struct vm_are
if (vma->vm_pgoff)
atomic_inc(&event->rb->aux_mmap_count);

- if (event->pmu && event->pmu->event_mapped)
- event->pmu->event_mapped(event, vma->vm_mm);
+ if (mapped)
+ mapped(event, vma->vm_mm);
}

static void perf_pmu_output_stop(struct perf_event *event);
@@ -6459,6 +6472,7 @@ static void perf_pmu_output_stop(struct
static void perf_mmap_close(struct vm_area_struct *vma)
{
struct perf_event *event = vma->vm_file->private_data;
+ mapped_f unmapped = get_mapped(event, event_unmapped);
struct perf_buffer *rb = ring_buffer_get(event);
struct user_struct *mmap_user = rb->mmap_user;
int mmap_locked = rb->mmap_locked;
@@ -6466,8 +6480,8 @@ static void perf_mmap_close(struct vm_ar
bool detach_rest = false;

/* FIXIES vs perf_pmu_unregister() */
- if (event->pmu && event->pmu->event_unmapped)
- event->pmu->event_unmapped(event, vma->vm_mm);
+ if (unmapped)
+ unmapped(event, vma->vm_mm);

/*
* The AUX buffer is strictly a sub-buffer, serialize using aux_mutex
@@ -6660,6 +6674,7 @@ static int perf_mmap(struct file *file,
unsigned long nr_pages;
long user_extra = 0, extra = 0;
int ret, flags = 0;
+ mapped_f mapped;

/*
* Don't allow mmap() of inherited per-task counters. This would
@@ -6878,8 +6893,9 @@ static int perf_mmap(struct file *file,
if (!ret)
ret = map_range(rb, vma);

- if (!ret && event->pmu && event->pmu->event_mapped)
- event->pmu->event_mapped(event, vma->vm_mm);
+ mapped = get_mapped(event, event_mapped);
+ if (mapped)
+ mapped(event, vma->vm_mm);

return ret;
}