1026 lines
24 KiB
C
1026 lines
24 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Common code for ARM v8 MPAM
|
|
* - allocation and monitor management
|
|
*
|
|
* Copyright (C) 2018-2019 Huawei Technologies Co., Ltd
|
|
*
|
|
* Author: Xie XiuQi <xiexiuqi@huawei.com>
|
|
*
|
|
* Code was partially borrowed from arch/x86/kernel/cpu/intel_rdt*.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* More information about MPAM be found in the Arm Architecture Reference
|
|
* Manual.
|
|
*
|
|
* https://static.docs.arm.com/ddi0598/a/DDI0598_MPAM_supp_armv8a.pdf
|
|
*/
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
#include <linux/kernfs.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/slab.h>
|
|
#include <asm/mpam.h>
|
|
|
|
#include "mpam_resource.h"
|
|
#include "mpam_internal.h"
|
|
|
|
/* schemata content list */
|
|
LIST_HEAD(resctrl_all_schema);
|
|
|
|
/* Init schemata content */
|
|
static int add_schema(enum resctrl_conf_type t, struct resctrl_resource *r)
|
|
{
|
|
int ret = 0;
|
|
char *suffix = "";
|
|
struct resctrl_schema *s;
|
|
struct raw_resctrl_resource *rr;
|
|
struct resctrl_schema_ctrl *sc, *tmp;
|
|
enum resctrl_ctrl_type type;
|
|
|
|
s = kzalloc(sizeof(*s), GFP_KERNEL);
|
|
if (!s)
|
|
return -ENOMEM;
|
|
|
|
s->res = r;
|
|
s->conf_type = t;
|
|
|
|
/*
|
|
* code and data is separated for resources LxCache but
|
|
* not for MB(Memory Bandwidth), it's necessary to set
|
|
* cdp_mc_both to let resctrl know operating the two closid/
|
|
* monitor simultaneously when configuring/monitoring.
|
|
*/
|
|
if (is_resctrl_cdp_enabled())
|
|
s->cdp_mc_both = !r->cdp_enable;
|
|
|
|
switch (t) {
|
|
case CDP_CODE:
|
|
suffix = "CODE";
|
|
break;
|
|
case CDP_DATA:
|
|
suffix = "DATA";
|
|
break;
|
|
case CDP_BOTH:
|
|
suffix = "";
|
|
break;
|
|
default:
|
|
kfree(s);
|
|
return -EINVAL;
|
|
}
|
|
|
|
WARN_ON_ONCE(strlen(r->name) + strlen(suffix) + 1 > RESCTRL_NAME_LEN);
|
|
snprintf(s->name, sizeof(s->name), "%s%s", r->name, suffix);
|
|
|
|
INIT_LIST_HEAD(&s->list);
|
|
list_add_tail(&s->list, &resctrl_all_schema);
|
|
|
|
/*
|
|
* Initialize extension ctrl type with MPAM capabilities,
|
|
* e.g. priority/hardlimit.
|
|
*/
|
|
rr = r->res;
|
|
INIT_LIST_HEAD(&s->schema_ctrl_list);
|
|
for_each_extend_ctrl_type(type) {
|
|
struct resctrl_ctrl_feature *feature =
|
|
&rr->ctrl_features[type];
|
|
|
|
if (!rr->ctrl_features[type].enabled ||
|
|
!rr->ctrl_features[type].max_wd)
|
|
continue;
|
|
|
|
sc = kzalloc(sizeof(*sc), GFP_KERNEL);
|
|
if (!sc) {
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
sc->ctrl_type = type;
|
|
|
|
WARN_ON_ONCE(strlen(r->name) + strlen(suffix) +
|
|
strlen(feature->ctrl_suffix) + 1 > RESCTRL_NAME_LEN);
|
|
snprintf(sc->name, sizeof(sc->name), "%s%s%s", r->name,
|
|
suffix, feature->ctrl_suffix);
|
|
|
|
list_add_tail(&sc->list, &s->schema_ctrl_list);
|
|
}
|
|
|
|
return 0;
|
|
|
|
err:
|
|
list_for_each_entry_safe(sc, tmp, &s->schema_ctrl_list, list) {
|
|
list_del(&sc->list);
|
|
kfree(sc);
|
|
}
|
|
list_del(&s->list);
|
|
kfree(s);
|
|
return ret;
|
|
}
|
|
|
|
int schemata_list_init(void)
|
|
{
|
|
int ret;
|
|
struct mpam_resctrl_res *res;
|
|
struct resctrl_resource *r;
|
|
|
|
for_each_supported_resctrl_exports(res) {
|
|
r = &res->resctrl_res;
|
|
if (!r || !r->alloc_capable)
|
|
continue;
|
|
|
|
if (r->cdp_enable) {
|
|
ret = add_schema(CDP_CODE, r);
|
|
ret |= add_schema(CDP_DATA, r);
|
|
} else {
|
|
ret = add_schema(CDP_BOTH, r);
|
|
}
|
|
if (ret)
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* During resctrl_kill_sb(), the mba_sc state is reset before
|
|
* schemata_list_destroy() is called: unconditionally try to free the
|
|
* array.
|
|
*/
|
|
void schemata_list_destroy(void)
|
|
{
|
|
struct resctrl_schema *s, *tmp;
|
|
struct resctrl_schema_ctrl *sc, *sc_tmp;
|
|
|
|
list_for_each_entry_safe(s, tmp, &resctrl_all_schema, list) {
|
|
list_for_each_entry_safe(sc, sc_tmp, &s->schema_ctrl_list, list) {
|
|
list_del(&sc->list);
|
|
kfree(sc);
|
|
}
|
|
list_del(&s->list);
|
|
kfree(s);
|
|
}
|
|
}
|
|
|
|
static void
|
|
resctrl_dom_ctrl_config(bool cdp_both_ctrl, struct resctrl_resource *r,
|
|
struct rdt_domain *dom, struct msr_param *para)
|
|
{
|
|
struct raw_resctrl_resource *rr;
|
|
|
|
rr = r->res;
|
|
rr->msr_update(r, dom, para);
|
|
|
|
if (cdp_both_ctrl) {
|
|
resctrl_cdp_mpamid_map_val(para->closid->reqpartid, CDP_DATA,
|
|
para->closid->reqpartid);
|
|
rr->msr_update(r, dom, para);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resync resctrl group domain ctrls, use rdtgrp->resync to indicate
|
|
* whether the resync procedure will be called. When resync==1, all
|
|
* domain ctrls of this group be synchronized again. This happens
|
|
* when rmid of this group is changed, and all configurations need to
|
|
* be remapped again accordingly.
|
|
*/
|
|
static void resctrl_group_resync_domain_ctrls(struct rdtgroup *rdtgrp,
|
|
struct resctrl_resource *r, struct rdt_domain *dom)
|
|
{
|
|
int i;
|
|
int staged_start, staged_end;
|
|
struct resctrl_staged_config *cfg;
|
|
struct sd_closid closid;
|
|
struct list_head *head;
|
|
struct rdtgroup *entry;
|
|
struct msr_param para;
|
|
bool cdp_both_ctrl;
|
|
|
|
cfg = dom->staged_cfg;
|
|
para.closid = &closid;
|
|
|
|
staged_start = (r->cdp_enable) ? CDP_CODE : CDP_BOTH;
|
|
staged_end = (r->cdp_enable) ? CDP_DATA : CDP_BOTH;
|
|
|
|
for (i = staged_start; i <= staged_end; i++) {
|
|
cdp_both_ctrl = cfg[i].cdp_both_ctrl;
|
|
/*
|
|
* for ctrl group configuration, hw_closid of cfg[i] equals
|
|
* to rdtgrp->closid.intpartid.
|
|
*/
|
|
closid.intpartid = hw_closid_val(cfg[i].hw_closid);
|
|
resctrl_cdp_mpamid_map_val(rdtgrp->closid.reqpartid,
|
|
cfg[i].conf_type, closid.reqpartid);
|
|
resctrl_dom_ctrl_config(cdp_both_ctrl, r, dom, ¶);
|
|
|
|
/*
|
|
* we should synchronize all child mon groups'
|
|
* configuration from this ctrl rdtgrp
|
|
*/
|
|
head = &rdtgrp->mon.crdtgrp_list;
|
|
list_for_each_entry(entry, head, mon.crdtgrp_list) {
|
|
resctrl_cdp_mpamid_map_val(entry->closid.reqpartid,
|
|
cfg[i].conf_type, closid.reqpartid);
|
|
resctrl_dom_ctrl_config(cdp_both_ctrl, r, dom, ¶);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void resctrl_group_update_domain_ctrls(struct rdtgroup *rdtgrp,
|
|
struct resctrl_resource *r, struct rdt_domain *dom)
|
|
{
|
|
int i;
|
|
struct resctrl_staged_config *cfg;
|
|
enum resctrl_ctrl_type type;
|
|
struct sd_closid closid;
|
|
struct list_head *head;
|
|
struct rdtgroup *entry;
|
|
struct msr_param para;
|
|
bool update_on, cdp_both_ctrl;
|
|
|
|
cfg = dom->staged_cfg;
|
|
para.closid = &closid;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(dom->staged_cfg); i++) {
|
|
if (!cfg[i].have_new_ctrl)
|
|
continue;
|
|
update_on = false;
|
|
cdp_both_ctrl = cfg[i].cdp_both_ctrl;
|
|
/*
|
|
* for ctrl group configuration, hw_closid of cfg[i] equals
|
|
* to rdtgrp->closid.intpartid.
|
|
*/
|
|
closid.intpartid = hw_closid_val(cfg[i].hw_closid);
|
|
for_each_ctrl_type(type) {
|
|
/* if ctrl group's config has changed, refresh it first. */
|
|
if (dom->ctrl_val[closid.intpartid] != cfg[i].new_ctrl) {
|
|
/*
|
|
* duplicate ctrl group's configuration indexed
|
|
* by intpartid from domain ctrl_val array.
|
|
*/
|
|
resctrl_cdp_mpamid_map_val(rdtgrp->closid.reqpartid,
|
|
cfg[i].conf_type, closid.reqpartid);
|
|
|
|
dom->ctrl_val[type][closid.intpartid] =
|
|
cfg[i].new_ctrl[type];
|
|
dom->have_new_ctrl = true;
|
|
update_on = true;
|
|
}
|
|
}
|
|
if (update_on)
|
|
resctrl_dom_ctrl_config(cdp_both_ctrl, r, dom, ¶);
|
|
|
|
/*
|
|
* we should synchronize all child mon groups'
|
|
* configuration from this ctrl rdtgrp
|
|
*/
|
|
head = &rdtgrp->mon.crdtgrp_list;
|
|
list_for_each_entry(entry, head, mon.crdtgrp_list) {
|
|
resctrl_cdp_mpamid_map_val(entry->closid.reqpartid,
|
|
cfg[i].conf_type, closid.reqpartid);
|
|
resctrl_dom_ctrl_config(cdp_both_ctrl, r, dom, ¶);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int resctrl_group_update_domains(struct rdtgroup *rdtgrp,
|
|
struct resctrl_resource *r)
|
|
{
|
|
struct rdt_domain *d;
|
|
|
|
list_for_each_entry(d, &r->domains, list) {
|
|
if (rdtgrp->resync)
|
|
resctrl_group_resync_domain_ctrls(rdtgrp, r, d);
|
|
else
|
|
resctrl_group_update_domain_ctrls(rdtgrp, r, d);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* For each domain in this resource we expect to find a series of:
|
|
* id=mask
|
|
* separated by ";". The "id" is in decimal, and must match one of
|
|
* the "id"s for this resource.
|
|
*/
|
|
static int
|
|
parse_line(char *line, struct resctrl_resource *r,
|
|
enum resctrl_conf_type conf_type,
|
|
enum resctrl_ctrl_type ctrl_type, u32 closid)
|
|
{
|
|
struct raw_resctrl_resource *rr = r->res;
|
|
char *dom = NULL;
|
|
char *id;
|
|
struct rdt_domain *d;
|
|
unsigned long dom_id;
|
|
hw_closid_t hw_closid;
|
|
|
|
if (!rr->ctrl_features[ctrl_type].enabled)
|
|
return -EINVAL;
|
|
|
|
next:
|
|
if (!line || line[0] == '\0')
|
|
return 0;
|
|
dom = strsep(&line, ";");
|
|
id = strsep(&dom, "=");
|
|
if (!dom || kstrtoul(id, 10, &dom_id)) {
|
|
rdt_last_cmd_puts("Missing '=' or non-numeric domain\n");
|
|
return -EINVAL;
|
|
}
|
|
dom = strim(dom);
|
|
list_for_each_entry(d, &r->domains, list) {
|
|
if (d->id == dom_id) {
|
|
resctrl_cdp_mpamid_map(closid, conf_type, hw_closid);
|
|
if (rr->parse_ctrlval(dom, r,
|
|
&d->staged_cfg[conf_type], ctrl_type))
|
|
return -EINVAL;
|
|
d->staged_cfg[conf_type].hw_closid = hw_closid;
|
|
d->staged_cfg[conf_type].conf_type = conf_type;
|
|
d->staged_cfg[conf_type].ctrl_type = ctrl_type;
|
|
goto next;
|
|
}
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int
|
|
resctrl_group_parse_schema_resource(char *resname, char *tok, u32 closid)
|
|
{
|
|
struct resctrl_resource *r;
|
|
struct resctrl_schema *s;
|
|
enum resctrl_conf_type t;
|
|
struct resctrl_schema_ctrl *sc;
|
|
|
|
list_for_each_entry(s, &resctrl_all_schema, list) {
|
|
r = s->res;
|
|
|
|
if (!r)
|
|
continue;
|
|
|
|
if (r->alloc_enabled) {
|
|
if (closid >= mpam_sysprops_num_partid())
|
|
continue;
|
|
t = conf_name_to_conf_type(s->name);
|
|
if (!strcmp(resname, s->name))
|
|
return parse_line(tok, r, t,
|
|
SCHEMA_COMM, closid);
|
|
|
|
list_for_each_entry(sc, &s->schema_ctrl_list, list) {
|
|
if (!strcmp(resname, sc->name))
|
|
return parse_line(tok, r, t,
|
|
sc->ctrl_type,
|
|
closid);
|
|
}
|
|
}
|
|
}
|
|
rdt_last_cmd_printf("unknown/unsupported resource name '%s'\n", resname);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
ssize_t resctrl_group_schemata_write(struct kernfs_open_file *of,
|
|
char *buf, size_t nbytes, loff_t off)
|
|
{
|
|
struct rdtgroup *rdtgrp;
|
|
struct rdt_domain *dom;
|
|
struct resctrl_resource *r;
|
|
struct mpam_resctrl_res *res;
|
|
enum resctrl_conf_type conf_type;
|
|
struct resctrl_staged_config *cfg;
|
|
char *tok, *resname;
|
|
u32 closid;
|
|
int ret = 0;
|
|
|
|
/* Valid input requires a trailing newline */
|
|
if (nbytes == 0 || buf[nbytes - 1] != '\n')
|
|
return -EINVAL;
|
|
buf[nbytes - 1] = '\0';
|
|
|
|
rdtgrp = resctrl_group_kn_lock_live(of->kn);
|
|
if (!rdtgrp) {
|
|
resctrl_group_kn_unlock(of->kn);
|
|
return -ENOENT;
|
|
}
|
|
|
|
rdt_last_cmd_clear();
|
|
|
|
closid = rdtgrp->closid.intpartid;
|
|
|
|
for_each_supported_resctrl_exports(res) {
|
|
r = &res->resctrl_res;
|
|
|
|
if (r->alloc_enabled) {
|
|
list_for_each_entry(dom, &r->domains, list) {
|
|
dom->have_new_ctrl = false;
|
|
for_each_conf_type(conf_type) {
|
|
cfg = &dom->staged_cfg[conf_type];
|
|
cfg->have_new_ctrl = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
while ((tok = strsep(&buf, "\n")) != NULL) {
|
|
resname = strim(strsep(&tok, ":"));
|
|
if (!tok) {
|
|
rdt_last_cmd_puts("Missing ':'\n");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
if (tok[0] == '\0') {
|
|
rdt_last_cmd_printf("Missing '%s' value\n", resname);
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
ret = resctrl_group_parse_schema_resource(resname, tok, closid);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
ret = resctrl_update_groups_config(rdtgrp);
|
|
out:
|
|
resctrl_group_kn_unlock(of->kn);
|
|
return ret ?: nbytes;
|
|
}
|
|
|
|
/**
|
|
* MPAM resources such as L2 may have too many domains for arm64,
|
|
* at this time we should rearrange this display for brevity and
|
|
* harmonious interaction.
|
|
*
|
|
* Before rearrangement: L2:0=ff;1=ff;2=fc;3=ff;4=f;....;255=ff
|
|
* After rearrangement: L2:S;2=fc;S;4=f;S
|
|
* Those continuous fully sharable domains will be combined into
|
|
* a single "S" simply.
|
|
*/
|
|
static void show_doms(struct seq_file *s, struct resctrl_resource *r,
|
|
char *schema_name, enum resctrl_ctrl_type type,
|
|
struct sd_closid *closid)
|
|
{
|
|
struct raw_resctrl_resource *rr = r->res;
|
|
struct rdt_domain *dom;
|
|
struct msr_param para;
|
|
bool sep = false;
|
|
bool rg = false;
|
|
bool prev_auto_fill = false;
|
|
u32 reg_val;
|
|
|
|
if (!rr->ctrl_features[type].enabled)
|
|
return;
|
|
|
|
para.closid = closid;
|
|
para.type = type;
|
|
|
|
if (r->dom_num > RESCTRL_SHOW_DOM_MAX_NUM)
|
|
rg = true;
|
|
|
|
seq_printf(s, "%*s:", max_name_width, schema_name);
|
|
list_for_each_entry(dom, &r->domains, list) {
|
|
reg_val = rr->msr_read(r, dom, ¶);
|
|
|
|
if (reg_val == rr->ctrl_features[SCHEMA_COMM].default_ctrl &&
|
|
rg && prev_auto_fill == true)
|
|
continue;
|
|
|
|
if (sep)
|
|
seq_puts(s, ";");
|
|
if (rg && reg_val == rr->ctrl_features[SCHEMA_COMM].default_ctrl) {
|
|
prev_auto_fill = true;
|
|
seq_puts(s, "S");
|
|
} else {
|
|
seq_printf(s, rr->format_str, dom->id,
|
|
max_data_width, reg_val);
|
|
}
|
|
sep = true;
|
|
}
|
|
seq_puts(s, "\n");
|
|
}
|
|
|
|
int resctrl_group_schemata_show(struct kernfs_open_file *of,
|
|
struct seq_file *s, void *v)
|
|
{
|
|
struct rdtgroup *rdtgrp;
|
|
struct resctrl_resource *r;
|
|
struct resctrl_schema *rs;
|
|
int ret = 0;
|
|
struct sd_closid closid;
|
|
struct resctrl_schema_ctrl *sc;
|
|
|
|
rdtgrp = resctrl_group_kn_lock_live(of->kn);
|
|
if (rdtgrp) {
|
|
list_for_each_entry(rs, &resctrl_all_schema, list) {
|
|
r = rs->res;
|
|
if (!r)
|
|
continue;
|
|
if (r->alloc_enabled) {
|
|
resctrl_cdp_mpamid_map_val(rdtgrp->closid.intpartid,
|
|
rs->conf_type, closid.intpartid);
|
|
|
|
resctrl_cdp_mpamid_map_val(rdtgrp->closid.reqpartid,
|
|
rs->conf_type, closid.reqpartid);
|
|
|
|
show_doms(s, r, rs->name, SCHEMA_COMM, &closid);
|
|
list_for_each_entry(sc, &rs->schema_ctrl_list, list) {
|
|
show_doms(s, r, sc->name, sc->ctrl_type, &closid);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
ret = -ENOENT;
|
|
}
|
|
resctrl_group_kn_unlock(of->kn);
|
|
return ret;
|
|
}
|
|
|
|
static inline char *kernfs_node_name(struct kernfs_open_file *of)
|
|
{
|
|
return (char *)(of ? of->kn->name : NULL);
|
|
}
|
|
|
|
static inline void put_resource_name(char *res)
|
|
{
|
|
kfree(res);
|
|
}
|
|
|
|
/*
|
|
* pick resource name from mon data name
|
|
* eg. from mon_L3_01 we got L3
|
|
* */
|
|
static inline char *get_resource_name(char *name)
|
|
{
|
|
char *s, *p, *res;
|
|
|
|
if (!name)
|
|
return NULL;
|
|
|
|
s = name + 4; /* skip "mon_" prefix */
|
|
p = strrchr(name, '_');
|
|
res = kmemdup_nul(s, p - s, GFP_KERNEL);
|
|
if (!res)
|
|
res = NULL;
|
|
|
|
return res;
|
|
}
|
|
|
|
static u64 resctrl_dom_mon_data(struct resctrl_resource *r,
|
|
struct rdt_domain *d, void *md_priv)
|
|
{
|
|
u64 ret;
|
|
union mon_data_bits md;
|
|
struct raw_resctrl_resource *rr;
|
|
|
|
md.priv = md_priv;
|
|
rr = r->res;
|
|
ret = rr->mon_read(d, md.priv);
|
|
if (md.u.cdp_both_mon) {
|
|
resctrl_cdp_mpamid_map_val(md.u.partid, CDP_DATA, md.u.partid);
|
|
ret += rr->mon_read(d, md.priv);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int resctrl_group_mondata_show(struct seq_file *m, void *arg)
|
|
{
|
|
struct kernfs_open_file *of = m->private;
|
|
struct rdtgroup *rdtgrp;
|
|
struct rdt_domain *d;
|
|
struct resctrl_resource *r;
|
|
union mon_data_bits md;
|
|
int ret = 0;
|
|
char *resname = get_resource_name(kernfs_node_name(of));
|
|
u64 usage;
|
|
int pmg;
|
|
|
|
if (!resname)
|
|
return -ENOMEM;
|
|
|
|
rdtgrp = resctrl_group_kn_lock_live(of->kn);
|
|
if (!rdtgrp) {
|
|
ret = -ENOENT;
|
|
goto out;
|
|
}
|
|
|
|
md.priv = of->kn->priv;
|
|
|
|
r = mpam_resctrl_get_resource(md.u.rid);
|
|
|
|
/* show monitor data */
|
|
d = mpam_find_domain(r, md.u.domid, NULL);
|
|
if (IS_ERR_OR_NULL(d)) {
|
|
pr_warn("Could't find domain id %d\n", md.u.domid);
|
|
ret = -ENOENT;
|
|
goto out;
|
|
}
|
|
|
|
usage = resctrl_dom_mon_data(r, d, md.priv);
|
|
|
|
/*
|
|
* if this rdtgroup is ctrlmon group, also collect it's
|
|
* mon groups' monitor data.
|
|
*/
|
|
if (rdtgrp->type == RDTCTRL_GROUP) {
|
|
struct list_head *head;
|
|
struct rdtgroup *entry;
|
|
hw_closid_t hw_closid;
|
|
enum resctrl_conf_type type = CDP_CODE;
|
|
|
|
resctrl_cdp_mpamid_map(rdtgrp->closid.reqpartid,
|
|
CDP_CODE, hw_closid);
|
|
/* CDP_CODE share the same closid with CDP_BOTH */
|
|
if (md.u.partid != hw_closid_val(hw_closid))
|
|
type = CDP_DATA;
|
|
|
|
head = &rdtgrp->mon.crdtgrp_list;
|
|
list_for_each_entry(entry, head, mon.crdtgrp_list) {
|
|
resctrl_cdp_mpamid_map_val(entry->closid.reqpartid,
|
|
type, md.u.partid);
|
|
|
|
ret = mpam_rmid_to_partid_pmg(entry->mon.rmid,
|
|
NULL, &pmg);
|
|
if (ret)
|
|
return ret;
|
|
|
|
md.u.pmg = pmg;
|
|
resctrl_cdp_mpamid_map_val(get_rmid_mon(entry->mon.rmid,
|
|
r->rid), type, md.u.mon);
|
|
|
|
usage += resctrl_dom_mon_data(r, d, md.priv);
|
|
}
|
|
}
|
|
|
|
seq_printf(m, "%llu\n", usage);
|
|
|
|
out:
|
|
put_resource_name(resname);
|
|
resctrl_group_kn_unlock(of->kn);
|
|
return ret;
|
|
}
|
|
|
|
static struct kernfs_ops kf_mondata_ops = {
|
|
.atomic_write_len = PAGE_SIZE,
|
|
.seq_show = resctrl_group_mondata_show,
|
|
};
|
|
|
|
/* set uid and gid of resctrl_group dirs and files to that of the creator */
|
|
static int resctrl_group_kn_set_ugid(struct kernfs_node *kn)
|
|
{
|
|
struct iattr iattr = { .ia_valid = ATTR_UID | ATTR_GID,
|
|
.ia_uid = current_fsuid(),
|
|
.ia_gid = current_fsgid(), };
|
|
|
|
if (uid_eq(iattr.ia_uid, GLOBAL_ROOT_UID) &&
|
|
gid_eq(iattr.ia_gid, GLOBAL_ROOT_GID))
|
|
return 0;
|
|
|
|
return kernfs_setattr(kn, &iattr);
|
|
}
|
|
|
|
static int resctrl_mkdir_mondata_dom(struct kernfs_node *parent_kn,
|
|
struct rdt_domain *d, struct resctrl_schema *s,
|
|
struct resctrl_group *prgrp)
|
|
|
|
{
|
|
struct resctrl_resource *r;
|
|
struct raw_resctrl_resource *rr;
|
|
union mon_data_bits md;
|
|
struct kernfs_node *kn;
|
|
char name[32];
|
|
int ret = 0;
|
|
int pmg;
|
|
|
|
r = s->res;
|
|
rr = r->res;
|
|
|
|
md.u.rid = r->rid;
|
|
md.u.domid = d->id;
|
|
/* monitoring use reqpartid (reqpartid) */
|
|
resctrl_cdp_mpamid_map_val(prgrp->closid.reqpartid, s->conf_type,
|
|
md.u.partid);
|
|
resctrl_cdp_mpamid_map_val(get_rmid_mon(prgrp->mon.rmid, r->rid),
|
|
s->conf_type, md.u.mon);
|
|
|
|
ret = mpam_rmid_to_partid_pmg(prgrp->mon.rmid, NULL, &pmg);
|
|
if (ret)
|
|
return ret;
|
|
md.u.pmg = pmg;
|
|
|
|
md.u.cdp_both_mon = s->cdp_mc_both;
|
|
|
|
if (!parent_kn) {
|
|
pr_err("%s: error parent_kn null\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
snprintf(name, sizeof(name), "mon_%s_%02d", s->name, d->id);
|
|
kn = kernfs_find_and_get(parent_kn, name);
|
|
if (!kn) {
|
|
kn = __kernfs_create_file(parent_kn, name, 0444,
|
|
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, 0,
|
|
&kf_mondata_ops, md.priv, NULL, NULL);
|
|
if (IS_ERR(kn))
|
|
return PTR_ERR(kn);
|
|
|
|
ret = resctrl_group_kn_set_ugid(kn);
|
|
if (ret) {
|
|
pr_info("%s: create name %s, error ret %d\n",
|
|
__func__, name, ret);
|
|
kernfs_remove(kn);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
kn->priv = md.priv;
|
|
|
|
/* Could we remove the MATCH_* param ? */
|
|
rr->mon_write(d, md.priv);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int resctrl_mkdir_mondata_subdir_alldom(struct kernfs_node *parent_kn,
|
|
struct resctrl_schema *s, struct resctrl_group *prgrp)
|
|
{
|
|
struct resctrl_resource *r;
|
|
struct rdt_domain *dom;
|
|
int ret;
|
|
|
|
r = s->res;
|
|
list_for_each_entry(dom, &r->domains, list) {
|
|
ret = resctrl_mkdir_mondata_dom(parent_kn, dom, s, prgrp);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int resctrl_mkdir_mondata_all_subdir(struct kernfs_node *parent_kn,
|
|
struct resctrl_group *prgrp)
|
|
{
|
|
struct resctrl_schema *s;
|
|
struct resctrl_resource *r;
|
|
int ret;
|
|
|
|
/*
|
|
* Create the subdirectories for each domain. Note that all events
|
|
* in a domain like L3 are grouped into a resource whose domain is L3
|
|
*/
|
|
list_for_each_entry(s, &resctrl_all_schema, list) {
|
|
r = s->res;
|
|
|
|
if (r->mon_enabled) {
|
|
struct raw_resctrl_resource *rr;
|
|
|
|
rr = r->res;
|
|
|
|
ret = resctrl_mkdir_mondata_subdir_alldom(parent_kn,
|
|
s, prgrp);
|
|
if (ret)
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int resctrl_group_mkdir_info_resdir(struct resctrl_resource *r,
|
|
char *name,unsigned long fflags, struct kernfs_node *kn_info)
|
|
{
|
|
struct kernfs_node *kn_subdir;
|
|
int ret;
|
|
|
|
kn_subdir = kernfs_create_dir(kn_info, name,
|
|
kn_info->mode, r);
|
|
if (IS_ERR(kn_subdir))
|
|
return PTR_ERR(kn_subdir);
|
|
|
|
kernfs_get(kn_subdir);
|
|
ret = resctrl_group_kn_set_ugid(kn_subdir);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = resctrl_group_add_files(kn_subdir, fflags);
|
|
if (!ret)
|
|
kernfs_activate(kn_subdir);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int resctrl_group_create_info_dir(struct kernfs_node *parent_kn,
|
|
struct kernfs_node **kn_info)
|
|
{
|
|
struct resctrl_schema *s;
|
|
struct resctrl_resource *r;
|
|
struct raw_resctrl_resource *rr;
|
|
unsigned long fflags;
|
|
char name[32];
|
|
int ret;
|
|
|
|
/* create the directory */
|
|
*kn_info = kernfs_create_dir(parent_kn, "info", parent_kn->mode, NULL);
|
|
if (IS_ERR(*kn_info))
|
|
return PTR_ERR(*kn_info);
|
|
kernfs_get(*kn_info);
|
|
|
|
ret = resctrl_group_add_files(*kn_info, RF_TOP_INFO);
|
|
if (ret)
|
|
goto out_destroy;
|
|
|
|
list_for_each_entry(s, &resctrl_all_schema, list) {
|
|
r = s->res;
|
|
if (!r)
|
|
continue;
|
|
rr = r->res;
|
|
if (r->alloc_enabled) {
|
|
fflags = rr->fflags | RF_CTRL_INFO;
|
|
ret = resctrl_group_mkdir_info_resdir(r, s->name,
|
|
fflags, *kn_info);
|
|
if (ret)
|
|
goto out_destroy;
|
|
}
|
|
}
|
|
|
|
list_for_each_entry(s, &resctrl_all_schema, list) {
|
|
r = s->res;
|
|
if (!r)
|
|
continue;
|
|
rr = r->res;
|
|
if (r->mon_enabled) {
|
|
fflags = rr->fflags | RF_MON_INFO;
|
|
snprintf(name, sizeof(name), "%s_MON", s->name);
|
|
ret = resctrl_group_mkdir_info_resdir(r, name,
|
|
fflags, *kn_info);
|
|
if (ret)
|
|
goto out_destroy;
|
|
}
|
|
}
|
|
|
|
/*
|
|
m This extra ref will be put in kernfs_remove() and guarantees
|
|
* that @rdtgrp->kn is always accessible.
|
|
*/
|
|
kernfs_get(*kn_info);
|
|
|
|
ret = resctrl_group_kn_set_ugid(*kn_info);
|
|
if (ret)
|
|
goto out_destroy;
|
|
|
|
kernfs_activate(*kn_info);
|
|
|
|
return 0;
|
|
|
|
out_destroy:
|
|
kernfs_remove(*kn_info);
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
/* Initialize MBA resource with default values. */
|
|
static void rdtgroup_init_mba(struct resctrl_schema *s, u32 closid)
|
|
{
|
|
struct resctrl_staged_config *cfg;
|
|
struct resctrl_resource *r;
|
|
struct raw_resctrl_resource *rr;
|
|
struct rdt_domain *d;
|
|
enum resctrl_ctrl_type t;
|
|
|
|
r = s->res;
|
|
if (WARN_ON(!r))
|
|
return;
|
|
rr = r->res;
|
|
|
|
list_for_each_entry(d, &s->res->domains, list) {
|
|
cfg = &d->staged_cfg[CDP_BOTH];
|
|
cfg->cdp_both_ctrl = s->cdp_mc_both;
|
|
cfg->new_ctrl[SCHEMA_COMM] = rr->ctrl_features[SCHEMA_COMM].default_ctrl;
|
|
resctrl_cdp_mpamid_map(closid, CDP_BOTH, cfg->hw_closid);
|
|
cfg->have_new_ctrl = true;
|
|
/* Set extension ctrl default value, e.g. priority/hardlimit */
|
|
for_each_extend_ctrl_type(t) {
|
|
cfg->new_ctrl[t] = rr->ctrl_features[t].default_ctrl;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Initialize cache resources with default values.
|
|
*
|
|
* A new resctrl group is being created on an allocation capable (CAT)
|
|
* supporting system. Set this group up to start off with all usable
|
|
* allocations.
|
|
*
|
|
* 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)
|
|
{
|
|
struct resctrl_staged_config *cfg;
|
|
enum resctrl_conf_type conf_type = s->conf_type;
|
|
enum resctrl_ctrl_type ctrl_type;
|
|
struct rdt_domain *d;
|
|
struct resctrl_resource *r;
|
|
struct raw_resctrl_resource *rr;
|
|
u32 used_b = 0;
|
|
u32 unused_b = 0;
|
|
unsigned long tmp_cbm;
|
|
|
|
r = s->res;
|
|
if (WARN_ON(!r))
|
|
return -EINVAL;
|
|
rr = r->res;
|
|
|
|
list_for_each_entry(d, &s->res->domains, list) {
|
|
cfg = &d->staged_cfg[conf_type];
|
|
cfg->cdp_both_ctrl = s->cdp_mc_both;
|
|
cfg->have_new_ctrl = false;
|
|
cfg->new_ctrl[SCHEMA_COMM] = r->cache.shareable_bits;
|
|
used_b = r->cache.shareable_bits;
|
|
|
|
unused_b = used_b ^ (BIT_MASK(r->cache.cbm_len) - 1);
|
|
unused_b &= BIT_MASK(r->cache.cbm_len) - 1;
|
|
cfg->new_ctrl[SCHEMA_COMM] |= unused_b;
|
|
|
|
/* Ensure cbm does not access out-of-bound */
|
|
tmp_cbm = cfg->new_ctrl[SCHEMA_COMM];
|
|
if (bitmap_weight(&tmp_cbm, r->cache.cbm_len) <
|
|
r->cache.min_cbm_bits) {
|
|
rdt_last_cmd_printf("No space on %s:%d\n",
|
|
r->name, d->id);
|
|
return -ENOSPC;
|
|
}
|
|
|
|
resctrl_cdp_mpamid_map(closid, conf_type, cfg->hw_closid);
|
|
cfg->have_new_ctrl = true;
|
|
|
|
/*
|
|
* Set extension ctrl default value, e.g. priority/hardlimit
|
|
* with MPAM capabilities.
|
|
*/
|
|
for_each_extend_ctrl_type(ctrl_type) {
|
|
cfg->new_ctrl[ctrl_type] =
|
|
rr->ctrl_features[ctrl_type].default_ctrl;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Initialize the resctrl group's allocations. */
|
|
int resctrl_group_init_alloc(struct rdtgroup *rdtgrp)
|
|
{
|
|
struct resctrl_schema *s;
|
|
struct resctrl_resource *r;
|
|
int ret;
|
|
|
|
list_for_each_entry(s, &resctrl_all_schema, list) {
|
|
r = s->res;
|
|
if (r->rid == RDT_RESOURCE_MC) {
|
|
rdtgroup_init_mba(s, rdtgrp->closid.intpartid);
|
|
} else {
|
|
ret = rdtgroup_init_cat(s, rdtgrp->closid.intpartid);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
ret = resctrl_group_update_domains(rdtgrp, r);
|
|
if (ret < 0) {
|
|
rdt_last_cmd_puts("Failed to initialize allocations\n");
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int resctrl_update_groups_config(struct rdtgroup *rdtgrp)
|
|
{
|
|
int ret = 0;
|
|
struct resctrl_resource *r;
|
|
struct mpam_resctrl_res *res;
|
|
|
|
for_each_supported_resctrl_exports(res) {
|
|
r = &res->resctrl_res;
|
|
if (r->alloc_enabled) {
|
|
ret = resctrl_group_update_domains(rdtgrp, r);
|
|
if (ret)
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* after resync all configurations, restore resync to 0 */
|
|
rdtgrp->resync = 0;
|
|
|
|
return ret;
|
|
}
|