]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
fs/resctrl: Add user interface to enable/disable io_alloc feature
authorBabu Moger <babu.moger@amd.com>
Thu, 13 Nov 2025 00:57:32 +0000 (18:57 -0600)
committerBorislav Petkov (AMD) <bp@alien8.de>
Fri, 21 Nov 2025 22:01:54 +0000 (23:01 +0100)
AMD's SDCIAE forces all SDCI lines to be placed into the L3 cache portions
identified by the highest-supported L3_MASK_n register, where n is the maximum
supported CLOSID.

To support this, when io_alloc resctrl feature is enabled, reserve the highest
CLOSID exclusively for I/O allocation traffic making it no longer available for
general CPU cache allocation.

Introduce user interface to enable/disable io_alloc feature and encourage users
to enable io_alloc only when running workloads that can benefit from this
functionality. On enable, initialize the io_alloc CLOSID with all usable CBMs
across all the domains.

Since CLOSIDs are managed by resctrl fs, it is least invasive to make "io_alloc
is supported by maximum supported CLOSID" part of the initial resctrl fs
support for io_alloc. Take care to minimally (only in error messages) expose
this use of CLOSID for io_alloc to user space so that this is not required from
other architectures that may support io_alloc differently in the future.

When resctrl is mounted with "-o cdp" to enable code/data prioritization,
there are two L3 resources that can support I/O allocation: L3CODE and
L3DATA.  From resctrl fs perspective the two resources share a CLOSID and
the architecture's available CLOSID are halved to support this.

The architecture's underlying CLOSID used by SDCIAE when CDP is enabled is the
CLOSID associated with the CDP_CODE resource, but from resctrl's perspective
there is only one CLOSID for both CDP_CODE and CDP_DATA. CDP_DATA is thus not
usable for general (CPU) cache allocation nor I/O allocation.

Keep the CDP_CODE and CDP_DATA I/O alloc status in sync to avoid any confusion
to user space.  That is, enabling io_alloc on CDP_CODE does so on CDP_DATA and
vice-versa, and keep the I/O allocation CBMs of CDP_CODE and CDP_DATA in sync.

Signed-off-by: Babu Moger <babu.moger@amd.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Reviewed-by: Reinette Chatre <reinette.chatre@intel.com>
Link: https://patch.msgid.link/c7d3037795e653e22b02d8fc73ca80d9b075031c.1762995456.git.babu.moger@amd.com
Documentation/filesystems/resctrl.rst
fs/resctrl/ctrlmondata.c
fs/resctrl/internal.h
fs/resctrl/rdtgroup.c

index 108995640ca51daafd95dfc386869ea02818e157..91c71e254bbd76aa27a2b7832b8f97bf8c7f73b7 100644 (file)
@@ -73,6 +73,11 @@ The 'info' directory contains information about the enabled
 resources. Each resource has its own subdirectory. The subdirectory
 names reflect the resource names.
 
+Most of the files in the resource's subdirectory are read-only, and
+describe properties of the resource. Resources that support global
+configuration options also include writable files that can be used
+to modify those settings.
+
 Each subdirectory contains the following files with respect to
 allocation:
 
@@ -152,6 +157,31 @@ related to allocation:
                        "not supported":
                              Support not available for this resource.
 
+               The feature can be modified by writing to the interface, for example:
+
+               To enable::
+
+                       # echo 1 > /sys/fs/resctrl/info/L3/io_alloc
+
+               To disable::
+
+                       # echo 0 > /sys/fs/resctrl/info/L3/io_alloc
+
+               The underlying implementation may reduce resources available to
+               general (CPU) cache allocation. See architecture specific notes
+               below. Depending on usage requirements the feature can be enabled
+               or disabled.
+
+               On AMD systems, io_alloc feature is supported by the L3 Smart
+               Data Cache Injection Allocation Enforcement (SDCIAE). The CLOSID for
+               io_alloc is the highest CLOSID supported by the resource. When
+               io_alloc is enabled, the highest CLOSID is dedicated to io_alloc and
+               no longer available for general (CPU) cache allocation. When CDP is
+               enabled, io_alloc routes I/O traffic using the highest CLOSID allocated
+               for the instruction cache (CDP_CODE), making this CLOSID no longer
+               available for general (CPU) cache allocation for both the CDP_CODE
+               and CDP_DATA resources.
+
 Memory bandwidth(MB) subdirectory contains the following files
 with respect to allocation:
 
index 78a8e7b4ba242b76d11056e9c70f4d6b797ee871..454fdf3b9f3c4989f5e33ade2ff3c653f5b350f4 100644 (file)
@@ -697,3 +697,129 @@ int resctrl_io_alloc_show(struct kernfs_open_file *of, struct seq_file *seq, voi
 
        return 0;
 }
+
+/*
+ * resctrl_io_alloc_closid_supported() - io_alloc feature utilizes the
+ * highest CLOSID value to direct I/O traffic. Ensure that io_alloc_closid
+ * is in the supported range.
+ */
+static bool resctrl_io_alloc_closid_supported(u32 io_alloc_closid)
+{
+       return io_alloc_closid < closids_supported();
+}
+
+/*
+ * Initialize io_alloc CLOSID cache resource CBM with all usable (shared
+ * and unused) cache portions.
+ */
+static int resctrl_io_alloc_init_cbm(struct resctrl_schema *s, u32 closid)
+{
+       enum resctrl_conf_type peer_type;
+       struct rdt_resource *r = s->res;
+       struct rdt_ctrl_domain *d;
+       int ret;
+
+       rdt_staged_configs_clear();
+
+       ret = rdtgroup_init_cat(s, closid);
+       if (ret < 0)
+               goto out;
+
+       /* Keep CDP_CODE and CDP_DATA of io_alloc CLOSID's CBM in sync. */
+       if (resctrl_arch_get_cdp_enabled(r->rid)) {
+               peer_type = resctrl_peer_type(s->conf_type);
+               list_for_each_entry(d, &s->res->ctrl_domains, hdr.list)
+                       memcpy(&d->staged_config[peer_type],
+                              &d->staged_config[s->conf_type],
+                              sizeof(d->staged_config[0]));
+       }
+
+       ret = resctrl_arch_update_domains(r, closid);
+out:
+       rdt_staged_configs_clear();
+       return ret;
+}
+
+/*
+ * resctrl_io_alloc_closid() - io_alloc feature routes I/O traffic using
+ * the highest available CLOSID. Retrieve the maximum CLOSID supported by the
+ * resource. Note that if Code Data Prioritization (CDP) is enabled, the number
+ * of available CLOSIDs is reduced by half.
+ */
+static u32 resctrl_io_alloc_closid(struct rdt_resource *r)
+{
+       if (resctrl_arch_get_cdp_enabled(r->rid))
+               return resctrl_arch_get_num_closid(r) / 2  - 1;
+       else
+               return resctrl_arch_get_num_closid(r) - 1;
+}
+
+ssize_t resctrl_io_alloc_write(struct kernfs_open_file *of, char *buf,
+                              size_t nbytes, loff_t off)
+{
+       struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
+       struct rdt_resource *r = s->res;
+       char const *grp_name;
+       u32 io_alloc_closid;
+       bool enable;
+       int ret;
+
+       ret = kstrtobool(buf, &enable);
+       if (ret)
+               return ret;
+
+       cpus_read_lock();
+       mutex_lock(&rdtgroup_mutex);
+
+       rdt_last_cmd_clear();
+
+       if (!r->cache.io_alloc_capable) {
+               rdt_last_cmd_printf("io_alloc is not supported on %s\n", s->name);
+               ret = -ENODEV;
+               goto out_unlock;
+       }
+
+       /* If the feature is already up to date, no action is needed. */
+       if (resctrl_arch_get_io_alloc_enabled(r) == enable)
+               goto out_unlock;
+
+       io_alloc_closid = resctrl_io_alloc_closid(r);
+       if (!resctrl_io_alloc_closid_supported(io_alloc_closid)) {
+               rdt_last_cmd_printf("io_alloc CLOSID (ctrl_hw_id) %u is not available\n",
+                                   io_alloc_closid);
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+
+       if (enable) {
+               if (!closid_alloc_fixed(io_alloc_closid)) {
+                       grp_name = rdtgroup_name_by_closid(io_alloc_closid);
+                       WARN_ON_ONCE(!grp_name);
+                       rdt_last_cmd_printf("CLOSID (ctrl_hw_id) %u for io_alloc is used by %s group\n",
+                                           io_alloc_closid, grp_name ? grp_name : "another");
+                       ret = -ENOSPC;
+                       goto out_unlock;
+               }
+
+               ret = resctrl_io_alloc_init_cbm(s, io_alloc_closid);
+               if (ret) {
+                       rdt_last_cmd_puts("Failed to initialize io_alloc allocations\n");
+                       closid_free(io_alloc_closid);
+                       goto out_unlock;
+               }
+       } else {
+               closid_free(io_alloc_closid);
+       }
+
+       ret = resctrl_arch_io_alloc_enable(r, enable);
+       if (enable && ret) {
+               rdt_last_cmd_puts("Failed to enable io_alloc feature\n");
+               closid_free(io_alloc_closid);
+       }
+
+out_unlock:
+       mutex_unlock(&rdtgroup_mutex);
+       cpus_read_unlock();
+
+       return ret ?: nbytes;
+}
index a18ed88893963208d942119c0faf11099423c68e..145e22f9a3504b27c1adfa2698d5438a1263c89e 100644 (file)
@@ -390,6 +390,8 @@ void rdt_staged_configs_clear(void);
 
 bool closid_allocated(unsigned int closid);
 
+bool closid_alloc_fixed(u32 closid);
+
 int resctrl_find_cleanest_closid(void);
 
 void *rdt_kn_parent_priv(struct kernfs_node *kn);
@@ -428,6 +430,15 @@ ssize_t mbm_L3_assignments_write(struct kernfs_open_file *of, char *buf, size_t
                                 loff_t off);
 int resctrl_io_alloc_show(struct kernfs_open_file *of, struct seq_file *seq, void *v);
 
+int rdtgroup_init_cat(struct resctrl_schema *s, u32 closid);
+
+enum resctrl_conf_type resctrl_peer_type(enum resctrl_conf_type my_type);
+
+ssize_t resctrl_io_alloc_write(struct kernfs_open_file *of, char *buf,
+                              size_t nbytes, loff_t off);
+
+const char *rdtgroup_name_by_closid(u32 closid);
+
 #ifdef CONFIG_RESCTRL_FS_PSEUDO_LOCK
 int rdtgroup_locksetup_enter(struct rdtgroup *rdtgrp);
 
index c1a603bb0c798a225da3448c200839dc5aaf6a0c..9f9bd31e966257845b0fb8ddf8f9da803884c727 100644 (file)
@@ -226,6 +226,11 @@ bool closid_allocated(unsigned int closid)
        return !test_bit(closid, closid_free_map);
 }
 
+bool closid_alloc_fixed(u32 closid)
+{
+       return __test_and_clear_bit(closid, closid_free_map);
+}
+
 /**
  * rdtgroup_mode_by_closid - Return mode of resource group with closid
  * @closid: closid if the resource group
@@ -1247,7 +1252,7 @@ static int rdtgroup_mode_show(struct kernfs_open_file *of,
        return 0;
 }
 
-static enum resctrl_conf_type resctrl_peer_type(enum resctrl_conf_type my_type)
+enum resctrl_conf_type resctrl_peer_type(enum resctrl_conf_type my_type)
 {
        switch (my_type) {
        case CDP_CODE:
@@ -1838,6 +1843,18 @@ void resctrl_bmec_files_show(struct rdt_resource *r, struct kernfs_node *l3_mon_
                kernfs_put(mon_kn);
 }
 
+const char *rdtgroup_name_by_closid(u32 closid)
+{
+       struct rdtgroup *rdtgrp;
+
+       list_for_each_entry(rdtgrp, &rdt_all_groups, rdtgroup_list) {
+               if (rdtgrp->closid == closid)
+                       return rdt_kn_name(rdtgrp->kn);
+       }
+
+       return NULL;
+}
+
 /* rdtgroup information files for one cache resource. */
 static struct rftype res_common_files[] = {
        {
@@ -1949,9 +1966,10 @@ static struct rftype res_common_files[] = {
        },
        {
                .name           = "io_alloc",
-               .mode           = 0444,
+               .mode           = 0644,
                .kf_ops         = &rdtgroup_kf_single_ops,
                .seq_show       = resctrl_io_alloc_show,
+               .write          = resctrl_io_alloc_write,
        },
        {
                .name           = "max_threshold_occupancy",
@@ -3501,7 +3519,7 @@ static int __init_one_rdt_domain(struct rdt_ctrl_domain *d, struct resctrl_schem
  * If there are no more shareable bits available on any domain then
  * the entire allocation will fail.
  */
-static int rdtgroup_init_cat(struct resctrl_schema *s, u32 closid)
+int rdtgroup_init_cat(struct resctrl_schema *s, u32 closid)
 {
        struct rdt_ctrl_domain *d;
        int ret;