From nobody Sat Jul 25 20:47:09 2026 Received: from galois.linutronix.de (Galois.linutronix.de [193.142.43.55]) (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 BB6C335CB95; Mon, 13 Jul 2026 21:24:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=193.142.43.55 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783977881; cv=none; b=iOsZamLi8nwmkkiJJYOy9Qt34OaEpvue8mEzrxb6ojjaaA+WajIhE/gf4/4MGICFx6FzWCmGaVxyUYrA+U9BgVQf05Qp/vK6HSKr6xtvc2qUunhWOQisGgaLxRxWs0nWt5FRNKflMRhWngO1cD4sFt92xx5F3eKgRfWfZDk8TxY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783977881; c=relaxed/simple; bh=guZwiJJLT2Au9H2bVVww/p9M5gpBdOjT2Mk+0CCMfK8=; h=Date:From:To:Subject:Cc:In-Reply-To:References:MIME-Version: Message-ID:Content-Type; b=P1RNlCeQ1IJqLLfhMW4LJ2EK6x6SU1teVs1tlX5PxWzFNE/YFOpoGraG0cGBkggLJ/duL2+O1u80nln32AyAatW4HKgkgL0KtiSt5g3NwaHT3jXajhCGlohOvHcDupOVYaEGq/u+N6BS+znVVjuZB/dMnSzlJ0KRadVGbwPHgyQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linutronix.de; spf=pass smtp.mailfrom=linutronix.de; dkim=pass (2048-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=lJpfWwDf; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b=J6l6ImG1; arc=none smtp.client-ip=193.142.43.55 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linutronix.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linutronix.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="lJpfWwDf"; dkim=permerror (0-bit key) header.d=linutronix.de header.i=@linutronix.de header.b="J6l6ImG1" Date: Mon, 13 Jul 2026 21:24:36 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020; t=1783977878; h=from:from:sender:sender:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=1/WNiSaDZFgxf6AU7+2ot9vlGinCU/xAdrvZHHU8BGI=; b=lJpfWwDfb2NhI2qkyO/SNy0BgCOxEYOUjiGNtA37MsrAV4NsiHn9LKi3/98pm3coJmZFCH A3b0Or5LW/Ybdjv1OC5cPFDBbCKeIaAFrIwrl/6q5R/zDKKhZ0zfBtp7fZGaTaRY6LKWtv vp4WBL4dfm51l4rNmKhP3Qa4rfixYecywbFrrR7Z7dBdWX5Vlv58rU+elm6fm9+e7rStIn Krx9j/peRwj0/GszFpXr18FslnbTtZrOLxp7zWIwKzXMGER2GjkDXq5D+a+ZRcJwiPB3yV aEBSTzaQc/aTRTip0feZRD1/7C1fQuk039po5t7S7/wKhyeLcsqyAXSs4Pml4Q== DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=linutronix.de; s=2020e; t=1783977878; h=from:from:sender:sender:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=1/WNiSaDZFgxf6AU7+2ot9vlGinCU/xAdrvZHHU8BGI=; b=J6l6ImG1ohHWQ21/JbLV2UyPM/coPbiZuXFWBwvoYh+jUShYmT9kYfIIW/tgtNqREqHYbA 9QB0fAEDkj8/qTCg== From: "tip-bot2 for Reinette Chatre" Sender: tip-bot2@linutronix.de Reply-to: linux-kernel@vger.kernel.org To: linux-tip-commits@vger.kernel.org Subject: [tip: x86/cache] fs/resctrl: Prevent deadlock and use-after-free in info file handlers Cc: Sashiko , Reinette Chatre , "Borislav Petkov (AMD)" , Tony Luck , x86@kernel.org, linux-kernel@vger.kernel.org In-Reply-To: =?utf-8?q?=3C0b5238486bd058704d908d39a75aff2815bd18aa=2E1783963?= =?utf-8?q?505=2Egit=2Ereinette=2Echatre=40intel=2Ecom=3E?= References: =?utf-8?q?=3C0b5238486bd058704d908d39a75aff2815bd18aa=2E17839635?= =?utf-8?q?05=2Egit=2Ereinette=2Echatre=40intel=2Ecom=3E?= Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-ID: <178397787668.1844600.2430346570983077105.tip-bot2@tip-bot2> Robot-ID: Robot-Unsubscribe: Contact to get blacklisted from these emails Precedence: bulk Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable The following commit has been merged into the x86/cache branch of tip: Commit-ID: 2d77f976885089975dcf244fd468c31c66e3cb01 Gitweb: https://git.kernel.org/tip/2d77f976885089975dcf244fd468c31c6= 6e3cb01 Author: Reinette Chatre AuthorDate: Mon, 13 Jul 2026 10:39:41 -07:00 Committer: Borislav Petkov (AMD) CommitterDate: Mon, 13 Jul 2026 13:06:07 -07:00 fs/resctrl: Prevent deadlock and use-after-free in info file handlers resctrl provides files under the info/ directory to expose global configuration and capabilities to userspace. These files are instantiated statically during filesystem mount and expose data associated with internal schema structures via kernfs private pointers. A potential deadlock exists between userspace readers of these info files and the unmount filesystem teardown process. Reading an info file invokes kernfs which acquires an active reference, after which the handler typically attempts to acquire the rdtgroup_mutex. Concurrently, unmounting the filesystem holds the rdtgroup_mutex and then attempts to recursively remove the info kernfs nodes involving kernfs_drain= () which blocks until all active references are released. Another problem exists where info files might be accessed from an outdated mount if the filesystem is unmounted and remounted during a reader's execution, leading to a use-after-free when reading the now-deleted private schema data. Introduce info_kn_lock() and info_kn_unlock() helpers to coordinate locking across all info handlers. These helpers mirror similar logic used by resour= ce group handlers by deliberately breaking the kernfs active protection before attempting to acquire the rdtgroup_mutex, preventing the deadlock. To guard against the vulnerability from rapid mount cycling, info_kn_lock() securely walks the parent lineage of the kernfs node under an RCU section to confirm the node belongs to the globally active root before permitting the operation to proceed. Convert all info file handlers to use this helper and only de-reference the schema after it is determined safe to do so. Make no attempt to output an error message to last_cmd_status on failure since failure implies there is no filesystem with which to display the error to user space. [ bp: Massage commit message. ] Closes: https://sashiko.dev/#/patchset/20260515193944.15114-1-tony.luck%40i= ntel.com?part=3D3 Reported-by: Sashiko Assisted-by: GitHub_Copilot:gemini-3.1-pro Signed-off-by: Reinette Chatre Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Tony Luck Link: https://patch.msgid.link/0b5238486bd058704d908d39a75aff2815bd18aa.178= 3963505.git.reinette.chatre@intel.com --- fs/resctrl/ctrlmondata.c | 38 ++++---- fs/resctrl/internal.h | 3 +- fs/resctrl/monitor.c | 48 +++++----- fs/resctrl/rdtgroup.c | 192 ++++++++++++++++++++++++++++++-------- 4 files changed, 202 insertions(+), 79 deletions(-) diff --git a/fs/resctrl/ctrlmondata.c b/fs/resctrl/ctrlmondata.c index f33712c..2b29fb5 100644 --- a/fs/resctrl/ctrlmondata.c +++ b/fs/resctrl/ctrlmondata.c @@ -771,10 +771,12 @@ out: int resctrl_io_alloc_show(struct kernfs_open_file *of, struct seq_file *se= q, void *v) { struct resctrl_schema *s =3D rdt_kn_parent_priv(of->kn); - struct rdt_resource *r =3D s->res; + struct rdt_resource *r; =20 - mutex_lock(&rdtgroup_mutex); + if (!info_kn_lock(of->kn)) + return -ENOENT; =20 + r =3D s->res; if (r->cache.io_alloc_capable) { if (resctrl_arch_get_io_alloc_enabled(r)) seq_puts(seq, "enabled\n"); @@ -784,7 +786,7 @@ int resctrl_io_alloc_show(struct kernfs_open_file *of, = struct seq_file *seq, voi seq_puts(seq, "not supported\n"); } =20 - mutex_unlock(&rdtgroup_mutex); + info_kn_unlock(of->kn); =20 return 0; } @@ -849,7 +851,7 @@ ssize_t resctrl_io_alloc_write(struct kernfs_open_file = *of, char *buf, size_t nbytes, loff_t off) { struct resctrl_schema *s =3D rdt_kn_parent_priv(of->kn); - struct rdt_resource *r =3D s->res; + struct rdt_resource *r; char const *grp_name; u32 io_alloc_closid; bool enable; @@ -859,9 +861,10 @@ ssize_t resctrl_io_alloc_write(struct kernfs_open_file= *of, char *buf, if (ret) return ret; =20 - cpus_read_lock(); - mutex_lock(&rdtgroup_mutex); + if (!info_kn_lock(of->kn)) + return -ENOENT; =20 + r =3D s->res; rdt_last_cmd_clear(); =20 if (!r->cache.io_alloc_capable) { @@ -909,8 +912,7 @@ ssize_t resctrl_io_alloc_write(struct kernfs_open_file = *of, char *buf, } =20 out_unlock: - mutex_unlock(&rdtgroup_mutex); - cpus_read_unlock(); + info_kn_unlock(of->kn); =20 return ret ?: nbytes; } @@ -918,14 +920,15 @@ out_unlock: int resctrl_io_alloc_cbm_show(struct kernfs_open_file *of, struct seq_file= *seq, void *v) { struct resctrl_schema *s =3D rdt_kn_parent_priv(of->kn); - struct rdt_resource *r =3D s->res; + struct rdt_resource *r; int ret =3D 0; =20 - cpus_read_lock(); - mutex_lock(&rdtgroup_mutex); + if (!info_kn_lock(of->kn)) + return -ENOENT; =20 rdt_last_cmd_clear(); =20 + r =3D s->res; if (!r->cache.io_alloc_capable) { rdt_last_cmd_printf("io_alloc is not supported on %s\n", s->name); ret =3D -ENODEV; @@ -947,8 +950,7 @@ int resctrl_io_alloc_cbm_show(struct kernfs_open_file *= of, struct seq_file *seq, show_doms(seq, s, NULL, resctrl_io_alloc_closid(r)); =20 out_unlock: - mutex_unlock(&rdtgroup_mutex); - cpus_read_unlock(); + info_kn_unlock(of->kn); return ret; } =20 @@ -1015,7 +1017,7 @@ ssize_t resctrl_io_alloc_cbm_write(struct kernfs_open= _file *of, char *buf, size_t nbytes, loff_t off) { struct resctrl_schema *s =3D rdt_kn_parent_priv(of->kn); - struct rdt_resource *r =3D s->res; + struct rdt_resource *r; u32 io_alloc_closid; int ret =3D 0; =20 @@ -1025,10 +1027,11 @@ ssize_t resctrl_io_alloc_cbm_write(struct kernfs_op= en_file *of, char *buf, =20 buf[nbytes - 1] =3D '\0'; =20 - cpus_read_lock(); - mutex_lock(&rdtgroup_mutex); + if (!info_kn_lock(of->kn)) + return -ENOENT; rdt_last_cmd_clear(); =20 + r =3D s->res; if (!r->cache.io_alloc_capable) { rdt_last_cmd_printf("io_alloc is not supported on %s\n", s->name); ret =3D -ENODEV; @@ -1053,8 +1056,7 @@ ssize_t resctrl_io_alloc_cbm_write(struct kernfs_open= _file *of, char *buf, out_clear_configs: rdt_staged_configs_clear(); out_unlock: - mutex_unlock(&rdtgroup_mutex); - cpus_read_unlock(); + info_kn_unlock(of->kn); =20 return ret ?: nbytes; } diff --git a/fs/resctrl/internal.h b/fs/resctrl/internal.h index 48af75b..e62a277 100644 --- a/fs/resctrl/internal.h +++ b/fs/resctrl/internal.h @@ -335,8 +335,9 @@ __printf(1, 2) void rdt_last_cmd_printf(const char *fmt, ...); =20 struct rdtgroup *rdtgroup_kn_lock_live(struct kernfs_node *kn); - void rdtgroup_kn_unlock(struct kernfs_node *kn); +bool info_kn_lock(struct kernfs_node *kn); +void info_kn_unlock(struct kernfs_node *kn); =20 int rdtgroup_kn_mode_restrict(struct rdtgroup *r, const char *name); =20 diff --git a/fs/resctrl/monitor.c b/fs/resctrl/monitor.c index 2dacb58..15e3eed 100644 --- a/fs/resctrl/monitor.c +++ b/fs/resctrl/monitor.c @@ -1062,7 +1062,8 @@ int event_filter_show(struct kernfs_open_file *of, st= ruct seq_file *seq, void *v bool sep =3D false; int ret =3D 0, i; =20 - mutex_lock(&rdtgroup_mutex); + if (!info_kn_lock(of->kn)) + return -ENOENT; rdt_last_cmd_clear(); =20 r =3D resctrl_arch_get_resource(mevt->rid); @@ -1083,7 +1084,7 @@ int event_filter_show(struct kernfs_open_file *of, st= ruct seq_file *seq, void *v seq_putc(seq, '\n'); =20 out_unlock: - mutex_unlock(&rdtgroup_mutex); + info_kn_unlock(of->kn); =20 return ret; } @@ -1094,7 +1095,8 @@ int resctrl_mbm_assign_on_mkdir_show(struct kernfs_op= en_file *of, struct seq_fil struct rdt_resource *r =3D rdt_kn_parent_priv(of->kn); int ret =3D 0; =20 - mutex_lock(&rdtgroup_mutex); + if (!info_kn_lock(of->kn)) + return -ENOENT; rdt_last_cmd_clear(); =20 if (!resctrl_arch_mbm_cntr_assign_enabled(r)) { @@ -1106,7 +1108,7 @@ int resctrl_mbm_assign_on_mkdir_show(struct kernfs_op= en_file *of, struct seq_fil seq_printf(s, "%u\n", r->mon.mbm_assign_on_mkdir); =20 out_unlock: - mutex_unlock(&rdtgroup_mutex); + info_kn_unlock(of->kn); =20 return ret; } @@ -1122,7 +1124,8 @@ ssize_t resctrl_mbm_assign_on_mkdir_write(struct kern= fs_open_file *of, char *buf if (ret) return ret; =20 - mutex_lock(&rdtgroup_mutex); + if (!info_kn_lock(of->kn)) + return -ENOENT; rdt_last_cmd_clear(); =20 if (!resctrl_arch_mbm_cntr_assign_enabled(r)) { @@ -1134,7 +1137,7 @@ ssize_t resctrl_mbm_assign_on_mkdir_write(struct kern= fs_open_file *of, char *buf r->mon.mbm_assign_on_mkdir =3D value; =20 out_unlock: - mutex_unlock(&rdtgroup_mutex); + info_kn_unlock(of->kn); =20 return ret ?: nbytes; } @@ -1424,8 +1427,8 @@ ssize_t event_filter_write(struct kernfs_open_file *o= f, char *buf, size_t nbytes =20 buf[nbytes - 1] =3D '\0'; =20 - cpus_read_lock(); - mutex_lock(&rdtgroup_mutex); + if (!info_kn_lock(of->kn)) + return -ENOENT; =20 rdt_last_cmd_clear(); =20 @@ -1448,8 +1451,7 @@ ssize_t event_filter_write(struct kernfs_open_file *o= f, char *buf, size_t nbytes } =20 out_unlock: - mutex_unlock(&rdtgroup_mutex); - cpus_read_unlock(); + info_kn_unlock(of->kn); =20 return ret ?: nbytes; } @@ -1460,7 +1462,8 @@ int resctrl_mbm_assign_mode_show(struct kernfs_open_f= ile *of, struct rdt_resource *r =3D rdt_kn_parent_priv(of->kn); bool enabled; =20 - mutex_lock(&rdtgroup_mutex); + if (!info_kn_lock(of->kn)) + return -ENOENT; enabled =3D resctrl_arch_mbm_cntr_assign_enabled(r); =20 if (r->mon.mbm_cntr_assignable) { @@ -1479,7 +1482,7 @@ int resctrl_mbm_assign_mode_show(struct kernfs_open_f= ile *of, seq_puts(s, "[default]\n"); } =20 - mutex_unlock(&rdtgroup_mutex); + info_kn_unlock(of->kn); =20 return 0; } @@ -1498,8 +1501,8 @@ ssize_t resctrl_mbm_assign_mode_write(struct kernfs_o= pen_file *of, char *buf, =20 buf[nbytes - 1] =3D '\0'; =20 - cpus_read_lock(); - mutex_lock(&rdtgroup_mutex); + if (!info_kn_lock(of->kn)) + return -ENOENT; =20 rdt_last_cmd_clear(); =20 @@ -1557,8 +1560,7 @@ ssize_t resctrl_mbm_assign_mode_write(struct kernfs_o= pen_file *of, char *buf, } =20 out_unlock: - mutex_unlock(&rdtgroup_mutex); - cpus_read_unlock(); + info_kn_unlock(of->kn); =20 return ret ?: nbytes; } @@ -1570,8 +1572,8 @@ int resctrl_num_mbm_cntrs_show(struct kernfs_open_fil= e *of, struct rdt_l3_mon_domain *dom; bool sep =3D false; =20 - cpus_read_lock(); - mutex_lock(&rdtgroup_mutex); + if (!info_kn_lock(of->kn)) + return -ENOENT; =20 list_for_each_entry_rcu(dom, &r->mon_domains, hdr.list, lockdep_is_cpus_h= eld()) { if (sep) @@ -1582,8 +1584,7 @@ int resctrl_num_mbm_cntrs_show(struct kernfs_open_fil= e *of, } seq_putc(s, '\n'); =20 - mutex_unlock(&rdtgroup_mutex); - cpus_read_unlock(); + info_kn_unlock(of->kn); return 0; } =20 @@ -1596,8 +1597,8 @@ int resctrl_available_mbm_cntrs_show(struct kernfs_op= en_file *of, u32 cntrs, i; int ret =3D 0; =20 - cpus_read_lock(); - mutex_lock(&rdtgroup_mutex); + if (!info_kn_lock(of->kn)) + return -ENOENT; =20 rdt_last_cmd_clear(); =20 @@ -1623,8 +1624,7 @@ int resctrl_available_mbm_cntrs_show(struct kernfs_op= en_file *of, seq_putc(s, '\n'); =20 out_unlock: - mutex_unlock(&rdtgroup_mutex); - cpus_read_unlock(); + info_kn_unlock(of->kn); =20 return ret; } diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c index 77c9d22..2fd6873 100644 --- a/fs/resctrl/rdtgroup.c +++ b/fs/resctrl/rdtgroup.c @@ -977,13 +977,14 @@ static int rdt_last_cmd_status_show(struct kernfs_ope= n_file *of, { int len; =20 - mutex_lock(&rdtgroup_mutex); + if (!info_kn_lock(of->kn)) + return -ENOENT; len =3D seq_buf_used(&last_cmd_status); if (len) seq_printf(seq, "%.*s", len, last_cmd_status_buf); else seq_puts(seq, "ok\n"); - mutex_unlock(&rdtgroup_mutex); + info_kn_unlock(of->kn); return 0; } =20 @@ -1002,7 +1003,11 @@ static int rdt_num_closids_show(struct kernfs_open_f= ile *of, { struct resctrl_schema *s =3D rdt_kn_parent_priv(of->kn); =20 + if (!info_kn_lock(of->kn)) + return -ENOENT; seq_printf(seq, "%u\n", s->num_closid); + info_kn_unlock(of->kn); + return 0; } =20 @@ -1010,9 +1015,14 @@ static int rdt_default_ctrl_show(struct kernfs_open_= file *of, struct seq_file *seq, void *v) { struct resctrl_schema *s =3D rdt_kn_parent_priv(of->kn); - struct rdt_resource *r =3D s->res; + struct rdt_resource *r; =20 + if (!info_kn_lock(of->kn)) + return -ENOENT; + r =3D s->res; seq_printf(seq, "%x\n", resctrl_get_default_ctrl(r)); + info_kn_unlock(of->kn); + return 0; } =20 @@ -1020,9 +1030,14 @@ static int rdt_min_cbm_bits_show(struct kernfs_open_= file *of, struct seq_file *seq, void *v) { struct resctrl_schema *s =3D rdt_kn_parent_priv(of->kn); - struct rdt_resource *r =3D s->res; + struct rdt_resource *r; =20 + if (!info_kn_lock(of->kn)) + return -ENOENT; + r =3D s->res; seq_printf(seq, "%u\n", r->cache.min_cbm_bits); + info_kn_unlock(of->kn); + return 0; } =20 @@ -1030,9 +1045,14 @@ static int rdt_shareable_bits_show(struct kernfs_ope= n_file *of, struct seq_file *seq, void *v) { struct resctrl_schema *s =3D rdt_kn_parent_priv(of->kn); - struct rdt_resource *r =3D s->res; + struct rdt_resource *r; =20 + if (!info_kn_lock(of->kn)) + return -ENOENT; + r =3D s->res; seq_printf(seq, "%x\n", r->cache.shareable_bits); + info_kn_unlock(of->kn); + return 0; } =20 @@ -1060,15 +1080,16 @@ static int rdt_bit_usage_show(struct kernfs_open_fi= le *of, */ unsigned long sw_shareable =3D 0, hw_shareable =3D 0; unsigned long exclusive =3D 0, pseudo_locked =3D 0; - struct rdt_resource *r =3D s->res; struct rdt_ctrl_domain *dom; int i, hwb, swb, excl, psl; + struct rdt_resource *r; enum rdtgrp_mode mode; bool sep =3D false; u32 ctrl_val; =20 - cpus_read_lock(); - mutex_lock(&rdtgroup_mutex); + if (!info_kn_lock(of->kn)) + return -ENOENT; + r =3D s->res; list_for_each_entry_rcu(dom, &r->ctrl_domains, hdr.list, lockdep_is_cpus_= held()) { if (sep) seq_putc(seq, ';'); @@ -1144,8 +1165,7 @@ static int rdt_bit_usage_show(struct kernfs_open_file= *of, sep =3D true; } seq_putc(seq, '\n'); - mutex_unlock(&rdtgroup_mutex); - cpus_read_unlock(); + info_kn_unlock(of->kn); return 0; } =20 @@ -1153,9 +1173,14 @@ static int rdt_min_bw_show(struct kernfs_open_file *= of, struct seq_file *seq, void *v) { struct resctrl_schema *s =3D rdt_kn_parent_priv(of->kn); - struct rdt_resource *r =3D s->res; + struct rdt_resource *r; =20 + if (!info_kn_lock(of->kn)) + return -ENOENT; + r =3D s->res; seq_printf(seq, "%u\n", r->membw.min_bw); + info_kn_unlock(of->kn); + return 0; } =20 @@ -1164,8 +1189,12 @@ static int rdt_num_rmids_show(struct kernfs_open_fil= e *of, { struct rdt_resource *r =3D rdt_kn_parent_priv(of->kn); =20 + if (!info_kn_lock(of->kn)) + return -ENOENT; seq_printf(seq, "%u\n", r->mon.num_rmid); =20 + info_kn_unlock(of->kn); + return 0; } =20 @@ -1175,6 +1204,8 @@ static int rdt_mon_features_show(struct kernfs_open_f= ile *of, struct rdt_resource *r =3D rdt_kn_parent_priv(of->kn); struct mon_evt *mevt; =20 + if (!info_kn_lock(of->kn)) + return -ENOENT; for_each_mon_event(mevt) { if (mevt->rid !=3D r->rid || !mevt->enabled) continue; @@ -1184,6 +1215,8 @@ static int rdt_mon_features_show(struct kernfs_open_f= ile *of, seq_printf(seq, "%s_config\n", mevt->name); } =20 + info_kn_unlock(of->kn); + return 0; } =20 @@ -1191,9 +1224,14 @@ static int rdt_bw_gran_show(struct kernfs_open_file = *of, struct seq_file *seq, void *v) { struct resctrl_schema *s =3D rdt_kn_parent_priv(of->kn); - struct rdt_resource *r =3D s->res; + struct rdt_resource *r; =20 + if (!info_kn_lock(of->kn)) + return -ENOENT; + r =3D s->res; seq_printf(seq, "%u\n", r->membw.bw_gran); + info_kn_unlock(of->kn); + return 0; } =20 @@ -1201,16 +1239,24 @@ static int rdt_delay_linear_show(struct kernfs_open= _file *of, struct seq_file *seq, void *v) { struct resctrl_schema *s =3D rdt_kn_parent_priv(of->kn); - struct rdt_resource *r =3D s->res; + struct rdt_resource *r; =20 + if (!info_kn_lock(of->kn)) + return -ENOENT; + r =3D s->res; seq_printf(seq, "%u\n", r->membw.delay_linear); + info_kn_unlock(of->kn); + return 0; } =20 static int max_threshold_occ_show(struct kernfs_open_file *of, struct seq_file *seq, void *v) { + if (!info_kn_lock(of->kn)) + return -ENOENT; seq_printf(seq, "%u\n", resctrl_rmid_realloc_threshold); + info_kn_unlock(of->kn); =20 return 0; } @@ -1219,22 +1265,28 @@ static int rdt_thread_throttle_mode_show(struct ker= nfs_open_file *of, struct seq_file *seq, void *v) { struct resctrl_schema *s =3D rdt_kn_parent_priv(of->kn); - struct rdt_resource *r =3D s->res; + struct rdt_resource *r; + + if (!info_kn_lock(of->kn)) + return -ENOENT; =20 + r =3D s->res; switch (r->membw.throttle_mode) { case THREAD_THROTTLE_PER_THREAD: seq_puts(seq, "per-thread\n"); - return 0; + break; case THREAD_THROTTLE_MAX: seq_puts(seq, "max\n"); - return 0; + break; case THREAD_THROTTLE_UNDEFINED: seq_puts(seq, "undefined\n"); - return 0; + break; + default: + WARN_ON_ONCE(1); + break; } =20 - WARN_ON_ONCE(1); - + info_kn_unlock(of->kn); return 0; } =20 @@ -1248,12 +1300,20 @@ static ssize_t max_threshold_occ_write(struct kernf= s_open_file *of, if (ret) return ret; =20 - if (bytes > resctrl_rmid_realloc_limit) - return -EINVAL; + if (!info_kn_lock(of->kn)) + return -ENOENT; + + if (bytes > resctrl_rmid_realloc_limit) { + ret =3D -EINVAL; + goto out_unlock; + } =20 resctrl_rmid_realloc_threshold =3D resctrl_arch_round_mon_val(bytes); =20 - return nbytes; +out_unlock: + info_kn_unlock(of->kn); + + return ret ?: nbytes; } =20 /* @@ -1293,10 +1353,15 @@ static int rdt_has_sparse_bitmasks_show(struct kern= fs_open_file *of, struct seq_file *seq, void *v) { struct resctrl_schema *s =3D rdt_kn_parent_priv(of->kn); - struct rdt_resource *r =3D s->res; + struct rdt_resource *r; =20 + if (!info_kn_lock(of->kn)) + return -ENOENT; + r =3D s->res; seq_printf(seq, "%u\n", r->cache.arch_has_sparse_bitmasks); =20 + info_kn_unlock(of->kn); + return 0; } =20 @@ -1652,8 +1717,8 @@ static int mbm_config_show(struct seq_file *s, struct= rdt_resource *r, u32 evtid struct rdt_l3_mon_domain *dom; bool sep =3D false; =20 - cpus_read_lock(); - mutex_lock(&rdtgroup_mutex); + lockdep_assert_cpus_held(); + lockdep_assert_held(&rdtgroup_mutex); =20 list_for_each_entry_rcu(dom, &r->mon_domains, hdr.list, lockdep_is_cpus_h= eld()) { if (sep) @@ -1670,9 +1735,6 @@ static int mbm_config_show(struct seq_file *s, struct= rdt_resource *r, u32 evtid } seq_puts(s, "\n"); =20 - mutex_unlock(&rdtgroup_mutex); - cpus_read_unlock(); - return 0; } =20 @@ -1681,8 +1743,12 @@ static int mbm_total_bytes_config_show(struct kernfs= _open_file *of, { struct rdt_resource *r =3D rdt_kn_parent_priv(of->kn); =20 + if (!info_kn_lock(of->kn)) + return -ENOENT; + mbm_config_show(seq, r, QOS_L3_MBM_TOTAL_EVENT_ID); =20 + info_kn_unlock(of->kn); return 0; } =20 @@ -1691,8 +1757,12 @@ static int mbm_local_bytes_config_show(struct kernfs= _open_file *of, { struct rdt_resource *r =3D rdt_kn_parent_priv(of->kn); =20 + if (!info_kn_lock(of->kn)) + return -ENOENT; + mbm_config_show(seq, r, QOS_L3_MBM_LOCAL_EVENT_ID); =20 + info_kn_unlock(of->kn); return 0; } =20 @@ -1790,8 +1860,8 @@ static ssize_t mbm_total_bytes_config_write(struct ke= rnfs_open_file *of, if (nbytes =3D=3D 0 || buf[nbytes - 1] !=3D '\n') return -EINVAL; =20 - cpus_read_lock(); - mutex_lock(&rdtgroup_mutex); + if (!info_kn_lock(of->kn)) + return -ENOENT; =20 rdt_last_cmd_clear(); =20 @@ -1799,8 +1869,7 @@ static ssize_t mbm_total_bytes_config_write(struct ke= rnfs_open_file *of, =20 ret =3D mon_config_write(r, buf, QOS_L3_MBM_TOTAL_EVENT_ID); =20 - mutex_unlock(&rdtgroup_mutex); - cpus_read_unlock(); + info_kn_unlock(of->kn); =20 return ret ?: nbytes; } @@ -1816,8 +1885,8 @@ static ssize_t mbm_local_bytes_config_write(struct ke= rnfs_open_file *of, if (nbytes =3D=3D 0 || buf[nbytes - 1] !=3D '\n') return -EINVAL; =20 - cpus_read_lock(); - mutex_lock(&rdtgroup_mutex); + if (!info_kn_lock(of->kn)) + return -ENOENT; =20 rdt_last_cmd_clear(); =20 @@ -1825,8 +1894,7 @@ static ssize_t mbm_local_bytes_config_write(struct ke= rnfs_open_file *of, =20 ret =3D mon_config_write(r, buf, QOS_L3_MBM_LOCAL_EVENT_ID); =20 - mutex_unlock(&rdtgroup_mutex); - cpus_read_unlock(); + info_kn_unlock(of->kn); =20 return ret ?: nbytes; } @@ -2659,6 +2727,58 @@ void rdtgroup_kn_unlock(struct kernfs_node *kn) rdtgroup_kn_put(rdtgrp, kn); } =20 +/* + * Accessing the kn after breaking active protection is safe since the open + * of resctrl file holds a kernfs base reference (different from active + * protection) on the kn ensuring that it remains accessible even if it was + * unlinked. Each kn in turn holds base reference to parent so the kn's + * genealogy remains in memory until all base references dropped. + */ +static bool is_active_resctrl_node(struct kernfs_node *kn) +{ + struct kernfs_node *p; + bool match =3D false; + + guard(rcu)(); + p =3D kn; + while (p) { + if (p =3D=3D rdtgroup_default.kn) { + match =3D true; + break; + } + p =3D rcu_dereference(p->__parent); + } + + return match; +} + +bool info_kn_lock(struct kernfs_node *kn) +{ + kernfs_break_active_protection(kn); + cpus_read_lock(); + mutex_lock(&rdtgroup_mutex); + + /* + * Check both if resctrl is torn down (!rdtgroup_default.kn) and + * if the reader's kernfs_node originates from a dead mount. + */ + if (!rdtgroup_default.kn || !is_active_resctrl_node(kn)) { + mutex_unlock(&rdtgroup_mutex); + cpus_read_unlock(); + kernfs_unbreak_active_protection(kn); + return false; + } + + return true; +} + +void info_kn_unlock(struct kernfs_node *kn) +{ + mutex_unlock(&rdtgroup_mutex); + cpus_read_unlock(); + kernfs_unbreak_active_protection(kn); +} + static int mkdir_mondata_all(struct kernfs_node *parent_kn, struct rdtgroup *prgrp, struct kernfs_node **mon_data_kn);