All of lore.kernel.org
 help / color / mirror / Atom feed
From: Fenghua Yu <fenghuay@nvidia.com>
To: Reinette Chatre <reinette.chatre@intel.com>,
	Tony Luck <tony.luck@intel.com>, Ben Horgan <ben.horgan@arm.com>,
	James Morse <james.morse@arm.com>,
	Dave Martin <Dave.Martin@arm.com>,
	Shaopeng Tan <tan.shaopeng@fujitsu.com>,
	Chen Yu <yu.c.chen@intel.com>, Babu Moger <babu.moger@amd.com>,
	Drew Fustini <fustini@kernel.org>,
	Vikram Sethi <vsethi@nvidia.com>,
	Shanker Donthineni <sdonthineni@nvidia.com>,
	Newton Liu <newtonl@nvidia.com>, Gavin Shan <gshan@redhat.com>
Cc: linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	Fenghua Yu <fenghuay@nvidia.com>
Subject: [PATCH 07/23] resctrl: Rebuild resource_schemata subdirs on MBA mode change
Date: Thu, 16 Jul 2026 14:02:57 -0700	[thread overview]
Message-ID: <20260716210329.2914625-7-fenghuay@nvidia.com> (raw)
In-Reply-To: <cover.1784217438.git.fenghuay@nvidia.com>

The resource_schemata directory layout is mode dependent: in legacy mode
an emulated control (one without MBW hardware) is nested under the default
control that emulates it, while in native mode every control sits directly
under resource_schemata. The mode file added earlier is read-only, so the
layout is built once at mount time and can never change.

Make the mode file writable and rebuild the control subdirectories when
the mode changes, so the tree stays consistent with the new mode and with
the schemata file contents. Add resctrl_ctrl_mb_mode_write() and register
it on the mode file (switching it from 0444 to 0644). Factor the
per-control directory creation out of resctrl_mkdir_schemata_dir() into
resctrl_ctrl_create_subdirs(), add resctrl_ctrl_remove_subdirs() to tear
the control directories down, and drive both from
resctrl_ctrl_rebuild_subdirs(), which is called from
resctrl_ctrl_mb_mode_write() whenever the mode actually changes.

Signed-off-by: Fenghua Yu <fenghuay@nvidia.com>
---
 fs/resctrl/rdtgroup.c | 334 ++++++++++++++++++++++++++++++++----------
 1 file changed, 260 insertions(+), 74 deletions(-)

diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c
index a34c7ed8f874..d81877a5cd72 100644
--- a/fs/resctrl/rdtgroup.c
+++ b/fs/resctrl/rdtgroup.c
@@ -995,6 +995,15 @@ void *rdt_kn_parent_priv(struct kernfs_node *kn)
 	return rcu_dereference(kn->__parent)->priv;
 }
 
+static struct kernfs_node *rdt_kn_parent(struct kernfs_node *kn)
+{
+	/*
+	 * Valid within the RCU section it was obtained or while rdtgroup_mutex
+	 * is held.
+	 */
+	return rcu_dereference_check(kn->__parent, lockdep_is_held(&rdtgroup_mutex));
+}
+
 static int rdt_num_closids_show(struct kernfs_open_file *of,
 				struct seq_file *seq, void *v)
 {
@@ -2713,6 +2722,173 @@ static unsigned long fflags_from_resource(struct rdt_resource *r)
 	return WARN_ON_ONCE(1);
 }
 
+/*
+ * Format the sysfs directory name of @ctrl (for example "MB" or "MB_NODE")
+ * into @buf. Returns -ENOSPC if the name would be truncated.
+ */
+static int resctrl_ctrl_full_name(struct rdt_resource_final *f,
+				  struct resctrl_ctrl *ctrl, char *buf, size_t size)
+{
+	int ret;
+
+	ret = snprintf(buf, size, "%s%s%s",
+		       f->name, resctrl_ctrl_is_default(ctrl) ? "" : "_",
+		       resctrl_ctrl_is_default(ctrl) ? "" : resctrl_ctrl_name_str(ctrl->name));
+	if (ret >= size)
+		return -ENOSPC;
+
+	return 0;
+}
+
+static int resctrl_ctrl_create_subdir(struct kernfs_node *kn_dir,
+				      struct rdt_resource_final *f,
+				      struct resctrl_ctrl *ctrl, struct kernfs_node **kn_ctrl)
+{
+	char ctrl_full_name[20];
+	int ret;
+
+	ret = resctrl_ctrl_full_name(f, ctrl, ctrl_full_name, sizeof(ctrl_full_name));
+	if (ret)
+		return ret;
+
+	*kn_ctrl = kernfs_create_dir(kn_dir, ctrl_full_name, kn_dir->mode, ctrl);
+	if (IS_ERR(*kn_ctrl))
+		return PTR_ERR(*kn_ctrl);
+
+	return rdtgroup_kn_set_ugid(*kn_ctrl);
+}
+
+/*
+ * Create a control subdirectory for each control under the
+ * resource_schemata directory @kn_subdir.
+ *
+ * No need to cleanup on exit - caller removes the created directories on
+ * failure (either via the recursive kernfs_remove() of an ancestor or by
+ * rebuilding the subdirectories).
+ */
+static int resctrl_ctrl_create_subdirs(struct kernfs_node *kn_subdir,
+				       struct rdt_resource_final *f)
+{
+	struct kernfs_node *kn_ctrl, *kn_ctrl_def = NULL;
+	struct resctrl_ctrl *ctrl, *ctrl_def = NULL;
+	struct rdt_resource *r = f->res;
+	int ret;
+
+	lockdep_assert_held(&rdtgroup_mutex);
+
+	/*
+	 * Create the default control sub-dir first. Emulated controls are
+	 * nested underneath it and may be iterated before it, so it has to
+	 * exist before the rest of the controls are created.
+	 */
+	for_each_resource_ctrl(ctrl, f->res) {
+		if (ctrl->name != RESCTRL_CTRL_NAME_DEF)
+			continue;
+
+		ret = resctrl_ctrl_create_subdir(kn_subdir, f, ctrl, &kn_ctrl_def);
+		if (ret)
+			return ret;
+		ret = resctrl_add_ctrl_files(kn_ctrl_def, ctrl);
+		if (ret)
+			return ret;
+		ctrl_def = ctrl;
+		break;
+	}
+
+	for_each_resource_ctrl(ctrl, f->res) {
+		if (ctrl->name == RESCTRL_CTRL_NAME_DEF)
+			continue;
+
+		/*
+		 * In legacy mode the emulating control is nested under the
+		 * default control it emulates (the default control's
+		 * emulated_by points at it). In native mode there is no
+		 * emulation, so every control sits directly under
+		 * resource_schemata.
+		 */
+		if (r->mode == RESCTRL_CTRL_LEGACY &&
+		    ctrl_def && ctrl_def->emulated_by == ctrl)
+			ret = resctrl_ctrl_create_subdir(kn_ctrl_def, f, ctrl, &kn_ctrl);
+		else
+			ret = resctrl_ctrl_create_subdir(kn_subdir, f, ctrl, &kn_ctrl);
+		if (ret)
+			return ret;
+
+		ret = resctrl_add_ctrl_files(kn_ctrl, ctrl);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * Remove every control subdirectory under the resource_schemata directory
+ * @kn_subdir, leaving the directory itself and its own files (for example
+ * "mode") in place. The default control directory is removed recursively so
+ * any control nested underneath it is removed too; removing a control that is
+ * not a direct child is a no-op.
+ */
+static void resctrl_ctrl_remove_subdirs(struct kernfs_node *kn_subdir,
+					struct rdt_resource_final *f)
+{
+	char ctrl_full_name[20];
+	struct resctrl_ctrl *ctrl;
+
+	lockdep_assert_held(&rdtgroup_mutex);
+
+	for_each_resource_ctrl(ctrl, f->res) {
+		if (resctrl_ctrl_full_name(f, ctrl, ctrl_full_name, sizeof(ctrl_full_name)))
+			continue;
+		kernfs_remove_by_name(kn_subdir, ctrl_full_name);
+	}
+}
+
+/*
+ * Rebuild the control subdirectories under resource_schemata to match the
+ * current mode. Used when the mode changes at runtime.
+ *
+ * The control directories have the same names in both modes (only their
+ * nesting differs), so the old directories must be removed before the new
+ * ones can be created - kernfs_create_dir() would otherwise fail with
+ * -EEXIST. Between the removal and the following kernfs_activate() there is
+ * a brief window in which resource_schemata contains only its own files (for
+ * example "mode") and no control subdirectories. This is acceptable: the
+ * rebuild only happens on an explicit mode write, runs under rdtgroup_mutex,
+ * and the control subdirectories are purely informational (scope, type,
+ * status) - the actual bandwidth configuration lives in each group's
+ * "schemata" file and is unaffected.
+ *
+ * On failure any partially created subdirectories are removed, so
+ * resource_schemata is left with no control subdirectories at all (its own
+ * files such as "mode" remain). The caller is expected to restore a
+ * consistent layout, typically by reverting the change that triggered the
+ * rebuild and calling this function again. If that second rebuild also
+ * fails there is no further automatic recovery and the control
+ * subdirectories stay missing until the next successful rebuild or a
+ * remount; the caller should report this to user space.
+ */
+static int resctrl_ctrl_rebuild_subdirs(struct kernfs_node *kn_subdir,
+					struct rdt_resource_final *f)
+{
+	int ret;
+
+	lockdep_assert_held(&rdtgroup_mutex);
+
+	resctrl_ctrl_remove_subdirs(kn_subdir, f);
+
+	ret = resctrl_ctrl_create_subdirs(kn_subdir, f);
+	if (ret) {
+		/* Drop any partially created subdirectories. */
+		resctrl_ctrl_remove_subdirs(kn_subdir, f);
+		return ret;
+	}
+
+	kernfs_activate(kn_subdir);
+
+	return 0;
+}
+
 static int resctrl_ctrl_mb_mode_show(struct kernfs_open_file *of,
 				     struct seq_file *seq, void *v)
 {
@@ -2738,12 +2914,91 @@ static int resctrl_ctrl_mb_mode_show(struct kernfs_open_file *of,
 	return 0;
 }
 
+static ssize_t resctrl_ctrl_mb_mode_write(struct kernfs_open_file *of,
+					  char *buf, size_t nbytes, loff_t off)
+{
+	struct rdt_resource_final *f = rdt_kn_parent_priv(of->kn);
+	struct rdt_resource *r = f->res;
+	enum resctrl_ctrl_mode mode;
+	int ret = 0;
+
+	guard(mutex)(&rdtgroup_mutex);
+	rdt_last_cmd_clear();
+
+	/* Valid input requires a trailing newline */
+	if (nbytes == 0 || buf[nbytes - 1] != '\n') {
+		rdt_last_cmd_puts("Invalid input\n");
+		return -EINVAL;
+	}
+
+	buf[nbytes - 1] = '\0';
+
+	if (!strcmp(buf, "native")) {
+		mode = RESCTRL_CTRL_NATIVE;
+	} else if (!strcmp(buf, "legacy")) {
+		mode = RESCTRL_CTRL_LEGACY;
+	} else {
+		rdt_last_cmd_puts("Invalid input\n");
+		return -EINVAL;
+	}
+
+	if (mode != r->mode) {
+		enum resctrl_ctrl_mode old_mode = r->mode;
+
+		r->mode = mode;
+
+		/*
+		 * The resource_schemata directory layout depends on the mode
+		 * (emulated controls are nested in legacy mode only), so rebuild
+		 * the control subdirectories to match the new mode.
+		 */
+		ret = resctrl_ctrl_rebuild_subdirs(rdt_kn_parent(of->kn), f);
+		if (ret) {
+			int rollback_ret;
+
+			/*
+			 * Roll back to the previous mode and rebuild so the
+			 * mode reported by this file stays consistent with the
+			 * directory layout.
+			 */
+			r->mode = old_mode;
+			rollback_ret = resctrl_ctrl_rebuild_subdirs(rdt_kn_parent(of->kn), f);
+			if (rollback_ret) {
+				/*
+				 * The rollback rebuild also failed:
+				 * resource_schemata may now be missing control
+				 * subdirectories. Report the inconsistency to the
+				 * kernel log and to last_cmd_status, and return
+				 * -EIO so user space can tell from the write()
+				 * error that the filesystem state is unreliable
+				 * rather than just that the mode switch failed.
+				 */
+				pr_err("%s: failed to restore resource_schemata (mode change error %d, restore error %d), control subdirectories may be missing\n",
+				       f->name, ret, rollback_ret);
+				rdt_last_cmd_puts("resource_schemata rebuild failed\n");
+				ret = -EIO;
+			} else {
+				/*
+				 * The requested mode change failed but the
+				 * previous mode and its directory layout were
+				 * restored, so the filesystem is consistent. Keep
+				 * the original error from the forward rebuild.
+				 */
+				rdt_last_cmd_puts("Failed to switch mode, reverted\n");
+			}
+		}
+	}
+
+	return ret ?: nbytes;
+}
+
 static struct rftype resctrl_ctrl_mb_files[] = {
 	{
 		.name		= "mode",
-		.mode		= 0444,
+		.mode		= 0644,
 		.kf_ops		= &rdtgroup_kf_single_ops,
 		.seq_show	= resctrl_ctrl_mb_mode_show,
+		.write		= resctrl_ctrl_mb_mode_write,
 		/*
 		 * Directory-level file, not per-control: fflags is only a
 		 * presence flag here, not the BIT(ctrl->type) type filter used
@@ -2781,26 +3036,6 @@ static int resctrl_ctrl_add_files(struct kernfs_node *kn)
 	return ret;
 }
 
-static int resctrl_ctrl_create_subdir(struct kernfs_node *kn_dir,
-				      struct rdt_resource_final *f,
-				      struct resctrl_ctrl *ctrl, struct kernfs_node **kn_ctrl)
-{
-	char ctrl_full_name[20];
-	int ret;
-
-	ret = snprintf(ctrl_full_name, sizeof(ctrl_full_name), "%s%s%s",
-		       f->name, resctrl_ctrl_is_default(ctrl) ? "" : "_",
-		       resctrl_ctrl_is_default(ctrl) ? "" : resctrl_ctrl_name_str(ctrl->name));
-	if (ret >= sizeof(ctrl_full_name))
-		return -ENOSPC;
-
-	*kn_ctrl = kernfs_create_dir(kn_dir, ctrl_full_name, kn_dir->mode, ctrl);
-	if (IS_ERR(*kn_ctrl))
-		return PTR_ERR(*kn_ctrl);
-
-	return rdtgroup_kn_set_ugid(*kn_ctrl);
-}
-
 /*
  * No need to cleanup on exit - caller calls the recursive kernfs_remove()
  * on failure.
@@ -2808,8 +3043,7 @@ static int resctrl_ctrl_create_subdir(struct kernfs_node *kn_dir,
 static int resctrl_mkdir_schemata_dir(struct kernfs_node *kn,
 				      struct rdt_resource_final *f)
 {
-	struct kernfs_node *kn_subdir, *kn_ctrl, *kn_ctrl_def = NULL;
-	struct resctrl_ctrl *ctrl, *ctrl_def = NULL;
+	struct kernfs_node *kn_subdir;
 	struct rdt_resource *r = f->res;
 	int ret;
 
@@ -2827,48 +3061,9 @@ static int resctrl_mkdir_schemata_dir(struct kernfs_node *kn,
 			return ret;
 	}
 
-	/*
-	 * Create the default control sub-dir first. Emulated controls are
-	 * nested underneath it and may be iterated before it, so it has to
-	 * exist before the rest of the controls are created.
-	 */
-	for_each_resource_ctrl(ctrl, f->res) {
-		if (ctrl->name != RESCTRL_CTRL_NAME_DEF)
-			continue;
-
-		ret = resctrl_ctrl_create_subdir(kn_subdir, f, ctrl, &kn_ctrl_def);
-		if (ret)
-			return ret;
-		ret = resctrl_add_ctrl_files(kn_ctrl_def, ctrl);
-		if (ret)
-			return ret;
-		ctrl_def = ctrl;
-		break;
-	}
-
-	for_each_resource_ctrl(ctrl, f->res) {
-		if (ctrl->name == RESCTRL_CTRL_NAME_DEF)
-			continue;
-
-		/*
-		 * In legacy mode the emulating control is nested under the
-		 * default control it emulates (the default control's
-		 * emulated_by points at it). In native mode there is no
-		 * emulation, so every control sits directly under
-		 * resource_schemata.
-		 */
-		if (r->mode == RESCTRL_CTRL_LEGACY &&
-		    ctrl_def && ctrl_def->emulated_by == ctrl)
-			ret = resctrl_ctrl_create_subdir(kn_ctrl_def, f, ctrl, &kn_ctrl);
-		else
-			ret = resctrl_ctrl_create_subdir(kn_subdir, f, ctrl, &kn_ctrl);
-		if (ret)
-			return ret;
-
-		ret = resctrl_add_ctrl_files(kn_ctrl, ctrl);
-		if (ret)
-			return ret;
-	}
+	ret = resctrl_ctrl_create_subdirs(kn_subdir, f);
+	if (ret)
+		return ret;
 
 	kernfs_activate(kn_subdir);
 
@@ -4566,15 +4761,6 @@ static int rdtgroup_rmdir_ctrl(struct rdtgroup *rdtgrp, cpumask_var_t tmpmask)
 	return 0;
 }
 
-static struct kernfs_node *rdt_kn_parent(struct kernfs_node *kn)
-{
-	/*
-	 * Valid within the RCU section it was obtained or while rdtgroup_mutex
-	 * is held.
-	 */
-	return rcu_dereference_check(kn->__parent, lockdep_is_held(&rdtgroup_mutex));
-}
-
 static int rdtgroup_rmdir(struct kernfs_node *kn)
 {
 	struct kernfs_node *parent_kn;
-- 
2.43.0



  parent reply	other threads:[~2026-07-16 21:04 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-16 21:02 [PATCH RFC 00/23] resctrl: MBA control emulation and ARM MPAM MB_NODE support Fenghua Yu
2026-07-16 21:02 ` [PATCH 01/23] resctrl: Fix ownership of resource_schemata control subdirectories Fenghua Yu
2026-07-16 21:02 ` [PATCH 02/23] arm_mpam: Fix NULL address access issue Fenghua Yu
2026-07-16 21:02 ` [PATCH 03/23] resctrl: Expose MBA resource_schemata mode sysfs Fenghua Yu
2026-07-17  8:54   ` Ben Horgan
2026-07-17 10:13     ` Ben Horgan
2026-07-16 21:02 ` [PATCH 04/23] resctrl: Expose per-control status in resource_schemata Fenghua Yu
2026-07-16 21:02 ` [PATCH 05/23] resctrl: Add nested resource_schemata support for emulated controls Fenghua Yu
2026-07-16 21:02 ` [PATCH 06/23] resctrl: Mirror schemata for controls without MBW hardware Fenghua Yu
2026-07-16 21:02 ` Fenghua Yu [this message]
2026-07-16 21:02 ` [PATCH 08/23] Documentation: resctrl: document MBA control emulation Fenghua Yu
2026-07-16 21:02 ` [PATCH 09/23] resctrl: De-hardcode L3 monitor infrastructure Fenghua Yu
2026-07-16 21:03 ` [PATCH 10/23] resctrl: Expose MBA MBM counter assignment sysfs Fenghua Yu
2026-07-16 21:03 ` [PATCH 11/23] resctrl: name node-scoped monitor domains mon_NODE_<id> Fenghua Yu
2026-07-16 21:03 ` [PATCH 12/23] resctrl: Add node-scope MBM total event Fenghua Yu
2026-07-16 21:03 ` [PATCH 13/23] resctrl: Make MBM paths resource-aware Fenghua Yu
2026-07-16 21:03 ` [PATCH 14/23] arm_mpam: Support memory-level MSCs and ABMC per class Fenghua Yu
2026-07-16 21:03 ` [PATCH 15/23] arm_mpam: Refine L3 topology and class selection Fenghua Yu
2026-07-17  9:18   ` Ben Horgan
2026-07-16 21:03 ` [PATCH 16/23] arm_mpam: Include all MSC components during domain setup Fenghua Yu
2026-07-16 21:03 ` [PATCH 17/23] arm_mpam: Handle CPU-less numa nodes Fenghua Yu
2026-07-16 21:03 ` [PATCH 18/23] arm_mpam: Emulate MB control with node-scoped MB_NODE control Fenghua Yu
2026-07-16 21:03 ` [PATCH 19/23] Documentation: arm64: mpam: document memory-level MB control and NUMA nodes Fenghua Yu
2026-07-16 21:03 ` [PATCH 20/23] Documentation: resctrl: document NODE-scoped MBA domains and mon_NODE monitoring Fenghua Yu
2026-07-16 21:03 ` [PATCH 21/23] Documentation: resctrl: document MB_NODE emulation example on ARM MPAM Fenghua Yu
2026-07-16 21:03 ` [PATCH 22/23] arm_mpam: Add KUnit test for CPU-less NUMA node affinity Fenghua Yu
2026-07-16 21:03 ` [PATCH 23/23] selftests/resctrl: Add MB emulation test for ARM MPAM Fenghua Yu

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=20260716210329.2914625-7-fenghuay@nvidia.com \
    --to=fenghuay@nvidia.com \
    --cc=Dave.Martin@arm.com \
    --cc=babu.moger@amd.com \
    --cc=ben.horgan@arm.com \
    --cc=fustini@kernel.org \
    --cc=gshan@redhat.com \
    --cc=james.morse@arm.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=newtonl@nvidia.com \
    --cc=reinette.chatre@intel.com \
    --cc=sdonthineni@nvidia.com \
    --cc=tan.shaopeng@fujitsu.com \
    --cc=tony.luck@intel.com \
    --cc=vsethi@nvidia.com \
    --cc=yu.c.chen@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.