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:
parent
51c5ecfd85
commit
1d4487d167
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue