From: Jonathan Perry <yonch@yonch.com>
To: Tony Luck <tony.luck@intel.com>,
Reinette Chatre <reinette.chatre@intel.com>,
linux-kernel@vger.kernel.org
Cc: linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org,
Jonathan Corbet <corbet@lwn.net>,
James Morse <james.morse@arm.com>,
Roman Storozhenko <romeusmeister@gmail.com>,
Jonathan Perry <yonch@yonch.com>
Subject: [PATCH 7/8] resctrl/pmu: Use mon_event_setup_read() and validate CPU
Date: Thu, 16 Oct 2025 09:46:55 -0500 [thread overview]
Message-ID: <20251016144656.74928-8-yonch@yonch.com> (raw)
In-Reply-To: <20251016144656.74928-1-yonch@yonch.com>
During event_init, extract mon_data from the monitoring file and call
mon_event_setup_read() to prepare rmid_read and the valid CPU mask for
that file. Require a CPU-bound event and verify the bound CPU is in
the mask.
Store the prepared rmid_read and CPU mask in the event private data
along with the pinned rdtgroup.
Split the helper that gets the pinned rdtgroup in two, so event_init
can get the mon_data from kernfs_open_file:
- rdtgroup_get_mondata_open_file() gets kernfs_open_file from file
- rdtgroup_get_from_mondata_file() gets pinned rdtgroup from
kernfs_open_file
Extend the selftest to test CPU validation and verify that pid-bound
events are rejected.
Signed-off-by: Jonathan Perry <yonch@yonch.com>
---
fs/resctrl/internal.h | 2 +
fs/resctrl/pmu.c | 59 +++++++-
fs/resctrl/rdtgroup.c | 24 ++-
tools/testing/selftests/resctrl/pmu_test.c | 164 ++++++++++++++++++---
4 files changed, 214 insertions(+), 35 deletions(-)
diff --git a/fs/resctrl/internal.h b/fs/resctrl/internal.h
index b42c625569a8..8cc3a3747c2f 100644
--- a/fs/resctrl/internal.h
+++ b/fs/resctrl/internal.h
@@ -365,6 +365,8 @@ 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);
+struct kernfs_open_file *rdtgroup_get_mondata_open_file(struct file *file);
+struct rdtgroup *rdtgroup_get_from_mondata_file(struct kernfs_open_file *of);
/* PMU support */
/*
diff --git a/fs/resctrl/pmu.c b/fs/resctrl/pmu.c
index e7915a0a3520..bdca0b3a5b0b 100644
--- a/fs/resctrl/pmu.c
+++ b/fs/resctrl/pmu.c
@@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/seq_file.h>
+#include <linux/cpu.h>
#include "internal.h"
static struct pmu resctrl_pmu;
@@ -21,6 +22,8 @@ static struct pmu resctrl_pmu;
*/
struct resctrl_pmu_event {
struct rdtgroup *rdtgrp; /* Reference to rdtgroup being monitored */
+ struct rmid_read rr; /* RMID read setup for monitoring */
+ cpumask_t *cpumask; /* Valid CPUs for this monitoring file */
};
static void resctrl_event_destroy(struct perf_event *event);
@@ -34,9 +37,16 @@ static int resctrl_event_init(struct perf_event *event)
struct resctrl_pmu_event *resctrl_event;
struct file *file;
struct rdtgroup *rdtgrp;
+ struct kernfs_open_file *of;
+ struct mon_data *md;
+ struct rmid_read rr = {0};
+ cpumask_t *cpumask;
int fd;
int ret;
+ if (event->cpu < 0)
+ return -EINVAL;
+
fd = (int)event->attr.config;
if (fd < 0)
return -EINVAL;
@@ -45,11 +55,46 @@ static int resctrl_event_init(struct perf_event *event)
if (!file)
return -EBADF;
- /* Resolve rdtgroup from the monitoring file and take a reference */
- rdtgrp = rdtgroup_get_from_file(file);
+ of = rdtgroup_get_mondata_open_file(file);
+ if (IS_ERR(of)) {
+ ret = PTR_ERR(of);
+ goto out_fput;
+ }
+
+ /* Extract mon_data which specifies which resource to measure */
+ if (!of->kn || !of->kn->priv) {
+ ret = -EIO;
+ goto out_fput;
+ }
+ md = of->kn->priv;
+
+ rdtgrp = rdtgroup_get_from_mondata_file(of);
+ if (IS_ERR(rdtgrp)) {
+ ret = PTR_ERR(rdtgrp);
+ goto out_fput;
+ }
+
fput(file);
- if (IS_ERR(rdtgrp))
- return PTR_ERR(rdtgrp);
+ file = NULL;
+
+ cpus_read_lock();
+
+ ret = mon_event_setup_read(&rr, &cpumask, md, rdtgrp);
+ if (ret) {
+ cpus_read_unlock();
+ rdtgroup_put(rdtgrp);
+ return ret;
+ }
+
+ /* Validate that the requested CPU is in the valid CPU mask for this monitoring file */
+ if (!cpumask_test_cpu(event->cpu, cpumask)) {
+ ret = -EINVAL;
+ cpus_read_unlock();
+ rdtgroup_put(rdtgrp);
+ return ret;
+ }
+
+ cpus_read_unlock();
resctrl_event = kzalloc(sizeof(*resctrl_event), GFP_KERNEL);
if (!resctrl_event) {
@@ -58,10 +103,16 @@ static int resctrl_event_init(struct perf_event *event)
}
resctrl_event->rdtgrp = rdtgrp;
+ resctrl_event->rr = rr;
+ resctrl_event->cpumask = cpumask;
event->pmu_private = resctrl_event;
event->destroy = resctrl_event_destroy;
return 0;
+
+out_fput:
+ fput(file);
+ return ret;
}
static void resctrl_event_destroy(struct perf_event *event)
diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c
index 4f4139edafbf..ed7d9feccd94 100644
--- a/fs/resctrl/rdtgroup.c
+++ b/fs/resctrl/rdtgroup.c
@@ -3429,19 +3429,16 @@ void rdtgroup_mondata_release(struct kernfs_open_file *of)
}
/*
- * rdtgroup_get_from_file - Resolve rdtgroup from a resctrl mon data file
+ * Resolve kernfs_open_file 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.
+ * file (kf_mondata_ops).
*
- * Returns: rdtgroup* with an extra reference on success; ERR_PTR on failure.
+ * Returns: kernfs_open_file* on success; ERR_PTR on failure.
*/
-struct rdtgroup *rdtgroup_get_from_file(struct file *file)
+struct kernfs_open_file *rdtgroup_get_mondata_open_file(struct file *file)
{
- struct rdtgroup *rdtgrp = NULL;
struct kernfs_open_file *of;
struct seq_file *seq;
struct inode *inode;
@@ -3464,6 +3461,19 @@ struct rdtgroup *rdtgroup_get_from_file(struct file *file)
if (!of || !of->kn || of->kn->attr.ops != &kf_mondata_ops)
return ERR_PTR(-EINVAL);
+ return of;
+}
+
+/*
+ * Get rdtgroup from a resctrl mon data open file.
+ * @of: kernfs_open_file opened on a resctrl monitoring data file
+ *
+ * Returns: rdtgroup* with an extra reference on success; ERR_PTR on failure.
+ */
+struct rdtgroup *rdtgroup_get_from_mondata_file(struct kernfs_open_file *of)
+{
+ struct rdtgroup *rdtgrp = NULL;
+
/* Hold rdtgroup_mutex to prevent race with release callback */
guard(mutex)(&rdtgroup_mutex);
diff --git a/tools/testing/selftests/resctrl/pmu_test.c b/tools/testing/selftests/resctrl/pmu_test.c
index 29a0ac329619..fb3eec721e43 100644
--- a/tools/testing/selftests/resctrl/pmu_test.c
+++ b/tools/testing/selftests/resctrl/pmu_test.c
@@ -3,13 +3,16 @@
* 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.
+ * Walks resctrl filesystem and verifies only allowed monitoring files
+ * can be used with the resctrl PMU via perf_event_open when pinned to
+ * CPUs in the correct L3 domain. Also validates that PID-bound events
+ * are rejected for all files.
*/
#include "resctrl.h"
#include <fcntl.h>
#include <dirent.h>
+#include <unistd.h>
#define RESCTRL_PMU_NAME "resctrl"
@@ -52,11 +55,51 @@ static bool is_allowed_file(const char *filename)
!strcmp(base, "mbm_local_bytes"));
}
+/* Extract base filename from a path */
+static const char *base_name(const char *path)
+{
+ const char *slash = strrchr(path, '/');
+
+ return slash ? slash + 1 : path;
+}
+
+/* Parse mon_L3_XX ID from a monitoring path. Returns true on success. */
+static bool parse_l3_id_from_path(const char *path, int *l3_id)
+{
+ const char *needle = "mon_data/mon_L3_";
+ const char *p = strstr(path, needle);
+ char *endptr;
+ long id;
+
+ if (!p)
+ return false;
+
+ p += strlen(needle);
+
+ if (!isdigit((unsigned char)*p))
+ return false;
+
+ errno = 0;
+ id = strtol(p, &endptr, 10);
+ if (errno || endptr == p)
+ return false;
+
+ /* Accept only non-negative IDs */
+ if (id < 0)
+ return false;
+
+ *l3_id = (int)id;
+ return true;
+}
+
static int test_file_safety(int pmu_type, const char *filepath)
{
struct perf_event_attr pe = { 0 };
int fd, perf_fd;
- bool should_succeed;
+ bool is_monitoring = false;
+ int file_l3_id = -1;
+ int ret = 0;
+ const char *fname = base_name(filepath);
/* Try to open the file */
fd = open(filepath, O_RDONLY);
@@ -65,7 +108,8 @@ static int test_file_safety(int pmu_type, const char *filepath)
return 0;
}
- should_succeed = is_allowed_file(filepath);
+ /* Determine if this is a monitoring file under mon_L3_XX and allowed */
+ is_monitoring = (is_allowed_file(fname) && parse_l3_id_from_path(filepath, &file_l3_id));
/* Setup perf event attributes */
pe.type = pmu_type;
@@ -75,34 +119,106 @@ static int test_file_safety(int pmu_type, const char *filepath)
pe.exclude_kernel = 0;
pe.exclude_hv = 0;
- /* Try to open the perf event */
- perf_fd = perf_event_open(&pe, -1, 0, -1, 0);
+ /* PID-bound negative attempt: should fail for all files */
+ perf_fd = perf_event_open(&pe, getpid(), -1, -1, 0);
+ if (perf_fd >= 0) {
+ ksft_print_msg("FAIL: pid-bound perf_event_open unexpectedly succeeded for %s\n",
+ filepath);
+ close(perf_fd);
+ close(fd);
+ return -1;
+ }
+
+ int success_count = 0;
+ cpu_set_t mask;
+ int max_cpus, nconf;
+
+ CPU_ZERO(&mask);
+ if (sched_getaffinity(0, sizeof(mask), &mask)) {
+ ksft_perror("sched_getaffinity failed");
+ goto out;
+ }
+
+ nconf = (int)sysconf(_SC_NPROCESSORS_CONF);
+ max_cpus = (nconf > 0 && nconf < CPU_SETSIZE) ? nconf : CPU_SETSIZE;
+
+ for (int cpu = 0; cpu < max_cpus; cpu++) {
+ int cpu_l3;
+
+ if (!CPU_ISSET(cpu, &mask))
+ continue;
+
+ if (get_domain_id("L3", cpu, &cpu_l3) < 0) {
+ ksft_print_msg("Failed to get L3 domain ID for CPU %d\n", cpu);
+ ret = -1;
+ break;
+ }
- 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;
+ perf_fd = perf_event_open(&pe, -1, cpu, -1, 0);
+
+ if (is_monitoring) {
+ bool expected_ok = (cpu_l3 == file_l3_id);
+
+ if (expected_ok) {
+ if (perf_fd < 0) {
+ ksft_print_msg("FAIL: %s CPU %d (L3=%d) expected success, got %s\n",
+ filepath, cpu, cpu_l3, strerror(errno));
+ ret = -1;
+ break;
+ }
+ success_count++;
+ close(perf_fd);
+ } else {
+ if (perf_fd >= 0) {
+ ksft_print_msg("FAIL: %s CPU %d (L3=%d) expected EINVAL fail, but opened\n",
+ filepath, cpu, cpu_l3);
+ close(perf_fd);
+ ret = -1;
+ break;
+ }
+ if (errno != EINVAL) {
+ ksft_print_msg("FAIL: %s CPU %d expected errno=EINVAL, got %d (%s)\n",
+ filepath, cpu, errno, strerror(errno));
+ ret = -1;
+ break;
+ }
+ }
+ } else {
+ /* Non-monitoring files must fail on all CPUs with EINVAL */
+ if (perf_fd >= 0) {
+ ksft_print_msg("FAIL: non-monitoring file %s CPU %d unexpectedly opened\n",
+ filepath, cpu);
+ close(perf_fd);
+ ret = -1;
+ break;
+ }
+ if (errno != EINVAL) {
+ ksft_print_msg("FAIL: non-monitoring file %s CPU %d expected errno=EINVAL, got %d (%s)\n",
+ filepath, cpu, errno, strerror(errno));
+ ret = -1;
+ break;
+ }
}
- ksft_print_msg("PASS: Allowed file %s successfully opened perf event\n",
+ }
+
+ if (!ret && is_monitoring && success_count < 1) {
+ ksft_print_msg("FAIL: monitoring file %s had no successful CPU opens\n",
filepath);
- close(perf_fd);
- } else {
- if (perf_fd >= 0) {
- ksft_print_msg("FAIL: unexpected - perf_event_open succeeded for %s\n",
+ ret = -1;
+ }
+
+ if (!ret) {
+ if (is_monitoring)
+ ksft_print_msg("PASS: monitoring %s: %d CPU(s) opened in-domain, others rejected\n",
+ filepath, success_count);
+ else
+ ksft_print_msg("PASS: non-monitoring %s: all CPU-bound opens rejected with EINVAL\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;
+ return ret;
}
static int walk_directory_recursive(int pmu_type, const char *dir_path)
next prev parent reply other threads:[~2025-10-16 14:47 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-16 14:46 [PATCH 0/8] resctrl: Add perf PMU for resctrl monitoring Jonathan Perry
2025-10-16 14:46 ` [PATCH 1/8] resctrl: Pin rdtgroup for mon_data file lifetime Jonathan Perry
2025-10-16 14:46 ` [PATCH 2/8] resctrl/mon: Split RMID read init from execution Jonathan Perry
2025-10-16 14:46 ` [PATCH 3/8] resctrl/mon: Select cpumask before invoking mon_event_read() Jonathan Perry
2025-10-16 14:46 ` [PATCH 4/8] resctrl/mon: Create mon_event_setup_read() helper Jonathan Perry
2025-10-16 14:46 ` [PATCH 5/8] resctrl: Propagate CPU mask validation error via rr->err Jonathan Perry
2025-10-16 14:46 ` [PATCH 6/8] resctrl/pmu: Introduce skeleton PMU and selftests Jonathan Perry
2025-10-16 14:46 ` Jonathan Perry [this message]
2025-10-16 14:46 ` [PATCH 8/8] resctrl/pmu: Implement .read via direct RMID read; add LLC selftest Jonathan Perry
2025-10-16 21:25 ` [PATCH 0/8] resctrl: Add perf PMU for resctrl monitoring Luck, Tony
2025-10-16 21:51 ` Luck, Tony
2025-10-17 18:17 ` Jonathan Perry
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251016144656.74928-8-yonch@yonch.com \
--to=yonch@yonch.com \
--cc=corbet@lwn.net \
--cc=james.morse@arm.com \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=reinette.chatre@intel.com \
--cc=romeusmeister@gmail.com \
--cc=tony.luck@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.