xref: /linux/fs/resctrl/ctrlmondata.c (revision 5c00eca95a9a20e662bd290c3ef3f2e07dfa9baa)
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