109e61dafSJames Morse // SPDX-License-Identifier: GPL-2.0 209e61dafSJames Morse // Copyright (C) 2025 Arm Ltd. 309e61dafSJames Morse 409e61dafSJames Morse #define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__ 509e61dafSJames Morse 609e61dafSJames Morse #include <linux/arm_mpam.h> 709e61dafSJames Morse #include <linux/cacheinfo.h> 809e61dafSJames Morse #include <linux/cpu.h> 909e61dafSJames Morse #include <linux/cpumask.h> 1009e61dafSJames Morse #include <linux/errno.h> 119d2e1a99SJames Morse #include <linux/limits.h> 1209e61dafSJames Morse #include <linux/list.h> 1380d147d2SDave Martin #include <linux/math.h> 1409e61dafSJames Morse #include <linux/printk.h> 1509e61dafSJames Morse #include <linux/rculist.h> 1609e61dafSJames Morse #include <linux/resctrl.h> 1709e61dafSJames Morse #include <linux/slab.h> 1809e61dafSJames Morse #include <linux/types.h> 191c1e2968SBen Horgan #include <linux/wait.h> 2009e61dafSJames Morse 2109e61dafSJames Morse #include <asm/mpam.h> 2209e61dafSJames Morse 2309e61dafSJames Morse #include "mpam_internal.h" 2409e61dafSJames Morse 2509e61dafSJames Morse /* 2609e61dafSJames Morse * The classes we've picked to map to resctrl resources, wrapped 2709e61dafSJames Morse * in with their resctrl structure. 2809e61dafSJames Morse * Class pointer may be NULL. 2909e61dafSJames Morse */ 3009e61dafSJames Morse static struct mpam_resctrl_res mpam_resctrl_controls[RDT_NUM_RESOURCES]; 3109e61dafSJames Morse 3209e61dafSJames Morse #define for_each_mpam_resctrl_control(res, rid) \ 3309e61dafSJames Morse for (rid = 0, res = &mpam_resctrl_controls[rid]; \ 3409e61dafSJames Morse rid < RDT_NUM_RESOURCES; \ 3509e61dafSJames Morse rid++, res = &mpam_resctrl_controls[rid]) 3609e61dafSJames Morse 3709e61dafSJames Morse /* The lock for modifying resctrl's domain lists from cpuhp callbacks. */ 3809e61dafSJames Morse static DEFINE_MUTEX(domain_list_lock); 3909e61dafSJames Morse 406789fb99SJames Morse /* 416789fb99SJames Morse * MPAM emulates CDP by setting different PARTID in the I/D fields of MPAM0_EL1. 426789fb99SJames Morse * This applies globally to all traffic the CPU generates. 436789fb99SJames Morse */ 449d2e1a99SJames Morse static bool cdp_enabled; 459d2e1a99SJames Morse 461c1e2968SBen Horgan /* 471c1e2968SBen Horgan * We use cacheinfo to discover the size of the caches and their id. cacheinfo 481c1e2968SBen Horgan * populates this from a device_initcall(). mpam_resctrl_setup() must wait. 491c1e2968SBen Horgan */ 501c1e2968SBen Horgan static bool cacheinfo_ready; 511c1e2968SBen Horgan static DECLARE_WAIT_QUEUE_HEAD(wait_cacheinfo_ready); 521c1e2968SBen Horgan 5309e61dafSJames Morse bool resctrl_arch_alloc_capable(void) 5409e61dafSJames Morse { 5509e61dafSJames Morse struct mpam_resctrl_res *res; 5609e61dafSJames Morse enum resctrl_res_level rid; 5709e61dafSJames Morse 5809e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 5909e61dafSJames Morse if (res->resctrl_res.alloc_capable) 6009e61dafSJames Morse return true; 6109e61dafSJames Morse } 6209e61dafSJames Morse 6309e61dafSJames Morse return false; 6409e61dafSJames Morse } 6509e61dafSJames Morse 666789fb99SJames Morse bool resctrl_arch_get_cdp_enabled(enum resctrl_res_level rid) 676789fb99SJames Morse { 686789fb99SJames Morse return mpam_resctrl_controls[rid].cdp_enabled; 696789fb99SJames Morse } 706789fb99SJames Morse 716789fb99SJames Morse /** 726789fb99SJames Morse * resctrl_reset_task_closids() - Reset the PARTID/PMG values for all tasks. 736789fb99SJames Morse * 746789fb99SJames Morse * At boot, all existing tasks use partid zero for D and I. 756789fb99SJames Morse * To enable/disable CDP emulation, all these tasks need relabelling. 766789fb99SJames Morse */ 776789fb99SJames Morse static void resctrl_reset_task_closids(void) 786789fb99SJames Morse { 796789fb99SJames Morse struct task_struct *p, *t; 806789fb99SJames Morse 816789fb99SJames Morse read_lock(&tasklist_lock); 826789fb99SJames Morse for_each_process_thread(p, t) { 836789fb99SJames Morse resctrl_arch_set_closid_rmid(t, RESCTRL_RESERVED_CLOSID, 846789fb99SJames Morse RESCTRL_RESERVED_RMID); 856789fb99SJames Morse } 866789fb99SJames Morse read_unlock(&tasklist_lock); 876789fb99SJames Morse } 886789fb99SJames Morse 896789fb99SJames Morse int resctrl_arch_set_cdp_enabled(enum resctrl_res_level rid, bool enable) 906789fb99SJames Morse { 916789fb99SJames Morse u32 partid_i = RESCTRL_RESERVED_CLOSID, partid_d = RESCTRL_RESERVED_CLOSID; 926789fb99SJames Morse int cpu; 936789fb99SJames Morse 9401a0021fSBen Horgan if (!IS_ENABLED(CONFIG_EXPERT) && enable) { 9501a0021fSBen Horgan /* 9601a0021fSBen Horgan * If the resctrl fs is mounted more than once, sequentially, 9701a0021fSBen Horgan * then CDP can lead to the use of out of range PARTIDs. 9801a0021fSBen Horgan */ 9901a0021fSBen Horgan pr_warn("CDP not supported\n"); 10001a0021fSBen Horgan return -EOPNOTSUPP; 10101a0021fSBen Horgan } 10201a0021fSBen Horgan 10301a0021fSBen Horgan if (enable) 10401a0021fSBen Horgan pr_warn("CDP is an expert feature and may cause MPAM to malfunction.\n"); 10501a0021fSBen Horgan 1066789fb99SJames Morse /* 1076789fb99SJames Morse * resctrl_arch_set_cdp_enabled() is only called with enable set to 1086789fb99SJames Morse * false on error and unmount. 1096789fb99SJames Morse */ 1106789fb99SJames Morse cdp_enabled = enable; 1116789fb99SJames Morse mpam_resctrl_controls[rid].cdp_enabled = enable; 1126789fb99SJames Morse 1136789fb99SJames Morse /* The mbw_max feature can't hide cdp as it's a per-partid maximum. */ 1146789fb99SJames Morse if (cdp_enabled && !mpam_resctrl_controls[RDT_RESOURCE_MBA].cdp_enabled) 1156789fb99SJames Morse mpam_resctrl_controls[RDT_RESOURCE_MBA].resctrl_res.alloc_capable = false; 1166789fb99SJames Morse 1176789fb99SJames Morse if (mpam_resctrl_controls[RDT_RESOURCE_MBA].cdp_enabled && 1186789fb99SJames Morse mpam_resctrl_controls[RDT_RESOURCE_MBA].class) 1196789fb99SJames Morse mpam_resctrl_controls[RDT_RESOURCE_MBA].resctrl_res.alloc_capable = true; 1206789fb99SJames Morse 1216789fb99SJames Morse if (enable) { 1226789fb99SJames Morse if (mpam_partid_max < 1) 1236789fb99SJames Morse return -EINVAL; 1246789fb99SJames Morse 1256789fb99SJames Morse partid_d = resctrl_get_config_index(RESCTRL_RESERVED_CLOSID, CDP_DATA); 1266789fb99SJames Morse partid_i = resctrl_get_config_index(RESCTRL_RESERVED_CLOSID, CDP_CODE); 1276789fb99SJames Morse } 1286789fb99SJames Morse 1296789fb99SJames Morse mpam_set_task_partid_pmg(current, partid_d, partid_i, 0, 0); 1306789fb99SJames Morse WRITE_ONCE(arm64_mpam_global_default, mpam_get_regval(current)); 1316789fb99SJames Morse 1326789fb99SJames Morse resctrl_reset_task_closids(); 1336789fb99SJames Morse 1346789fb99SJames Morse for_each_possible_cpu(cpu) 1356789fb99SJames Morse mpam_set_cpu_defaults(cpu, partid_d, partid_i, 0, 0); 1366789fb99SJames Morse on_each_cpu(resctrl_arch_sync_cpu_closid_rmid, NULL, 1); 1376789fb99SJames Morse 1386789fb99SJames Morse return 0; 1396789fb99SJames Morse } 1406789fb99SJames Morse 1416789fb99SJames Morse static bool mpam_resctrl_hide_cdp(enum resctrl_res_level rid) 1426789fb99SJames Morse { 1436789fb99SJames Morse return cdp_enabled && !resctrl_arch_get_cdp_enabled(rid); 1446789fb99SJames Morse } 1456789fb99SJames Morse 14609e61dafSJames Morse /* 14709e61dafSJames Morse * MSC may raise an error interrupt if it sees an out or range partid/pmg, 14809e61dafSJames Morse * and go on to truncate the value. Regardless of what the hardware supports, 14909e61dafSJames Morse * only the system wide safe value is safe to use. 15009e61dafSJames Morse */ 15109e61dafSJames Morse u32 resctrl_arch_get_num_closid(struct rdt_resource *ignored) 15209e61dafSJames Morse { 15309e61dafSJames Morse return mpam_partid_max + 1; 15409e61dafSJames Morse } 15509e61dafSJames Morse 1563e9b3582SBen Horgan u32 resctrl_arch_system_num_rmid_idx(void) 1573e9b3582SBen Horgan { 1583e9b3582SBen Horgan return (mpam_pmg_max + 1) * (mpam_partid_max + 1); 1593e9b3582SBen Horgan } 1603e9b3582SBen Horgan 1613e9b3582SBen Horgan u32 resctrl_arch_rmid_idx_encode(u32 closid, u32 rmid) 1623e9b3582SBen Horgan { 1633e9b3582SBen Horgan return closid * (mpam_pmg_max + 1) + rmid; 1643e9b3582SBen Horgan } 1653e9b3582SBen Horgan 1663e9b3582SBen Horgan void resctrl_arch_rmid_idx_decode(u32 idx, u32 *closid, u32 *rmid) 1673e9b3582SBen Horgan { 1683e9b3582SBen Horgan *closid = idx / (mpam_pmg_max + 1); 1693e9b3582SBen Horgan *rmid = idx % (mpam_pmg_max + 1); 1703e9b3582SBen Horgan } 1713e9b3582SBen Horgan 1729d2e1a99SJames Morse void resctrl_arch_sched_in(struct task_struct *tsk) 1739d2e1a99SJames Morse { 1749d2e1a99SJames Morse lockdep_assert_preemption_disabled(); 1759d2e1a99SJames Morse 1769d2e1a99SJames Morse mpam_thread_switch(tsk); 1779d2e1a99SJames Morse } 1789d2e1a99SJames Morse 1799d2e1a99SJames Morse void resctrl_arch_set_cpu_default_closid_rmid(int cpu, u32 closid, u32 rmid) 1809d2e1a99SJames Morse { 1819d2e1a99SJames Morse WARN_ON_ONCE(closid > U16_MAX); 1829d2e1a99SJames Morse WARN_ON_ONCE(rmid > U8_MAX); 1839d2e1a99SJames Morse 1849d2e1a99SJames Morse if (!cdp_enabled) { 1859d2e1a99SJames Morse mpam_set_cpu_defaults(cpu, closid, closid, rmid, rmid); 1869d2e1a99SJames Morse } else { 1879d2e1a99SJames Morse /* 1889d2e1a99SJames Morse * When CDP is enabled, resctrl halves the closid range and we 1899d2e1a99SJames Morse * use odd/even partid for one closid. 1909d2e1a99SJames Morse */ 1919d2e1a99SJames Morse u32 partid_d = resctrl_get_config_index(closid, CDP_DATA); 1929d2e1a99SJames Morse u32 partid_i = resctrl_get_config_index(closid, CDP_CODE); 1939d2e1a99SJames Morse 1949d2e1a99SJames Morse mpam_set_cpu_defaults(cpu, partid_d, partid_i, rmid, rmid); 1959d2e1a99SJames Morse } 1969d2e1a99SJames Morse } 1979d2e1a99SJames Morse 1989d2e1a99SJames Morse void resctrl_arch_sync_cpu_closid_rmid(void *info) 1999d2e1a99SJames Morse { 2009d2e1a99SJames Morse struct resctrl_cpu_defaults *r = info; 2019d2e1a99SJames Morse 2029d2e1a99SJames Morse lockdep_assert_preemption_disabled(); 2039d2e1a99SJames Morse 2049d2e1a99SJames Morse if (r) { 2059d2e1a99SJames Morse resctrl_arch_set_cpu_default_closid_rmid(smp_processor_id(), 2069d2e1a99SJames Morse r->closid, r->rmid); 2079d2e1a99SJames Morse } 2089d2e1a99SJames Morse 2099d2e1a99SJames Morse resctrl_arch_sched_in(current); 2109d2e1a99SJames Morse } 2119d2e1a99SJames Morse 2129d2e1a99SJames Morse void resctrl_arch_set_closid_rmid(struct task_struct *tsk, u32 closid, u32 rmid) 2139d2e1a99SJames Morse { 2149d2e1a99SJames Morse WARN_ON_ONCE(closid > U16_MAX); 2159d2e1a99SJames Morse WARN_ON_ONCE(rmid > U8_MAX); 2169d2e1a99SJames Morse 2179d2e1a99SJames Morse if (!cdp_enabled) { 2189d2e1a99SJames Morse mpam_set_task_partid_pmg(tsk, closid, closid, rmid, rmid); 2199d2e1a99SJames Morse } else { 2209d2e1a99SJames Morse u32 partid_d = resctrl_get_config_index(closid, CDP_DATA); 2219d2e1a99SJames Morse u32 partid_i = resctrl_get_config_index(closid, CDP_CODE); 2229d2e1a99SJames Morse 2239d2e1a99SJames Morse mpam_set_task_partid_pmg(tsk, partid_d, partid_i, rmid, rmid); 2249d2e1a99SJames Morse } 2259d2e1a99SJames Morse } 2269d2e1a99SJames Morse 2276789fb99SJames Morse bool resctrl_arch_match_closid(struct task_struct *tsk, u32 closid) 2286789fb99SJames Morse { 2296789fb99SJames Morse u64 regval = mpam_get_regval(tsk); 2306789fb99SJames Morse u32 tsk_closid = FIELD_GET(MPAM0_EL1_PARTID_D, regval); 2316789fb99SJames Morse 2326789fb99SJames Morse if (cdp_enabled) 2336789fb99SJames Morse tsk_closid >>= 1; 2346789fb99SJames Morse 2356789fb99SJames Morse return tsk_closid == closid; 2366789fb99SJames Morse } 2376789fb99SJames Morse 2386789fb99SJames Morse /* The task's pmg is not unique, the partid must be considered too */ 2396789fb99SJames Morse bool resctrl_arch_match_rmid(struct task_struct *tsk, u32 closid, u32 rmid) 2406789fb99SJames Morse { 2416789fb99SJames Morse u64 regval = mpam_get_regval(tsk); 2426789fb99SJames Morse u32 tsk_closid = FIELD_GET(MPAM0_EL1_PARTID_D, regval); 2436789fb99SJames Morse u32 tsk_rmid = FIELD_GET(MPAM0_EL1_PMG_D, regval); 2446789fb99SJames Morse 2456789fb99SJames Morse if (cdp_enabled) 2466789fb99SJames Morse tsk_closid >>= 1; 2476789fb99SJames Morse 2486789fb99SJames Morse return (tsk_closid == closid) && (tsk_rmid == rmid); 2496789fb99SJames Morse } 2506789fb99SJames Morse 25109e61dafSJames Morse struct rdt_resource *resctrl_arch_get_resource(enum resctrl_res_level l) 25209e61dafSJames Morse { 25309e61dafSJames Morse if (l >= RDT_NUM_RESOURCES) 25409e61dafSJames Morse return NULL; 25509e61dafSJames Morse 25609e61dafSJames Morse return &mpam_resctrl_controls[l].resctrl_res; 25709e61dafSJames Morse } 25809e61dafSJames Morse 25952a4edb1SJames Morse static bool cache_has_usable_cpor(struct mpam_class *class) 26052a4edb1SJames Morse { 26152a4edb1SJames Morse struct mpam_props *cprops = &class->props; 26252a4edb1SJames Morse 26352a4edb1SJames Morse if (!mpam_has_feature(mpam_feat_cpor_part, cprops)) 26452a4edb1SJames Morse return false; 26552a4edb1SJames Morse 26652a4edb1SJames Morse /* resctrl uses u32 for all bitmap configurations */ 26752a4edb1SJames Morse return class->props.cpbm_wd <= 32; 26852a4edb1SJames Morse } 26952a4edb1SJames Morse 27036528c76SJames Morse static bool mba_class_use_mbw_max(struct mpam_props *cprops) 27136528c76SJames Morse { 27236528c76SJames Morse return (mpam_has_feature(mpam_feat_mbw_max, cprops) && 27336528c76SJames Morse cprops->bwa_wd); 27436528c76SJames Morse } 27536528c76SJames Morse 27636528c76SJames Morse static bool class_has_usable_mba(struct mpam_props *cprops) 27736528c76SJames Morse { 27836528c76SJames Morse return mba_class_use_mbw_max(cprops); 27936528c76SJames Morse } 28036528c76SJames Morse 28136528c76SJames Morse /* 28236528c76SJames Morse * Calculate the worst-case percentage change from each implemented step 28336528c76SJames Morse * in the control. 28436528c76SJames Morse */ 28536528c76SJames Morse static u32 get_mba_granularity(struct mpam_props *cprops) 28636528c76SJames Morse { 28736528c76SJames Morse if (!mba_class_use_mbw_max(cprops)) 28836528c76SJames Morse return 0; 28936528c76SJames Morse 29036528c76SJames Morse /* 29136528c76SJames Morse * bwa_wd is the number of bits implemented in the 0.xxx 29236528c76SJames Morse * fixed point fraction. 1 bit is 50%, 2 is 25% etc. 29336528c76SJames Morse */ 29436528c76SJames Morse return DIV_ROUND_UP(MAX_MBA_BW, 1 << cprops->bwa_wd); 29536528c76SJames Morse } 29636528c76SJames Morse 29780d147d2SDave Martin /* 29880d147d2SDave Martin * Each fixed-point hardware value architecturally represents a range 29980d147d2SDave Martin * of values: the full range 0% - 100% is split contiguously into 30080d147d2SDave Martin * (1 << cprops->bwa_wd) equal bands. 30180d147d2SDave Martin * 30280d147d2SDave Martin * Although the bwa_bwd fields have 6 bits the maximum valid value is 16 30380d147d2SDave Martin * as it reports the width of fields that are at most 16 bits. When 30480d147d2SDave Martin * fewer than 16 bits are valid the least significant bits are 30580d147d2SDave Martin * ignored. The implied binary point is kept between bits 15 and 16 and 30680d147d2SDave Martin * so the valid bits are leftmost. 30780d147d2SDave Martin * 30880d147d2SDave Martin * See ARM IHI0099B.a "MPAM system component specification", Section 9.3, 30980d147d2SDave Martin * "The fixed-point fractional format" for more information. 31080d147d2SDave Martin * 31180d147d2SDave Martin * Find the nearest percentage value to the upper bound of the selected band: 31280d147d2SDave Martin */ 31380d147d2SDave Martin static u32 mbw_max_to_percent(u16 mbw_max, struct mpam_props *cprops) 31480d147d2SDave Martin { 31580d147d2SDave Martin u32 val = mbw_max; 31680d147d2SDave Martin 31780d147d2SDave Martin val >>= 16 - cprops->bwa_wd; 31880d147d2SDave Martin val += 1; 31980d147d2SDave Martin val *= MAX_MBA_BW; 32080d147d2SDave Martin val = DIV_ROUND_CLOSEST(val, 1 << cprops->bwa_wd); 32180d147d2SDave Martin 32280d147d2SDave Martin return val; 32380d147d2SDave Martin } 32480d147d2SDave Martin 32580d147d2SDave Martin /* 32680d147d2SDave Martin * Find the band whose upper bound is closest to the specified percentage. 32780d147d2SDave Martin * 32880d147d2SDave Martin * A round-to-nearest policy is followed here as a balanced compromise 32980d147d2SDave Martin * between unexpected under-commit of the resource (where the total of 33080d147d2SDave Martin * a set of resource allocations after conversion is less than the 33180d147d2SDave Martin * expected total, due to rounding of the individual converted 33280d147d2SDave Martin * percentages) and over-commit (where the total of the converted 33380d147d2SDave Martin * allocations is greater than expected). 33480d147d2SDave Martin */ 33580d147d2SDave Martin static u16 percent_to_mbw_max(u8 pc, struct mpam_props *cprops) 33680d147d2SDave Martin { 33780d147d2SDave Martin u32 val = pc; 33880d147d2SDave Martin 33980d147d2SDave Martin val <<= cprops->bwa_wd; 34080d147d2SDave Martin val = DIV_ROUND_CLOSEST(val, MAX_MBA_BW); 34180d147d2SDave Martin val = max(val, 1) - 1; 34280d147d2SDave Martin val <<= 16 - cprops->bwa_wd; 34380d147d2SDave Martin 34480d147d2SDave Martin return val; 34580d147d2SDave Martin } 34680d147d2SDave Martin 34736528c76SJames Morse static u32 get_mba_min(struct mpam_props *cprops) 34836528c76SJames Morse { 34936528c76SJames Morse if (!mba_class_use_mbw_max(cprops)) { 35036528c76SJames Morse WARN_ON_ONCE(1); 35136528c76SJames Morse return 0; 35236528c76SJames Morse } 35336528c76SJames Morse 35436528c76SJames Morse return mbw_max_to_percent(0, cprops); 35536528c76SJames Morse } 35636528c76SJames Morse 35736528c76SJames Morse /* Find the L3 cache that has affinity with this CPU */ 35836528c76SJames Morse static int find_l3_equivalent_bitmask(int cpu, cpumask_var_t tmp_cpumask) 35936528c76SJames Morse { 36036528c76SJames Morse u32 cache_id = get_cpu_cacheinfo_id(cpu, 3); 36136528c76SJames Morse 36236528c76SJames Morse lockdep_assert_cpus_held(); 36336528c76SJames Morse 36436528c76SJames Morse return mpam_get_cpumask_from_cache_id(cache_id, 3, tmp_cpumask); 36536528c76SJames Morse } 36636528c76SJames Morse 36736528c76SJames Morse /* 36836528c76SJames Morse * topology_matches_l3() - Is the provided class the same shape as L3 36936528c76SJames Morse * @victim: The class we'd like to pretend is L3. 37036528c76SJames Morse * 37136528c76SJames Morse * resctrl expects all the world's a Xeon, and all counters are on the 37236528c76SJames Morse * L3. We allow some mapping counters on other classes. This requires 37336528c76SJames Morse * that the CPU->domain mapping is the same kind of shape. 37436528c76SJames Morse * 37536528c76SJames Morse * Using cacheinfo directly would make this work even if resctrl can't 37636528c76SJames Morse * use the L3 - but cacheinfo can't tell us anything about offline CPUs. 37736528c76SJames Morse * Using the L3 resctrl domain list also depends on CPUs being online. 37836528c76SJames Morse * Using the mpam_class we picked for L3 so we can use its domain list 37936528c76SJames Morse * assumes that there are MPAM controls on the L3. 38036528c76SJames Morse * Instead, this path eventually uses the mpam_get_cpumask_from_cache_id() 38136528c76SJames Morse * helper which can tell us about offline CPUs ... but getting the cache_id 38236528c76SJames Morse * to start with relies on at least one CPU per L3 cache being online at 38336528c76SJames Morse * boot. 38436528c76SJames Morse * 38536528c76SJames Morse * Walk the victim component list and compare the affinity mask with the 38636528c76SJames Morse * corresponding L3. The topology matches if each victim:component's affinity 38736528c76SJames Morse * mask is the same as the CPU's corresponding L3's. These lists/masks are 38836528c76SJames Morse * computed from firmware tables so don't change at runtime. 38936528c76SJames Morse */ 39036528c76SJames Morse static bool topology_matches_l3(struct mpam_class *victim) 39136528c76SJames Morse { 39236528c76SJames Morse int cpu, err; 39336528c76SJames Morse struct mpam_component *victim_iter; 39436528c76SJames Morse 39536528c76SJames Morse lockdep_assert_cpus_held(); 39636528c76SJames Morse 39736528c76SJames Morse cpumask_var_t __free(free_cpumask_var) tmp_cpumask = CPUMASK_VAR_NULL; 39836528c76SJames Morse if (!alloc_cpumask_var(&tmp_cpumask, GFP_KERNEL)) 39936528c76SJames Morse return false; 40036528c76SJames Morse 40136528c76SJames Morse guard(srcu)(&mpam_srcu); 40236528c76SJames Morse list_for_each_entry_srcu(victim_iter, &victim->components, class_list, 40336528c76SJames Morse srcu_read_lock_held(&mpam_srcu)) { 40436528c76SJames Morse if (cpumask_empty(&victim_iter->affinity)) { 40536528c76SJames Morse pr_debug("class %u has CPU-less component %u - can't match L3!\n", 40636528c76SJames Morse victim->level, victim_iter->comp_id); 40736528c76SJames Morse return false; 40836528c76SJames Morse } 40936528c76SJames Morse 41036528c76SJames Morse cpu = cpumask_any_and(&victim_iter->affinity, cpu_online_mask); 41136528c76SJames Morse if (WARN_ON_ONCE(cpu >= nr_cpu_ids)) 41236528c76SJames Morse return false; 41336528c76SJames Morse 41436528c76SJames Morse cpumask_clear(tmp_cpumask); 41536528c76SJames Morse err = find_l3_equivalent_bitmask(cpu, tmp_cpumask); 41636528c76SJames Morse if (err) { 41736528c76SJames Morse pr_debug("Failed to find L3's equivalent component to class %u component %u\n", 41836528c76SJames Morse victim->level, victim_iter->comp_id); 41936528c76SJames Morse return false; 42036528c76SJames Morse } 42136528c76SJames Morse 42236528c76SJames Morse /* Any differing bits in the affinity mask? */ 42336528c76SJames Morse if (!cpumask_equal(tmp_cpumask, &victim_iter->affinity)) { 42436528c76SJames Morse pr_debug("class %u component %u has Mismatched CPU mask with L3 equivalent\n" 42536528c76SJames Morse "L3:%*pbl != victim:%*pbl\n", 42636528c76SJames Morse victim->level, victim_iter->comp_id, 42736528c76SJames Morse cpumask_pr_args(tmp_cpumask), 42836528c76SJames Morse cpumask_pr_args(&victim_iter->affinity)); 42936528c76SJames Morse 43036528c76SJames Morse return false; 43136528c76SJames Morse } 43236528c76SJames Morse } 43336528c76SJames Morse 43436528c76SJames Morse return true; 43536528c76SJames Morse } 43636528c76SJames Morse 43736528c76SJames Morse /* 43836528c76SJames Morse * Test if the traffic for a class matches that at egress from the L3. For 43936528c76SJames Morse * MSC at memory controllers this is only possible if there is a single L3 44036528c76SJames Morse * as otherwise the counters at the memory can include bandwidth from the 44136528c76SJames Morse * non-local L3. 44236528c76SJames Morse */ 44336528c76SJames Morse static bool traffic_matches_l3(struct mpam_class *class) 44436528c76SJames Morse { 44536528c76SJames Morse int err, cpu; 44636528c76SJames Morse 44736528c76SJames Morse lockdep_assert_cpus_held(); 44836528c76SJames Morse 44936528c76SJames Morse if (class->type == MPAM_CLASS_CACHE && class->level == 3) 45036528c76SJames Morse return true; 45136528c76SJames Morse 45236528c76SJames Morse if (class->type == MPAM_CLASS_CACHE && class->level != 3) { 45336528c76SJames Morse pr_debug("class %u is a different cache from L3\n", class->level); 45436528c76SJames Morse return false; 45536528c76SJames Morse } 45636528c76SJames Morse 45736528c76SJames Morse if (class->type != MPAM_CLASS_MEMORY) { 45836528c76SJames Morse pr_debug("class %u is neither of type cache or memory\n", class->level); 45936528c76SJames Morse return false; 46036528c76SJames Morse } 46136528c76SJames Morse 46236528c76SJames Morse cpumask_var_t __free(free_cpumask_var) tmp_cpumask = CPUMASK_VAR_NULL; 46336528c76SJames Morse if (!alloc_cpumask_var(&tmp_cpumask, GFP_KERNEL)) { 46436528c76SJames Morse pr_debug("cpumask allocation failed\n"); 46536528c76SJames Morse return false; 46636528c76SJames Morse } 46736528c76SJames Morse 46836528c76SJames Morse cpu = cpumask_any_and(&class->affinity, cpu_online_mask); 46936528c76SJames Morse err = find_l3_equivalent_bitmask(cpu, tmp_cpumask); 47036528c76SJames Morse if (err) { 47136528c76SJames Morse pr_debug("Failed to find L3 downstream to cpu %d\n", cpu); 47236528c76SJames Morse return false; 47336528c76SJames Morse } 47436528c76SJames Morse 47536528c76SJames Morse if (!cpumask_equal(tmp_cpumask, cpu_possible_mask)) { 47636528c76SJames Morse pr_debug("There is more than one L3\n"); 47736528c76SJames Morse return false; 47836528c76SJames Morse } 47936528c76SJames Morse 48036528c76SJames Morse /* Be strict; the traffic might stop in the intermediate cache. */ 48136528c76SJames Morse if (get_cpu_cacheinfo_id(cpu, 4) != -1) { 48236528c76SJames Morse pr_debug("L3 isn't the last level of cache\n"); 48336528c76SJames Morse return false; 48436528c76SJames Morse } 48536528c76SJames Morse 48636528c76SJames Morse if (num_possible_nodes() > 1) { 48736528c76SJames Morse pr_debug("There is more than one numa node\n"); 48836528c76SJames Morse return false; 48936528c76SJames Morse } 49036528c76SJames Morse 49136528c76SJames Morse #ifdef CONFIG_HMEM_REPORTING 49236528c76SJames Morse if (node_devices[cpu_to_node(cpu)]->cache_dev) { 49336528c76SJames Morse pr_debug("There is a memory side cache\n"); 49436528c76SJames Morse return false; 49536528c76SJames Morse } 49636528c76SJames Morse #endif 49736528c76SJames Morse 49836528c76SJames Morse return true; 49936528c76SJames Morse } 50036528c76SJames Morse 50152a4edb1SJames Morse /* Test whether we can export MPAM_CLASS_CACHE:{2,3}? */ 50252a4edb1SJames Morse static void mpam_resctrl_pick_caches(void) 50352a4edb1SJames Morse { 50452a4edb1SJames Morse struct mpam_class *class; 50552a4edb1SJames Morse struct mpam_resctrl_res *res; 50652a4edb1SJames Morse 50752a4edb1SJames Morse lockdep_assert_cpus_held(); 50852a4edb1SJames Morse 50952a4edb1SJames Morse guard(srcu)(&mpam_srcu); 51052a4edb1SJames Morse list_for_each_entry_srcu(class, &mpam_classes, classes_list, 51152a4edb1SJames Morse srcu_read_lock_held(&mpam_srcu)) { 51252a4edb1SJames Morse if (class->type != MPAM_CLASS_CACHE) { 51352a4edb1SJames Morse pr_debug("class %u is not a cache\n", class->level); 51452a4edb1SJames Morse continue; 51552a4edb1SJames Morse } 51652a4edb1SJames Morse 51752a4edb1SJames Morse if (class->level != 2 && class->level != 3) { 51852a4edb1SJames Morse pr_debug("class %u is not L2 or L3\n", class->level); 51952a4edb1SJames Morse continue; 52052a4edb1SJames Morse } 52152a4edb1SJames Morse 52252a4edb1SJames Morse if (!cache_has_usable_cpor(class)) { 52352a4edb1SJames Morse pr_debug("class %u cache misses CPOR\n", class->level); 52452a4edb1SJames Morse continue; 52552a4edb1SJames Morse } 52652a4edb1SJames Morse 52752a4edb1SJames Morse if (!cpumask_equal(&class->affinity, cpu_possible_mask)) { 52852a4edb1SJames Morse pr_debug("class %u has missing CPUs, mask %*pb != %*pb\n", class->level, 52952a4edb1SJames Morse cpumask_pr_args(&class->affinity), 53052a4edb1SJames Morse cpumask_pr_args(cpu_possible_mask)); 53152a4edb1SJames Morse continue; 53252a4edb1SJames Morse } 53352a4edb1SJames Morse 53452a4edb1SJames Morse if (class->level == 2) 53552a4edb1SJames Morse res = &mpam_resctrl_controls[RDT_RESOURCE_L2]; 53652a4edb1SJames Morse else 53752a4edb1SJames Morse res = &mpam_resctrl_controls[RDT_RESOURCE_L3]; 53852a4edb1SJames Morse res->class = class; 53952a4edb1SJames Morse } 54052a4edb1SJames Morse } 54152a4edb1SJames Morse 54236528c76SJames Morse static void mpam_resctrl_pick_mba(void) 54336528c76SJames Morse { 54436528c76SJames Morse struct mpam_class *class, *candidate_class = NULL; 54536528c76SJames Morse struct mpam_resctrl_res *res; 54636528c76SJames Morse 54736528c76SJames Morse lockdep_assert_cpus_held(); 54836528c76SJames Morse 54936528c76SJames Morse guard(srcu)(&mpam_srcu); 55036528c76SJames Morse list_for_each_entry_srcu(class, &mpam_classes, classes_list, 55136528c76SJames Morse srcu_read_lock_held(&mpam_srcu)) { 55236528c76SJames Morse struct mpam_props *cprops = &class->props; 55336528c76SJames Morse 55436528c76SJames Morse if (class->level != 3 && class->type == MPAM_CLASS_CACHE) { 55536528c76SJames Morse pr_debug("class %u is a cache but not the L3\n", class->level); 55636528c76SJames Morse continue; 55736528c76SJames Morse } 55836528c76SJames Morse 55936528c76SJames Morse if (!class_has_usable_mba(cprops)) { 56036528c76SJames Morse pr_debug("class %u has no bandwidth control\n", 56136528c76SJames Morse class->level); 56236528c76SJames Morse continue; 56336528c76SJames Morse } 56436528c76SJames Morse 56536528c76SJames Morse if (!cpumask_equal(&class->affinity, cpu_possible_mask)) { 56636528c76SJames Morse pr_debug("class %u has missing CPUs\n", class->level); 56736528c76SJames Morse continue; 56836528c76SJames Morse } 56936528c76SJames Morse 57036528c76SJames Morse if (!topology_matches_l3(class)) { 57136528c76SJames Morse pr_debug("class %u topology doesn't match L3\n", 57236528c76SJames Morse class->level); 57336528c76SJames Morse continue; 57436528c76SJames Morse } 57536528c76SJames Morse 57636528c76SJames Morse if (!traffic_matches_l3(class)) { 57736528c76SJames Morse pr_debug("class %u traffic doesn't match L3 egress\n", 57836528c76SJames Morse class->level); 57936528c76SJames Morse continue; 58036528c76SJames Morse } 58136528c76SJames Morse 58236528c76SJames Morse /* 58336528c76SJames Morse * Pick a resource to be MBA that as close as possible to 58436528c76SJames Morse * the L3. mbm_total counts the bandwidth leaving the L3 58536528c76SJames Morse * cache and MBA should correspond as closely as possible 58636528c76SJames Morse * for proper operation of mba_sc. 58736528c76SJames Morse */ 58836528c76SJames Morse if (!candidate_class || class->level < candidate_class->level) 58936528c76SJames Morse candidate_class = class; 59036528c76SJames Morse } 59136528c76SJames Morse 59236528c76SJames Morse if (candidate_class) { 59336528c76SJames Morse pr_debug("selected class %u to back MBA\n", 59436528c76SJames Morse candidate_class->level); 59536528c76SJames Morse res = &mpam_resctrl_controls[RDT_RESOURCE_MBA]; 59636528c76SJames Morse res->class = candidate_class; 59736528c76SJames Morse } 59836528c76SJames Morse } 59936528c76SJames Morse 60009e61dafSJames Morse static int mpam_resctrl_control_init(struct mpam_resctrl_res *res) 60109e61dafSJames Morse { 60252a4edb1SJames Morse struct mpam_class *class = res->class; 60336528c76SJames Morse struct mpam_props *cprops = &class->props; 60452a4edb1SJames Morse struct rdt_resource *r = &res->resctrl_res; 60552a4edb1SJames Morse 60652a4edb1SJames Morse switch (r->rid) { 60752a4edb1SJames Morse case RDT_RESOURCE_L2: 60852a4edb1SJames Morse case RDT_RESOURCE_L3: 60952a4edb1SJames Morse r->schema_fmt = RESCTRL_SCHEMA_BITMAP; 61052a4edb1SJames Morse r->cache.arch_has_sparse_bitmasks = true; 61152a4edb1SJames Morse 61252a4edb1SJames Morse r->cache.cbm_len = class->props.cpbm_wd; 61352a4edb1SJames Morse /* mpam_devices will reject empty bitmaps */ 61452a4edb1SJames Morse r->cache.min_cbm_bits = 1; 61552a4edb1SJames Morse 61652a4edb1SJames Morse if (r->rid == RDT_RESOURCE_L2) { 61752a4edb1SJames Morse r->name = "L2"; 61852a4edb1SJames Morse r->ctrl_scope = RESCTRL_L2_CACHE; 61952a4edb1SJames Morse r->cdp_capable = true; 62052a4edb1SJames Morse } else { 62152a4edb1SJames Morse r->name = "L3"; 62252a4edb1SJames Morse r->ctrl_scope = RESCTRL_L3_CACHE; 62352a4edb1SJames Morse r->cdp_capable = true; 62452a4edb1SJames Morse } 62552a4edb1SJames Morse 62652a4edb1SJames Morse /* 62752a4edb1SJames Morse * Which bits are shared with other ...things... Unknown 62852a4edb1SJames Morse * devices use partid-0 which uses all the bitmap fields. Until 62952a4edb1SJames Morse * we have configured the SMMU and GIC not to do this 'all the 63052a4edb1SJames Morse * bits' is the correct answer here. 63152a4edb1SJames Morse */ 63252a4edb1SJames Morse r->cache.shareable_bits = resctrl_get_default_ctrl(r); 63352a4edb1SJames Morse r->alloc_capable = true; 63452a4edb1SJames Morse break; 63536528c76SJames Morse case RDT_RESOURCE_MBA: 63636528c76SJames Morse r->schema_fmt = RESCTRL_SCHEMA_RANGE; 63736528c76SJames Morse r->ctrl_scope = RESCTRL_L3_CACHE; 63836528c76SJames Morse 63936528c76SJames Morse r->membw.delay_linear = true; 64036528c76SJames Morse r->membw.throttle_mode = THREAD_THROTTLE_UNDEFINED; 64136528c76SJames Morse r->membw.min_bw = get_mba_min(cprops); 64236528c76SJames Morse r->membw.max_bw = MAX_MBA_BW; 64336528c76SJames Morse r->membw.bw_gran = get_mba_granularity(cprops); 64436528c76SJames Morse 64536528c76SJames Morse r->name = "MB"; 64636528c76SJames Morse r->alloc_capable = true; 64736528c76SJames Morse break; 64852a4edb1SJames Morse default: 64952a4edb1SJames Morse return -EINVAL; 65052a4edb1SJames Morse } 65109e61dafSJames Morse 65209e61dafSJames Morse return 0; 65309e61dafSJames Morse } 65409e61dafSJames Morse 65509e61dafSJames Morse static int mpam_resctrl_pick_domain_id(int cpu, struct mpam_component *comp) 65609e61dafSJames Morse { 65709e61dafSJames Morse struct mpam_class *class = comp->class; 65809e61dafSJames Morse 65909e61dafSJames Morse if (class->type == MPAM_CLASS_CACHE) 66009e61dafSJames Morse return comp->comp_id; 66109e61dafSJames Morse 66236528c76SJames Morse if (topology_matches_l3(class)) { 66336528c76SJames Morse /* Use the corresponding L3 component ID as the domain ID */ 66436528c76SJames Morse int id = get_cpu_cacheinfo_id(cpu, 3); 66536528c76SJames Morse 66636528c76SJames Morse /* Implies topology_matches_l3() made a mistake */ 66736528c76SJames Morse if (WARN_ON_ONCE(id == -1)) 66836528c76SJames Morse return comp->comp_id; 66936528c76SJames Morse 67036528c76SJames Morse return id; 67136528c76SJames Morse } 67236528c76SJames Morse 67309e61dafSJames Morse /* Otherwise, expose the ID used by the firmware table code. */ 67409e61dafSJames Morse return comp->comp_id; 67509e61dafSJames Morse } 67609e61dafSJames Morse 67702cc6616SJames Morse u32 resctrl_arch_get_config(struct rdt_resource *r, struct rdt_ctrl_domain *d, 67802cc6616SJames Morse u32 closid, enum resctrl_conf_type type) 67902cc6616SJames Morse { 68002cc6616SJames Morse u32 partid; 68102cc6616SJames Morse struct mpam_config *cfg; 68202cc6616SJames Morse struct mpam_props *cprops; 68302cc6616SJames Morse struct mpam_resctrl_res *res; 68402cc6616SJames Morse struct mpam_resctrl_dom *dom; 68502cc6616SJames Morse enum mpam_device_features configured_by; 68602cc6616SJames Morse 68702cc6616SJames Morse lockdep_assert_cpus_held(); 68802cc6616SJames Morse 68902cc6616SJames Morse if (!mpam_is_enabled()) 69002cc6616SJames Morse return resctrl_get_default_ctrl(r); 69102cc6616SJames Morse 69202cc6616SJames Morse res = container_of(r, struct mpam_resctrl_res, resctrl_res); 69302cc6616SJames Morse dom = container_of(d, struct mpam_resctrl_dom, resctrl_ctrl_dom); 69402cc6616SJames Morse cprops = &res->class->props; 69502cc6616SJames Morse 6966789fb99SJames Morse /* 6976789fb99SJames Morse * When CDP is enabled, but the resource doesn't support it, 6986789fb99SJames Morse * the control is cloned across both partids. 6996789fb99SJames Morse * Pick one at random to read: 7006789fb99SJames Morse */ 7016789fb99SJames Morse if (mpam_resctrl_hide_cdp(r->rid)) 7026789fb99SJames Morse type = CDP_DATA; 7036789fb99SJames Morse 70402cc6616SJames Morse partid = resctrl_get_config_index(closid, type); 70502cc6616SJames Morse cfg = &dom->ctrl_comp->cfg[partid]; 70602cc6616SJames Morse 70702cc6616SJames Morse switch (r->rid) { 70802cc6616SJames Morse case RDT_RESOURCE_L2: 70902cc6616SJames Morse case RDT_RESOURCE_L3: 71002cc6616SJames Morse configured_by = mpam_feat_cpor_part; 71102cc6616SJames Morse break; 71236528c76SJames Morse case RDT_RESOURCE_MBA: 71336528c76SJames Morse if (mpam_has_feature(mpam_feat_mbw_max, cprops)) { 71436528c76SJames Morse configured_by = mpam_feat_mbw_max; 71536528c76SJames Morse break; 71636528c76SJames Morse } 71736528c76SJames Morse fallthrough; 71802cc6616SJames Morse default: 71902cc6616SJames Morse return resctrl_get_default_ctrl(r); 72002cc6616SJames Morse } 72102cc6616SJames Morse 72202cc6616SJames Morse if (!r->alloc_capable || partid >= resctrl_arch_get_num_closid(r) || 72302cc6616SJames Morse !mpam_has_feature(configured_by, cfg)) 72402cc6616SJames Morse return resctrl_get_default_ctrl(r); 72502cc6616SJames Morse 72602cc6616SJames Morse switch (configured_by) { 72702cc6616SJames Morse case mpam_feat_cpor_part: 72802cc6616SJames Morse return cfg->cpbm; 72936528c76SJames Morse case mpam_feat_mbw_max: 73036528c76SJames Morse return mbw_max_to_percent(cfg->mbw_max, cprops); 73102cc6616SJames Morse default: 73202cc6616SJames Morse return resctrl_get_default_ctrl(r); 73302cc6616SJames Morse } 73402cc6616SJames Morse } 73502cc6616SJames Morse 7369cd2b522SJames Morse int resctrl_arch_update_one(struct rdt_resource *r, struct rdt_ctrl_domain *d, 7379cd2b522SJames Morse u32 closid, enum resctrl_conf_type t, u32 cfg_val) 7389cd2b522SJames Morse { 7396789fb99SJames Morse int err; 7409cd2b522SJames Morse u32 partid; 7419cd2b522SJames Morse struct mpam_config cfg; 7429cd2b522SJames Morse struct mpam_props *cprops; 7439cd2b522SJames Morse struct mpam_resctrl_res *res; 7449cd2b522SJames Morse struct mpam_resctrl_dom *dom; 7459cd2b522SJames Morse 7469cd2b522SJames Morse lockdep_assert_cpus_held(); 7479cd2b522SJames Morse lockdep_assert_irqs_enabled(); 7489cd2b522SJames Morse 7499cd2b522SJames Morse /* 7509cd2b522SJames Morse * No need to check the CPU as mpam_apply_config() doesn't care, and 7519cd2b522SJames Morse * resctrl_arch_update_domains() relies on this. 7529cd2b522SJames Morse */ 7539cd2b522SJames Morse res = container_of(r, struct mpam_resctrl_res, resctrl_res); 7549cd2b522SJames Morse dom = container_of(d, struct mpam_resctrl_dom, resctrl_ctrl_dom); 7559cd2b522SJames Morse cprops = &res->class->props; 7569cd2b522SJames Morse 7576789fb99SJames Morse if (mpam_resctrl_hide_cdp(r->rid)) 7586789fb99SJames Morse t = CDP_DATA; 7596789fb99SJames Morse 7609cd2b522SJames Morse partid = resctrl_get_config_index(closid, t); 7619cd2b522SJames Morse if (!r->alloc_capable || partid >= resctrl_arch_get_num_closid(r)) { 7629cd2b522SJames Morse pr_debug("Not alloc capable or computed PARTID out of range\n"); 7639cd2b522SJames Morse return -EINVAL; 7649cd2b522SJames Morse } 7659cd2b522SJames Morse 7669cd2b522SJames Morse /* 7679cd2b522SJames Morse * Copy the current config to avoid clearing other resources when the 7689cd2b522SJames Morse * same component is exposed multiple times through resctrl. 7699cd2b522SJames Morse */ 7709cd2b522SJames Morse cfg = dom->ctrl_comp->cfg[partid]; 7719cd2b522SJames Morse 7729cd2b522SJames Morse switch (r->rid) { 7739cd2b522SJames Morse case RDT_RESOURCE_L2: 7749cd2b522SJames Morse case RDT_RESOURCE_L3: 7759cd2b522SJames Morse cfg.cpbm = cfg_val; 7769cd2b522SJames Morse mpam_set_feature(mpam_feat_cpor_part, &cfg); 7779cd2b522SJames Morse break; 77836528c76SJames Morse case RDT_RESOURCE_MBA: 77936528c76SJames Morse if (mpam_has_feature(mpam_feat_mbw_max, cprops)) { 78036528c76SJames Morse cfg.mbw_max = percent_to_mbw_max(cfg_val, cprops); 78136528c76SJames Morse mpam_set_feature(mpam_feat_mbw_max, &cfg); 78236528c76SJames Morse break; 78336528c76SJames Morse } 78436528c76SJames Morse fallthrough; 7859cd2b522SJames Morse default: 7869cd2b522SJames Morse return -EINVAL; 7879cd2b522SJames Morse } 7889cd2b522SJames Morse 7896789fb99SJames Morse /* 7906789fb99SJames Morse * When CDP is enabled, but the resource doesn't support it, we need to 7916789fb99SJames Morse * apply the same configuration to the other partid. 7926789fb99SJames Morse */ 7936789fb99SJames Morse if (mpam_resctrl_hide_cdp(r->rid)) { 7946789fb99SJames Morse partid = resctrl_get_config_index(closid, CDP_CODE); 7956789fb99SJames Morse err = mpam_apply_config(dom->ctrl_comp, partid, &cfg); 7966789fb99SJames Morse if (err) 7976789fb99SJames Morse return err; 7986789fb99SJames Morse 7996789fb99SJames Morse partid = resctrl_get_config_index(closid, CDP_DATA); 8006789fb99SJames Morse return mpam_apply_config(dom->ctrl_comp, partid, &cfg); 8016789fb99SJames Morse } 8026789fb99SJames Morse 8039cd2b522SJames Morse return mpam_apply_config(dom->ctrl_comp, partid, &cfg); 8049cd2b522SJames Morse } 8059cd2b522SJames Morse 8069cd2b522SJames Morse int resctrl_arch_update_domains(struct rdt_resource *r, u32 closid) 8079cd2b522SJames Morse { 8089cd2b522SJames Morse int err; 8099cd2b522SJames Morse struct rdt_ctrl_domain *d; 8109cd2b522SJames Morse 8119cd2b522SJames Morse lockdep_assert_cpus_held(); 8129cd2b522SJames Morse lockdep_assert_irqs_enabled(); 8139cd2b522SJames Morse 8149cd2b522SJames Morse list_for_each_entry_rcu(d, &r->ctrl_domains, hdr.list) { 8159cd2b522SJames Morse for (enum resctrl_conf_type t = 0; t < CDP_NUM_TYPES; t++) { 8169cd2b522SJames Morse struct resctrl_staged_config *cfg = &d->staged_config[t]; 8179cd2b522SJames Morse 8189cd2b522SJames Morse if (!cfg->have_new_ctrl) 8199cd2b522SJames Morse continue; 8209cd2b522SJames Morse 8219cd2b522SJames Morse err = resctrl_arch_update_one(r, d, closid, t, 8229cd2b522SJames Morse cfg->new_ctrl); 8239cd2b522SJames Morse if (err) 8249cd2b522SJames Morse return err; 8259cd2b522SJames Morse } 8269cd2b522SJames Morse } 8279cd2b522SJames Morse 8289cd2b522SJames Morse return 0; 8299cd2b522SJames Morse } 8309cd2b522SJames Morse 831370d166dSJames Morse void resctrl_arch_reset_all_ctrls(struct rdt_resource *r) 832370d166dSJames Morse { 833370d166dSJames Morse struct mpam_resctrl_res *res; 834370d166dSJames Morse 835370d166dSJames Morse lockdep_assert_cpus_held(); 836370d166dSJames Morse 837370d166dSJames Morse if (!mpam_is_enabled()) 838370d166dSJames Morse return; 839370d166dSJames Morse 840370d166dSJames Morse res = container_of(r, struct mpam_resctrl_res, resctrl_res); 841370d166dSJames Morse mpam_reset_class_locked(res->class); 842370d166dSJames Morse } 843370d166dSJames Morse 84409e61dafSJames Morse static void mpam_resctrl_domain_hdr_init(int cpu, struct mpam_component *comp, 84509e61dafSJames Morse enum resctrl_res_level rid, 84609e61dafSJames Morse struct rdt_domain_hdr *hdr) 84709e61dafSJames Morse { 84809e61dafSJames Morse lockdep_assert_cpus_held(); 84909e61dafSJames Morse 85009e61dafSJames Morse INIT_LIST_HEAD(&hdr->list); 85109e61dafSJames Morse hdr->id = mpam_resctrl_pick_domain_id(cpu, comp); 85209e61dafSJames Morse hdr->rid = rid; 85309e61dafSJames Morse cpumask_set_cpu(cpu, &hdr->cpu_mask); 85409e61dafSJames Morse } 85509e61dafSJames Morse 85609e61dafSJames Morse static void mpam_resctrl_online_domain_hdr(unsigned int cpu, 85709e61dafSJames Morse struct rdt_domain_hdr *hdr) 85809e61dafSJames Morse { 85909e61dafSJames Morse lockdep_assert_cpus_held(); 86009e61dafSJames Morse 86109e61dafSJames Morse cpumask_set_cpu(cpu, &hdr->cpu_mask); 86209e61dafSJames Morse } 86309e61dafSJames Morse 86409e61dafSJames Morse /** 86509e61dafSJames Morse * mpam_resctrl_offline_domain_hdr() - Update the domain header to remove a CPU. 86609e61dafSJames Morse * @cpu: The CPU to remove from the domain. 86709e61dafSJames Morse * @hdr: The domain's header. 86809e61dafSJames Morse * 86909e61dafSJames Morse * Removes @cpu from the header mask. If this was the last CPU in the domain, 87009e61dafSJames Morse * the domain header is removed from its parent list and true is returned, 87109e61dafSJames Morse * indicating the parent structure can be freed. 87209e61dafSJames Morse * If there are other CPUs in the domain, returns false. 87309e61dafSJames Morse */ 87409e61dafSJames Morse static bool mpam_resctrl_offline_domain_hdr(unsigned int cpu, 87509e61dafSJames Morse struct rdt_domain_hdr *hdr) 87609e61dafSJames Morse { 87709e61dafSJames Morse lockdep_assert_held(&domain_list_lock); 87809e61dafSJames Morse 87909e61dafSJames Morse cpumask_clear_cpu(cpu, &hdr->cpu_mask); 88009e61dafSJames Morse if (cpumask_empty(&hdr->cpu_mask)) { 88109e61dafSJames Morse list_del_rcu(&hdr->list); 88209e61dafSJames Morse synchronize_rcu(); 88309e61dafSJames Morse return true; 88409e61dafSJames Morse } 88509e61dafSJames Morse 88609e61dafSJames Morse return false; 88709e61dafSJames Morse } 88809e61dafSJames Morse 88909e61dafSJames Morse static void mpam_resctrl_domain_insert(struct list_head *list, 89009e61dafSJames Morse struct rdt_domain_hdr *new) 89109e61dafSJames Morse { 89209e61dafSJames Morse struct rdt_domain_hdr *err; 89309e61dafSJames Morse struct list_head *pos = NULL; 89409e61dafSJames Morse 89509e61dafSJames Morse lockdep_assert_held(&domain_list_lock); 89609e61dafSJames Morse 89709e61dafSJames Morse err = resctrl_find_domain(list, new->id, &pos); 89809e61dafSJames Morse if (WARN_ON_ONCE(err)) 89909e61dafSJames Morse return; 90009e61dafSJames Morse 90109e61dafSJames Morse list_add_tail_rcu(&new->list, pos); 90209e61dafSJames Morse } 90309e61dafSJames Morse 90409e61dafSJames Morse static struct mpam_resctrl_dom * 90509e61dafSJames Morse mpam_resctrl_alloc_domain(unsigned int cpu, struct mpam_resctrl_res *res) 90609e61dafSJames Morse { 90709e61dafSJames Morse int err; 90809e61dafSJames Morse struct mpam_resctrl_dom *dom; 90909e61dafSJames Morse struct rdt_ctrl_domain *ctrl_d; 91009e61dafSJames Morse struct mpam_class *class = res->class; 91109e61dafSJames Morse struct mpam_component *comp_iter, *ctrl_comp; 91209e61dafSJames Morse struct rdt_resource *r = &res->resctrl_res; 91309e61dafSJames Morse 91409e61dafSJames Morse lockdep_assert_held(&domain_list_lock); 91509e61dafSJames Morse 91609e61dafSJames Morse ctrl_comp = NULL; 91709e61dafSJames Morse guard(srcu)(&mpam_srcu); 91809e61dafSJames Morse list_for_each_entry_srcu(comp_iter, &class->components, class_list, 91909e61dafSJames Morse srcu_read_lock_held(&mpam_srcu)) { 92009e61dafSJames Morse if (cpumask_test_cpu(cpu, &comp_iter->affinity)) { 92109e61dafSJames Morse ctrl_comp = comp_iter; 92209e61dafSJames Morse break; 92309e61dafSJames Morse } 92409e61dafSJames Morse } 92509e61dafSJames Morse 92609e61dafSJames Morse /* class has no component for this CPU */ 92709e61dafSJames Morse if (WARN_ON_ONCE(!ctrl_comp)) 92809e61dafSJames Morse return ERR_PTR(-EINVAL); 92909e61dafSJames Morse 93009e61dafSJames Morse dom = kzalloc_node(sizeof(*dom), GFP_KERNEL, cpu_to_node(cpu)); 93109e61dafSJames Morse if (!dom) 93209e61dafSJames Morse return ERR_PTR(-ENOMEM); 93309e61dafSJames Morse 93409e61dafSJames Morse if (r->alloc_capable) { 93509e61dafSJames Morse dom->ctrl_comp = ctrl_comp; 93609e61dafSJames Morse 93709e61dafSJames Morse ctrl_d = &dom->resctrl_ctrl_dom; 93809e61dafSJames Morse mpam_resctrl_domain_hdr_init(cpu, ctrl_comp, r->rid, &ctrl_d->hdr); 93909e61dafSJames Morse ctrl_d->hdr.type = RESCTRL_CTRL_DOMAIN; 94009e61dafSJames Morse err = resctrl_online_ctrl_domain(r, ctrl_d); 94109e61dafSJames Morse if (err) 94209e61dafSJames Morse goto free_domain; 94309e61dafSJames Morse 94409e61dafSJames Morse mpam_resctrl_domain_insert(&r->ctrl_domains, &ctrl_d->hdr); 94509e61dafSJames Morse } else { 94609e61dafSJames Morse pr_debug("Skipped control domain online - no controls\n"); 94709e61dafSJames Morse } 94809e61dafSJames Morse return dom; 94909e61dafSJames Morse 95009e61dafSJames Morse free_domain: 95109e61dafSJames Morse kfree(dom); 95209e61dafSJames Morse dom = ERR_PTR(err); 95309e61dafSJames Morse 95409e61dafSJames Morse return dom; 95509e61dafSJames Morse } 95609e61dafSJames Morse 95709e61dafSJames Morse static struct mpam_resctrl_dom * 95809e61dafSJames Morse mpam_resctrl_get_domain_from_cpu(int cpu, struct mpam_resctrl_res *res) 95909e61dafSJames Morse { 96009e61dafSJames Morse struct mpam_resctrl_dom *dom; 96109e61dafSJames Morse struct rdt_resource *r = &res->resctrl_res; 96209e61dafSJames Morse 96309e61dafSJames Morse lockdep_assert_cpus_held(); 96409e61dafSJames Morse 96509e61dafSJames Morse list_for_each_entry_rcu(dom, &r->ctrl_domains, resctrl_ctrl_dom.hdr.list) { 96609e61dafSJames Morse if (cpumask_test_cpu(cpu, &dom->ctrl_comp->affinity)) 96709e61dafSJames Morse return dom; 96809e61dafSJames Morse } 96909e61dafSJames Morse 97009e61dafSJames Morse return NULL; 97109e61dafSJames Morse } 97209e61dafSJames Morse 97309e61dafSJames Morse int mpam_resctrl_online_cpu(unsigned int cpu) 97409e61dafSJames Morse { 97509e61dafSJames Morse struct mpam_resctrl_res *res; 97609e61dafSJames Morse enum resctrl_res_level rid; 97709e61dafSJames Morse 97809e61dafSJames Morse guard(mutex)(&domain_list_lock); 97909e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 98009e61dafSJames Morse struct mpam_resctrl_dom *dom; 98109e61dafSJames Morse struct rdt_resource *r = &res->resctrl_res; 98209e61dafSJames Morse 98309e61dafSJames Morse if (!res->class) 98409e61dafSJames Morse continue; // dummy_resource; 98509e61dafSJames Morse 98609e61dafSJames Morse dom = mpam_resctrl_get_domain_from_cpu(cpu, res); 98709e61dafSJames Morse if (!dom) { 98809e61dafSJames Morse dom = mpam_resctrl_alloc_domain(cpu, res); 98909e61dafSJames Morse if (IS_ERR(dom)) 99009e61dafSJames Morse return PTR_ERR(dom); 99109e61dafSJames Morse } else { 99209e61dafSJames Morse if (r->alloc_capable) { 99309e61dafSJames Morse struct rdt_ctrl_domain *ctrl_d = &dom->resctrl_ctrl_dom; 99409e61dafSJames Morse 99509e61dafSJames Morse mpam_resctrl_online_domain_hdr(cpu, &ctrl_d->hdr); 99609e61dafSJames Morse } 99709e61dafSJames Morse } 99809e61dafSJames Morse } 99909e61dafSJames Morse 100009e61dafSJames Morse resctrl_online_cpu(cpu); 100109e61dafSJames Morse 100209e61dafSJames Morse return 0; 100309e61dafSJames Morse } 100409e61dafSJames Morse 100509e61dafSJames Morse void mpam_resctrl_offline_cpu(unsigned int cpu) 100609e61dafSJames Morse { 100709e61dafSJames Morse struct mpam_resctrl_res *res; 100809e61dafSJames Morse enum resctrl_res_level rid; 100909e61dafSJames Morse 101009e61dafSJames Morse resctrl_offline_cpu(cpu); 101109e61dafSJames Morse 101209e61dafSJames Morse guard(mutex)(&domain_list_lock); 101309e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 101409e61dafSJames Morse struct mpam_resctrl_dom *dom; 101509e61dafSJames Morse struct rdt_ctrl_domain *ctrl_d; 101609e61dafSJames Morse bool ctrl_dom_empty; 101709e61dafSJames Morse struct rdt_resource *r = &res->resctrl_res; 101809e61dafSJames Morse 101909e61dafSJames Morse if (!res->class) 102009e61dafSJames Morse continue; // dummy resource 102109e61dafSJames Morse 102209e61dafSJames Morse dom = mpam_resctrl_get_domain_from_cpu(cpu, res); 102309e61dafSJames Morse if (WARN_ON_ONCE(!dom)) 102409e61dafSJames Morse continue; 102509e61dafSJames Morse 102609e61dafSJames Morse if (r->alloc_capable) { 102709e61dafSJames Morse ctrl_d = &dom->resctrl_ctrl_dom; 102809e61dafSJames Morse ctrl_dom_empty = mpam_resctrl_offline_domain_hdr(cpu, &ctrl_d->hdr); 102909e61dafSJames Morse if (ctrl_dom_empty) 103009e61dafSJames Morse resctrl_offline_ctrl_domain(&res->resctrl_res, ctrl_d); 103109e61dafSJames Morse } else { 103209e61dafSJames Morse ctrl_dom_empty = true; 103309e61dafSJames Morse } 103409e61dafSJames Morse 103509e61dafSJames Morse if (ctrl_dom_empty) 103609e61dafSJames Morse kfree(dom); 103709e61dafSJames Morse } 103809e61dafSJames Morse } 103909e61dafSJames Morse 104009e61dafSJames Morse int mpam_resctrl_setup(void) 104109e61dafSJames Morse { 104209e61dafSJames Morse int err = 0; 104309e61dafSJames Morse struct mpam_resctrl_res *res; 104409e61dafSJames Morse enum resctrl_res_level rid; 104509e61dafSJames Morse 10461c1e2968SBen Horgan wait_event(wait_cacheinfo_ready, cacheinfo_ready); 10471c1e2968SBen Horgan 104809e61dafSJames Morse cpus_read_lock(); 104909e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 105009e61dafSJames Morse INIT_LIST_HEAD_RCU(&res->resctrl_res.ctrl_domains); 105109e61dafSJames Morse res->resctrl_res.rid = rid; 105209e61dafSJames Morse } 105309e61dafSJames Morse 105452a4edb1SJames Morse /* Find some classes to use for controls */ 105552a4edb1SJames Morse mpam_resctrl_pick_caches(); 105636528c76SJames Morse mpam_resctrl_pick_mba(); 105709e61dafSJames Morse 105809e61dafSJames Morse /* Initialise the resctrl structures from the classes */ 105909e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 106009e61dafSJames Morse if (!res->class) 106109e61dafSJames Morse continue; // dummy resource 106209e61dafSJames Morse 106309e61dafSJames Morse err = mpam_resctrl_control_init(res); 106409e61dafSJames Morse if (err) { 106509e61dafSJames Morse pr_debug("Failed to initialise rid %u\n", rid); 106609e61dafSJames Morse break; 106709e61dafSJames Morse } 106809e61dafSJames Morse } 106909e61dafSJames Morse cpus_read_unlock(); 107009e61dafSJames Morse 107109e61dafSJames Morse if (err) { 107209e61dafSJames Morse pr_debug("Internal error %d - resctrl not supported\n", err); 107309e61dafSJames Morse return err; 107409e61dafSJames Morse } 107509e61dafSJames Morse 107609e61dafSJames Morse if (!resctrl_arch_alloc_capable()) { 107709e61dafSJames Morse pr_debug("No alloc(%u) found - resctrl not supported\n", 107809e61dafSJames Morse resctrl_arch_alloc_capable()); 107909e61dafSJames Morse return -EOPNOTSUPP; 108009e61dafSJames Morse } 108109e61dafSJames Morse 108209e61dafSJames Morse /* TODO: call resctrl_init() */ 108309e61dafSJames Morse 108409e61dafSJames Morse return 0; 108509e61dafSJames Morse } 10861c1e2968SBen Horgan 10871c1e2968SBen Horgan static int __init __cacheinfo_ready(void) 10881c1e2968SBen Horgan { 10891c1e2968SBen Horgan cacheinfo_ready = true; 10901c1e2968SBen Horgan wake_up(&wait_cacheinfo_ready); 10911c1e2968SBen Horgan 10921c1e2968SBen Horgan return 0; 10931c1e2968SBen Horgan } 10941c1e2968SBen Horgan device_initcall_sync(__cacheinfo_ready); 1095*5dc8f73eSDave Martin 1096*5dc8f73eSDave Martin #ifdef CONFIG_MPAM_KUNIT_TEST 1097*5dc8f73eSDave Martin #include "test_mpam_resctrl.c" 1098*5dc8f73eSDave Martin #endif 1099