fs/resctrl: Add user interface to enable/disable io_alloc feature

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
This commit is contained in:
Babu Moger 2025-11-12 18:57:32 -06:00 committed by Borislav Petkov (AMD)
parent 48068e5650
commit 9445c7059c
4 changed files with 188 additions and 3 deletions

View 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:

View 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;
}

View 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);

View 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;