[PATCH 5/5] perf/dummy_pmu: Track and disable active events

From: Lucas De Marchi
Date: Tue Oct 08 2024 - 14:37:09 EST


When unregistering the PMU, disable the currently active events. This
allows userspace to see the change and possibly reacting on it, like
reopening the fd. With perf-stat, "<not counted>" starts to be printed:

$ stat -e dummy_pmu_0/test-event-1/ -I1000
1.001227905 12 dummy_pmu_0/test-event-1/
2.004009349 11 dummy_pmu_0/test-event-1/
3.005785067 0 dummy_pmu_0/test-event-1/
4.008565935 <not counted> dummy_pmu_0/test-event-1/
5.010446891 <not counted> dummy_pmu_0/test-event-1/

Signed-off-by: Lucas De Marchi <lucas.demarchi@xxxxxxxxx>
---
kernel/events/dummy_pmu.c | 40 +++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)

diff --git a/kernel/events/dummy_pmu.c b/kernel/events/dummy_pmu.c
index c07e111bff01..496cb8469a05 100644
--- a/kernel/events/dummy_pmu.c
+++ b/kernel/events/dummy_pmu.c
@@ -14,6 +14,7 @@
#include <linux/random.h>
#include <linux/seq_file.h>
#include <linux/types.h>
+#include <linux/xarray.h>

struct dummy_mod {
struct dentry *debugfs_root;
@@ -25,6 +26,7 @@ struct dummy_mod {
struct dummy_pmu {
struct pmu base;
char *name;
+ struct xarray active_events;
bool registered;
};

@@ -97,9 +99,25 @@ static const struct attribute_group *attr_groups[] = {
NULL,
};

+static void dummy_pmu_event_destroy(struct perf_event *event)
+{
+ struct dummy_pmu *pmu = event_to_pmu(event);
+ unsigned long idx;
+ struct perf_event *e;
+
+ /* Event not active anymore */
+ xa_for_each(&pmu->active_events, idx, e)
+ if (e == event) {
+ xa_erase(&pmu->active_events, idx);
+ break;
+ }
+}
+
static int dummy_pmu_event_init(struct perf_event *event)
{
struct dummy_pmu *pmu = event_to_pmu(event);
+ u32 event_id;
+ int ret;

if (!pmu->registered)
return -ENODEV;
@@ -117,6 +135,13 @@ static int dummy_pmu_event_init(struct perf_event *event)
if (event->cpu < 0)
return -EINVAL;

+ ret = xa_alloc(&pmu->active_events, &event_id, event,
+ xa_limit_32b, GFP_KERNEL);
+ if (ret)
+ return ret;
+
+ event->destroy = dummy_pmu_event_destroy;
+
return 0;
}

@@ -232,6 +257,8 @@ static int device_init(struct dummy_device *d)
if (ret)
goto fail;

+ xa_init_flags(&d->pmu.active_events, XA_FLAGS_ALLOC);
+
d->pmu.registered = true;
pr_info("Device registered: %s\n", d->pmu.name);

@@ -248,9 +275,22 @@ static int device_init(struct dummy_device *d)
return ret;
}

+static void disable_active_events(struct dummy_pmu *pmu)
+{
+ struct perf_event *event;
+ unsigned long idx;
+
+ xa_for_each(&pmu->active_events, idx, event) {
+ xa_erase(&pmu->active_events, idx);
+ perf_event_disable(event);
+ }
+}
+
static void device_exit(struct dummy_device *d)
{
d->pmu.registered = false;
+
+ disable_active_events(&d->pmu);
perf_pmu_unregister(&d->pmu.base);

pr_info("Device released: %s\n", d->pmu.name);
--
2.46.2