17168ae33SJames Morse // SPDX-License-Identifier: GPL-2.0-only
27168ae33SJames Morse /*
37168ae33SJames Morse * Resource Director Technology(RDT)
47168ae33SJames Morse * - Cache Allocation code.
57168ae33SJames Morse *
67168ae33SJames Morse * Copyright (C) 2016 Intel Corporation
77168ae33SJames Morse *
87168ae33SJames Morse * Authors:
97168ae33SJames Morse * Fenghua Yu <fenghua.yu@intel.com>
107168ae33SJames Morse * Tony Luck <tony.luck@intel.com>
117168ae33SJames Morse *
127168ae33SJames Morse * More information about RDT be found in the Intel (R) x86 Architecture
137168ae33SJames Morse * Software Developer Manual June 2016, volume 3, section 17.17.
147168ae33SJames Morse */
157168ae33SJames Morse
167168ae33SJames Morse #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
177168ae33SJames Morse
187168ae33SJames Morse #include <linux/cpu.h>
197168ae33SJames Morse #include <linux/kernfs.h>
207168ae33SJames Morse #include <linux/seq_file.h>
217168ae33SJames Morse #include <linux/slab.h>
227168ae33SJames Morse #include <linux/tick.h>
237168ae33SJames Morse
247168ae33SJames Morse #include "internal.h"
257168ae33SJames Morse
267168ae33SJames Morse struct rdt_parse_data {
277168ae33SJames Morse struct rdtgroup *rdtgrp;
287168ae33SJames Morse char *buf;
297168ae33SJames Morse };
307168ae33SJames Morse
317168ae33SJames Morse typedef int (ctrlval_parser_t)(struct rdt_parse_data *data,
327168ae33SJames Morse struct resctrl_schema *s,
337168ae33SJames Morse struct rdt_ctrl_domain *d);
347168ae33SJames Morse
357168ae33SJames Morse /*
367168ae33SJames Morse * Check whether MBA bandwidth percentage value is correct. The value is
377168ae33SJames Morse * checked against the minimum and max bandwidth values specified by the
387168ae33SJames Morse * hardware. The allocated bandwidth percentage is rounded to the next
397168ae33SJames Morse * control step available on the hardware.
407168ae33SJames Morse */
bw_validate(char * buf,u32 * data,struct rdt_resource * r)417168ae33SJames Morse static bool bw_validate(char *buf, u32 *data, struct rdt_resource *r)
427168ae33SJames Morse {
437168ae33SJames Morse int ret;
447168ae33SJames Morse u32 bw;
457168ae33SJames Morse
467168ae33SJames Morse /*
477168ae33SJames Morse * Only linear delay values is supported for current Intel SKUs.
487168ae33SJames Morse */
497168ae33SJames Morse if (!r->membw.delay_linear && r->membw.arch_needs_linear) {
507168ae33SJames Morse rdt_last_cmd_puts("No support for non-linear MB domains\n");
517168ae33SJames Morse return false;
527168ae33SJames Morse }
537168ae33SJames Morse
547168ae33SJames Morse ret = kstrtou32(buf, 10, &bw);
557168ae33SJames Morse if (ret) {
567168ae33SJames Morse rdt_last_cmd_printf("Invalid MB value %s\n", buf);
577168ae33SJames Morse return false;
587168ae33SJames Morse }
597168ae33SJames Morse
607168ae33SJames Morse /* Nothing else to do if software controller is enabled. */
617168ae33SJames Morse if (is_mba_sc(r)) {
627168ae33SJames Morse *data = bw;
637168ae33SJames Morse return true;
647168ae33SJames Morse }
657168ae33SJames Morse
667168ae33SJames Morse if (bw < r->membw.min_bw || bw > r->membw.max_bw) {
677168ae33SJames Morse rdt_last_cmd_printf("MB value %u out of range [%d,%d]\n",
687168ae33SJames Morse bw, r->membw.min_bw, r->membw.max_bw);
697168ae33SJames Morse return false;
707168ae33SJames Morse }
717168ae33SJames Morse
727168ae33SJames Morse *data = roundup(bw, (unsigned long)r->membw.bw_gran);
737168ae33SJames Morse return true;
747168ae33SJames Morse }
757168ae33SJames Morse
parse_bw(struct rdt_parse_data * data,struct resctrl_schema * s,struct rdt_ctrl_domain * d)767168ae33SJames Morse static int parse_bw(struct rdt_parse_data *data, struct resctrl_schema *s,
777168ae33SJames Morse struct rdt_ctrl_domain *d)
787168ae33SJames Morse {
797168ae33SJames Morse struct resctrl_staged_config *cfg;
807168ae33SJames Morse u32 closid = data->rdtgrp->closid;
817168ae33SJames Morse struct rdt_resource *r = s->res;
827168ae33SJames Morse u32 bw_val;
837168ae33SJames Morse
847168ae33SJames Morse cfg = &d->staged_config[s->conf_type];
857168ae33SJames Morse if (cfg->have_new_ctrl) {
867168ae33SJames Morse rdt_last_cmd_printf("Duplicate domain %d\n", d->hdr.id);
877168ae33SJames Morse return -EINVAL;
887168ae33SJames Morse }
897168ae33SJames Morse
907168ae33SJames Morse if (!bw_validate(data->buf, &bw_val, r))
917168ae33SJames Morse return -EINVAL;
927168ae33SJames Morse
937168ae33SJames Morse if (is_mba_sc(r)) {
947168ae33SJames Morse d->mbps_val[closid] = bw_val;
957168ae33SJames Morse return 0;
967168ae33SJames Morse }
977168ae33SJames Morse
987168ae33SJames Morse cfg->new_ctrl = bw_val;
997168ae33SJames Morse cfg->have_new_ctrl = true;
1007168ae33SJames Morse
1017168ae33SJames Morse return 0;
1027168ae33SJames Morse }
1037168ae33SJames Morse
1047168ae33SJames Morse /*
1057168ae33SJames Morse * Check whether a cache bit mask is valid.
1067168ae33SJames Morse * On Intel CPUs, non-contiguous 1s value support is indicated by CPUID:
1077168ae33SJames Morse * - CPUID.0x10.1:ECX[3]: L3 non-contiguous 1s value supported if 1
1087168ae33SJames Morse * - CPUID.0x10.2:ECX[3]: L2 non-contiguous 1s value supported if 1
1097168ae33SJames Morse *
1107168ae33SJames Morse * Haswell does not support a non-contiguous 1s value and additionally
1117168ae33SJames Morse * requires at least two bits set.
1127168ae33SJames Morse * AMD allows non-contiguous bitmasks.
1137168ae33SJames Morse */
cbm_validate(char * buf,u32 * data,struct rdt_resource * r)1147168ae33SJames Morse static bool cbm_validate(char *buf, u32 *data, struct rdt_resource *r)
1157168ae33SJames Morse {
1167168ae33SJames Morse u32 supported_bits = BIT_MASK(r->cache.cbm_len) - 1;
1177168ae33SJames Morse unsigned int cbm_len = r->cache.cbm_len;
1187168ae33SJames Morse unsigned long first_bit, zero_bit, val;
1197168ae33SJames Morse int ret;
1207168ae33SJames Morse
1217168ae33SJames Morse ret = kstrtoul(buf, 16, &val);
1227168ae33SJames Morse if (ret) {
1237168ae33SJames Morse rdt_last_cmd_printf("Non-hex character in the mask %s\n", buf);
1247168ae33SJames Morse return false;
1257168ae33SJames Morse }
1267168ae33SJames Morse
1277168ae33SJames Morse if ((r->cache.min_cbm_bits > 0 && val == 0) || val > supported_bits) {
1287168ae33SJames Morse rdt_last_cmd_puts("Mask out of range\n");
1297168ae33SJames Morse return false;
1307168ae33SJames Morse }
1317168ae33SJames Morse
1327168ae33SJames Morse first_bit = find_first_bit(&val, cbm_len);
1337168ae33SJames Morse zero_bit = find_next_zero_bit(&val, cbm_len, first_bit);
1347168ae33SJames Morse
1357168ae33SJames Morse /* Are non-contiguous bitmasks allowed? */
1367168ae33SJames Morse if (!r->cache.arch_has_sparse_bitmasks &&
1377168ae33SJames Morse (find_next_bit(&val, cbm_len, zero_bit) < cbm_len)) {
1387168ae33SJames Morse rdt_last_cmd_printf("The mask %lx has non-consecutive 1-bits\n", val);
1397168ae33SJames Morse return false;
1407168ae33SJames Morse }
1417168ae33SJames Morse
1427168ae33SJames Morse if ((zero_bit - first_bit) < r->cache.min_cbm_bits) {
1437168ae33SJames Morse rdt_last_cmd_printf("Need at least %d bits in the mask\n",
1447168ae33SJames Morse r->cache.min_cbm_bits);
1457168ae33SJames Morse return false;
1467168ae33SJames Morse }
1477168ae33SJames Morse
1487168ae33SJames Morse *data = val;
1497168ae33SJames Morse return true;
1507168ae33SJames Morse }
1517168ae33SJames Morse
1527168ae33SJames Morse /*
1537168ae33SJames Morse * Read one cache bit mask (hex). Check that it is valid for the current
1547168ae33SJames Morse * resource type.
1557168ae33SJames Morse */
parse_cbm(struct rdt_parse_data * data,struct resctrl_schema * s,struct rdt_ctrl_domain * d)1567168ae33SJames Morse static int parse_cbm(struct rdt_parse_data *data, struct resctrl_schema *s,
1577168ae33SJames Morse struct rdt_ctrl_domain *d)
1587168ae33SJames Morse {
1597168ae33SJames Morse struct rdtgroup *rdtgrp = data->rdtgrp;
1607168ae33SJames Morse struct resctrl_staged_config *cfg;
1617168ae33SJames Morse struct rdt_resource *r = s->res;
1627168ae33SJames Morse u32 cbm_val;
1637168ae33SJames Morse
1647168ae33SJames Morse cfg = &d->staged_config[s->conf_type];
1657168ae33SJames Morse if (cfg->have_new_ctrl) {
1667168ae33SJames Morse rdt_last_cmd_printf("Duplicate domain %d\n", d->hdr.id);
1677168ae33SJames Morse return -EINVAL;
1687168ae33SJames Morse }
1697168ae33SJames Morse
1707168ae33SJames Morse /*
1717168ae33SJames Morse * Cannot set up more than one pseudo-locked region in a cache
1727168ae33SJames Morse * hierarchy.
1737168ae33SJames Morse */
1747168ae33SJames Morse if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP &&
1757168ae33SJames Morse rdtgroup_pseudo_locked_in_hierarchy(d)) {
1767168ae33SJames Morse rdt_last_cmd_puts("Pseudo-locked region in hierarchy\n");
1777168ae33SJames Morse return -EINVAL;
1787168ae33SJames Morse }
1797168ae33SJames Morse
1807168ae33SJames Morse if (!cbm_validate(data->buf, &cbm_val, r))
1817168ae33SJames Morse return -EINVAL;
1827168ae33SJames Morse
1837168ae33SJames Morse if ((rdtgrp->mode == RDT_MODE_EXCLUSIVE ||
1847168ae33SJames Morse rdtgrp->mode == RDT_MODE_SHAREABLE) &&
1857168ae33SJames Morse rdtgroup_cbm_overlaps_pseudo_locked(d, cbm_val)) {
1867168ae33SJames Morse rdt_last_cmd_puts("CBM overlaps with pseudo-locked region\n");
1877168ae33SJames Morse return -EINVAL;
1887168ae33SJames Morse }
1897168ae33SJames Morse
1907168ae33SJames Morse /*
1917168ae33SJames Morse * The CBM may not overlap with the CBM of another closid if
1927168ae33SJames Morse * either is exclusive.
1937168ae33SJames Morse */
1947168ae33SJames Morse if (rdtgroup_cbm_overlaps(s, d, cbm_val, rdtgrp->closid, true)) {
1957168ae33SJames Morse rdt_last_cmd_puts("Overlaps with exclusive group\n");
1967168ae33SJames Morse return -EINVAL;
1977168ae33SJames Morse }
1987168ae33SJames Morse
1997168ae33SJames Morse if (rdtgroup_cbm_overlaps(s, d, cbm_val, rdtgrp->closid, false)) {
2007168ae33SJames Morse if (rdtgrp->mode == RDT_MODE_EXCLUSIVE ||
2017168ae33SJames Morse rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP) {
2027168ae33SJames Morse rdt_last_cmd_puts("Overlaps with other group\n");
2037168ae33SJames Morse return -EINVAL;
2047168ae33SJames Morse }
2057168ae33SJames Morse }
2067168ae33SJames Morse
2077168ae33SJames Morse cfg->new_ctrl = cbm_val;
2087168ae33SJames Morse cfg->have_new_ctrl = true;
2097168ae33SJames Morse
2107168ae33SJames Morse return 0;
2117168ae33SJames Morse }
2127168ae33SJames Morse
2137168ae33SJames Morse /*
2147168ae33SJames Morse * For each domain in this resource we expect to find a series of:
2157168ae33SJames Morse * id=mask
2167168ae33SJames Morse * separated by ";". The "id" is in decimal, and must match one of
2177168ae33SJames Morse * the "id"s for this resource.
2187168ae33SJames Morse */
parse_line(char * line,struct resctrl_schema * s,struct rdtgroup * rdtgrp)2197168ae33SJames Morse static int parse_line(char *line, struct resctrl_schema *s,
2207168ae33SJames Morse struct rdtgroup *rdtgrp)
2217168ae33SJames Morse {
2227168ae33SJames Morse enum resctrl_conf_type t = s->conf_type;
2237168ae33SJames Morse ctrlval_parser_t *parse_ctrlval = NULL;
2247168ae33SJames Morse struct resctrl_staged_config *cfg;
2257168ae33SJames Morse struct rdt_resource *r = s->res;
2267168ae33SJames Morse struct rdt_parse_data data;
2277168ae33SJames Morse struct rdt_ctrl_domain *d;
2287168ae33SJames Morse char *dom = NULL, *id;
2297168ae33SJames Morse unsigned long dom_id;
2307168ae33SJames Morse
2317168ae33SJames Morse /* Walking r->domains, ensure it can't race with cpuhp */
2327168ae33SJames Morse lockdep_assert_cpus_held();
2337168ae33SJames Morse
2347168ae33SJames Morse switch (r->schema_fmt) {
2357168ae33SJames Morse case RESCTRL_SCHEMA_BITMAP:
2367168ae33SJames Morse parse_ctrlval = &parse_cbm;
2377168ae33SJames Morse break;
2387168ae33SJames Morse case RESCTRL_SCHEMA_RANGE:
2397168ae33SJames Morse parse_ctrlval = &parse_bw;
2407168ae33SJames Morse break;
2417168ae33SJames Morse }
2427168ae33SJames Morse
2437168ae33SJames Morse if (WARN_ON_ONCE(!parse_ctrlval))
2447168ae33SJames Morse return -EINVAL;
2457168ae33SJames Morse
2467168ae33SJames Morse if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP &&
2477168ae33SJames Morse (r->rid == RDT_RESOURCE_MBA || r->rid == RDT_RESOURCE_SMBA)) {
2487168ae33SJames Morse rdt_last_cmd_puts("Cannot pseudo-lock MBA resource\n");
2497168ae33SJames Morse return -EINVAL;
2507168ae33SJames Morse }
2517168ae33SJames Morse
2527168ae33SJames Morse next:
2537168ae33SJames Morse if (!line || line[0] == '\0')
2547168ae33SJames Morse return 0;
2557168ae33SJames Morse dom = strsep(&line, ";");
2567168ae33SJames Morse id = strsep(&dom, "=");
2577168ae33SJames Morse if (!dom || kstrtoul(id, 10, &dom_id)) {
2587168ae33SJames Morse rdt_last_cmd_puts("Missing '=' or non-numeric domain\n");
2597168ae33SJames Morse return -EINVAL;
2607168ae33SJames Morse }
2617168ae33SJames Morse dom = strim(dom);
2627168ae33SJames Morse list_for_each_entry(d, &r->ctrl_domains, hdr.list) {
2637168ae33SJames Morse if (d->hdr.id == dom_id) {
2647168ae33SJames Morse data.buf = dom;
2657168ae33SJames Morse data.rdtgrp = rdtgrp;
2667168ae33SJames Morse if (parse_ctrlval(&data, s, d))
2677168ae33SJames Morse return -EINVAL;
2687168ae33SJames Morse if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP) {
2697168ae33SJames Morse cfg = &d->staged_config[t];
2707168ae33SJames Morse /*
2717168ae33SJames Morse * In pseudo-locking setup mode and just
2727168ae33SJames Morse * parsed a valid CBM that should be
2737168ae33SJames Morse * pseudo-locked. Only one locked region per
2747168ae33SJames Morse * resource group and domain so just do
2757168ae33SJames Morse * the required initialization for single
2767168ae33SJames Morse * region and return.
2777168ae33SJames Morse */
2787168ae33SJames Morse rdtgrp->plr->s = s;
2797168ae33SJames Morse rdtgrp->plr->d = d;
2807168ae33SJames Morse rdtgrp->plr->cbm = cfg->new_ctrl;
2817168ae33SJames Morse d->plr = rdtgrp->plr;
2827168ae33SJames Morse return 0;
2837168ae33SJames Morse }
2847168ae33SJames Morse goto next;
2857168ae33SJames Morse }
2867168ae33SJames Morse }
2877168ae33SJames Morse return -EINVAL;
2887168ae33SJames Morse }
2897168ae33SJames Morse
rdtgroup_parse_resource(char * resname,char * tok,struct rdtgroup * rdtgrp)2907168ae33SJames Morse static int rdtgroup_parse_resource(char *resname, char *tok,
2917168ae33SJames Morse struct rdtgroup *rdtgrp)
2927168ae33SJames Morse {
2937168ae33SJames Morse struct resctrl_schema *s;
2947168ae33SJames Morse
2957168ae33SJames Morse list_for_each_entry(s, &resctrl_schema_all, list) {
2967168ae33SJames Morse if (!strcmp(resname, s->name) && rdtgrp->closid < s->num_closid)
2977168ae33SJames Morse return parse_line(tok, s, rdtgrp);
2987168ae33SJames Morse }
2997168ae33SJames Morse rdt_last_cmd_printf("Unknown or unsupported resource name '%s'\n", resname);
3007168ae33SJames Morse return -EINVAL;
3017168ae33SJames Morse }
3027168ae33SJames Morse
rdtgroup_schemata_write(struct kernfs_open_file * of,char * buf,size_t nbytes,loff_t off)3037168ae33SJames Morse ssize_t rdtgroup_schemata_write(struct kernfs_open_file *of,
3047168ae33SJames Morse char *buf, size_t nbytes, loff_t off)
3057168ae33SJames Morse {
3067168ae33SJames Morse struct resctrl_schema *s;
3077168ae33SJames Morse struct rdtgroup *rdtgrp;
3087168ae33SJames Morse struct rdt_resource *r;
3097168ae33SJames Morse char *tok, *resname;
3107168ae33SJames Morse int ret = 0;
3117168ae33SJames Morse
3127168ae33SJames Morse /* Valid input requires a trailing newline */
3137168ae33SJames Morse if (nbytes == 0 || buf[nbytes - 1] != '\n')
3147168ae33SJames Morse return -EINVAL;
3157168ae33SJames Morse buf[nbytes - 1] = '\0';
3167168ae33SJames Morse
3177168ae33SJames Morse rdtgrp = rdtgroup_kn_lock_live(of->kn);
3187168ae33SJames Morse if (!rdtgrp) {
3197168ae33SJames Morse rdtgroup_kn_unlock(of->kn);
3207168ae33SJames Morse return -ENOENT;
3217168ae33SJames Morse }
3227168ae33SJames Morse rdt_last_cmd_clear();
3237168ae33SJames Morse
3247168ae33SJames Morse /*
3257168ae33SJames Morse * No changes to pseudo-locked region allowed. It has to be removed
3267168ae33SJames Morse * and re-created instead.
3277168ae33SJames Morse */
3287168ae33SJames Morse if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) {
3297168ae33SJames Morse ret = -EINVAL;
3307168ae33SJames Morse rdt_last_cmd_puts("Resource group is pseudo-locked\n");
3317168ae33SJames Morse goto out;
3327168ae33SJames Morse }
3337168ae33SJames Morse
3347168ae33SJames Morse rdt_staged_configs_clear();
3357168ae33SJames Morse
3367168ae33SJames Morse while ((tok = strsep(&buf, "\n")) != NULL) {
3377168ae33SJames Morse resname = strim(strsep(&tok, ":"));
3387168ae33SJames Morse if (!tok) {
3397168ae33SJames Morse rdt_last_cmd_puts("Missing ':'\n");
3407168ae33SJames Morse ret = -EINVAL;
3417168ae33SJames Morse goto out;
3427168ae33SJames Morse }
3437168ae33SJames Morse if (tok[0] == '\0') {
3447168ae33SJames Morse rdt_last_cmd_printf("Missing '%s' value\n", resname);
3457168ae33SJames Morse ret = -EINVAL;
3467168ae33SJames Morse goto out;
3477168ae33SJames Morse }
3487168ae33SJames Morse ret = rdtgroup_parse_resource(resname, tok, rdtgrp);
3497168ae33SJames Morse if (ret)
3507168ae33SJames Morse goto out;
3517168ae33SJames Morse }
3527168ae33SJames Morse
3537168ae33SJames Morse list_for_each_entry(s, &resctrl_schema_all, list) {
3547168ae33SJames Morse r = s->res;
3557168ae33SJames Morse
3567168ae33SJames Morse /*
3577168ae33SJames Morse * Writes to mba_sc resources update the software controller,
3587168ae33SJames Morse * not the control MSR.
3597168ae33SJames Morse */
3607168ae33SJames Morse if (is_mba_sc(r))
3617168ae33SJames Morse continue;
3627168ae33SJames Morse
3637168ae33SJames Morse ret = resctrl_arch_update_domains(r, rdtgrp->closid);
3647168ae33SJames Morse if (ret)
3657168ae33SJames Morse goto out;
3667168ae33SJames Morse }
3677168ae33SJames Morse
3687168ae33SJames Morse if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP) {
3697168ae33SJames Morse /*
3707168ae33SJames Morse * If pseudo-locking fails we keep the resource group in
3717168ae33SJames Morse * mode RDT_MODE_PSEUDO_LOCKSETUP with its class of service
3727168ae33SJames Morse * active and updated for just the domain the pseudo-locked
3737168ae33SJames Morse * region was requested for.
3747168ae33SJames Morse */
3757168ae33SJames Morse ret = rdtgroup_pseudo_lock_create(rdtgrp);
3767168ae33SJames Morse }
3777168ae33SJames Morse
3787168ae33SJames Morse out:
3797168ae33SJames Morse rdt_staged_configs_clear();
3807168ae33SJames Morse rdtgroup_kn_unlock(of->kn);
3817168ae33SJames Morse return ret ?: nbytes;
3827168ae33SJames Morse }
3837168ae33SJames Morse
show_doms(struct seq_file * s,struct resctrl_schema * schema,int closid)3847168ae33SJames Morse static void show_doms(struct seq_file *s, struct resctrl_schema *schema, int closid)
3857168ae33SJames Morse {
3867168ae33SJames Morse struct rdt_resource *r = schema->res;
3877168ae33SJames Morse struct rdt_ctrl_domain *dom;
3887168ae33SJames Morse bool sep = false;
3897168ae33SJames Morse u32 ctrl_val;
3907168ae33SJames Morse
3917168ae33SJames Morse /* Walking r->domains, ensure it can't race with cpuhp */
3927168ae33SJames Morse lockdep_assert_cpus_held();
3937168ae33SJames Morse
3947168ae33SJames Morse seq_printf(s, "%*s:", max_name_width, schema->name);
3957168ae33SJames Morse list_for_each_entry(dom, &r->ctrl_domains, hdr.list) {
3967168ae33SJames Morse if (sep)
3977168ae33SJames Morse seq_puts(s, ";");
3987168ae33SJames Morse
3997168ae33SJames Morse if (is_mba_sc(r))
4007168ae33SJames Morse ctrl_val = dom->mbps_val[closid];
4017168ae33SJames Morse else
4027168ae33SJames Morse ctrl_val = resctrl_arch_get_config(r, dom, closid,
4037168ae33SJames Morse schema->conf_type);
4047168ae33SJames Morse
4057168ae33SJames Morse seq_printf(s, schema->fmt_str, dom->hdr.id, ctrl_val);
4067168ae33SJames Morse sep = true;
4077168ae33SJames Morse }
4087168ae33SJames Morse seq_puts(s, "\n");
4097168ae33SJames Morse }
4107168ae33SJames Morse
rdtgroup_schemata_show(struct kernfs_open_file * of,struct seq_file * s,void * v)4117168ae33SJames Morse int rdtgroup_schemata_show(struct kernfs_open_file *of,
4127168ae33SJames Morse struct seq_file *s, void *v)
4137168ae33SJames Morse {
4147168ae33SJames Morse struct resctrl_schema *schema;
4157168ae33SJames Morse struct rdtgroup *rdtgrp;
4167168ae33SJames Morse int ret = 0;
4177168ae33SJames Morse u32 closid;
4187168ae33SJames Morse
4197168ae33SJames Morse rdtgrp = rdtgroup_kn_lock_live(of->kn);
4207168ae33SJames Morse if (rdtgrp) {
4217168ae33SJames Morse if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP) {
4227168ae33SJames Morse list_for_each_entry(schema, &resctrl_schema_all, list) {
4237168ae33SJames Morse seq_printf(s, "%s:uninitialized\n", schema->name);
4247168ae33SJames Morse }
4257168ae33SJames Morse } else if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) {
4267168ae33SJames Morse if (!rdtgrp->plr->d) {
4277168ae33SJames Morse rdt_last_cmd_clear();
4287168ae33SJames Morse rdt_last_cmd_puts("Cache domain offline\n");
4297168ae33SJames Morse ret = -ENODEV;
4307168ae33SJames Morse } else {
4317168ae33SJames Morse seq_printf(s, "%s:%d=%x\n",
4327168ae33SJames Morse rdtgrp->plr->s->res->name,
4337168ae33SJames Morse rdtgrp->plr->d->hdr.id,
4347168ae33SJames Morse rdtgrp->plr->cbm);
4357168ae33SJames Morse }
4367168ae33SJames Morse } else {
4377168ae33SJames Morse closid = rdtgrp->closid;
4387168ae33SJames Morse list_for_each_entry(schema, &resctrl_schema_all, list) {
4397168ae33SJames Morse if (closid < schema->num_closid)
4407168ae33SJames Morse show_doms(s, schema, closid);
4417168ae33SJames Morse }
4427168ae33SJames Morse }
4437168ae33SJames Morse } else {
4447168ae33SJames Morse ret = -ENOENT;
4457168ae33SJames Morse }
4467168ae33SJames Morse rdtgroup_kn_unlock(of->kn);
4477168ae33SJames Morse return ret;
4487168ae33SJames Morse }
4497168ae33SJames Morse
smp_mon_event_count(void * arg)4507168ae33SJames Morse static int smp_mon_event_count(void *arg)
4517168ae33SJames Morse {
4527168ae33SJames Morse mon_event_count(arg);
4537168ae33SJames Morse
4547168ae33SJames Morse return 0;
4557168ae33SJames Morse }
4567168ae33SJames Morse
rdtgroup_mba_mbps_event_write(struct kernfs_open_file * of,char * buf,size_t nbytes,loff_t off)4577168ae33SJames Morse ssize_t rdtgroup_mba_mbps_event_write(struct kernfs_open_file *of,
4587168ae33SJames Morse char *buf, size_t nbytes, loff_t off)
4597168ae33SJames Morse {
4607168ae33SJames Morse struct rdtgroup *rdtgrp;
4617168ae33SJames Morse int ret = 0;
4627168ae33SJames Morse
4637168ae33SJames Morse /* Valid input requires a trailing newline */
4647168ae33SJames Morse if (nbytes == 0 || buf[nbytes - 1] != '\n')
4657168ae33SJames Morse return -EINVAL;
4667168ae33SJames Morse buf[nbytes - 1] = '\0';
4677168ae33SJames Morse
4687168ae33SJames Morse rdtgrp = rdtgroup_kn_lock_live(of->kn);
4697168ae33SJames Morse if (!rdtgrp) {
4707168ae33SJames Morse rdtgroup_kn_unlock(of->kn);
4717168ae33SJames Morse return -ENOENT;
4727168ae33SJames Morse }
4737168ae33SJames Morse rdt_last_cmd_clear();
4747168ae33SJames Morse
4757168ae33SJames Morse if (!strcmp(buf, "mbm_local_bytes")) {
4767168ae33SJames Morse if (resctrl_arch_is_mbm_local_enabled())
4777168ae33SJames Morse rdtgrp->mba_mbps_event = QOS_L3_MBM_LOCAL_EVENT_ID;
4787168ae33SJames Morse else
4797168ae33SJames Morse ret = -EINVAL;
4807168ae33SJames Morse } else if (!strcmp(buf, "mbm_total_bytes")) {
4817168ae33SJames Morse if (resctrl_arch_is_mbm_total_enabled())
4827168ae33SJames Morse rdtgrp->mba_mbps_event = QOS_L3_MBM_TOTAL_EVENT_ID;
4837168ae33SJames Morse else
4847168ae33SJames Morse ret = -EINVAL;
4857168ae33SJames Morse } else {
4867168ae33SJames Morse ret = -EINVAL;
4877168ae33SJames Morse }
4887168ae33SJames Morse
4897168ae33SJames Morse if (ret)
4907168ae33SJames Morse rdt_last_cmd_printf("Unsupported event id '%s'\n", buf);
4917168ae33SJames Morse
4927168ae33SJames Morse rdtgroup_kn_unlock(of->kn);
4937168ae33SJames Morse
4947168ae33SJames Morse return ret ?: nbytes;
4957168ae33SJames Morse }
4967168ae33SJames Morse
rdtgroup_mba_mbps_event_show(struct kernfs_open_file * of,struct seq_file * s,void * v)4977168ae33SJames Morse int rdtgroup_mba_mbps_event_show(struct kernfs_open_file *of,
4987168ae33SJames Morse struct seq_file *s, void *v)
4997168ae33SJames Morse {
5007168ae33SJames Morse struct rdtgroup *rdtgrp;
5017168ae33SJames Morse int ret = 0;
5027168ae33SJames Morse
5037168ae33SJames Morse rdtgrp = rdtgroup_kn_lock_live(of->kn);
5047168ae33SJames Morse
5057168ae33SJames Morse if (rdtgrp) {
5067168ae33SJames Morse switch (rdtgrp->mba_mbps_event) {
5077168ae33SJames Morse case QOS_L3_MBM_LOCAL_EVENT_ID:
5087168ae33SJames Morse seq_puts(s, "mbm_local_bytes\n");
5097168ae33SJames Morse break;
5107168ae33SJames Morse case QOS_L3_MBM_TOTAL_EVENT_ID:
5117168ae33SJames Morse seq_puts(s, "mbm_total_bytes\n");
5127168ae33SJames Morse break;
5137168ae33SJames Morse default:
5147168ae33SJames Morse pr_warn_once("Bad event %d\n", rdtgrp->mba_mbps_event);
5157168ae33SJames Morse ret = -EINVAL;
5167168ae33SJames Morse break;
5177168ae33SJames Morse }
5187168ae33SJames Morse } else {
5197168ae33SJames Morse ret = -ENOENT;
5207168ae33SJames Morse }
5217168ae33SJames Morse
5227168ae33SJames Morse rdtgroup_kn_unlock(of->kn);
5237168ae33SJames Morse
5247168ae33SJames Morse return ret;
5257168ae33SJames Morse }
5267168ae33SJames Morse
resctrl_find_domain(struct list_head * h,int id,struct list_head ** pos)5277168ae33SJames Morse struct rdt_domain_hdr *resctrl_find_domain(struct list_head *h, int id,
5287168ae33SJames Morse struct list_head **pos)
5297168ae33SJames Morse {
5307168ae33SJames Morse struct rdt_domain_hdr *d;
5317168ae33SJames Morse struct list_head *l;
5327168ae33SJames Morse
5337168ae33SJames Morse list_for_each(l, h) {
5347168ae33SJames Morse d = list_entry(l, struct rdt_domain_hdr, list);
5357168ae33SJames Morse /* When id is found, return its domain. */
5367168ae33SJames Morse if (id == d->id)
5377168ae33SJames Morse return d;
5387168ae33SJames Morse /* Stop searching when finding id's position in sorted list. */
5397168ae33SJames Morse if (id < d->id)
5407168ae33SJames Morse break;
5417168ae33SJames Morse }
5427168ae33SJames Morse
5437168ae33SJames Morse if (pos)
5447168ae33SJames Morse *pos = l;
5457168ae33SJames Morse
5467168ae33SJames Morse return NULL;
5477168ae33SJames Morse }
5487168ae33SJames Morse
mon_event_read(struct rmid_read * rr,struct rdt_resource * r,struct rdt_mon_domain * d,struct rdtgroup * rdtgrp,cpumask_t * cpumask,int evtid,int first)5497168ae33SJames Morse void mon_event_read(struct rmid_read *rr, struct rdt_resource *r,
5507168ae33SJames Morse struct rdt_mon_domain *d, struct rdtgroup *rdtgrp,
5517168ae33SJames Morse cpumask_t *cpumask, int evtid, int first)
5527168ae33SJames Morse {
5537168ae33SJames Morse int cpu;
5547168ae33SJames Morse
5557168ae33SJames Morse /* When picking a CPU from cpu_mask, ensure it can't race with cpuhp */
5567168ae33SJames Morse lockdep_assert_cpus_held();
5577168ae33SJames Morse
5587168ae33SJames Morse /*
5597168ae33SJames Morse * Setup the parameters to pass to mon_event_count() to read the data.
5607168ae33SJames Morse */
5617168ae33SJames Morse rr->rgrp = rdtgrp;
5627168ae33SJames Morse rr->evtid = evtid;
5637168ae33SJames Morse rr->r = r;
5647168ae33SJames Morse rr->d = d;
5657168ae33SJames Morse rr->first = first;
5667168ae33SJames Morse rr->arch_mon_ctx = resctrl_arch_mon_ctx_alloc(r, evtid);
5677168ae33SJames Morse if (IS_ERR(rr->arch_mon_ctx)) {
5687168ae33SJames Morse rr->err = -EINVAL;
5697168ae33SJames Morse return;
5707168ae33SJames Morse }
5717168ae33SJames Morse
5727168ae33SJames Morse cpu = cpumask_any_housekeeping(cpumask, RESCTRL_PICK_ANY_CPU);
5737168ae33SJames Morse
5747168ae33SJames Morse /*
5757168ae33SJames Morse * cpumask_any_housekeeping() prefers housekeeping CPUs, but
5767168ae33SJames Morse * are all the CPUs nohz_full? If yes, pick a CPU to IPI.
5777168ae33SJames Morse * MPAM's resctrl_arch_rmid_read() is unable to read the
5787168ae33SJames Morse * counters on some platforms if its called in IRQ context.
5797168ae33SJames Morse */
5807168ae33SJames Morse if (tick_nohz_full_cpu(cpu))
5817168ae33SJames Morse smp_call_function_any(cpumask, mon_event_count, rr, 1);
5827168ae33SJames Morse else
5837168ae33SJames Morse smp_call_on_cpu(cpu, smp_mon_event_count, rr, false);
5847168ae33SJames Morse
5857168ae33SJames Morse resctrl_arch_mon_ctx_free(r, evtid, rr->arch_mon_ctx);
5867168ae33SJames Morse }
5877168ae33SJames Morse
rdtgroup_mondata_show(struct seq_file * m,void * arg)5887168ae33SJames Morse int rdtgroup_mondata_show(struct seq_file *m, void *arg)
5897168ae33SJames Morse {
5907168ae33SJames Morse struct kernfs_open_file *of = m->private;
5917168ae33SJames Morse enum resctrl_res_level resid;
5927168ae33SJames Morse enum resctrl_event_id evtid;
5937168ae33SJames Morse struct rdt_domain_hdr *hdr;
5947168ae33SJames Morse struct rmid_read rr = {0};
5957168ae33SJames Morse struct rdt_mon_domain *d;
5967168ae33SJames Morse struct rdtgroup *rdtgrp;
597*594902c9SQinyun Tan int domid, cpu, ret = 0;
5987168ae33SJames Morse struct rdt_resource *r;
599*594902c9SQinyun Tan struct cacheinfo *ci;
6007168ae33SJames Morse struct mon_data *md;
6017168ae33SJames Morse
6027168ae33SJames Morse rdtgrp = rdtgroup_kn_lock_live(of->kn);
6037168ae33SJames Morse if (!rdtgrp) {
6047168ae33SJames Morse ret = -ENOENT;
6057168ae33SJames Morse goto out;
6067168ae33SJames Morse }
6077168ae33SJames Morse
6087168ae33SJames Morse md = of->kn->priv;
6097168ae33SJames Morse if (WARN_ON_ONCE(!md)) {
6107168ae33SJames Morse ret = -EIO;
6117168ae33SJames Morse goto out;
6127168ae33SJames Morse }
6137168ae33SJames Morse
6147168ae33SJames Morse resid = md->rid;
6157168ae33SJames Morse domid = md->domid;
6167168ae33SJames Morse evtid = md->evtid;
6177168ae33SJames Morse r = resctrl_arch_get_resource(resid);
6187168ae33SJames Morse
6197168ae33SJames Morse if (md->sum) {
6207168ae33SJames Morse /*
6217168ae33SJames Morse * This file requires summing across all domains that share
6227168ae33SJames Morse * the L3 cache id that was provided in the "domid" field of the
6237168ae33SJames Morse * struct mon_data. Search all domains in the resource for
6247168ae33SJames Morse * one that matches this cache id.
6257168ae33SJames Morse */
6267168ae33SJames Morse list_for_each_entry(d, &r->mon_domains, hdr.list) {
627*594902c9SQinyun Tan if (d->ci_id == domid) {
628*594902c9SQinyun Tan rr.ci_id = d->ci_id;
629*594902c9SQinyun Tan cpu = cpumask_any(&d->hdr.cpu_mask);
630*594902c9SQinyun Tan ci = get_cpu_cacheinfo_level(cpu, RESCTRL_L3_CACHE);
631*594902c9SQinyun Tan if (!ci)
632*594902c9SQinyun Tan continue;
6337168ae33SJames Morse mon_event_read(&rr, r, NULL, rdtgrp,
634*594902c9SQinyun Tan &ci->shared_cpu_map, evtid, false);
6357168ae33SJames Morse goto checkresult;
6367168ae33SJames Morse }
6377168ae33SJames Morse }
6387168ae33SJames Morse ret = -ENOENT;
6397168ae33SJames Morse goto out;
6407168ae33SJames Morse } else {
6417168ae33SJames Morse /*
6427168ae33SJames Morse * This file provides data from a single domain. Search
6437168ae33SJames Morse * the resource to find the domain with "domid".
6447168ae33SJames Morse */
6457168ae33SJames Morse hdr = resctrl_find_domain(&r->mon_domains, domid, NULL);
6467168ae33SJames Morse if (!hdr || WARN_ON_ONCE(hdr->type != RESCTRL_MON_DOMAIN)) {
6477168ae33SJames Morse ret = -ENOENT;
6487168ae33SJames Morse goto out;
6497168ae33SJames Morse }
6507168ae33SJames Morse d = container_of(hdr, struct rdt_mon_domain, hdr);
6517168ae33SJames Morse mon_event_read(&rr, r, d, rdtgrp, &d->hdr.cpu_mask, evtid, false);
6527168ae33SJames Morse }
6537168ae33SJames Morse
6547168ae33SJames Morse checkresult:
6557168ae33SJames Morse
6567168ae33SJames Morse if (rr.err == -EIO)
6577168ae33SJames Morse seq_puts(m, "Error\n");
6587168ae33SJames Morse else if (rr.err == -EINVAL)
6597168ae33SJames Morse seq_puts(m, "Unavailable\n");
6607168ae33SJames Morse else
6617168ae33SJames Morse seq_printf(m, "%llu\n", rr.val);
6627168ae33SJames Morse
6637168ae33SJames Morse out:
6647168ae33SJames Morse rdtgroup_kn_unlock(of->kn);
6657168ae33SJames Morse return ret;
6667168ae33SJames Morse }
667