From nobody Mon Feb 9 01:44:02 2026 Received: from mail-oi1-f180.google.com (mail-oi1-f180.google.com [209.85.167.180]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7DE063469EC for ; Thu, 16 Oct 2025 14:47:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760626031; cv=none; b=GaPdT5RMn9UlQDTT0cvNKBOFk6IiHkObGHFSlgKkVx/b2W7Si+ZZvmyQA2+BQW852zBS0rqqEUBO/4/wHFA43e9ok7Ng8/fr8X9R85YTxghymw4VaLO58h94gEsZYGM2SmzODbjKJRiHeLKlAqnq6orLoiTYgyruw1POfdUMsVo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760626031; c=relaxed/simple; bh=kcz2Za79jyB/wIwvQCRpYXCOBX9SihfQEP++MwdUQKg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=g+Ds9H2xTePc5oiCmOzmESRWFU4na2pD2RVMaWxXXsZAgpi3VhAVyMhLM1ef+zylK+t6HW/BpieCpWvymNpyIUFNT6UiSBgzLFCl1NZ8OCwQkjNeu/oJx78flg+S18Dad6TEexGteC5dnJIKbHpS2yvKLFY8KaXBQND1WSpzHdU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=yonch.com; spf=pass smtp.mailfrom=gmail.com; arc=none smtp.client-ip=209.85.167.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=yonch.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-oi1-f180.google.com with SMTP id 5614622812f47-43f64a5014aso489438b6e.1 for ; Thu, 16 Oct 2025 07:47:09 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1760626028; x=1761230828; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=uKKw+1FOvuGpZeJoYuJP4D2T/0Igapy6n2jKP9t0Gf4=; b=NjMSAN0mS5XMky36D3bfHVutXTigQyzQ0T4mInnXLinsDnn9vNba8bAzyeLXbEg2Ny PcuqNmzlgEXmdQXj61mpBuYJ9p4pyJ46IPqt0sKrKS0OcPlxF22mGeO74Qfq0t+Pu3wf Pm59GnhitamhYSI6m9GDM2q/+WlM+Eq/UfKdxmnnbrFAJF/PxPX2x/ldR9HylUIsMB2k lNAOpMnjxTT26/vQM58QfJZc7ZLVjsJPpWNzQVDNlmoivg0BpvJJKd889wBB4/1V4nvJ KNDlk0UMXVI0rJPj6F/Ocv/aDR+i4npyp1/uWtyhg88xbTEmYaQIrZdOglLfwgtnJrGW bCOQ== X-Forwarded-Encrypted: i=1; AJvYcCXjsvUWJOS9TYPUC7OeaVj7/RhYAOIecD1gwVsvNVVtIIoS8nfLFYtwcx5R+Cd5e3hi9jh9B8/Y4dj2Pnk=@vger.kernel.org X-Gm-Message-State: AOJu0Yz9b9dMW4EOgi6aUdoyg7hb/6Fqimd0v943zBvwdmrIpWGVvkba ATN8+pGwMYfgpyXWaw/2zvZsObz3XA7tv75jMbAGj76cObS6R4iHUJf3 X-Gm-Gg: ASbGncscu0VFwZ4b8p802PVQWYRaVwOFafiUsW9xxSKKRCBCyjErAO1azsd0InQ6oQw 14987EOJUzTxmuokBrELcOWkcjR1KQs3TohMvpJp3g/OdmpHJ5pc1yeG3ZqR7/NwYPhAREplbz9 0H2vUOFng8nY4KsLGP5x8LQB8T5KZGWvW56jdp2gbLbMY/eSX6HO42uJysVXKgYIA/EbMwakPPB 8IYa3oe+hMeWfGddnY+vkcXcZT7jJX8Ru9q02nl1hrqqc0Do6jzF+v7rTNiB2omMQTcUJHd43MG xmymUH3Zc809f7noJcKhQMMHQopXEav18CvcTIFkRpCaBDN3nQFvMAhl5pav9mL2LDO1eDleK28 ou3XU6wIwG03xscIYvfNd04UsrftsYtmK8OPPbwYbsJUg8NRV9D1lz8fphzFekD+OFJ/KGcfx1W vkBX8OlLpnw4ujjT3VeBjfEgesRVHsrdmgc4akec7h1h+q7N4+lya4QI8c2syijClxIbc= X-Google-Smtp-Source: AGHT+IEl+McCmwNj5n5UABPh5EXtH7pj4NhPOnj1CYi5KF1wA0afnYHvrsjxYZpRFSVlVKKD3j3GHQ== X-Received: by 2002:a05:6808:16a4:b0:441:8f74:fcd with SMTP id 5614622812f47-443a309882cmr102090b6e.58.1760626028347; Thu, 16 Oct 2025 07:47:08 -0700 (PDT) Received: from localhost.localdomain (syn-067-079-108-173.biz.spectrum.com. [67.79.108.173]) by smtp.gmail.com with ESMTPSA id 5614622812f47-441cc812f24sm3678018b6e.12.2025.10.16.07.47.07 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Thu, 16 Oct 2025 07:47:08 -0700 (PDT) From: Jonathan Perry To: Tony Luck , Reinette Chatre , linux-kernel@vger.kernel.org Cc: linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org, Jonathan Corbet , James Morse , Roman Storozhenko , Jonathan Perry Subject: [PATCH 7/8] resctrl/pmu: Use mon_event_setup_read() and validate CPU Date: Thu, 16 Oct 2025 09:46:55 -0500 Message-ID: <20251016144656.74928-8-yonch@yonch.com> X-Mailer: git-send-email 2.47.1 In-Reply-To: <20251016144656.74928-1-yonch@yonch.com> References: <20251016144656.74928-1-yonch@yonch.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- 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 *o= f); =20 /* 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 #include #include +#include #include "internal.h" =20 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 */ }; =20 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 =3D {0}; + cpumask_t *cpumask; int fd; int ret; =20 + if (event->cpu < 0) + return -EINVAL; + fd =3D (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; =20 - /* Resolve rdtgroup from the monitoring file and take a reference */ - rdtgrp =3D rdtgroup_get_from_file(file); + of =3D rdtgroup_get_mondata_open_file(file); + if (IS_ERR(of)) { + ret =3D PTR_ERR(of); + goto out_fput; + } + + /* Extract mon_data which specifies which resource to measure */ + if (!of->kn || !of->kn->priv) { + ret =3D -EIO; + goto out_fput; + } + md =3D of->kn->priv; + + rdtgrp =3D rdtgroup_get_from_mondata_file(of); + if (IS_ERR(rdtgrp)) { + ret =3D PTR_ERR(rdtgrp); + goto out_fput; + } + fput(file); - if (IS_ERR(rdtgrp)) - return PTR_ERR(rdtgrp); + file =3D NULL; + + cpus_read_lock(); + + ret =3D 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 moni= toring file */ + if (!cpumask_test_cpu(event->cpu, cpumask)) { + ret =3D -EINVAL; + cpus_read_unlock(); + rdtgroup_put(rdtgrp); + return ret; + } + + cpus_read_unlock(); =20 resctrl_event =3D kzalloc(sizeof(*resctrl_event), GFP_KERNEL); if (!resctrl_event) { @@ -58,10 +103,16 @@ static int resctrl_event_init(struct perf_event *event) } =20 resctrl_event->rdtgrp =3D rdtgrp; + resctrl_event->rr =3D rr; + resctrl_event->cpumask =3D cpumask; event->pmu_private =3D resctrl_event; event->destroy =3D resctrl_event_destroy; =20 return 0; + +out_fput: + fput(file); + return ret; } =20 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_fi= le *of) } =20 /* - * 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 ta= ke - * 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 failu= re. + * 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 =3D 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 !=3D &kf_mondata_ops) return ERR_PTR(-EINVAL); =20 + 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 failu= re. + */ +struct rdtgroup *rdtgroup_get_from_mondata_file(struct kernfs_open_file *o= f) +{ + struct rdtgroup *rdtgrp =3D NULL; + /* Hold rdtgroup_mutex to prevent race with release callback */ guard(mutex)(&rdtgroup_mutex); =20 diff --git a/tools/testing/selftests/resctrl/pmu_test.c b/tools/testing/sel= ftests/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. */ =20 #include "resctrl.h" #include #include +#include =20 #define RESCTRL_PMU_NAME "resctrl" =20 @@ -52,11 +55,51 @@ static bool is_allowed_file(const char *filename) !strcmp(base, "mbm_local_bytes")); } =20 +/* Extract base filename from a path */ +static const char *base_name(const char *path) +{ + const char *slash =3D 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 =3D "mon_data/mon_L3_"; + const char *p =3D strstr(path, needle); + char *endptr; + long id; + + if (!p) + return false; + + p +=3D strlen(needle); + + if (!isdigit((unsigned char)*p)) + return false; + + errno =3D 0; + id =3D strtol(p, &endptr, 10); + if (errno || endptr =3D=3D p) + return false; + + /* Accept only non-negative IDs */ + if (id < 0) + return false; + + *l3_id =3D (int)id; + return true; +} + static int test_file_safety(int pmu_type, const char *filepath) { struct perf_event_attr pe =3D { 0 }; int fd, perf_fd; - bool should_succeed; + bool is_monitoring =3D false; + int file_l3_id =3D -1; + int ret =3D 0; + const char *fname =3D base_name(filepath); =20 /* Try to open the file */ fd =3D open(filepath, O_RDONLY); @@ -65,7 +108,8 @@ static int test_file_safety(int pmu_type, const char *fi= lepath) return 0; } =20 - should_succeed =3D is_allowed_file(filepath); + /* Determine if this is a monitoring file under mon_L3_XX and allowed */ + is_monitoring =3D (is_allowed_file(fname) && parse_l3_id_from_path(filepa= th, &file_l3_id)); =20 /* Setup perf event attributes */ pe.type =3D pmu_type; @@ -75,34 +119,106 @@ static int test_file_safety(int pmu_type, const char = *filepath) pe.exclude_kernel =3D 0; pe.exclude_hv =3D 0; =20 - /* Try to open the perf event */ - perf_fd =3D perf_event_open(&pe, -1, 0, -1, 0); + /* PID-bound negative attempt: should fail for all files */ + perf_fd =3D perf_event_open(&pe, getpid(), -1, -1, 0); + if (perf_fd >=3D 0) { + ksft_print_msg("FAIL: pid-bound perf_event_open unexpectedly succeeded f= or %s\n", + filepath); + close(perf_fd); + close(fd); + return -1; + } + + int success_count =3D 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 =3D (int)sysconf(_SC_NPROCESSORS_CONF); + max_cpus =3D (nconf > 0 && nconf < CPU_SETSIZE) ? nconf : CPU_SETSIZE; + + for (int cpu =3D 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 =3D -1; + break; + } =20 - 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 =3D perf_event_open(&pe, -1, cpu, -1, 0); + + if (is_monitoring) { + bool expected_ok =3D (cpu_l3 =3D=3D file_l3_id); + + if (expected_ok) { + if (perf_fd < 0) { + ksft_print_msg("FAIL: %s CPU %d (L3=3D%d) expected success, got %s\n", + filepath, cpu, cpu_l3, strerror(errno)); + ret =3D -1; + break; + } + success_count++; + close(perf_fd); + } else { + if (perf_fd >=3D 0) { + ksft_print_msg("FAIL: %s CPU %d (L3=3D%d) expected EINVAL fail, but o= pened\n", + filepath, cpu, cpu_l3); + close(perf_fd); + ret =3D -1; + break; + } + if (errno !=3D EINVAL) { + ksft_print_msg("FAIL: %s CPU %d expected errno=3DEINVAL, got %d (%s)\= n", + filepath, cpu, errno, strerror(errno)); + ret =3D -1; + break; + } + } + } else { + /* Non-monitoring files must fail on all CPUs with EINVAL */ + if (perf_fd >=3D 0) { + ksft_print_msg("FAIL: non-monitoring file %s CPU %d unexpectedly opene= d\n", + filepath, cpu); + close(perf_fd); + ret =3D -1; + break; + } + if (errno !=3D EINVAL) { + ksft_print_msg("FAIL: non-monitoring file %s CPU %d expected errno=3DE= INVAL, got %d (%s)\n", + filepath, cpu, errno, strerror(errno)); + ret =3D -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 >=3D 0) { - ksft_print_msg("FAIL: unexpected - perf_event_open succeeded for %s\n", + ret =3D -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 w= ith 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)); } =20 out: close(fd); - return 0; + return ret; } =20 static int walk_directory_recursive(int pmu_type, const char *dir_path)