From patchwork Tue Oct 8 18:34:57 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Lucas De Marchi X-Patchwork-Id: 13826822 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 03FAACEFC3D for ; Tue, 8 Oct 2024 18:35:43 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 5F00910E237; Tue, 8 Oct 2024 18:35:41 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=intel.com header.i=@intel.com header.b="DYPUdSkI"; dkim-atps=neutral Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.9]) by gabe.freedesktop.org (Postfix) with ESMTPS id 6885A10E139 for ; Tue, 8 Oct 2024 18:35:38 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1728412539; x=1759948539; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=wcVQHgu0ANeHR4UYpWHs2sNLqJA/YG+05getslSrXfE=; b=DYPUdSkIyfIbvdpAIDk4EljVAhmzvejXD6yvIgJkhPbh32Gs1TzZPpKj 01gWZ7xhR5+Jm4V1oLr6TfY1KOY32O42NO3sI7gfqntptAJfzwsU3xksl ud7zJjBn+Jx5k3GpJS8nDhi+6RPvsWeRDnwRgwRF8Ghw2SJr1TS3NflqF EXKn9ZpNqNLWvEUC+fLKYrAIepeYDRTy9b/cPMqwSSKq+QgZljGpf398g fpSVtTkUPWRvKz3JKgl/PZ+aIDVSXrOsxJoPVG9nboEoQGeI26SnxAjdo KkJMi6RwTGYljRfiU16By3QcnMjCZ0aP2WB/s32TysqJY6Gl8SBIu76RW g==; X-CSE-ConnectionGUID: VBQWWYS3Qu6dba9n8HNoUQ== X-CSE-MsgGUID: Q+qIhY03SVaeRHxDGxipiw== X-IronPort-AV: E=McAfee;i="6700,10204,11219"; a="38295335" X-IronPort-AV: E=Sophos;i="6.11,187,1725346800"; d="scan'208";a="38295335" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 08 Oct 2024 11:35:38 -0700 X-CSE-ConnectionGUID: A9Fd8u2BQsyq0fW1dgvljw== X-CSE-MsgGUID: dkoM+u6tRHGfQ3RHWy9y6w== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.11,187,1725346800"; d="scan'208";a="80530907" Received: from ldmartin-desk2.corp.intel.com (HELO ldmartin-desk2.intel.com) ([10.125.110.138]) by fmviesa004-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 08 Oct 2024 11:35:37 -0700 From: Lucas De Marchi To: linux-kernel@vger.kernel.org Cc: dri-devel@lists.freedesktop.org, Peter Zijlstra , Ingo Molnar , Arnaldo Carvalho de Melo , Umesh Nerlige Ramappa , Ian Rogers , Tvrtko Ursulin , Lucas De Marchi Subject: [PATCH 1/5] perf: Add dummy pmu module Date: Tue, 8 Oct 2024 13:34:57 -0500 Message-ID: <20241008183501.1354695-2-lucas.demarchi@intel.com> X-Mailer: git-send-email 2.46.2 In-Reply-To: <20241008183501.1354695-1-lucas.demarchi@intel.com> References: <20241008183501.1354695-1-lucas.demarchi@intel.com> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" Simple dummy module that mimics what can be done with drivers to bind/unbind them from the bus, which should trigger resource release. This is mostly based on how i915 and (pending changes for) xe drivers are interacting with perf pmu. A few differences due to not having backing hardware or for simplicity: - Instead of using BDF for bind/unbind, use a single number. - Unbind is triggered either via debugfs or when removing the module. - event::destroy() is always assigned as there should only be a few additional calls Signed-off-by: Lucas De Marchi --- kernel/events/Makefile | 1 + kernel/events/dummy_pmu.c | 426 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 427 insertions(+) create mode 100644 kernel/events/dummy_pmu.c diff --git a/kernel/events/Makefile b/kernel/events/Makefile index 91a62f566743..2993fed2d091 100644 --- a/kernel/events/Makefile +++ b/kernel/events/Makefile @@ -4,3 +4,4 @@ obj-y := core.o ring_buffer.o callchain.o obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o obj-$(CONFIG_HW_BREAKPOINT_KUNIT_TEST) += hw_breakpoint_test.o obj-$(CONFIG_UPROBES) += uprobes.o +obj-m += dummy_pmu.o diff --git a/kernel/events/dummy_pmu.c b/kernel/events/dummy_pmu.c new file mode 100644 index 000000000000..cdba3a831e4a --- /dev/null +++ b/kernel/events/dummy_pmu.c @@ -0,0 +1,426 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright © 2024 Intel Corporation + */ + +#define pr_fmt(fmt) "%s: " fmt, KBUILD_MODNAME + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct dummy_mod { + struct dentry *debugfs_root; + + struct list_head device_list; + struct mutex mutex; +}; + +struct dummy_pmu { + struct pmu base; + char *name; + bool registered; +}; + +struct dummy_device { + unsigned int instance; + struct kref refcount; + struct list_head mod_entry; + struct dummy_pmu pmu; +}; + +static struct dummy_mod dm; + +static void device_release(struct kref *ref); + +static struct dummy_pmu *event_to_pmu(struct perf_event *event) +{ + return container_of(event->pmu, struct dummy_pmu, base); +} + +static struct dummy_device *pmu_to_device(struct dummy_pmu *pmu) +{ + return container_of(pmu, struct dummy_device, pmu); +} + +static ssize_t dummy_pmu_events_sysfs_show(struct device *dev, + struct device_attribute *attr, + char *page) +{ + struct perf_pmu_events_attr *pmu_attr; + + pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr); + + return sprintf(page, "event=0x%04llx\n", pmu_attr->id); +} + +#define DUMMY_PMU_EVENT_ATTR(name, config) \ + PMU_EVENT_ATTR_ID(name, dummy_pmu_events_sysfs_show, config) + +PMU_FORMAT_ATTR(event, "config:0-63"); + +#define DUMMY1_CONFIG 0x01 +#define DUMMY2_CONFIG 0x02 + +static struct attribute *dummy_pmu_event_attrs[] = { + DUMMY_PMU_EVENT_ATTR(test-event-1, DUMMY1_CONFIG), + DUMMY_PMU_EVENT_ATTR(test-event-2, DUMMY2_CONFIG), + NULL, +}; + +static struct attribute *dummy_pmu_format_attrs[] = { + &format_attr_event.attr, + NULL, +}; +static const struct attribute_group dummy_pmu_events_attr_group = { + .name = "events", + .attrs = dummy_pmu_event_attrs, +}; +static const struct attribute_group dummy_pmu_format_attr_group = { + .name = "format", + .attrs = dummy_pmu_format_attrs, +}; +static const struct attribute_group *attr_groups[] = { + &dummy_pmu_format_attr_group, + &dummy_pmu_events_attr_group, + 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; + + if (event->attr.type != event->pmu->type) + return -ENOENT; + + /* unsupported modes and filters */ + if (event->attr.sample_period) /* no sampling */ + return -EINVAL; + + if (has_branch_stack(event)) + return -EOPNOTSUPP; + + 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; +} + +static void dummy_pmu_event_start(struct perf_event *event, int flags) +{ + struct dummy_pmu *pmu = event_to_pmu(event); + + if (!pmu->registered) + return; + + event->hw.state = 0; +} + +static void dummy_pmu_event_read(struct perf_event *event) +{ + struct dummy_pmu *pmu = event_to_pmu(event); + u8 buf; + + if (!pmu->registered) { + event->hw.state = PERF_HES_STOPPED; + return; + } + + get_random_bytes(&buf, 1); + buf %= 10; + + switch (event->attr.config & 0xf) { + case DUMMY1_CONFIG: + break; + case DUMMY2_CONFIG: + buf *= 2; + break; + } + + local64_add(buf, &event->count); +} + +static void dummy_pmu_event_stop(struct perf_event *event, int flags) +{ + struct dummy_pmu *pmu = event_to_pmu(event); + + if (!pmu->registered) + goto out; + + if (flags & PERF_EF_UPDATE) + dummy_pmu_event_read(event); + +out: + event->hw.state = PERF_HES_STOPPED; +} + +static int dummy_pmu_event_add(struct perf_event *event, int flags) +{ + struct dummy_pmu *pmu = event_to_pmu(event); + + if (!pmu->registered) + return -ENODEV; + + if (flags & PERF_EF_START) + dummy_pmu_event_start(event, flags); + + return 0; + +} + +static void dummy_pmu_event_del(struct perf_event *event, int flags) +{ + dummy_pmu_event_stop(event, PERF_EF_UPDATE); +} + +static int device_init(struct dummy_device *d) +{ + int ret; + + d->pmu.base = (struct pmu){ + .attr_groups = attr_groups, + .module = THIS_MODULE, + .task_ctx_nr = perf_invalid_context, + .event_init = dummy_pmu_event_init, + .add = dummy_pmu_event_add, + .del = dummy_pmu_event_del, + .start = dummy_pmu_event_start, + .stop = dummy_pmu_event_stop, + .read = dummy_pmu_event_read, + }; + + d->pmu.name = kasprintf(GFP_KERNEL, "dummy_pmu_%u", d->instance); + if (!d->pmu.name) + return -ENOMEM; + + ret = perf_pmu_register(&d->pmu.base, d->pmu.name, -1); + if (ret) + return ret; + + d->pmu.registered = true; + pr_info("Device registered: %s\n", d->pmu.name); + + return 0; +} + +static void device_exit(struct dummy_device *d) +{ + d->pmu.registered = false; + perf_pmu_unregister(&d->pmu.base); + + pr_info("Device released: %s\n", d->pmu.name); +} + +static void device_release(struct kref *ref) +{ + struct dummy_device *d = container_of(ref, struct dummy_device, refcount); + + kfree(d->pmu.name); + kfree(d); +} + +static struct dummy_device *find_device_locked(struct dummy_mod *m, unsigned int instance) +{ + struct dummy_device *d; + + list_for_each_entry(d, &m->device_list, mod_entry) + if (d->instance == instance) + return d; + + return NULL; +} + +static int dummy_add_device(struct dummy_mod *m, unsigned int instance) +{ + struct dummy_device *d, *d2; + int ret = 0; + + mutex_lock(&m->mutex); + d = find_device_locked(m, instance); + mutex_unlock(&m->mutex); + if (d) + return -EINVAL; + + d = kcalloc(1, sizeof(*d), GFP_KERNEL); + if (!d) + return -ENOMEM; + + kref_init(&d->refcount); + d->instance = instance; + + ret = device_init(d); + if (ret < 0) + goto fail_put; + + mutex_lock(&m->mutex); + d2 = find_device_locked(m, instance); + if (d2) { + mutex_unlock(&m->mutex); + ret = -EINVAL; + goto fail_exit; + } + list_add(&d->mod_entry, &m->device_list); + mutex_unlock(&m->mutex); + + return 0; + +fail_exit: + device_exit(d); +fail_put: + kref_put(&d->refcount, device_release); + return ret; +} + +static int dummy_del_device(struct dummy_mod *m, unsigned int instance) +{ + struct dummy_device *d, *found = NULL; + + mutex_lock(&m->mutex); + list_for_each_entry(d, &m->device_list, mod_entry) { + if (d->instance == instance) { + list_del(&d->mod_entry); + found = d; + break; + } + } + mutex_unlock(&m->mutex); + + if (!found) + return -EINVAL; + + device_exit(found); + kref_put(&found->refcount, device_release); + + return 0; +} + +static int parse_device(const char __user *ubuf, size_t size, u32 *instance) +{ + char buf[16]; + ssize_t len; + + if (size > sizeof(buf) - 1) + return -E2BIG; + + len = strncpy_from_user(buf, ubuf, sizeof(buf)); + if (len < 0 || len >= sizeof(buf) - 1) + return -E2BIG; + + if (kstrtou32(buf, 0, instance)) + return -EINVAL; + + return size; +} + +static int bind_show(struct seq_file *s, void *unused) +{ + struct dummy_mod *m = s->private; + struct dummy_device *d; + + mutex_lock(&m->mutex); + list_for_each_entry(d, &m->device_list, mod_entry) + seq_printf(s, "%u\n", d->instance); + mutex_unlock(&m->mutex); + + return 0; +} + +static ssize_t bind_write(struct file *f, const char __user *ubuf, + size_t size, loff_t *pos) +{ + struct dummy_mod *m = file_inode(f)->i_private; + u32 instance; + ssize_t ret; + + ret = parse_device(ubuf, size, &instance); + if (ret < 0) + return ret; + + ret = dummy_add_device(m, instance); + if (ret < 0) + return ret; + + return size; +} +DEFINE_SHOW_STORE_ATTRIBUTE(bind); + +static int unbind_show(struct seq_file *s, void *unused) +{ + return -EPERM; +} + +static ssize_t unbind_write(struct file *f, const char __user *ubuf, + size_t size, loff_t *pos) +{ + struct dummy_mod *m = file_inode(f)->i_private; + unsigned int instance; + ssize_t ret; + + ret = parse_device(ubuf, size, &instance); + if (ret < 0) + return ret; + + ret = dummy_del_device(m, instance); + if (ret < 0) + return ret; + + return size; +} +DEFINE_SHOW_STORE_ATTRIBUTE(unbind); + +static int __init dummy_init(void) +{ + struct dentry *dir; + + dir = debugfs_create_dir(KBUILD_MODNAME, NULL); + debugfs_create_file("bind", 0600, dir, &dm, &bind_fops); + debugfs_create_file("unbind", 0200, dir, &dm, &unbind_fops); + + dm.debugfs_root = dir; + INIT_LIST_HEAD(&dm.device_list); + mutex_init(&dm.mutex); + + return 0; +} + +static void dummy_exit(void) +{ + struct dummy_device *d, *tmp; + + debugfs_remove_recursive(dm.debugfs_root); + + mutex_lock(&dm.mutex); + list_for_each_entry_safe(d, tmp, &dm.device_list, mod_entry) { + device_exit(d); + kref_put(&d->refcount, device_release); + } + mutex_unlock(&dm.mutex); +} + +module_init(dummy_init); +module_exit(dummy_exit); + +MODULE_AUTHOR("Lucas De Marchi "); +MODULE_LICENSE("GPL"); From patchwork Tue Oct 8 18:34:58 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lucas De Marchi X-Patchwork-Id: 13826821 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 63263CEFC3C for ; Tue, 8 Oct 2024 18:35:42 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 59D3F10E232; Tue, 8 Oct 2024 18:35:41 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=intel.com header.i=@intel.com header.b="TJ2pfb3o"; dkim-atps=neutral Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.9]) by gabe.freedesktop.org (Postfix) with ESMTPS id 6FCF710E232 for ; Tue, 8 Oct 2024 18:35:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1728412540; x=1759948540; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=NF8wAROMOzFMcdExLwjtr/ohn+EHVISxj9yvB+SoDkg=; b=TJ2pfb3ocJRzE0VAUpg/5GkRoahQUIY2fll2kAdyK4FqzZd/c2Zm+LAN Iz93S+G9SysqOq9CbU6602oPGToUucyPOuLy94ZYRRWIxw21ZxUkNxVos h7rt8ImHFoWSpxUHOKdJBmxNr2OPht3KLR4A5lLBH2iBlPizCAbpreWlU vIqaqq+pvm4e+039hcKVCYTOF2WUCGc/WdcjYQSraka//dZeFc81kT/kK Lu560X1MbUIXFCqmFZQ/FtIoWAWV4haOKDQ/ZSdKoJfPOHP+2N+1xLLJa nN9KtJip6uRL6nxyIMij8xhajF16w6r6F6V2aqECVAkgwcHfxL6I3WGY3 w==; X-CSE-ConnectionGUID: 3kU8Xw9oSKe5fKPaLs3Vjg== X-CSE-MsgGUID: TvS8ilAuSaWYkC26ShXESA== X-IronPort-AV: E=McAfee;i="6700,10204,11219"; a="38295346" X-IronPort-AV: E=Sophos;i="6.11,187,1725346800"; d="scan'208";a="38295346" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 08 Oct 2024 11:35:39 -0700 X-CSE-ConnectionGUID: uW9SEAZ9SHi/ZAaJsRS5Sw== X-CSE-MsgGUID: wmi9BkTXSzOHjlW7L/92sQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.11,187,1725346800"; d="scan'208";a="80530912" Received: from ldmartin-desk2.corp.intel.com (HELO ldmartin-desk2.intel.com) ([10.125.110.138]) by fmviesa004-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 08 Oct 2024 11:35:38 -0700 From: Lucas De Marchi To: linux-kernel@vger.kernel.org Cc: dri-devel@lists.freedesktop.org, Peter Zijlstra , Ingo Molnar , Arnaldo Carvalho de Melo , Umesh Nerlige Ramappa , Ian Rogers , Tvrtko Ursulin , Lucas De Marchi Subject: [PATCH 2/5] perf: Move free outside of the mutex Date: Tue, 8 Oct 2024 13:34:58 -0500 Message-ID: <20241008183501.1354695-3-lucas.demarchi@intel.com> X-Mailer: git-send-email 2.46.2 In-Reply-To: <20241008183501.1354695-1-lucas.demarchi@intel.com> References: <20241008183501.1354695-1-lucas.demarchi@intel.com> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" It's not needed to hold the mutex to free the percpu variables stored in pmu. Move them outside of the mutex protection in preparation for possibly allowing them to live longer, according to the lifecycle of the object owning/containing the pmu. Signed-off-by: Lucas De Marchi --- kernel/events/core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index 3b8b85adb10a..6395dbf67671 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -11845,7 +11845,6 @@ void perf_pmu_unregister(struct pmu *pmu) synchronize_srcu(&pmus_srcu); synchronize_rcu(); - free_percpu(pmu->pmu_disable_count); idr_remove(&pmu_idr, pmu->type); if (pmu_bus_running && pmu->dev && pmu->dev != PMU_NULL_DEV) { if (pmu->nr_addr_filters) @@ -11853,8 +11852,11 @@ void perf_pmu_unregister(struct pmu *pmu) device_del(pmu->dev); put_device(pmu->dev); } - free_pmu_context(pmu); + mutex_unlock(&pmus_lock); + + free_percpu(pmu->pmu_disable_count); + free_pmu_context(pmu); } EXPORT_SYMBOL_GPL(perf_pmu_unregister); From patchwork Tue Oct 8 18:34:59 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lucas De Marchi X-Patchwork-Id: 13826823 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 68F36CEFC3C for ; Tue, 8 Oct 2024 18:35:45 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 58F2110E23C; Tue, 8 Oct 2024 18:35:42 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=intel.com header.i=@intel.com header.b="joKeo7mX"; dkim-atps=neutral Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.9]) by gabe.freedesktop.org (Postfix) with ESMTPS id 86DC510E232 for ; Tue, 8 Oct 2024 18:35:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1728412541; x=1759948541; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=/JJZCwajkVb0QLSNq1aYtA8Fx0IU33m7ID0rexUyFaY=; b=joKeo7mXVXJDqhOIHOFTsllLkQ762cVCNJxIpAKoTZuxiUunuKmcdD1g drnH24uNxX3s01niojed0jhNAuCZyKh821CV0w2ibGtxcY3KK4Aw0PNPN 6ltHaIzxnXlRD9ow2tz19w6+bczgXTMTb03lWFW5242P59DzhOkIO1/iB pcymGoovdyuGl7iyA4ibCUJlh8DOwf+fukb0F5VdwQx23yR9Y+GBUqT7l SPWSQdIxLyHHXgsDEXHFj6Ix9n/WvezsJOJyTUAl7lv1s4j2zFhOT2PBx +nFe15UQi6trrinLaRefKUXFe+TR1Yv2k6dyrDKwhW+1Hbgikmie0Mz5Q g==; X-CSE-ConnectionGUID: Myubki5mScSt3NICjYiYDw== X-CSE-MsgGUID: RDKMwr1PQ+GJxT0Yk0QgYg== X-IronPort-AV: E=McAfee;i="6700,10204,11219"; a="38295357" X-IronPort-AV: E=Sophos;i="6.11,187,1725346800"; d="scan'208";a="38295357" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 08 Oct 2024 11:35:40 -0700 X-CSE-ConnectionGUID: m3qTCs16TxumPMmgGt5IFQ== X-CSE-MsgGUID: wQYexEUqQ5+vLU9t7EOaLQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.11,187,1725346800"; d="scan'208";a="80530917" Received: from ldmartin-desk2.corp.intel.com (HELO ldmartin-desk2.intel.com) ([10.125.110.138]) by fmviesa004-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 08 Oct 2024 11:35:39 -0700 From: Lucas De Marchi To: linux-kernel@vger.kernel.org Cc: dri-devel@lists.freedesktop.org, Peter Zijlstra , Ingo Molnar , Arnaldo Carvalho de Melo , Umesh Nerlige Ramappa , Ian Rogers , Tvrtko Ursulin , Lucas De Marchi Subject: [PATCH 3/5] perf: Add pmu get/put Date: Tue, 8 Oct 2024 13:34:59 -0500 Message-ID: <20241008183501.1354695-4-lucas.demarchi@intel.com> X-Mailer: git-send-email 2.46.2 In-Reply-To: <20241008183501.1354695-1-lucas.demarchi@intel.com> References: <20241008183501.1354695-1-lucas.demarchi@intel.com> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" If a pmu is unregistered while there's an active event, perf will still access the pmu via event->pmu, even after the event is destroyed. This makes it difficult for drivers like i915 that can be unbound from the HW. BUG: KASAN: use-after-free in exclusive_event_destroy+0xd8/0xf0 Read of size 4 at addr ffff88816e2bb63c by task perf/7748 i915 tries to cope with it by installing a event->destroy, but that is not sufficient: if pmu is released by the driver, it will still crash since event->pmu is still used. Moreover, even with that use-after-free fixed by adjusting the order in _free_event() or delaying the free by the driver, kernel still oops when closing the event fd related to a unregistered pmu: the percpu variables allocated on perf_pmu_register() would already be released. One such crash is: BUG: KASAN: user-memory-access in _raw_spin_lock_irqsave+0x88/0x100 Write of size 4 at addr 00000000ffffffff by task perf/727 CPU: 0 UID: 0 PID: 727 Comm: perf Not tainted 6.12.0-rc1-DEMARCHI-dxnf+ #9 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS unknown 2/2/2022 Call Trace: dump_stack_lvl+0x5f/0x90 print_report+0x4d3/0x50a ? __pfx__raw_spin_lock_irqsave+0x10/0x10 ? kasan_addr_to_slab+0xd/0xb0 kasan_report+0xe2/0x170 ? _raw_spin_lock_irqsave+0x88/0x100 ? _raw_spin_lock_irqsave+0x88/0x100 kasan_check_range+0x125/0x230 __kasan_check_write+0x14/0x30 _raw_spin_lock_irqsave+0x88/0x100 ? __pfx__raw_spin_lock_irqsave+0x10/0x10 _atomic_dec_and_raw_lock_irqsave+0x89/0x110 ? __kasan_check_write+0x14/0x30 put_pmu_ctx+0x98/0x330 The fix here is to provide a set of get/put hooks that drivers can implement to piggy back the perf's pmu lifecycle to the driver's instance lifecycle. With this, perf_pmu_unregister() can be called by the driver, which is then responsible for freeing the resources. Signed-off-by: Lucas De Marchi --- include/linux/perf_event.h | 12 ++++++++++++ kernel/events/core.c | 37 ++++++++++++++++++++++++++++++++----- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index fb908843f209..d6983dbf5a45 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -561,6 +561,17 @@ struct pmu { * Check period value for PERF_EVENT_IOC_PERIOD ioctl. */ int (*check_period) (struct perf_event *event, u64 value); /* optional */ + + /* + * Optional: get a reference. Typically needed by PMUs that are bound to a device + * that can be hotplugged, either physically or through sysfs' bind/unbind. When provided, + * pmu::put() is mandatory and it's driver responsibility to call perf_pmu_free() when + * resources can be released. + */ + struct pmu *(*get) (struct pmu *pmu); + + /* Optional: put a reference. See pmu::get() */ + void (*put) (struct pmu *pmu); }; enum perf_addr_filter_action_t { @@ -1104,6 +1115,7 @@ extern void perf_event_itrace_started(struct perf_event *event); extern int perf_pmu_register(struct pmu *pmu, const char *name, int type); extern void perf_pmu_unregister(struct pmu *pmu); +extern void perf_pmu_free(struct pmu *pmu); extern void __perf_event_task_sched_in(struct task_struct *prev, struct task_struct *task); diff --git a/kernel/events/core.c b/kernel/events/core.c index 6395dbf67671..bf5b8fc8979e 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -5319,8 +5319,24 @@ static void perf_pending_task_sync(struct perf_event *event) rcuwait_wait_event(&event->pending_work_wait, !event->pending_work, TASK_UNINTERRUPTIBLE); } +static void pmu_module_put(struct pmu **ppmu) +{ + struct pmu *pmu = *ppmu; + struct module *module = pmu->module; + + if (pmu->put) + pmu->put(pmu); + + module_put(module); + + /* Can't touch pmu anymore/ */ + *ppmu = NULL; +} + static void _free_event(struct perf_event *event) { + struct module *module; + irq_work_sync(&event->pending_irq); irq_work_sync(&event->pending_disable_irq); perf_pending_task_sync(event); @@ -5374,7 +5390,8 @@ static void _free_event(struct perf_event *event) put_ctx(event->ctx); exclusive_event_destroy(event); - module_put(event->pmu->module); + + pmu_module_put(&event->pmu); call_rcu(&event->rcu_head, free_event_rcu); } @@ -11512,10 +11529,12 @@ static int perf_event_idx_default(struct perf_event *event) return 0; } -static void free_pmu_context(struct pmu *pmu) +void perf_pmu_free(struct pmu *pmu) { free_percpu(pmu->cpu_pmu_context); + free_percpu(pmu->pmu_disable_count); } +EXPORT_SYMBOL_GPL(perf_pmu_free); /* * Let userspace know that this PMU supports address range filtering: @@ -11749,6 +11768,11 @@ int perf_pmu_register(struct pmu *pmu, const char *name, int type) goto free_pdc; } + if (WARN_ONCE((!!pmu->get) ^ (!!pmu->put), "Can not register a pmu with only get or put defined.\n")) { + ret = -EINVAL; + goto free_pdc; + } + pmu->name = name; if (type >= 0) @@ -11855,8 +11879,8 @@ void perf_pmu_unregister(struct pmu *pmu) mutex_unlock(&pmus_lock); - free_percpu(pmu->pmu_disable_count); - free_pmu_context(pmu); + if (!pmu->put) + perf_pmu_free(pmu); } EXPORT_SYMBOL_GPL(perf_pmu_unregister); @@ -11890,6 +11914,9 @@ static int perf_try_init_event(struct pmu *pmu, struct perf_event *event) BUG_ON(!ctx); } + if (pmu->get) + pmu->get(pmu); + event->pmu = pmu; ret = pmu->event_init(event); @@ -11926,7 +11953,7 @@ static int perf_try_init_event(struct pmu *pmu, struct perf_event *event) } if (ret) - module_put(pmu->module); + pmu_module_put(&pmu); return ret; } From patchwork Tue Oct 8 18:35:00 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lucas De Marchi X-Patchwork-Id: 13826824 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 8B53DCEFC3E for ; Tue, 8 Oct 2024 18:35:46 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 5F26610E58C; Tue, 8 Oct 2024 18:35:45 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=intel.com header.i=@intel.com header.b="BOWb547T"; dkim-atps=neutral Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.9]) by gabe.freedesktop.org (Postfix) with ESMTPS id A462310E23C for ; Tue, 8 Oct 2024 18:35:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1728412542; x=1759948542; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=9NC942Tnu9eotfuiQ8yTEZBW0BsoN3zsxEknTk/dLpA=; b=BOWb547TP/V0TvNa2disxv7ByD0NIVvQaI6tQj8RFmlcMnlQhpM5UTA+ 1T1eL7/22i03PwlbvecRFfVoG2xel/6govgohqjPNKjk/6d9tn7TPzJiD RDxxVwpq8VO7NYnLHHJADJqG0Vh774jsK1FBJeQQMRbfqlWXlv7B2kHbV Z37AtwVlitx1YkuGqx3ndPj3k9+cB/FgA9gcb6RzuRDy5RpfYjVfg7lGG 1l/7ykaSVmoR8ySThBlEtoYRklC+hsm4t/OhwQBwoeWr4hwXFXSFvvjJj iD4baZojPwlhOe0FTR+XlLJL7msiu9+a7qA0SeT99T6ZRQPALCQDlMEmh Q==; X-CSE-ConnectionGUID: 7GVsub5oShGKjivuNCww/g== X-CSE-MsgGUID: MYuHT/6qTZiXZaqujkj7EQ== X-IronPort-AV: E=McAfee;i="6700,10204,11219"; a="38295367" X-IronPort-AV: E=Sophos;i="6.11,187,1725346800"; d="scan'208";a="38295367" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 08 Oct 2024 11:35:42 -0700 X-CSE-ConnectionGUID: 1sJW7vDLTn64kfNX0ZMYPg== X-CSE-MsgGUID: k5K8oGCjTxiFbh0EcnRngQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.11,187,1725346800"; d="scan'208";a="80530929" Received: from ldmartin-desk2.corp.intel.com (HELO ldmartin-desk2.intel.com) ([10.125.110.138]) by fmviesa004-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 08 Oct 2024 11:35:40 -0700 From: Lucas De Marchi To: linux-kernel@vger.kernel.org Cc: dri-devel@lists.freedesktop.org, Peter Zijlstra , Ingo Molnar , Arnaldo Carvalho de Melo , Umesh Nerlige Ramappa , Ian Rogers , Tvrtko Ursulin , Lucas De Marchi Subject: [PATCH 4/5] perf/dummy_pmu: Tie pmu to device lifecycle Date: Tue, 8 Oct 2024 13:35:00 -0500 Message-ID: <20241008183501.1354695-5-lucas.demarchi@intel.com> X-Mailer: git-send-email 2.46.2 In-Reply-To: <20241008183501.1354695-1-lucas.demarchi@intel.com> References: <20241008183501.1354695-1-lucas.demarchi@intel.com> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" 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 --- 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); } From patchwork Tue Oct 8 18:35:01 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lucas De Marchi X-Patchwork-Id: 13826825 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id DDCC8CEFC3D for ; Tue, 8 Oct 2024 18:35:47 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 6242710E59D; Tue, 8 Oct 2024 18:35:47 +0000 (UTC) Authentication-Results: gabe.freedesktop.org; dkim=pass (2048-bit key; unprotected) header.d=intel.com header.i=@intel.com header.b="F4wjOeTG"; dkim-atps=neutral Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.9]) by gabe.freedesktop.org (Postfix) with ESMTPS id ADA0A10E588 for ; Tue, 8 Oct 2024 18:35:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1728412543; x=1759948543; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=nHrNeNJsF/XXWidU3270UWoFA4O2wgRb70EVT2SYX5E=; b=F4wjOeTGcX+BosAtfRymoqppJ3gSPg9vbillr7fNHsBgSM2hZPbOuRQE 0lBrMB5AJ8i8FriKZ7xhp9wkV55fMf3xzDfCOLlkn1lAoxeNRk7sXA2GY qf5hLSXvpfeWhL6Qsx55yINgjpII3l36FHuRDm1S5D97Puvnaqzry08HR crg44iyQIyeUno/nxYqKYN0ZOhjtwFsZntqvRmMWL+BCcifqTjxAOA1P1 hFwNhVajLq3MLR2tG69iUHgkr6CCoBU4W1umA6SVfTvWqaTRLLicXHPVW cjNQ9O//YTAuZjMitlDx9vrClm4wSPghM8F6dtix1R/1/GqkApl4Z1UC2 A==; X-CSE-ConnectionGUID: 4Kjp3ZOuSIm/AccLwX+i2g== X-CSE-MsgGUID: RRAqn9OjQJ2IXJ+1ppKZrA== X-IronPort-AV: E=McAfee;i="6700,10204,11219"; a="38295377" X-IronPort-AV: E=Sophos;i="6.11,187,1725346800"; d="scan'208";a="38295377" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa103.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 08 Oct 2024 11:35:43 -0700 X-CSE-ConnectionGUID: 75lAtwCFTgu/OxF3Np5Inw== X-CSE-MsgGUID: lzkJKALHSFeawfpQajiarQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.11,187,1725346800"; d="scan'208";a="80530939" Received: from ldmartin-desk2.corp.intel.com (HELO ldmartin-desk2.intel.com) ([10.125.110.138]) by fmviesa004-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 08 Oct 2024 11:35:41 -0700 From: Lucas De Marchi To: linux-kernel@vger.kernel.org Cc: dri-devel@lists.freedesktop.org, Peter Zijlstra , Ingo Molnar , Arnaldo Carvalho de Melo , Umesh Nerlige Ramappa , Ian Rogers , Tvrtko Ursulin , Lucas De Marchi Subject: [PATCH 5/5] perf/dummy_pmu: Track and disable active events Date: Tue, 8 Oct 2024 13:35:01 -0500 Message-ID: <20241008183501.1354695-6-lucas.demarchi@intel.com> X-Mailer: git-send-email 2.46.2 In-Reply-To: <20241008183501.1354695-1-lucas.demarchi@intel.com> References: <20241008183501.1354695-1-lucas.demarchi@intel.com> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" 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, "" 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 dummy_pmu_0/test-event-1/ 5.010446891 dummy_pmu_0/test-event-1/ Signed-off-by: Lucas De Marchi --- 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 #include #include +#include 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);