From nobody Wed Nov 27 18:40:19 2024 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.9]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0DE60212D09 for ; Tue, 8 Oct 2024 18:35:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.9 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1728412540; cv=none; b=GB/MPaY7b13gndyhkSAkJkmIQMC2d9H6ZWKYF4eUiQoZS+xr6vLTZFIposDrvrC0KcpExI7BhZlVMWAQQ0gtS3lTJEdLamHVTurfD8vJo+EWz9DUl5hLS5KerPmSKbqRnhnYy/nBFrbG0GzWACTANDEv36hO1FmLkQt3ecoUFVk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1728412540; c=relaxed/simple; bh=wcVQHgu0ANeHR4UYpWHs2sNLqJA/YG+05getslSrXfE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=l4rJeuQEqxqvHEAuD5/v2Ic6vBZJrXdooDOOQ7hIxYTEkSWafYCvdKkmCyITdkiF+hjuWkZJEJqStxdJErr997BiFZIqkI36/rvFANzr0XeyWuUCdqnfddfgDC/1gMIt7E7UGEyXXngU8+haoWYTkvZBant1ZzDxkslJ5v0NlQo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=DYPUdSkI; arc=none smtp.client-ip=192.198.163.9 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="DYPUdSkI" 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: yFrKv/EYS7+JP6ji4IsyRQ== X-CSE-MsgGUID: WR49J3dgSjO64aCX36Jmqw== X-IronPort-AV: E=McAfee;i="6700,10204,11219"; a="38295333" X-IronPort-AV: E=Sophos;i="6.11,187,1725346800"; d="scan'208";a="38295333" 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> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable 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 :=3D core.o ring_buffer.o callchain.o obj-$(CONFIG_HAVE_HW_BREAKPOINT) +=3D hw_breakpoint.o obj-$(CONFIG_HW_BREAKPOINT_KUNIT_TEST) +=3D hw_breakpoint_test.o obj-$(CONFIG_UPROBES) +=3D uprobes.o +obj-m +=3D 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 =C2=A9 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 =3D container_of(attr, struct perf_pmu_events_attr, attr); + + return sprintf(page, "event=3D0x%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[] =3D { + 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[] =3D { + &format_attr_event.attr, + NULL, +}; +static const struct attribute_group dummy_pmu_events_attr_group =3D { + .name =3D "events", + .attrs =3D dummy_pmu_event_attrs, +}; +static const struct attribute_group dummy_pmu_format_attr_group =3D { + .name =3D "format", + .attrs =3D dummy_pmu_format_attrs, +}; +static const struct attribute_group *attr_groups[] =3D { + &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 =3D event_to_pmu(event); + struct dummy_device *d =3D pmu_to_device(pmu); + + kref_put(&d->refcount, device_release); +} + +static int dummy_pmu_event_init(struct perf_event *event) +{ + struct dummy_pmu *pmu =3D event_to_pmu(event); + struct dummy_device *d =3D pmu_to_device(pmu); + + if (!pmu->registered) + return -ENODEV; + + if (event->attr.type !=3D 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 =3D dummy_pmu_event_destroy; + + return 0; +} + +static void dummy_pmu_event_start(struct perf_event *event, int flags) +{ + struct dummy_pmu *pmu =3D event_to_pmu(event); + + if (!pmu->registered) + return; + + event->hw.state =3D 0; +} + +static void dummy_pmu_event_read(struct perf_event *event) +{ + struct dummy_pmu *pmu =3D event_to_pmu(event); + u8 buf; + + if (!pmu->registered) { + event->hw.state =3D PERF_HES_STOPPED; + return; + } + + get_random_bytes(&buf, 1); + buf %=3D 10; + + switch (event->attr.config & 0xf) { + case DUMMY1_CONFIG: + break; + case DUMMY2_CONFIG: + buf *=3D 2; + break; + } + + local64_add(buf, &event->count); +} + +static void dummy_pmu_event_stop(struct perf_event *event, int flags) +{ + struct dummy_pmu *pmu =3D event_to_pmu(event); + + if (!pmu->registered) + goto out; + + if (flags & PERF_EF_UPDATE) + dummy_pmu_event_read(event); + +out: + event->hw.state =3D PERF_HES_STOPPED; +} + +static int dummy_pmu_event_add(struct perf_event *event, int flags) +{ + struct dummy_pmu *pmu =3D 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 =3D (struct pmu){ + .attr_groups =3D attr_groups, + .module =3D THIS_MODULE, + .task_ctx_nr =3D perf_invalid_context, + .event_init =3D dummy_pmu_event_init, + .add =3D dummy_pmu_event_add, + .del =3D dummy_pmu_event_del, + .start =3D dummy_pmu_event_start, + .stop =3D dummy_pmu_event_stop, + .read =3D dummy_pmu_event_read, + }; + + d->pmu.name =3D kasprintf(GFP_KERNEL, "dummy_pmu_%u", d->instance); + if (!d->pmu.name) + return -ENOMEM; + + ret =3D perf_pmu_register(&d->pmu.base, d->pmu.name, -1); + if (ret) + return ret; + + d->pmu.registered =3D true; + pr_info("Device registered: %s\n", d->pmu.name); + + return 0; +} + +static void device_exit(struct dummy_device *d) +{ + d->pmu.registered =3D 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 =3D container_of(ref, struct dummy_device, refcoun= t); + + kfree(d->pmu.name); + kfree(d); +} + +static struct dummy_device *find_device_locked(struct dummy_mod *m, unsign= ed int instance) +{ + struct dummy_device *d; + + list_for_each_entry(d, &m->device_list, mod_entry) + if (d->instance =3D=3D instance) + return d; + + return NULL; +} + +static int dummy_add_device(struct dummy_mod *m, unsigned int instance) +{ + struct dummy_device *d, *d2; + int ret =3D 0; + + mutex_lock(&m->mutex); + d =3D find_device_locked(m, instance); + mutex_unlock(&m->mutex); + if (d) + return -EINVAL; + + d =3D kcalloc(1, sizeof(*d), GFP_KERNEL); + if (!d) + return -ENOMEM; + + kref_init(&d->refcount); + d->instance =3D instance; + + ret =3D device_init(d); + if (ret < 0) + goto fail_put; + + mutex_lock(&m->mutex); + d2 =3D find_device_locked(m, instance); + if (d2) { + mutex_unlock(&m->mutex); + ret =3D -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 =3D NULL; + + mutex_lock(&m->mutex); + list_for_each_entry(d, &m->device_list, mod_entry) { + if (d->instance =3D=3D instance) { + list_del(&d->mod_entry); + found =3D 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 *instanc= e) +{ + char buf[16]; + ssize_t len; + + if (size > sizeof(buf) - 1) + return -E2BIG; + + len =3D strncpy_from_user(buf, ubuf, sizeof(buf)); + if (len < 0 || len >=3D 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 =3D 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 =3D file_inode(f)->i_private; + u32 instance; + ssize_t ret; + + ret =3D parse_device(ubuf, size, &instance); + if (ret < 0) + return ret; + + ret =3D 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 =3D file_inode(f)->i_private; + unsigned int instance; + ssize_t ret; + + ret =3D parse_device(ubuf, size, &instance); + if (ret < 0) + return ret; + + ret =3D 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 =3D 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 =3D 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"); --=20 2.46.2