x86/resctrl: Introduce interface to modify assignment states of the groups

ANBZ: #9790

cherry-picked from https://lore.kernel.org/all/cover.1722981659.git.babu.moger@amd.com/

Introduce the interface to assign MBM events in ABMC mode.

Events can be enabled or disabled by writing to file
/sys/fs/resctrl/info/L3_MON/mbm_control

Format is similar to the list format with addition of opcode for the
assignment operation.
 "<CTRL_MON group>/<MON group>/<domain_id><opcode><flags>"

Format for specific type of groups:

 * Default CTRL_MON group:
         "//<domain_id><opcode><flags>"

 * Non-default CTRL_MON group:
         "<CTRL_MON group>//<domain_id><opcode><flags>"

 * Child MON group of default CTRL_MON group:
         "/<MON group>/<domain_id><opcode><flags>"

 * Child MON group of non-default CTRL_MON group:
         "<CTRL_MON group>/<MON group>/<domain_id><opcode><flags>"

Domain_id '*' will apply the flags on all the domains.

Opcode can be one of the following:

 = Update the assignment to match the flags
 + assign a MBM event
 - unassign a MBM event

Assignment flags can be one of the following:
 t  MBM total event
 l  MBM local event
 tl Both total and local MBM events
 _  None of the MBM events. Valid only with '=' opcode.

Signed-off-by: Babu Moger <babu.moger@amd.com>
Signed-off-by: Kun(llfl) <llfl@linux.alibaba.com>
Reviewed-by: Artie Ding <artie.ding@linux.alibaba.com>
Link: https://gitee.com/anolis/cloud-kernel/pulls/3731
This commit is contained in:
Babu Moger 2024-08-06 17:00:59 -05:00 committed by 小龙
parent 51c5ecfd85
commit 1d4487d167
3 changed files with 413 additions and 1 deletions

View File

@ -361,6 +361,98 @@ with the following files:
There are four resctrl groups. All the groups have total and local MBM events
enabled on domain 0 and 1.
Assignment state can be updated by writing to the interface.
Format is similar to the list format with addition of opcode for the
assignment operation.
"<CTRL_MON group>/<MON group>/<domain_id><opcode><flags>"
Format for each type of groups:
* Default CTRL_MON group:
"//<domain_id><opcode><flags>"
* Non-default CTRL_MON group:
"<CTRL_MON group>//<domain_id><opcode><flags>"
* Child MON group of default CTRL_MON group:
"/<MON group>/<domain_id><opcode><flags>"
* Child MON group of non-default CTRL_MON group:
"<CTRL_MON group>/<MON group>/<domain_id><opcode><flags>"
Domain_id '*' wil apply the flags on all the domains.
Opcode can be one of the following:
::
= Update the assignment to match the MBM event.
+ Assign a MBM event.
- Unassign a MBM event.
Examples:
::
Initial group status:
# cat /sys/fs/resctrl/info/L3_MON/mbm_control
non_default_ctrl_mon_grp//0=tl;1=tl;
non_default_ctrl_mon_grp/child_non_default_mon_grp/0=tl;1=tl;
//0=tl;1=tl;
/child_default_mon_grp/0=tl;1=tl;
To update the default group to assign only total MBM event on domain 0:
# echo "//0=t" > /sys/fs/resctrl/info/L3_MON/mbm_control
Assignment status after the update:
# cat /sys/fs/resctrl/info/L3_MON/mbm_control
non_default_ctrl_mon_grp//0=tl;1=tl;
non_default_ctrl_mon_grp/child_non_default_mon_grp/0=tl;1=tl;
//0=t;1=tl;
/child_default_mon_grp/0=tl;1=tl;
To update the MON group child_default_mon_grp to remove total MBM event on domain 1:
# echo "/child_default_mon_grp/1-t" > /sys/fs/resctrl/info/L3_MON/mbm_control
Assignment status after the update:
$ cat /sys/fs/resctrl/info/L3_MON/mbm_control
non_default_ctrl_mon_grp//0=tl;1=tl;
non_default_ctrl_mon_grp/child_non_default_mon_grp/0=tl;1=tl;
//0=t;1=tl;
/child_default_mon_grp/0=tl;1=l;
To update the MON group non_default_ctrl_mon_grp/child_non_default_mon_grp to
unassign both local and total MBM events on domain 1:
# echo "non_default_ctrl_mon_grp/child_non_default_mon_grp/1=_" >
/sys/fs/resctrl/info/L3_MON/mbm_control
Assignment status after the update:
non_default_ctrl_mon_grp//0=tl;1=tl;
non_default_ctrl_mon_grp/child_non_default_mon_grp/0=tl;1=_;
//0=t;1=tl;
/child_default_mon_grp/0=tl;1=l;
To update the default group to add a local MBM event domain 0.
# echo "//0+l" > /sys/fs/resctrl/info/L3_MON/mbm_control
Assignment status after the update:
# cat /sys/fs/resctrl/info/L3_MON/mbm_control
non_default_ctrl_mon_grp//0=tl;1=tl;
non_default_ctrl_mon_grp/child_non_default_mon_grp/0=tl;1=_;
//0=tl;1=tl;
/child_default_mon_grp/0=tl;1=l;
To update the non default CTRL_MON group non_default_ctrl_mon_grp to unassign all
the MBM events on all the domains.
# echo "non_default_ctrl_mon_grp//*=_" > /sys/fs/resctrl/info/L3_MON/mbm_control
Assignment status after the update:
#cat /sys/fs/resctrl/info/L3_MON/mbm_control
non_default_ctrl_mon_grp//0=_;1=_;
non_default_ctrl_mon_grp/child_non_default_mon_grp/0=tl;1=_;
//0=tl;1=tl;
/child_default_mon_grp/0=tl;1=l;
"max_threshold_occupancy":
Read/write file provides the largest value (in
bytes) at which a previously used LLC_occupancy

View File

@ -955,6 +955,318 @@ static int rdtgroup_mbm_control_show(struct kernfs_open_file *of,
return 0;
}
/*
* Update the assign states for the domain.
*
* If this is a new assignment for the group then allocate a counter and update
* the assignment else just update the assign state
*/
static int rdtgroup_assign_update(struct rdtgroup *rdtgrp, enum resctrl_event_id evtid,
struct rdt_domain *d)
{
int ret, index;
index = mon_event_config_index_get(evtid);
if (index == INVALID_CONFIG_INDEX)
return -EINVAL;
if (rdtgrp->mon.cntr_id[index] == MON_CNTR_UNSET) {
ret = rdtgroup_alloc_cntr(rdtgrp, index);
if (ret < 0)
goto out_done;
}
/* Update the state on all domains if d == NULL */
if (d == NULL) {
ret = rdtgroup_assign_cntr(rdtgrp, evtid);
} else {
ret = resctrl_arch_assign_cntr(d, evtid, rdtgrp->mon.rmid,
rdtgrp->mon.cntr_id[index],
rdtgrp->closid, 1);
if (!ret)
set_bit(rdtgrp->mon.cntr_id[index], d->mbm_cntr_map);
}
out_done:
return ret;
}
/*
* Update the unassign state for the domain.
*
* Free the counter if it is unassigned on all the domains else just
* update the unassign state
*/
static int rdtgroup_unassign_update(struct rdtgroup *rdtgrp, enum resctrl_event_id evtid,
struct rdt_domain *d)
{
struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_L3);
int ret = 0, index;
index = mon_event_config_index_get(evtid);
if (index == INVALID_CONFIG_INDEX)
return -EINVAL;
if (rdtgrp->mon.cntr_id[index] == MON_CNTR_UNSET)
goto out_done;
if (d == NULL) {
ret = rdtgroup_unassign_cntr(rdtgrp, evtid);
} else {
ret = resctrl_arch_assign_cntr(d, evtid, rdtgrp->mon.rmid,
rdtgrp->mon.cntr_id[index],
rdtgrp->closid, 0);
if (!ret) {
clear_bit(rdtgrp->mon.cntr_id[index], d->mbm_cntr_map);
rdtgroup_free_cntr(r, rdtgrp, index);
}
}
out_done:
return ret;
}
static int rdtgroup_str_to_mon_state(char *flag)
{
int i, mon_state = 0;
for (i = 0; i < strlen(flag); i++) {
switch (*(flag + i)) {
case 't':
mon_state |= ASSIGN_TOTAL;
break;
case 'l':
mon_state |= ASSIGN_LOCAL;
break;
case '_':
mon_state = ASSIGN_NONE;
break;
default:
break;
}
}
return mon_state;
}
static struct rdtgroup *rdtgroup_find_grp(enum rdt_group_type rtype, char *p_grp, char *c_grp)
{
struct rdtgroup *rdtg, *crg;
if (rtype == RDTCTRL_GROUP && *p_grp == '\0') {
return &rdtgroup_default;
} else if (rtype == RDTCTRL_GROUP) {
list_for_each_entry(rdtg, &rdt_all_groups, rdtgroup_list)
if (!strcmp(p_grp, rdtg->kn->name))
return rdtg;
} else if (rtype == RDTMON_GROUP) {
list_for_each_entry(rdtg, &rdt_all_groups, rdtgroup_list) {
if (!strcmp(p_grp, rdtg->kn->name)) {
list_for_each_entry(crg, &rdtg->mon.crdtgrp_list,
mon.crdtgrp_list) {
if (!strcmp(c_grp, crg->kn->name))
return crg;
}
}
}
}
return NULL;
}
static int rdtgroup_process_flags(struct rdt_resource *r,
enum rdt_group_type rtype,
char *p_grp, char *c_grp, char *tok)
{
int op, mon_state, assign_state, unassign_state;
char *dom_str, *id_str, *op_str;
struct rdt_domain *d;
struct rdtgroup *rdtgrp;
unsigned long dom_id;
int ret, found = 0;
rdtgrp = rdtgroup_find_grp(rtype, p_grp, c_grp);
if (!rdtgrp) {
rdt_last_cmd_puts("Not a valid resctrl group\n");
return -EINVAL;
}
next:
if (!tok || tok[0] == '\0')
return 0;
/* Start processing the strings for each domain */
dom_str = strim(strsep(&tok, ";"));
op_str = strpbrk(dom_str, "=+-");
if (op_str) {
op = *op_str;
} else {
rdt_last_cmd_puts("Missing operation =, +, -, _ character\n");
return -EINVAL;
}
id_str = strsep(&dom_str, "=+-");
/* Check for domain id '*' which means all domains */
if (id_str && *id_str == '*') {
d = NULL;
goto check_state;
} else if (!id_str || kstrtoul(id_str, 10, &dom_id)) {
rdt_last_cmd_puts("Missing domain id\n");
return -EINVAL;
}
/* Verify if the dom_id is valid */
list_for_each_entry(d, &r->domains, list) {
if (d->id == dom_id) {
found = 1;
break;
}
}
if (!found) {
rdt_last_cmd_printf("Invalid domain id %ld\n", dom_id);
return -EINVAL;
}
check_state:
mon_state = rdtgroup_str_to_mon_state(dom_str);
assign_state = 0;
unassign_state = 0;
switch (op) {
case '+':
if (mon_state == ASSIGN_NONE) {
rdt_last_cmd_puts("Invalid assign opcode\n");
goto out_fail;
}
assign_state = mon_state;
break;
case '-':
if (mon_state == ASSIGN_NONE) {
rdt_last_cmd_puts("Invalid assign opcode\n");
goto out_fail;
}
unassign_state = mon_state;
break;
case '=':
assign_state = mon_state;
unassign_state = (ASSIGN_TOTAL | ASSIGN_LOCAL) & ~assign_state;
break;
default:
break;
}
if (assign_state & ASSIGN_TOTAL) {
ret = rdtgroup_assign_update(rdtgrp, QOS_L3_MBM_TOTAL_EVENT_ID, d);
if (ret)
goto out_fail;
}
if (assign_state & ASSIGN_LOCAL) {
ret = rdtgroup_assign_update(rdtgrp, QOS_L3_MBM_LOCAL_EVENT_ID, d);
if (ret)
goto out_fail;
}
if (unassign_state & ASSIGN_TOTAL) {
ret = rdtgroup_unassign_update(rdtgrp, QOS_L3_MBM_TOTAL_EVENT_ID, d);
if (ret)
goto out_fail;
}
if (unassign_state & ASSIGN_LOCAL) {
ret = rdtgroup_unassign_update(rdtgrp, QOS_L3_MBM_LOCAL_EVENT_ID, d);
if (ret)
goto out_fail;
}
goto next;
out_fail:
return -EINVAL;
}
static ssize_t rdtgroup_mbm_control_write(struct kernfs_open_file *of,
char *buf, size_t nbytes,
loff_t off)
{
struct rdt_resource *r = of->kn->parent->priv;
char *token, *cmon_grp, *mon_grp;
int ret;
if (!resctrl_arch_get_abmc_enabled())
return -EINVAL;
/* Valid input requires a trailing newline */
if (nbytes == 0 || buf[nbytes - 1] != '\n')
return -EINVAL;
buf[nbytes - 1] = '\0';
cpus_read_lock();
mutex_lock(&rdtgroup_mutex);
rdt_last_cmd_clear();
while ((token = strsep(&buf, "\n")) != NULL) {
if (strstr(token, "//")) {
/*
* The CTRL_MON group processing:
* default CTRL_MON group: "//<flags>"
* non-default CTRL_MON group: "<CTRL_MON group>//flags"
* The CTRL_MON group will be empty string if it is a
* default group.
*/
cmon_grp = strsep(&token, "//");
/*
* strsep returns empty string for contiguous delimiters.
* Make sure check for two consecutive delimiters and
* advance the token.
*/
mon_grp = strsep(&token, "//");
if (*mon_grp != '\0') {
rdt_last_cmd_printf("Invalid CTRL_MON group format %s\n", token);
ret = -EINVAL;
break;
}
ret = rdtgroup_process_flags(r, RDTCTRL_GROUP, cmon_grp, mon_grp, token);
if (ret)
break;
} else if (strstr(token, "/")) {
/*
* MON group processing:
* MON_GROUP inside default CTRL_MON group: "/<MON group>/<flags>"
* MON_GROUP within CTRL_MON group: "<CTRL_MON group>/<MON group>/<flags>"
*/
cmon_grp = strsep(&token, "/");
/* Extract the MON_GROUP. It cannot be empty string */
mon_grp = strsep(&token, "/");
if (*mon_grp == '\0') {
rdt_last_cmd_printf("Invalid MON_GROUP format %s\n", token);
ret = -EINVAL;
break;
}
ret = rdtgroup_process_flags(r, RDTMON_GROUP, cmon_grp, mon_grp, token);
if (ret)
break;
}
}
mutex_unlock(&rdtgroup_mutex);
cpus_read_unlock();
return ret ?: nbytes;
}
#ifdef CONFIG_PROC_CPU_RESCTRL
/*
@ -2136,9 +2448,10 @@ static struct rftype res_common_files[] = {
},
{
.name = "mbm_control",
.mode = 0444,
.mode = 0644,
.kf_ops = &rdtgroup_kf_single_ops,
.seq_show = rdtgroup_mbm_control_show,
.write = rdtgroup_mbm_control_write,
},
{
.name = "cpus_list",

View File

@ -74,4 +74,11 @@ enum resctrl_event_id {
#define RESCTRL_MAX_EVENT_NUM 4
#define INVALID_CONFIG_VALUE U32_MAX
/*
* Assignment flags for ABMC feature
*/
#define ASSIGN_NONE 0
#define ASSIGN_TOTAL BIT(QOS_L3_MBM_TOTAL_EVENT_ID)
#define ASSIGN_LOCAL BIT(QOS_L3_MBM_LOCAL_EVENT_ID)
#endif /* __LINUX_RESCTRL_TYPES_H */