Allow to unregister the PMU from perf with active events. When driver is being accessed perf keeps a reference that when released triggers the device cleanup. Signed-off-by: Lucas De Marchi <lucas.demarchi@xxxxxxxxx> --- kernel/events/dummy_pmu.c | 56 ++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/kernel/events/dummy_pmu.c b/kernel/events/dummy_pmu.c index cdba3a831e4a..c07e111bff01 100644 --- a/kernel/events/dummy_pmu.c +++ b/kernel/events/dummy_pmu.c @@ -49,6 +49,11 @@ static struct dummy_device *pmu_to_device(struct dummy_pmu *pmu) return container_of(pmu, struct dummy_device, pmu); } +static struct dummy_pmu *pmu_to_dummy(struct pmu *pmu) +{ + return container_of(pmu, struct dummy_pmu, base); +} + static ssize_t dummy_pmu_events_sysfs_show(struct device *dev, struct device_attribute *attr, char *page) @@ -92,18 +97,9 @@ 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); - struct dummy_device *d = pmu_to_device(pmu); - - kref_put(&d->refcount, device_release); -} - static int dummy_pmu_event_init(struct perf_event *event) { struct dummy_pmu *pmu = event_to_pmu(event); - struct dummy_device *d = pmu_to_device(pmu); if (!pmu->registered) return -ENODEV; @@ -121,10 +117,6 @@ static int dummy_pmu_event_init(struct perf_event *event) if (event->cpu < 0) return -EINVAL; - /* Event keeps a ref to maintain PMU allocated, even if it's unregistered */ - kref_get(&d->refcount); - event->destroy = dummy_pmu_event_destroy; - return 0; } @@ -195,10 +187,29 @@ static void dummy_pmu_event_del(struct perf_event *event, int flags) dummy_pmu_event_stop(event, PERF_EF_UPDATE); } +static struct pmu *dummy_pmu_get(struct pmu *pmu) +{ + struct dummy_device *d = pmu_to_device(pmu_to_dummy(pmu)); + + kref_get(&d->refcount); + + return pmu; +} + +static void dummy_pmu_put(struct pmu *pmu) +{ + struct dummy_device *d = pmu_to_device(pmu_to_dummy(pmu)); + + kref_put(&d->refcount, device_release); +} + static int device_init(struct dummy_device *d) { int ret; + if (WARN_ONCE(d->pmu.name, "Cannot re-register pmu.\n")) + return -EINVAL; + d->pmu.base = (struct pmu){ .attr_groups = attr_groups, .module = THIS_MODULE, @@ -209,6 +220,8 @@ static int device_init(struct dummy_device *d) .start = dummy_pmu_event_start, .stop = dummy_pmu_event_stop, .read = dummy_pmu_event_read, + .get = dummy_pmu_get, + .put = dummy_pmu_put, }; d->pmu.name = kasprintf(GFP_KERNEL, "dummy_pmu_%u", d->instance); @@ -217,12 +230,22 @@ static int device_init(struct dummy_device *d) ret = perf_pmu_register(&d->pmu.base, d->pmu.name, -1); if (ret) - return ret; + goto fail; d->pmu.registered = true; pr_info("Device registered: %s\n", d->pmu.name); return 0; + +fail: + /* + * See device_release: if name is non-NULL, dummy_pmu was registered + * with perf and needs cleanup + */ + kfree(d->pmu.name); + d->pmu.name = NULL; + + return ret; } static void device_exit(struct dummy_device *d) @@ -237,7 +260,10 @@ static void device_release(struct kref *ref) { struct dummy_device *d = container_of(ref, struct dummy_device, refcount); - kfree(d->pmu.name); + if (d->pmu.name) { + perf_pmu_free(&d->pmu.base); + kfree(d->pmu.name); + } kfree(d); } -- 2.46.2