lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251016144656.74928-7-yonch@yonch.com>
Date: Thu, 16 Oct 2025 09:46:54 -0500
From: Jonathan Perry <yonch@...ch.com>
To: Tony Luck <tony.luck@...el.com>,
	Reinette Chatre <reinette.chatre@...el.com>,
	linux-kernel@...r.kernel.org
Cc: linux-kselftest@...r.kernel.org,
	linux-doc@...r.kernel.org,
	Jonathan Corbet <corbet@....net>,
	James Morse <james.morse@....com>,
	Roman Storozhenko <romeusmeister@...il.com>,
	Jonathan Perry <yonch@...ch.com>
Subject: [PATCH 6/8] resctrl/pmu: Introduce skeleton PMU and selftests

Register a read-only "resctrl" PMU and implement minimal perf hooks
(event_init, add, del, start, stop, read, destroy). The PMU accepts a
resctrl monitoring file descriptor via attr.config, resolves the
rdtgroup, and pins it for the event's lifetime.

Call PMU init/exit in resctrl_init()/resctrl_exit().

Add a selftest to exercise PMU registration and verify that only
allowed monitoring files can be opened via perf.

Signed-off-by: Jonathan Perry <yonch@...ch.com>
---
 fs/resctrl/Makefile                           |   2 +-
 fs/resctrl/internal.h                         |  12 ++
 fs/resctrl/pmu.c                              | 139 ++++++++++++
 fs/resctrl/rdtgroup.c                         |  53 +++++
 tools/testing/selftests/resctrl/pmu_test.c    | 202 ++++++++++++++++++
 tools/testing/selftests/resctrl/resctrl.h     |   1 +
 .../testing/selftests/resctrl/resctrl_tests.c |   1 +
 7 files changed, 409 insertions(+), 1 deletion(-)
 create mode 100644 fs/resctrl/pmu.c
 create mode 100644 tools/testing/selftests/resctrl/pmu_test.c

diff --git a/fs/resctrl/Makefile b/fs/resctrl/Makefile
index e67f34d2236a..f738b0165ccc 100644
--- a/fs/resctrl/Makefile
+++ b/fs/resctrl/Makefile
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_RESCTRL_FS)		+= rdtgroup.o ctrlmondata.o monitor.o
+obj-$(CONFIG_RESCTRL_FS)		+= rdtgroup.o ctrlmondata.o monitor.o pmu.o
 obj-$(CONFIG_RESCTRL_FS_PSEUDO_LOCK)	+= pseudo_lock.o
 
 # To allow define_trace.h's recursive include:
diff --git a/fs/resctrl/internal.h b/fs/resctrl/internal.h
index 486cbca8d0ec..b42c625569a8 100644
--- a/fs/resctrl/internal.h
+++ b/fs/resctrl/internal.h
@@ -4,6 +4,7 @@
 
 #include <linux/resctrl.h>
 #include <linux/kernfs.h>
+#include <linux/fs.h>
 #include <linux/fs_context.h>
 #include <linux/tick.h>
 
@@ -362,6 +363,17 @@ void mon_event_count(void *info);
 int rdtgroup_mondata_show(struct seq_file *m, void *arg);
 int rdtgroup_mondata_open(struct kernfs_open_file *of);
 void rdtgroup_mondata_release(struct kernfs_open_file *of);
+void rdtgroup_get(struct rdtgroup *rdtgrp);
+void rdtgroup_put(struct rdtgroup *rdtgrp);
+
+/* PMU support */
+/*
+ * Get rdtgroup from a resctrl monitoring file and take a reference.
+ * Returns a valid pointer with an extra reference on success, or ERR_PTR on failure.
+ */
+struct rdtgroup *rdtgroup_get_from_file(struct file *file);
+int resctrl_pmu_init(void);
+void resctrl_pmu_exit(void);
 
 void rmid_read_init(struct rmid_read *rr, struct rdt_resource *r,
 		    struct rdt_mon_domain *d, struct rdtgroup *rdtgrp,
diff --git a/fs/resctrl/pmu.c b/fs/resctrl/pmu.c
new file mode 100644
index 000000000000..e7915a0a3520
--- /dev/null
+++ b/fs/resctrl/pmu.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Perf event access to resctrl monitoring (cache occupancy, memory bandwidth)
+ */
+
+#define pr_fmt(fmt) "resctrl_pmu: " fmt
+
+#include <linux/kernel.h>
+#include <linux/perf_event.h>
+#include <linux/errno.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/seq_file.h>
+#include "internal.h"
+
+static struct pmu resctrl_pmu;
+
+/*
+ * Event private data - stores information about the monitored resctrl group
+ */
+struct resctrl_pmu_event {
+	struct rdtgroup *rdtgrp;	/* Reference to rdtgroup being monitored */
+};
+
+static void resctrl_event_destroy(struct perf_event *event);
+
+/*
+ * Initialize a new resctrl perf event
+ * The config field contains the file descriptor of the monitoring file
+ */
+static int resctrl_event_init(struct perf_event *event)
+{
+	struct resctrl_pmu_event *resctrl_event;
+	struct file *file;
+	struct rdtgroup *rdtgrp;
+	int fd;
+	int ret;
+
+	fd = (int)event->attr.config;
+	if (fd < 0)
+		return -EINVAL;
+
+	file = fget(fd);
+	if (!file)
+		return -EBADF;
+
+	/* Resolve rdtgroup from the monitoring file and take a reference */
+	rdtgrp = rdtgroup_get_from_file(file);
+	fput(file);
+	if (IS_ERR(rdtgrp))
+		return PTR_ERR(rdtgrp);
+
+	resctrl_event = kzalloc(sizeof(*resctrl_event), GFP_KERNEL);
+	if (!resctrl_event) {
+		rdtgroup_put(rdtgrp);
+		return -ENOMEM;
+	}
+
+	resctrl_event->rdtgrp = rdtgrp;
+	event->pmu_private = resctrl_event;
+	event->destroy = resctrl_event_destroy;
+
+	return 0;
+}
+
+static void resctrl_event_destroy(struct perf_event *event)
+{
+	struct resctrl_pmu_event *resctrl_event = event->pmu_private;
+
+	if (resctrl_event) {
+		struct rdtgroup *rdtgrp = resctrl_event->rdtgrp;
+
+		if (rdtgrp)
+			rdtgroup_put(rdtgrp);
+
+		kfree(resctrl_event);
+		event->pmu_private = NULL;
+	}
+}
+
+static void resctrl_event_update(struct perf_event *event)
+{
+	/* Currently just a stub - would read actual cache occupancy here */
+	local64_set(&event->hw.prev_count, 0);
+}
+
+static void resctrl_event_start(struct perf_event *event, int flags)
+{
+	resctrl_event_update(event);
+}
+
+static void resctrl_event_stop(struct perf_event *event, int flags)
+{
+	if (flags & PERF_EF_UPDATE)
+		resctrl_event_update(event);
+}
+
+static int resctrl_event_add(struct perf_event *event, int flags)
+{
+	if (flags & PERF_EF_START)
+		resctrl_event_start(event, flags);
+
+	return 0;
+}
+
+static void resctrl_event_del(struct perf_event *event, int flags)
+{
+	resctrl_event_stop(event, PERF_EF_UPDATE);
+}
+
+static struct pmu resctrl_pmu = {
+	.task_ctx_nr	= perf_invalid_context,
+	.event_init	= resctrl_event_init,
+	.add		= resctrl_event_add,
+	.del		= resctrl_event_del,
+	.start		= resctrl_event_start,
+	.stop		= resctrl_event_stop,
+	.read		= resctrl_event_update,
+	.capabilities	= PERF_PMU_CAP_NO_INTERRUPT | PERF_PMU_CAP_NO_EXCLUDE,
+};
+
+int resctrl_pmu_init(void)
+{
+	int ret;
+
+	ret = perf_pmu_register(&resctrl_pmu, "resctrl", -1);
+	if (ret) {
+		pr_err("Failed to register resctrl PMU: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+void resctrl_pmu_exit(void)
+{
+	perf_pmu_unregister(&resctrl_pmu);
+}
diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c
index 34337abe5345..4f4139edafbf 100644
--- a/fs/resctrl/rdtgroup.c
+++ b/fs/resctrl/rdtgroup.c
@@ -3428,6 +3428,53 @@ void rdtgroup_mondata_release(struct kernfs_open_file *of)
 	}
 }
 
+/*
+ * rdtgroup_get_from_file - Resolve rdtgroup from a resctrl mon data file
+ * @file: struct file opened on a resctrl monitoring data file
+ *
+ * Validate that @file belongs to resctrl and refers to a monitoring data
+ * file (kf_mondata_ops). Then, using the kernfs_open_file stored in the
+ * seq_file, safely fetch the rdtgroup that was pinned at open time and take
+ * an additional rdtgroup reference for the caller under rdtgroup_mutex.
+ *
+ * Returns: rdtgroup* with an extra reference on success; ERR_PTR on failure.
+ */
+struct rdtgroup *rdtgroup_get_from_file(struct file *file)
+{
+	struct rdtgroup *rdtgrp = NULL;
+	struct kernfs_open_file *of;
+	struct seq_file *seq;
+	struct inode *inode;
+
+	if (!file)
+		return ERR_PTR(-EBADF);
+
+	inode = file_inode(file);
+	/* Check the file is part of the resctrl filesystem */
+	if (!inode || !inode->i_sb || inode->i_sb->s_type != &rdt_fs_type)
+		return ERR_PTR(-EINVAL);
+
+	/* kernfs monitoring files use seq_file; seq_file->private is kernfs_open_file */
+	seq = (struct seq_file *)file->private_data;
+	if (!seq)
+		return ERR_PTR(-EINVAL);
+
+	of = (struct kernfs_open_file *)seq->private;
+	/* Check this is a monitoring file */
+	if (!of || !of->kn || of->kn->attr.ops != &kf_mondata_ops)
+		return ERR_PTR(-EINVAL);
+
+	/* Hold rdtgroup_mutex to prevent race with release callback */
+	guard(mutex)(&rdtgroup_mutex);
+
+	rdtgrp = of->priv;
+	if (!rdtgrp || (rdtgrp->flags & RDT_DELETED))
+		return ERR_PTR(-ENOENT);
+
+	rdtgroup_get(rdtgrp);
+	return rdtgrp;
+}
+
 /**
  * cbm_ensure_valid - Enforce validity on provided CBM
  * @_val:	Candidate CBM
@@ -4509,6 +4556,10 @@ int resctrl_init(void)
 	 */
 	debugfs_resctrl = debugfs_create_dir("resctrl", NULL);
 
+	ret = resctrl_pmu_init();
+	if (ret)
+		pr_warn("Failed to initialize resctrl PMU: %d\n", ret);
+
 	return 0;
 
 cleanup_mountpoint:
@@ -4558,6 +4609,8 @@ static bool resctrl_online_domains_exist(void)
  */
 void resctrl_exit(void)
 {
+	resctrl_pmu_exit();
+
 	cpus_read_lock();
 	WARN_ON_ONCE(resctrl_online_domains_exist());
 
diff --git a/tools/testing/selftests/resctrl/pmu_test.c b/tools/testing/selftests/resctrl/pmu_test.c
new file mode 100644
index 000000000000..29a0ac329619
--- /dev/null
+++ b/tools/testing/selftests/resctrl/pmu_test.c
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Resctrl PMU test
+ *
+ * Test program to verify the resctrl PMU functionality.
+ * Walks resctrl filesystem and verifies only allowed files can be
+ * used with the resctrl PMU via perf_event_open.
+ */
+
+#include "resctrl.h"
+#include <fcntl.h>
+#include <dirent.h>
+
+#define RESCTRL_PMU_NAME "resctrl"
+
+static int find_pmu_type(const char *pmu_name)
+{
+	char path[256];
+	FILE *file;
+	int type;
+
+	snprintf(path, sizeof(path), "/sys/bus/event_source/devices/%s/type",
+		 pmu_name);
+
+	file = fopen(path, "r");
+	if (!file) {
+		ksft_print_msg("Failed to open %s: %s\n", path,
+			       strerror(errno));
+		return -1;
+	}
+
+	if (fscanf(file, "%d", &type) != 1) {
+		ksft_print_msg("Failed to read PMU type from %s\n", path);
+		fclose(file);
+		return -1;
+	}
+
+	fclose(file);
+	return type;
+}
+
+static bool is_allowed_file(const char *filename)
+{
+	const char *base;
+
+	/* Only exact llc_occupancy and mbm files (no *_config) are allowed */
+	base = strrchr(filename, '/');
+	base = base ? base + 1 : filename;
+
+	return (!strcmp(base, "llc_occupancy") ||
+		!strcmp(base, "mbm_total_bytes") ||
+		!strcmp(base, "mbm_local_bytes"));
+}
+
+static int test_file_safety(int pmu_type, const char *filepath)
+{
+	struct perf_event_attr pe = { 0 };
+	int fd, perf_fd;
+	bool should_succeed;
+
+	/* Try to open the file */
+	fd = open(filepath, O_RDONLY);
+	if (fd < 0) {
+		/* File couldn't be opened, skip it */
+		return 0;
+	}
+
+	should_succeed = is_allowed_file(filepath);
+
+	/* Setup perf event attributes */
+	pe.type = pmu_type;
+	pe.config = fd;
+	pe.size = sizeof(pe);
+	pe.disabled = 1;
+	pe.exclude_kernel = 0;
+	pe.exclude_hv = 0;
+
+	/* Try to open the perf event */
+	perf_fd = perf_event_open(&pe, -1, 0, -1, 0);
+
+	if (should_succeed) {
+		if (perf_fd < 0) {
+			ksft_print_msg("FAIL: unexpected - perf_event_open failed for %s: %s\n",
+				       filepath, strerror(errno));
+			close(fd);
+			return -1;
+		}
+		ksft_print_msg("PASS: Allowed file %s successfully opened perf event\n",
+			       filepath);
+		close(perf_fd);
+	} else {
+		if (perf_fd >= 0) {
+			ksft_print_msg("FAIL: unexpected - perf_event_open succeeded for %s\n",
+				       filepath);
+			close(perf_fd);
+			close(fd);
+			return -1;
+		}
+		ksft_print_msg("PASS: Blocked file %s correctly failed perf_event_open: %s\n",
+			       filepath, strerror(errno));
+	}
+
+out:
+	close(fd);
+	return 0;
+}
+
+static int walk_directory_recursive(int pmu_type, const char *dir_path)
+{
+	DIR *dir;
+	struct dirent *entry;
+	char full_path[1024];
+	struct stat statbuf;
+	int ret = 0;
+
+	dir = opendir(dir_path);
+	if (!dir) {
+		ksft_print_msg("Failed to open directory %s: %s\n", dir_path,
+			       strerror(errno));
+		return -1;
+	}
+
+	while ((entry = readdir(dir)) != NULL) {
+		/* Skip . and .. */
+		if (strcmp(entry->d_name, ".") == 0 ||
+		    strcmp(entry->d_name, "..") == 0)
+			continue;
+
+		snprintf(full_path, sizeof(full_path), "%s/%s", dir_path,
+			 entry->d_name);
+
+		if (stat(full_path, &statbuf) != 0) {
+			ksft_print_msg("Failed to stat %s: %s\n", full_path,
+				       strerror(errno));
+			continue;
+		}
+
+		if (S_ISDIR(statbuf.st_mode)) {
+			/* Recursively walk subdirectories */
+			if (walk_directory_recursive(pmu_type, full_path) != 0)
+				ret = -1;
+		} else if (S_ISREG(statbuf.st_mode)) {
+			/* Test regular files */
+			if (test_file_safety(pmu_type, full_path) != 0)
+				ret = -1;
+		}
+	}
+
+	closedir(dir);
+	return ret;
+}
+
+static int test_resctrl_pmu_safety(int pmu_type)
+{
+	ksft_print_msg("Testing resctrl PMU safety - walking all files in %s\n",
+		       RESCTRL_PATH);
+
+	/* Walk through all files and directories in /sys/fs/resctrl */
+	return walk_directory_recursive(pmu_type, RESCTRL_PATH);
+}
+
+static bool pmu_feature_check(const struct resctrl_test *test)
+{
+	return resctrl_mon_feature_exists("L3_MON", "llc_occupancy");
+}
+
+static int pmu_run_test(const struct resctrl_test *test,
+			const struct user_params *uparams)
+{
+	int pmu_type, ret;
+
+	ksft_print_msg("Testing resctrl PMU file access safety\n");
+
+	/* Find the resctrl PMU type */
+	pmu_type = find_pmu_type(RESCTRL_PMU_NAME);
+	if (pmu_type < 0) {
+		ksft_print_msg("Resctrl PMU not found - PMU is not registered?\n");
+		return -1;
+	}
+
+	ksft_print_msg("Found resctrl PMU with type: %d\n", pmu_type);
+
+	/* Run the safety test to ensure only appropriate files work */
+	ret = test_resctrl_pmu_safety(pmu_type);
+
+	if (ret == 0)
+		ksft_print_msg("Resctrl PMU safety test completed successfully\n");
+	else
+		ksft_print_msg("Resctrl PMU safety test failed\n");
+
+	return ret;
+}
+
+struct resctrl_test pmu_test = {
+	.name = "PMU",
+	.group = "pmu",
+	.resource = "L3",
+	.vendor_specific = 0,
+	.feature_check = pmu_feature_check,
+	.run_test = pmu_run_test,
+	.cleanup = NULL,
+};
diff --git a/tools/testing/selftests/resctrl/resctrl.h b/tools/testing/selftests/resctrl/resctrl.h
index cd3adfc14969..5b0e6074eaba 100644
--- a/tools/testing/selftests/resctrl/resctrl.h
+++ b/tools/testing/selftests/resctrl/resctrl.h
@@ -244,5 +244,6 @@ extern struct resctrl_test cmt_test;
 extern struct resctrl_test l3_cat_test;
 extern struct resctrl_test l3_noncont_cat_test;
 extern struct resctrl_test l2_noncont_cat_test;
+extern struct resctrl_test pmu_test;
 
 #endif /* RESCTRL_H */
diff --git a/tools/testing/selftests/resctrl/resctrl_tests.c b/tools/testing/selftests/resctrl/resctrl_tests.c
index 5154ffd821c4..11ba9000e015 100644
--- a/tools/testing/selftests/resctrl/resctrl_tests.c
+++ b/tools/testing/selftests/resctrl/resctrl_tests.c
@@ -21,6 +21,7 @@ static struct resctrl_test *resctrl_tests[] = {
 	&l3_cat_test,
 	&l3_noncont_cat_test,
 	&l2_noncont_cat_test,
+	&pmu_test,
 };
 
 static int detect_vendor(void)

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ