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 37*264c2859SBen Horgan /* 38*264c2859SBen Horgan * The classes we've picked to map to resctrl events. 39*264c2859SBen Horgan * Resctrl believes all the worlds a Xeon, and these are all on the L3. This 40*264c2859SBen Horgan * array lets us find the actual class backing the event counters. e.g. 41*264c2859SBen Horgan * the only memory bandwidth counters may be on the memory controller, but to 42*264c2859SBen Horgan * make use of them, we pretend they are on L3. Restrict the events considered 43*264c2859SBen Horgan * to those supported by MPAM. 44*264c2859SBen Horgan * Class pointer may be NULL. 45*264c2859SBen Horgan */ 46*264c2859SBen Horgan #define MPAM_MAX_EVENT QOS_L3_MBM_TOTAL_EVENT_ID 47*264c2859SBen Horgan static struct mpam_resctrl_mon mpam_resctrl_counters[MPAM_MAX_EVENT + 1]; 48*264c2859SBen Horgan 49*264c2859SBen Horgan #define for_each_mpam_resctrl_mon(mon, eventid) \ 50*264c2859SBen Horgan for (eventid = QOS_FIRST_EVENT, mon = &mpam_resctrl_counters[eventid]; \ 51*264c2859SBen Horgan eventid <= MPAM_MAX_EVENT; \ 52*264c2859SBen Horgan eventid++, mon = &mpam_resctrl_counters[eventid]) 53*264c2859SBen Horgan 5409e61dafSJames Morse /* The lock for modifying resctrl's domain lists from cpuhp callbacks. */ 5509e61dafSJames Morse static DEFINE_MUTEX(domain_list_lock); 5609e61dafSJames Morse 576789fb99SJames Morse /* 586789fb99SJames Morse * MPAM emulates CDP by setting different PARTID in the I/D fields of MPAM0_EL1. 596789fb99SJames Morse * This applies globally to all traffic the CPU generates. 606789fb99SJames Morse */ 619d2e1a99SJames Morse static bool cdp_enabled; 629d2e1a99SJames Morse 631c1e2968SBen Horgan /* 641c1e2968SBen Horgan * We use cacheinfo to discover the size of the caches and their id. cacheinfo 651c1e2968SBen Horgan * populates this from a device_initcall(). mpam_resctrl_setup() must wait. 661c1e2968SBen Horgan */ 671c1e2968SBen Horgan static bool cacheinfo_ready; 681c1e2968SBen Horgan static DECLARE_WAIT_QUEUE_HEAD(wait_cacheinfo_ready); 691c1e2968SBen Horgan 7009e61dafSJames Morse bool resctrl_arch_alloc_capable(void) 7109e61dafSJames Morse { 7209e61dafSJames Morse struct mpam_resctrl_res *res; 7309e61dafSJames Morse enum resctrl_res_level rid; 7409e61dafSJames Morse 7509e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 7609e61dafSJames Morse if (res->resctrl_res.alloc_capable) 7709e61dafSJames Morse return true; 7809e61dafSJames Morse } 7909e61dafSJames Morse 8009e61dafSJames Morse return false; 8109e61dafSJames Morse } 8209e61dafSJames Morse 83*264c2859SBen Horgan bool resctrl_arch_mon_capable(void) 84*264c2859SBen Horgan { 85*264c2859SBen Horgan struct mpam_resctrl_res *res = &mpam_resctrl_controls[RDT_RESOURCE_L3]; 86*264c2859SBen Horgan struct rdt_resource *l3 = &res->resctrl_res; 87*264c2859SBen Horgan 88*264c2859SBen Horgan /* All monitors are presented as being on the L3 cache */ 89*264c2859SBen Horgan return l3->mon_capable; 90*264c2859SBen Horgan } 91*264c2859SBen Horgan 926789fb99SJames Morse bool resctrl_arch_get_cdp_enabled(enum resctrl_res_level rid) 936789fb99SJames Morse { 946789fb99SJames Morse return mpam_resctrl_controls[rid].cdp_enabled; 956789fb99SJames Morse } 966789fb99SJames Morse 976789fb99SJames Morse /** 986789fb99SJames Morse * resctrl_reset_task_closids() - Reset the PARTID/PMG values for all tasks. 996789fb99SJames Morse * 1006789fb99SJames Morse * At boot, all existing tasks use partid zero for D and I. 1016789fb99SJames Morse * To enable/disable CDP emulation, all these tasks need relabelling. 1026789fb99SJames Morse */ 1036789fb99SJames Morse static void resctrl_reset_task_closids(void) 1046789fb99SJames Morse { 1056789fb99SJames Morse struct task_struct *p, *t; 1066789fb99SJames Morse 1076789fb99SJames Morse read_lock(&tasklist_lock); 1086789fb99SJames Morse for_each_process_thread(p, t) { 1096789fb99SJames Morse resctrl_arch_set_closid_rmid(t, RESCTRL_RESERVED_CLOSID, 1106789fb99SJames Morse RESCTRL_RESERVED_RMID); 1116789fb99SJames Morse } 1126789fb99SJames Morse read_unlock(&tasklist_lock); 1136789fb99SJames Morse } 1146789fb99SJames Morse 1156789fb99SJames Morse int resctrl_arch_set_cdp_enabled(enum resctrl_res_level rid, bool enable) 1166789fb99SJames Morse { 1176789fb99SJames Morse u32 partid_i = RESCTRL_RESERVED_CLOSID, partid_d = RESCTRL_RESERVED_CLOSID; 118*264c2859SBen Horgan struct mpam_resctrl_res *res = &mpam_resctrl_controls[RDT_RESOURCE_L3]; 119*264c2859SBen Horgan struct rdt_resource *l3 = &res->resctrl_res; 1206789fb99SJames Morse int cpu; 1216789fb99SJames Morse 12201a0021fSBen Horgan if (!IS_ENABLED(CONFIG_EXPERT) && enable) { 12301a0021fSBen Horgan /* 12401a0021fSBen Horgan * If the resctrl fs is mounted more than once, sequentially, 12501a0021fSBen Horgan * then CDP can lead to the use of out of range PARTIDs. 12601a0021fSBen Horgan */ 12701a0021fSBen Horgan pr_warn("CDP not supported\n"); 12801a0021fSBen Horgan return -EOPNOTSUPP; 12901a0021fSBen Horgan } 13001a0021fSBen Horgan 13101a0021fSBen Horgan if (enable) 13201a0021fSBen Horgan pr_warn("CDP is an expert feature and may cause MPAM to malfunction.\n"); 13301a0021fSBen Horgan 1346789fb99SJames Morse /* 1356789fb99SJames Morse * resctrl_arch_set_cdp_enabled() is only called with enable set to 1366789fb99SJames Morse * false on error and unmount. 1376789fb99SJames Morse */ 1386789fb99SJames Morse cdp_enabled = enable; 1396789fb99SJames Morse mpam_resctrl_controls[rid].cdp_enabled = enable; 1406789fb99SJames Morse 141*264c2859SBen Horgan if (enable) 142*264c2859SBen Horgan l3->mon.num_rmid = resctrl_arch_system_num_rmid_idx() / 2; 143*264c2859SBen Horgan else 144*264c2859SBen Horgan l3->mon.num_rmid = resctrl_arch_system_num_rmid_idx(); 145*264c2859SBen Horgan 1466789fb99SJames Morse /* The mbw_max feature can't hide cdp as it's a per-partid maximum. */ 1476789fb99SJames Morse if (cdp_enabled && !mpam_resctrl_controls[RDT_RESOURCE_MBA].cdp_enabled) 1486789fb99SJames Morse mpam_resctrl_controls[RDT_RESOURCE_MBA].resctrl_res.alloc_capable = false; 1496789fb99SJames Morse 1506789fb99SJames Morse if (mpam_resctrl_controls[RDT_RESOURCE_MBA].cdp_enabled && 1516789fb99SJames Morse mpam_resctrl_controls[RDT_RESOURCE_MBA].class) 1526789fb99SJames Morse mpam_resctrl_controls[RDT_RESOURCE_MBA].resctrl_res.alloc_capable = true; 1536789fb99SJames Morse 1546789fb99SJames Morse if (enable) { 1556789fb99SJames Morse if (mpam_partid_max < 1) 1566789fb99SJames Morse return -EINVAL; 1576789fb99SJames Morse 1586789fb99SJames Morse partid_d = resctrl_get_config_index(RESCTRL_RESERVED_CLOSID, CDP_DATA); 1596789fb99SJames Morse partid_i = resctrl_get_config_index(RESCTRL_RESERVED_CLOSID, CDP_CODE); 1606789fb99SJames Morse } 1616789fb99SJames Morse 1626789fb99SJames Morse mpam_set_task_partid_pmg(current, partid_d, partid_i, 0, 0); 1636789fb99SJames Morse WRITE_ONCE(arm64_mpam_global_default, mpam_get_regval(current)); 1646789fb99SJames Morse 1656789fb99SJames Morse resctrl_reset_task_closids(); 1666789fb99SJames Morse 1676789fb99SJames Morse for_each_possible_cpu(cpu) 1686789fb99SJames Morse mpam_set_cpu_defaults(cpu, partid_d, partid_i, 0, 0); 1696789fb99SJames Morse on_each_cpu(resctrl_arch_sync_cpu_closid_rmid, NULL, 1); 1706789fb99SJames Morse 1716789fb99SJames Morse return 0; 1726789fb99SJames Morse } 1736789fb99SJames Morse 1746789fb99SJames Morse static bool mpam_resctrl_hide_cdp(enum resctrl_res_level rid) 1756789fb99SJames Morse { 1766789fb99SJames Morse return cdp_enabled && !resctrl_arch_get_cdp_enabled(rid); 1776789fb99SJames Morse } 1786789fb99SJames Morse 17909e61dafSJames Morse /* 18009e61dafSJames Morse * MSC may raise an error interrupt if it sees an out or range partid/pmg, 18109e61dafSJames Morse * and go on to truncate the value. Regardless of what the hardware supports, 18209e61dafSJames Morse * only the system wide safe value is safe to use. 18309e61dafSJames Morse */ 18409e61dafSJames Morse u32 resctrl_arch_get_num_closid(struct rdt_resource *ignored) 18509e61dafSJames Morse { 18609e61dafSJames Morse return mpam_partid_max + 1; 18709e61dafSJames Morse } 18809e61dafSJames Morse 1893e9b3582SBen Horgan u32 resctrl_arch_system_num_rmid_idx(void) 1903e9b3582SBen Horgan { 1913e9b3582SBen Horgan return (mpam_pmg_max + 1) * (mpam_partid_max + 1); 1923e9b3582SBen Horgan } 1933e9b3582SBen Horgan 1943e9b3582SBen Horgan u32 resctrl_arch_rmid_idx_encode(u32 closid, u32 rmid) 1953e9b3582SBen Horgan { 1963e9b3582SBen Horgan return closid * (mpam_pmg_max + 1) + rmid; 1973e9b3582SBen Horgan } 1983e9b3582SBen Horgan 1993e9b3582SBen Horgan void resctrl_arch_rmid_idx_decode(u32 idx, u32 *closid, u32 *rmid) 2003e9b3582SBen Horgan { 2013e9b3582SBen Horgan *closid = idx / (mpam_pmg_max + 1); 2023e9b3582SBen Horgan *rmid = idx % (mpam_pmg_max + 1); 2033e9b3582SBen Horgan } 2043e9b3582SBen Horgan 2059d2e1a99SJames Morse void resctrl_arch_sched_in(struct task_struct *tsk) 2069d2e1a99SJames Morse { 2079d2e1a99SJames Morse lockdep_assert_preemption_disabled(); 2089d2e1a99SJames Morse 2099d2e1a99SJames Morse mpam_thread_switch(tsk); 2109d2e1a99SJames Morse } 2119d2e1a99SJames Morse 2129d2e1a99SJames Morse void resctrl_arch_set_cpu_default_closid_rmid(int cpu, 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_cpu_defaults(cpu, closid, closid, rmid, rmid); 2199d2e1a99SJames Morse } else { 2209d2e1a99SJames Morse /* 2219d2e1a99SJames Morse * When CDP is enabled, resctrl halves the closid range and we 2229d2e1a99SJames Morse * use odd/even partid for one closid. 2239d2e1a99SJames Morse */ 2249d2e1a99SJames Morse u32 partid_d = resctrl_get_config_index(closid, CDP_DATA); 2259d2e1a99SJames Morse u32 partid_i = resctrl_get_config_index(closid, CDP_CODE); 2269d2e1a99SJames Morse 2279d2e1a99SJames Morse mpam_set_cpu_defaults(cpu, partid_d, partid_i, rmid, rmid); 2289d2e1a99SJames Morse } 2299d2e1a99SJames Morse } 2309d2e1a99SJames Morse 2319d2e1a99SJames Morse void resctrl_arch_sync_cpu_closid_rmid(void *info) 2329d2e1a99SJames Morse { 2339d2e1a99SJames Morse struct resctrl_cpu_defaults *r = info; 2349d2e1a99SJames Morse 2359d2e1a99SJames Morse lockdep_assert_preemption_disabled(); 2369d2e1a99SJames Morse 2379d2e1a99SJames Morse if (r) { 2389d2e1a99SJames Morse resctrl_arch_set_cpu_default_closid_rmid(smp_processor_id(), 2399d2e1a99SJames Morse r->closid, r->rmid); 2409d2e1a99SJames Morse } 2419d2e1a99SJames Morse 2429d2e1a99SJames Morse resctrl_arch_sched_in(current); 2439d2e1a99SJames Morse } 2449d2e1a99SJames Morse 2459d2e1a99SJames Morse void resctrl_arch_set_closid_rmid(struct task_struct *tsk, u32 closid, u32 rmid) 2469d2e1a99SJames Morse { 2479d2e1a99SJames Morse WARN_ON_ONCE(closid > U16_MAX); 2489d2e1a99SJames Morse WARN_ON_ONCE(rmid > U8_MAX); 2499d2e1a99SJames Morse 2509d2e1a99SJames Morse if (!cdp_enabled) { 2519d2e1a99SJames Morse mpam_set_task_partid_pmg(tsk, closid, closid, rmid, rmid); 2529d2e1a99SJames Morse } else { 2539d2e1a99SJames Morse u32 partid_d = resctrl_get_config_index(closid, CDP_DATA); 2549d2e1a99SJames Morse u32 partid_i = resctrl_get_config_index(closid, CDP_CODE); 2559d2e1a99SJames Morse 2569d2e1a99SJames Morse mpam_set_task_partid_pmg(tsk, partid_d, partid_i, rmid, rmid); 2579d2e1a99SJames Morse } 2589d2e1a99SJames Morse } 2599d2e1a99SJames Morse 2606789fb99SJames Morse bool resctrl_arch_match_closid(struct task_struct *tsk, u32 closid) 2616789fb99SJames Morse { 2626789fb99SJames Morse u64 regval = mpam_get_regval(tsk); 2636789fb99SJames Morse u32 tsk_closid = FIELD_GET(MPAM0_EL1_PARTID_D, regval); 2646789fb99SJames Morse 2656789fb99SJames Morse if (cdp_enabled) 2666789fb99SJames Morse tsk_closid >>= 1; 2676789fb99SJames Morse 2686789fb99SJames Morse return tsk_closid == closid; 2696789fb99SJames Morse } 2706789fb99SJames Morse 2716789fb99SJames Morse /* The task's pmg is not unique, the partid must be considered too */ 2726789fb99SJames Morse bool resctrl_arch_match_rmid(struct task_struct *tsk, u32 closid, u32 rmid) 2736789fb99SJames Morse { 2746789fb99SJames Morse u64 regval = mpam_get_regval(tsk); 2756789fb99SJames Morse u32 tsk_closid = FIELD_GET(MPAM0_EL1_PARTID_D, regval); 2766789fb99SJames Morse u32 tsk_rmid = FIELD_GET(MPAM0_EL1_PMG_D, regval); 2776789fb99SJames Morse 2786789fb99SJames Morse if (cdp_enabled) 2796789fb99SJames Morse tsk_closid >>= 1; 2806789fb99SJames Morse 2816789fb99SJames Morse return (tsk_closid == closid) && (tsk_rmid == rmid); 2826789fb99SJames Morse } 2836789fb99SJames Morse 28409e61dafSJames Morse struct rdt_resource *resctrl_arch_get_resource(enum resctrl_res_level l) 28509e61dafSJames Morse { 28609e61dafSJames Morse if (l >= RDT_NUM_RESOURCES) 28709e61dafSJames Morse return NULL; 28809e61dafSJames Morse 28909e61dafSJames Morse return &mpam_resctrl_controls[l].resctrl_res; 29009e61dafSJames Morse } 29109e61dafSJames Morse 29252a4edb1SJames Morse static bool cache_has_usable_cpor(struct mpam_class *class) 29352a4edb1SJames Morse { 29452a4edb1SJames Morse struct mpam_props *cprops = &class->props; 29552a4edb1SJames Morse 29652a4edb1SJames Morse if (!mpam_has_feature(mpam_feat_cpor_part, cprops)) 29752a4edb1SJames Morse return false; 29852a4edb1SJames Morse 29952a4edb1SJames Morse /* resctrl uses u32 for all bitmap configurations */ 30052a4edb1SJames Morse return class->props.cpbm_wd <= 32; 30152a4edb1SJames Morse } 30252a4edb1SJames Morse 30336528c76SJames Morse static bool mba_class_use_mbw_max(struct mpam_props *cprops) 30436528c76SJames Morse { 30536528c76SJames Morse return (mpam_has_feature(mpam_feat_mbw_max, cprops) && 30636528c76SJames Morse cprops->bwa_wd); 30736528c76SJames Morse } 30836528c76SJames Morse 30936528c76SJames Morse static bool class_has_usable_mba(struct mpam_props *cprops) 31036528c76SJames Morse { 31136528c76SJames Morse return mba_class_use_mbw_max(cprops); 31236528c76SJames Morse } 31336528c76SJames Morse 31436528c76SJames Morse /* 31536528c76SJames Morse * Calculate the worst-case percentage change from each implemented step 31636528c76SJames Morse * in the control. 31736528c76SJames Morse */ 31836528c76SJames Morse static u32 get_mba_granularity(struct mpam_props *cprops) 31936528c76SJames Morse { 32036528c76SJames Morse if (!mba_class_use_mbw_max(cprops)) 32136528c76SJames Morse return 0; 32236528c76SJames Morse 32336528c76SJames Morse /* 32436528c76SJames Morse * bwa_wd is the number of bits implemented in the 0.xxx 32536528c76SJames Morse * fixed point fraction. 1 bit is 50%, 2 is 25% etc. 32636528c76SJames Morse */ 32736528c76SJames Morse return DIV_ROUND_UP(MAX_MBA_BW, 1 << cprops->bwa_wd); 32836528c76SJames Morse } 32936528c76SJames Morse 33080d147d2SDave Martin /* 33180d147d2SDave Martin * Each fixed-point hardware value architecturally represents a range 33280d147d2SDave Martin * of values: the full range 0% - 100% is split contiguously into 33380d147d2SDave Martin * (1 << cprops->bwa_wd) equal bands. 33480d147d2SDave Martin * 33580d147d2SDave Martin * Although the bwa_bwd fields have 6 bits the maximum valid value is 16 33680d147d2SDave Martin * as it reports the width of fields that are at most 16 bits. When 33780d147d2SDave Martin * fewer than 16 bits are valid the least significant bits are 33880d147d2SDave Martin * ignored. The implied binary point is kept between bits 15 and 16 and 33980d147d2SDave Martin * so the valid bits are leftmost. 34080d147d2SDave Martin * 34180d147d2SDave Martin * See ARM IHI0099B.a "MPAM system component specification", Section 9.3, 34280d147d2SDave Martin * "The fixed-point fractional format" for more information. 34380d147d2SDave Martin * 34480d147d2SDave Martin * Find the nearest percentage value to the upper bound of the selected band: 34580d147d2SDave Martin */ 34680d147d2SDave Martin static u32 mbw_max_to_percent(u16 mbw_max, struct mpam_props *cprops) 34780d147d2SDave Martin { 34880d147d2SDave Martin u32 val = mbw_max; 34980d147d2SDave Martin 35080d147d2SDave Martin val >>= 16 - cprops->bwa_wd; 35180d147d2SDave Martin val += 1; 35280d147d2SDave Martin val *= MAX_MBA_BW; 35380d147d2SDave Martin val = DIV_ROUND_CLOSEST(val, 1 << cprops->bwa_wd); 35480d147d2SDave Martin 35580d147d2SDave Martin return val; 35680d147d2SDave Martin } 35780d147d2SDave Martin 35880d147d2SDave Martin /* 35980d147d2SDave Martin * Find the band whose upper bound is closest to the specified percentage. 36080d147d2SDave Martin * 36180d147d2SDave Martin * A round-to-nearest policy is followed here as a balanced compromise 36280d147d2SDave Martin * between unexpected under-commit of the resource (where the total of 36380d147d2SDave Martin * a set of resource allocations after conversion is less than the 36480d147d2SDave Martin * expected total, due to rounding of the individual converted 36580d147d2SDave Martin * percentages) and over-commit (where the total of the converted 36680d147d2SDave Martin * allocations is greater than expected). 36780d147d2SDave Martin */ 36880d147d2SDave Martin static u16 percent_to_mbw_max(u8 pc, struct mpam_props *cprops) 36980d147d2SDave Martin { 37080d147d2SDave Martin u32 val = pc; 37180d147d2SDave Martin 37280d147d2SDave Martin val <<= cprops->bwa_wd; 37380d147d2SDave Martin val = DIV_ROUND_CLOSEST(val, MAX_MBA_BW); 37480d147d2SDave Martin val = max(val, 1) - 1; 37580d147d2SDave Martin val <<= 16 - cprops->bwa_wd; 37680d147d2SDave Martin 37780d147d2SDave Martin return val; 37880d147d2SDave Martin } 37980d147d2SDave Martin 38036528c76SJames Morse static u32 get_mba_min(struct mpam_props *cprops) 38136528c76SJames Morse { 38236528c76SJames Morse if (!mba_class_use_mbw_max(cprops)) { 38336528c76SJames Morse WARN_ON_ONCE(1); 38436528c76SJames Morse return 0; 38536528c76SJames Morse } 38636528c76SJames Morse 38736528c76SJames Morse return mbw_max_to_percent(0, cprops); 38836528c76SJames Morse } 38936528c76SJames Morse 39036528c76SJames Morse /* Find the L3 cache that has affinity with this CPU */ 39136528c76SJames Morse static int find_l3_equivalent_bitmask(int cpu, cpumask_var_t tmp_cpumask) 39236528c76SJames Morse { 39336528c76SJames Morse u32 cache_id = get_cpu_cacheinfo_id(cpu, 3); 39436528c76SJames Morse 39536528c76SJames Morse lockdep_assert_cpus_held(); 39636528c76SJames Morse 39736528c76SJames Morse return mpam_get_cpumask_from_cache_id(cache_id, 3, tmp_cpumask); 39836528c76SJames Morse } 39936528c76SJames Morse 40036528c76SJames Morse /* 40136528c76SJames Morse * topology_matches_l3() - Is the provided class the same shape as L3 40236528c76SJames Morse * @victim: The class we'd like to pretend is L3. 40336528c76SJames Morse * 40436528c76SJames Morse * resctrl expects all the world's a Xeon, and all counters are on the 40536528c76SJames Morse * L3. We allow some mapping counters on other classes. This requires 40636528c76SJames Morse * that the CPU->domain mapping is the same kind of shape. 40736528c76SJames Morse * 40836528c76SJames Morse * Using cacheinfo directly would make this work even if resctrl can't 40936528c76SJames Morse * use the L3 - but cacheinfo can't tell us anything about offline CPUs. 41036528c76SJames Morse * Using the L3 resctrl domain list also depends on CPUs being online. 41136528c76SJames Morse * Using the mpam_class we picked for L3 so we can use its domain list 41236528c76SJames Morse * assumes that there are MPAM controls on the L3. 41336528c76SJames Morse * Instead, this path eventually uses the mpam_get_cpumask_from_cache_id() 41436528c76SJames Morse * helper which can tell us about offline CPUs ... but getting the cache_id 41536528c76SJames Morse * to start with relies on at least one CPU per L3 cache being online at 41636528c76SJames Morse * boot. 41736528c76SJames Morse * 41836528c76SJames Morse * Walk the victim component list and compare the affinity mask with the 41936528c76SJames Morse * corresponding L3. The topology matches if each victim:component's affinity 42036528c76SJames Morse * mask is the same as the CPU's corresponding L3's. These lists/masks are 42136528c76SJames Morse * computed from firmware tables so don't change at runtime. 42236528c76SJames Morse */ 42336528c76SJames Morse static bool topology_matches_l3(struct mpam_class *victim) 42436528c76SJames Morse { 42536528c76SJames Morse int cpu, err; 42636528c76SJames Morse struct mpam_component *victim_iter; 42736528c76SJames Morse 42836528c76SJames Morse lockdep_assert_cpus_held(); 42936528c76SJames Morse 43036528c76SJames Morse cpumask_var_t __free(free_cpumask_var) tmp_cpumask = CPUMASK_VAR_NULL; 43136528c76SJames Morse if (!alloc_cpumask_var(&tmp_cpumask, GFP_KERNEL)) 43236528c76SJames Morse return false; 43336528c76SJames Morse 43436528c76SJames Morse guard(srcu)(&mpam_srcu); 43536528c76SJames Morse list_for_each_entry_srcu(victim_iter, &victim->components, class_list, 43636528c76SJames Morse srcu_read_lock_held(&mpam_srcu)) { 43736528c76SJames Morse if (cpumask_empty(&victim_iter->affinity)) { 43836528c76SJames Morse pr_debug("class %u has CPU-less component %u - can't match L3!\n", 43936528c76SJames Morse victim->level, victim_iter->comp_id); 44036528c76SJames Morse return false; 44136528c76SJames Morse } 44236528c76SJames Morse 44336528c76SJames Morse cpu = cpumask_any_and(&victim_iter->affinity, cpu_online_mask); 44436528c76SJames Morse if (WARN_ON_ONCE(cpu >= nr_cpu_ids)) 44536528c76SJames Morse return false; 44636528c76SJames Morse 44736528c76SJames Morse cpumask_clear(tmp_cpumask); 44836528c76SJames Morse err = find_l3_equivalent_bitmask(cpu, tmp_cpumask); 44936528c76SJames Morse if (err) { 45036528c76SJames Morse pr_debug("Failed to find L3's equivalent component to class %u component %u\n", 45136528c76SJames Morse victim->level, victim_iter->comp_id); 45236528c76SJames Morse return false; 45336528c76SJames Morse } 45436528c76SJames Morse 45536528c76SJames Morse /* Any differing bits in the affinity mask? */ 45636528c76SJames Morse if (!cpumask_equal(tmp_cpumask, &victim_iter->affinity)) { 45736528c76SJames Morse pr_debug("class %u component %u has Mismatched CPU mask with L3 equivalent\n" 45836528c76SJames Morse "L3:%*pbl != victim:%*pbl\n", 45936528c76SJames Morse victim->level, victim_iter->comp_id, 46036528c76SJames Morse cpumask_pr_args(tmp_cpumask), 46136528c76SJames Morse cpumask_pr_args(&victim_iter->affinity)); 46236528c76SJames Morse 46336528c76SJames Morse return false; 46436528c76SJames Morse } 46536528c76SJames Morse } 46636528c76SJames Morse 46736528c76SJames Morse return true; 46836528c76SJames Morse } 46936528c76SJames Morse 47036528c76SJames Morse /* 47136528c76SJames Morse * Test if the traffic for a class matches that at egress from the L3. For 47236528c76SJames Morse * MSC at memory controllers this is only possible if there is a single L3 47336528c76SJames Morse * as otherwise the counters at the memory can include bandwidth from the 47436528c76SJames Morse * non-local L3. 47536528c76SJames Morse */ 47636528c76SJames Morse static bool traffic_matches_l3(struct mpam_class *class) 47736528c76SJames Morse { 47836528c76SJames Morse int err, cpu; 47936528c76SJames Morse 48036528c76SJames Morse lockdep_assert_cpus_held(); 48136528c76SJames Morse 48236528c76SJames Morse if (class->type == MPAM_CLASS_CACHE && class->level == 3) 48336528c76SJames Morse return true; 48436528c76SJames Morse 48536528c76SJames Morse if (class->type == MPAM_CLASS_CACHE && class->level != 3) { 48636528c76SJames Morse pr_debug("class %u is a different cache from L3\n", class->level); 48736528c76SJames Morse return false; 48836528c76SJames Morse } 48936528c76SJames Morse 49036528c76SJames Morse if (class->type != MPAM_CLASS_MEMORY) { 49136528c76SJames Morse pr_debug("class %u is neither of type cache or memory\n", class->level); 49236528c76SJames Morse return false; 49336528c76SJames Morse } 49436528c76SJames Morse 49536528c76SJames Morse cpumask_var_t __free(free_cpumask_var) tmp_cpumask = CPUMASK_VAR_NULL; 49636528c76SJames Morse if (!alloc_cpumask_var(&tmp_cpumask, GFP_KERNEL)) { 49736528c76SJames Morse pr_debug("cpumask allocation failed\n"); 49836528c76SJames Morse return false; 49936528c76SJames Morse } 50036528c76SJames Morse 50136528c76SJames Morse cpu = cpumask_any_and(&class->affinity, cpu_online_mask); 50236528c76SJames Morse err = find_l3_equivalent_bitmask(cpu, tmp_cpumask); 50336528c76SJames Morse if (err) { 50436528c76SJames Morse pr_debug("Failed to find L3 downstream to cpu %d\n", cpu); 50536528c76SJames Morse return false; 50636528c76SJames Morse } 50736528c76SJames Morse 50836528c76SJames Morse if (!cpumask_equal(tmp_cpumask, cpu_possible_mask)) { 50936528c76SJames Morse pr_debug("There is more than one L3\n"); 51036528c76SJames Morse return false; 51136528c76SJames Morse } 51236528c76SJames Morse 51336528c76SJames Morse /* Be strict; the traffic might stop in the intermediate cache. */ 51436528c76SJames Morse if (get_cpu_cacheinfo_id(cpu, 4) != -1) { 51536528c76SJames Morse pr_debug("L3 isn't the last level of cache\n"); 51636528c76SJames Morse return false; 51736528c76SJames Morse } 51836528c76SJames Morse 51936528c76SJames Morse if (num_possible_nodes() > 1) { 52036528c76SJames Morse pr_debug("There is more than one numa node\n"); 52136528c76SJames Morse return false; 52236528c76SJames Morse } 52336528c76SJames Morse 52436528c76SJames Morse #ifdef CONFIG_HMEM_REPORTING 52536528c76SJames Morse if (node_devices[cpu_to_node(cpu)]->cache_dev) { 52636528c76SJames Morse pr_debug("There is a memory side cache\n"); 52736528c76SJames Morse return false; 52836528c76SJames Morse } 52936528c76SJames Morse #endif 53036528c76SJames Morse 53136528c76SJames Morse return true; 53236528c76SJames Morse } 53336528c76SJames Morse 53452a4edb1SJames Morse /* Test whether we can export MPAM_CLASS_CACHE:{2,3}? */ 53552a4edb1SJames Morse static void mpam_resctrl_pick_caches(void) 53652a4edb1SJames Morse { 53752a4edb1SJames Morse struct mpam_class *class; 53852a4edb1SJames Morse struct mpam_resctrl_res *res; 53952a4edb1SJames Morse 54052a4edb1SJames Morse lockdep_assert_cpus_held(); 54152a4edb1SJames Morse 54252a4edb1SJames Morse guard(srcu)(&mpam_srcu); 54352a4edb1SJames Morse list_for_each_entry_srcu(class, &mpam_classes, classes_list, 54452a4edb1SJames Morse srcu_read_lock_held(&mpam_srcu)) { 54552a4edb1SJames Morse if (class->type != MPAM_CLASS_CACHE) { 54652a4edb1SJames Morse pr_debug("class %u is not a cache\n", class->level); 54752a4edb1SJames Morse continue; 54852a4edb1SJames Morse } 54952a4edb1SJames Morse 55052a4edb1SJames Morse if (class->level != 2 && class->level != 3) { 55152a4edb1SJames Morse pr_debug("class %u is not L2 or L3\n", class->level); 55252a4edb1SJames Morse continue; 55352a4edb1SJames Morse } 55452a4edb1SJames Morse 55552a4edb1SJames Morse if (!cache_has_usable_cpor(class)) { 55652a4edb1SJames Morse pr_debug("class %u cache misses CPOR\n", class->level); 55752a4edb1SJames Morse continue; 55852a4edb1SJames Morse } 55952a4edb1SJames Morse 56052a4edb1SJames Morse if (!cpumask_equal(&class->affinity, cpu_possible_mask)) { 56152a4edb1SJames Morse pr_debug("class %u has missing CPUs, mask %*pb != %*pb\n", class->level, 56252a4edb1SJames Morse cpumask_pr_args(&class->affinity), 56352a4edb1SJames Morse cpumask_pr_args(cpu_possible_mask)); 56452a4edb1SJames Morse continue; 56552a4edb1SJames Morse } 56652a4edb1SJames Morse 56752a4edb1SJames Morse if (class->level == 2) 56852a4edb1SJames Morse res = &mpam_resctrl_controls[RDT_RESOURCE_L2]; 56952a4edb1SJames Morse else 57052a4edb1SJames Morse res = &mpam_resctrl_controls[RDT_RESOURCE_L3]; 57152a4edb1SJames Morse res->class = class; 57252a4edb1SJames Morse } 57352a4edb1SJames Morse } 57452a4edb1SJames Morse 57536528c76SJames Morse static void mpam_resctrl_pick_mba(void) 57636528c76SJames Morse { 57736528c76SJames Morse struct mpam_class *class, *candidate_class = NULL; 57836528c76SJames Morse struct mpam_resctrl_res *res; 57936528c76SJames Morse 58036528c76SJames Morse lockdep_assert_cpus_held(); 58136528c76SJames Morse 58236528c76SJames Morse guard(srcu)(&mpam_srcu); 58336528c76SJames Morse list_for_each_entry_srcu(class, &mpam_classes, classes_list, 58436528c76SJames Morse srcu_read_lock_held(&mpam_srcu)) { 58536528c76SJames Morse struct mpam_props *cprops = &class->props; 58636528c76SJames Morse 58736528c76SJames Morse if (class->level != 3 && class->type == MPAM_CLASS_CACHE) { 58836528c76SJames Morse pr_debug("class %u is a cache but not the L3\n", class->level); 58936528c76SJames Morse continue; 59036528c76SJames Morse } 59136528c76SJames Morse 59236528c76SJames Morse if (!class_has_usable_mba(cprops)) { 59336528c76SJames Morse pr_debug("class %u has no bandwidth control\n", 59436528c76SJames Morse class->level); 59536528c76SJames Morse continue; 59636528c76SJames Morse } 59736528c76SJames Morse 59836528c76SJames Morse if (!cpumask_equal(&class->affinity, cpu_possible_mask)) { 59936528c76SJames Morse pr_debug("class %u has missing CPUs\n", class->level); 60036528c76SJames Morse continue; 60136528c76SJames Morse } 60236528c76SJames Morse 60336528c76SJames Morse if (!topology_matches_l3(class)) { 60436528c76SJames Morse pr_debug("class %u topology doesn't match L3\n", 60536528c76SJames Morse class->level); 60636528c76SJames Morse continue; 60736528c76SJames Morse } 60836528c76SJames Morse 60936528c76SJames Morse if (!traffic_matches_l3(class)) { 61036528c76SJames Morse pr_debug("class %u traffic doesn't match L3 egress\n", 61136528c76SJames Morse class->level); 61236528c76SJames Morse continue; 61336528c76SJames Morse } 61436528c76SJames Morse 61536528c76SJames Morse /* 61636528c76SJames Morse * Pick a resource to be MBA that as close as possible to 61736528c76SJames Morse * the L3. mbm_total counts the bandwidth leaving the L3 61836528c76SJames Morse * cache and MBA should correspond as closely as possible 61936528c76SJames Morse * for proper operation of mba_sc. 62036528c76SJames Morse */ 62136528c76SJames Morse if (!candidate_class || class->level < candidate_class->level) 62236528c76SJames Morse candidate_class = class; 62336528c76SJames Morse } 62436528c76SJames Morse 62536528c76SJames Morse if (candidate_class) { 62636528c76SJames Morse pr_debug("selected class %u to back MBA\n", 62736528c76SJames Morse candidate_class->level); 62836528c76SJames Morse res = &mpam_resctrl_controls[RDT_RESOURCE_MBA]; 62936528c76SJames Morse res->class = candidate_class; 63036528c76SJames Morse } 63136528c76SJames Morse } 63236528c76SJames Morse 63309e61dafSJames Morse static int mpam_resctrl_control_init(struct mpam_resctrl_res *res) 63409e61dafSJames Morse { 63552a4edb1SJames Morse struct mpam_class *class = res->class; 63636528c76SJames Morse struct mpam_props *cprops = &class->props; 63752a4edb1SJames Morse struct rdt_resource *r = &res->resctrl_res; 63852a4edb1SJames Morse 63952a4edb1SJames Morse switch (r->rid) { 64052a4edb1SJames Morse case RDT_RESOURCE_L2: 64152a4edb1SJames Morse case RDT_RESOURCE_L3: 64252a4edb1SJames Morse r->schema_fmt = RESCTRL_SCHEMA_BITMAP; 64352a4edb1SJames Morse r->cache.arch_has_sparse_bitmasks = true; 64452a4edb1SJames Morse 64552a4edb1SJames Morse r->cache.cbm_len = class->props.cpbm_wd; 64652a4edb1SJames Morse /* mpam_devices will reject empty bitmaps */ 64752a4edb1SJames Morse r->cache.min_cbm_bits = 1; 64852a4edb1SJames Morse 64952a4edb1SJames Morse if (r->rid == RDT_RESOURCE_L2) { 65052a4edb1SJames Morse r->name = "L2"; 65152a4edb1SJames Morse r->ctrl_scope = RESCTRL_L2_CACHE; 65252a4edb1SJames Morse r->cdp_capable = true; 65352a4edb1SJames Morse } else { 65452a4edb1SJames Morse r->name = "L3"; 65552a4edb1SJames Morse r->ctrl_scope = RESCTRL_L3_CACHE; 65652a4edb1SJames Morse r->cdp_capable = true; 65752a4edb1SJames Morse } 65852a4edb1SJames Morse 65952a4edb1SJames Morse /* 66052a4edb1SJames Morse * Which bits are shared with other ...things... Unknown 66152a4edb1SJames Morse * devices use partid-0 which uses all the bitmap fields. Until 66252a4edb1SJames Morse * we have configured the SMMU and GIC not to do this 'all the 66352a4edb1SJames Morse * bits' is the correct answer here. 66452a4edb1SJames Morse */ 66552a4edb1SJames Morse r->cache.shareable_bits = resctrl_get_default_ctrl(r); 66652a4edb1SJames Morse r->alloc_capable = true; 66752a4edb1SJames Morse break; 66836528c76SJames Morse case RDT_RESOURCE_MBA: 66936528c76SJames Morse r->schema_fmt = RESCTRL_SCHEMA_RANGE; 67036528c76SJames Morse r->ctrl_scope = RESCTRL_L3_CACHE; 67136528c76SJames Morse 67236528c76SJames Morse r->membw.delay_linear = true; 67336528c76SJames Morse r->membw.throttle_mode = THREAD_THROTTLE_UNDEFINED; 67436528c76SJames Morse r->membw.min_bw = get_mba_min(cprops); 67536528c76SJames Morse r->membw.max_bw = MAX_MBA_BW; 67636528c76SJames Morse r->membw.bw_gran = get_mba_granularity(cprops); 67736528c76SJames Morse 67836528c76SJames Morse r->name = "MB"; 67936528c76SJames Morse r->alloc_capable = true; 68036528c76SJames Morse break; 68152a4edb1SJames Morse default: 68252a4edb1SJames Morse return -EINVAL; 68352a4edb1SJames Morse } 68409e61dafSJames Morse 68509e61dafSJames Morse return 0; 68609e61dafSJames Morse } 68709e61dafSJames Morse 68809e61dafSJames Morse static int mpam_resctrl_pick_domain_id(int cpu, struct mpam_component *comp) 68909e61dafSJames Morse { 69009e61dafSJames Morse struct mpam_class *class = comp->class; 69109e61dafSJames Morse 69209e61dafSJames Morse if (class->type == MPAM_CLASS_CACHE) 69309e61dafSJames Morse return comp->comp_id; 69409e61dafSJames Morse 69536528c76SJames Morse if (topology_matches_l3(class)) { 69636528c76SJames Morse /* Use the corresponding L3 component ID as the domain ID */ 69736528c76SJames Morse int id = get_cpu_cacheinfo_id(cpu, 3); 69836528c76SJames Morse 69936528c76SJames Morse /* Implies topology_matches_l3() made a mistake */ 70036528c76SJames Morse if (WARN_ON_ONCE(id == -1)) 70136528c76SJames Morse return comp->comp_id; 70236528c76SJames Morse 70336528c76SJames Morse return id; 70436528c76SJames Morse } 70536528c76SJames Morse 70609e61dafSJames Morse /* Otherwise, expose the ID used by the firmware table code. */ 70709e61dafSJames Morse return comp->comp_id; 70809e61dafSJames Morse } 70909e61dafSJames Morse 710*264c2859SBen Horgan static int mpam_resctrl_monitor_init(struct mpam_resctrl_mon *mon, 711*264c2859SBen Horgan enum resctrl_event_id type) 712*264c2859SBen Horgan { 713*264c2859SBen Horgan struct mpam_resctrl_res *res = &mpam_resctrl_controls[RDT_RESOURCE_L3]; 714*264c2859SBen Horgan struct rdt_resource *l3 = &res->resctrl_res; 715*264c2859SBen Horgan 716*264c2859SBen Horgan lockdep_assert_cpus_held(); 717*264c2859SBen Horgan 718*264c2859SBen Horgan /* 719*264c2859SBen Horgan * There also needs to be an L3 cache present. 720*264c2859SBen Horgan * The check just requires any online CPU and it can't go offline as we 721*264c2859SBen Horgan * hold the cpu lock. 722*264c2859SBen Horgan */ 723*264c2859SBen Horgan if (get_cpu_cacheinfo_id(raw_smp_processor_id(), 3) == -1) 724*264c2859SBen Horgan return 0; 725*264c2859SBen Horgan 726*264c2859SBen Horgan /* 727*264c2859SBen Horgan * If there are no MPAM resources on L3, force it into existence. 728*264c2859SBen Horgan * topology_matches_l3() already ensures this looks like the L3. 729*264c2859SBen Horgan * The domain-ids will be fixed up by mpam_resctrl_domain_hdr_init(). 730*264c2859SBen Horgan */ 731*264c2859SBen Horgan if (!res->class) { 732*264c2859SBen Horgan pr_warn_once("Faking L3 MSC to enable counters.\n"); 733*264c2859SBen Horgan res->class = mpam_resctrl_counters[type].class; 734*264c2859SBen Horgan } 735*264c2859SBen Horgan 736*264c2859SBen Horgan /* 737*264c2859SBen Horgan * Called multiple times!, once per event type that has a 738*264c2859SBen Horgan * monitoring class. 739*264c2859SBen Horgan * Setting name is necessary on monitor only platforms. 740*264c2859SBen Horgan */ 741*264c2859SBen Horgan l3->name = "L3"; 742*264c2859SBen Horgan l3->mon_scope = RESCTRL_L3_CACHE; 743*264c2859SBen Horgan 744*264c2859SBen Horgan /* 745*264c2859SBen Horgan * num-rmid is the upper bound for the number of monitoring groups that 746*264c2859SBen Horgan * can exist simultaneously, including the default monitoring group for 747*264c2859SBen Horgan * each control group. Hence, advertise the whole rmid_idx space even 748*264c2859SBen Horgan * though each control group has its own pmg/rmid space. Unfortunately, 749*264c2859SBen Horgan * this does mean userspace needs to know the architecture to correctly 750*264c2859SBen Horgan * interpret this value. 751*264c2859SBen Horgan */ 752*264c2859SBen Horgan l3->mon.num_rmid = resctrl_arch_system_num_rmid_idx(); 753*264c2859SBen Horgan 754*264c2859SBen Horgan if (resctrl_enable_mon_event(type, false, 0, NULL)) 755*264c2859SBen Horgan l3->mon_capable = true; 756*264c2859SBen Horgan 757*264c2859SBen Horgan return 0; 758*264c2859SBen Horgan } 759*264c2859SBen Horgan 76002cc6616SJames Morse u32 resctrl_arch_get_config(struct rdt_resource *r, struct rdt_ctrl_domain *d, 76102cc6616SJames Morse u32 closid, enum resctrl_conf_type type) 76202cc6616SJames Morse { 76302cc6616SJames Morse u32 partid; 76402cc6616SJames Morse struct mpam_config *cfg; 76502cc6616SJames Morse struct mpam_props *cprops; 76602cc6616SJames Morse struct mpam_resctrl_res *res; 76702cc6616SJames Morse struct mpam_resctrl_dom *dom; 76802cc6616SJames Morse enum mpam_device_features configured_by; 76902cc6616SJames Morse 77002cc6616SJames Morse lockdep_assert_cpus_held(); 77102cc6616SJames Morse 77202cc6616SJames Morse if (!mpam_is_enabled()) 77302cc6616SJames Morse return resctrl_get_default_ctrl(r); 77402cc6616SJames Morse 77502cc6616SJames Morse res = container_of(r, struct mpam_resctrl_res, resctrl_res); 77602cc6616SJames Morse dom = container_of(d, struct mpam_resctrl_dom, resctrl_ctrl_dom); 77702cc6616SJames Morse cprops = &res->class->props; 77802cc6616SJames Morse 7796789fb99SJames Morse /* 7806789fb99SJames Morse * When CDP is enabled, but the resource doesn't support it, 7816789fb99SJames Morse * the control is cloned across both partids. 7826789fb99SJames Morse * Pick one at random to read: 7836789fb99SJames Morse */ 7846789fb99SJames Morse if (mpam_resctrl_hide_cdp(r->rid)) 7856789fb99SJames Morse type = CDP_DATA; 7866789fb99SJames Morse 78702cc6616SJames Morse partid = resctrl_get_config_index(closid, type); 78802cc6616SJames Morse cfg = &dom->ctrl_comp->cfg[partid]; 78902cc6616SJames Morse 79002cc6616SJames Morse switch (r->rid) { 79102cc6616SJames Morse case RDT_RESOURCE_L2: 79202cc6616SJames Morse case RDT_RESOURCE_L3: 79302cc6616SJames Morse configured_by = mpam_feat_cpor_part; 79402cc6616SJames Morse break; 79536528c76SJames Morse case RDT_RESOURCE_MBA: 79636528c76SJames Morse if (mpam_has_feature(mpam_feat_mbw_max, cprops)) { 79736528c76SJames Morse configured_by = mpam_feat_mbw_max; 79836528c76SJames Morse break; 79936528c76SJames Morse } 80036528c76SJames Morse fallthrough; 80102cc6616SJames Morse default: 80202cc6616SJames Morse return resctrl_get_default_ctrl(r); 80302cc6616SJames Morse } 80402cc6616SJames Morse 80502cc6616SJames Morse if (!r->alloc_capable || partid >= resctrl_arch_get_num_closid(r) || 80602cc6616SJames Morse !mpam_has_feature(configured_by, cfg)) 80702cc6616SJames Morse return resctrl_get_default_ctrl(r); 80802cc6616SJames Morse 80902cc6616SJames Morse switch (configured_by) { 81002cc6616SJames Morse case mpam_feat_cpor_part: 81102cc6616SJames Morse return cfg->cpbm; 81236528c76SJames Morse case mpam_feat_mbw_max: 81336528c76SJames Morse return mbw_max_to_percent(cfg->mbw_max, cprops); 81402cc6616SJames Morse default: 81502cc6616SJames Morse return resctrl_get_default_ctrl(r); 81602cc6616SJames Morse } 81702cc6616SJames Morse } 81802cc6616SJames Morse 8199cd2b522SJames Morse int resctrl_arch_update_one(struct rdt_resource *r, struct rdt_ctrl_domain *d, 8209cd2b522SJames Morse u32 closid, enum resctrl_conf_type t, u32 cfg_val) 8219cd2b522SJames Morse { 8226789fb99SJames Morse int err; 8239cd2b522SJames Morse u32 partid; 8249cd2b522SJames Morse struct mpam_config cfg; 8259cd2b522SJames Morse struct mpam_props *cprops; 8269cd2b522SJames Morse struct mpam_resctrl_res *res; 8279cd2b522SJames Morse struct mpam_resctrl_dom *dom; 8289cd2b522SJames Morse 8299cd2b522SJames Morse lockdep_assert_cpus_held(); 8309cd2b522SJames Morse lockdep_assert_irqs_enabled(); 8319cd2b522SJames Morse 8329cd2b522SJames Morse /* 8339cd2b522SJames Morse * No need to check the CPU as mpam_apply_config() doesn't care, and 8349cd2b522SJames Morse * resctrl_arch_update_domains() relies on this. 8359cd2b522SJames Morse */ 8369cd2b522SJames Morse res = container_of(r, struct mpam_resctrl_res, resctrl_res); 8379cd2b522SJames Morse dom = container_of(d, struct mpam_resctrl_dom, resctrl_ctrl_dom); 8389cd2b522SJames Morse cprops = &res->class->props; 8399cd2b522SJames Morse 8406789fb99SJames Morse if (mpam_resctrl_hide_cdp(r->rid)) 8416789fb99SJames Morse t = CDP_DATA; 8426789fb99SJames Morse 8439cd2b522SJames Morse partid = resctrl_get_config_index(closid, t); 8449cd2b522SJames Morse if (!r->alloc_capable || partid >= resctrl_arch_get_num_closid(r)) { 8459cd2b522SJames Morse pr_debug("Not alloc capable or computed PARTID out of range\n"); 8469cd2b522SJames Morse return -EINVAL; 8479cd2b522SJames Morse } 8489cd2b522SJames Morse 8499cd2b522SJames Morse /* 8509cd2b522SJames Morse * Copy the current config to avoid clearing other resources when the 8519cd2b522SJames Morse * same component is exposed multiple times through resctrl. 8529cd2b522SJames Morse */ 8539cd2b522SJames Morse cfg = dom->ctrl_comp->cfg[partid]; 8549cd2b522SJames Morse 8559cd2b522SJames Morse switch (r->rid) { 8569cd2b522SJames Morse case RDT_RESOURCE_L2: 8579cd2b522SJames Morse case RDT_RESOURCE_L3: 8589cd2b522SJames Morse cfg.cpbm = cfg_val; 8599cd2b522SJames Morse mpam_set_feature(mpam_feat_cpor_part, &cfg); 8609cd2b522SJames Morse break; 86136528c76SJames Morse case RDT_RESOURCE_MBA: 86236528c76SJames Morse if (mpam_has_feature(mpam_feat_mbw_max, cprops)) { 86336528c76SJames Morse cfg.mbw_max = percent_to_mbw_max(cfg_val, cprops); 86436528c76SJames Morse mpam_set_feature(mpam_feat_mbw_max, &cfg); 86536528c76SJames Morse break; 86636528c76SJames Morse } 86736528c76SJames Morse fallthrough; 8689cd2b522SJames Morse default: 8699cd2b522SJames Morse return -EINVAL; 8709cd2b522SJames Morse } 8719cd2b522SJames Morse 8726789fb99SJames Morse /* 8736789fb99SJames Morse * When CDP is enabled, but the resource doesn't support it, we need to 8746789fb99SJames Morse * apply the same configuration to the other partid. 8756789fb99SJames Morse */ 8766789fb99SJames Morse if (mpam_resctrl_hide_cdp(r->rid)) { 8776789fb99SJames Morse partid = resctrl_get_config_index(closid, CDP_CODE); 8786789fb99SJames Morse err = mpam_apply_config(dom->ctrl_comp, partid, &cfg); 8796789fb99SJames Morse if (err) 8806789fb99SJames Morse return err; 8816789fb99SJames Morse 8826789fb99SJames Morse partid = resctrl_get_config_index(closid, CDP_DATA); 8836789fb99SJames Morse return mpam_apply_config(dom->ctrl_comp, partid, &cfg); 8846789fb99SJames Morse } 8856789fb99SJames Morse 8869cd2b522SJames Morse return mpam_apply_config(dom->ctrl_comp, partid, &cfg); 8879cd2b522SJames Morse } 8889cd2b522SJames Morse 8899cd2b522SJames Morse int resctrl_arch_update_domains(struct rdt_resource *r, u32 closid) 8909cd2b522SJames Morse { 8919cd2b522SJames Morse int err; 8929cd2b522SJames Morse struct rdt_ctrl_domain *d; 8939cd2b522SJames Morse 8949cd2b522SJames Morse lockdep_assert_cpus_held(); 8959cd2b522SJames Morse lockdep_assert_irqs_enabled(); 8969cd2b522SJames Morse 8979cd2b522SJames Morse list_for_each_entry_rcu(d, &r->ctrl_domains, hdr.list) { 8989cd2b522SJames Morse for (enum resctrl_conf_type t = 0; t < CDP_NUM_TYPES; t++) { 8999cd2b522SJames Morse struct resctrl_staged_config *cfg = &d->staged_config[t]; 9009cd2b522SJames Morse 9019cd2b522SJames Morse if (!cfg->have_new_ctrl) 9029cd2b522SJames Morse continue; 9039cd2b522SJames Morse 9049cd2b522SJames Morse err = resctrl_arch_update_one(r, d, closid, t, 9059cd2b522SJames Morse cfg->new_ctrl); 9069cd2b522SJames Morse if (err) 9079cd2b522SJames Morse return err; 9089cd2b522SJames Morse } 9099cd2b522SJames Morse } 9109cd2b522SJames Morse 9119cd2b522SJames Morse return 0; 9129cd2b522SJames Morse } 9139cd2b522SJames Morse 914370d166dSJames Morse void resctrl_arch_reset_all_ctrls(struct rdt_resource *r) 915370d166dSJames Morse { 916370d166dSJames Morse struct mpam_resctrl_res *res; 917370d166dSJames Morse 918370d166dSJames Morse lockdep_assert_cpus_held(); 919370d166dSJames Morse 920370d166dSJames Morse if (!mpam_is_enabled()) 921370d166dSJames Morse return; 922370d166dSJames Morse 923370d166dSJames Morse res = container_of(r, struct mpam_resctrl_res, resctrl_res); 924370d166dSJames Morse mpam_reset_class_locked(res->class); 925370d166dSJames Morse } 926370d166dSJames Morse 92709e61dafSJames Morse static void mpam_resctrl_domain_hdr_init(int cpu, struct mpam_component *comp, 92809e61dafSJames Morse enum resctrl_res_level rid, 92909e61dafSJames Morse struct rdt_domain_hdr *hdr) 93009e61dafSJames Morse { 93109e61dafSJames Morse lockdep_assert_cpus_held(); 93209e61dafSJames Morse 93309e61dafSJames Morse INIT_LIST_HEAD(&hdr->list); 93409e61dafSJames Morse hdr->id = mpam_resctrl_pick_domain_id(cpu, comp); 93509e61dafSJames Morse hdr->rid = rid; 93609e61dafSJames Morse cpumask_set_cpu(cpu, &hdr->cpu_mask); 93709e61dafSJames Morse } 93809e61dafSJames Morse 93909e61dafSJames Morse static void mpam_resctrl_online_domain_hdr(unsigned int cpu, 94009e61dafSJames Morse struct rdt_domain_hdr *hdr) 94109e61dafSJames Morse { 94209e61dafSJames Morse lockdep_assert_cpus_held(); 94309e61dafSJames Morse 94409e61dafSJames Morse cpumask_set_cpu(cpu, &hdr->cpu_mask); 94509e61dafSJames Morse } 94609e61dafSJames Morse 94709e61dafSJames Morse /** 94809e61dafSJames Morse * mpam_resctrl_offline_domain_hdr() - Update the domain header to remove a CPU. 94909e61dafSJames Morse * @cpu: The CPU to remove from the domain. 95009e61dafSJames Morse * @hdr: The domain's header. 95109e61dafSJames Morse * 95209e61dafSJames Morse * Removes @cpu from the header mask. If this was the last CPU in the domain, 95309e61dafSJames Morse * the domain header is removed from its parent list and true is returned, 95409e61dafSJames Morse * indicating the parent structure can be freed. 95509e61dafSJames Morse * If there are other CPUs in the domain, returns false. 95609e61dafSJames Morse */ 95709e61dafSJames Morse static bool mpam_resctrl_offline_domain_hdr(unsigned int cpu, 95809e61dafSJames Morse struct rdt_domain_hdr *hdr) 95909e61dafSJames Morse { 96009e61dafSJames Morse lockdep_assert_held(&domain_list_lock); 96109e61dafSJames Morse 96209e61dafSJames Morse cpumask_clear_cpu(cpu, &hdr->cpu_mask); 96309e61dafSJames Morse if (cpumask_empty(&hdr->cpu_mask)) { 96409e61dafSJames Morse list_del_rcu(&hdr->list); 96509e61dafSJames Morse synchronize_rcu(); 96609e61dafSJames Morse return true; 96709e61dafSJames Morse } 96809e61dafSJames Morse 96909e61dafSJames Morse return false; 97009e61dafSJames Morse } 97109e61dafSJames Morse 97209e61dafSJames Morse static void mpam_resctrl_domain_insert(struct list_head *list, 97309e61dafSJames Morse struct rdt_domain_hdr *new) 97409e61dafSJames Morse { 97509e61dafSJames Morse struct rdt_domain_hdr *err; 97609e61dafSJames Morse struct list_head *pos = NULL; 97709e61dafSJames Morse 97809e61dafSJames Morse lockdep_assert_held(&domain_list_lock); 97909e61dafSJames Morse 98009e61dafSJames Morse err = resctrl_find_domain(list, new->id, &pos); 98109e61dafSJames Morse if (WARN_ON_ONCE(err)) 98209e61dafSJames Morse return; 98309e61dafSJames Morse 98409e61dafSJames Morse list_add_tail_rcu(&new->list, pos); 98509e61dafSJames Morse } 98609e61dafSJames Morse 987*264c2859SBen Horgan static struct mpam_component *find_component(struct mpam_class *class, int cpu) 988*264c2859SBen Horgan { 989*264c2859SBen Horgan struct mpam_component *comp; 990*264c2859SBen Horgan 991*264c2859SBen Horgan guard(srcu)(&mpam_srcu); 992*264c2859SBen Horgan list_for_each_entry_srcu(comp, &class->components, class_list, 993*264c2859SBen Horgan srcu_read_lock_held(&mpam_srcu)) { 994*264c2859SBen Horgan if (cpumask_test_cpu(cpu, &comp->affinity)) 995*264c2859SBen Horgan return comp; 996*264c2859SBen Horgan } 997*264c2859SBen Horgan 998*264c2859SBen Horgan return NULL; 999*264c2859SBen Horgan } 1000*264c2859SBen Horgan 100109e61dafSJames Morse static struct mpam_resctrl_dom * 100209e61dafSJames Morse mpam_resctrl_alloc_domain(unsigned int cpu, struct mpam_resctrl_res *res) 100309e61dafSJames Morse { 100409e61dafSJames Morse int err; 100509e61dafSJames Morse struct mpam_resctrl_dom *dom; 1006*264c2859SBen Horgan struct rdt_l3_mon_domain *mon_d; 100709e61dafSJames Morse struct rdt_ctrl_domain *ctrl_d; 100809e61dafSJames Morse struct mpam_class *class = res->class; 100909e61dafSJames Morse struct mpam_component *comp_iter, *ctrl_comp; 101009e61dafSJames Morse struct rdt_resource *r = &res->resctrl_res; 101109e61dafSJames Morse 101209e61dafSJames Morse lockdep_assert_held(&domain_list_lock); 101309e61dafSJames Morse 101409e61dafSJames Morse ctrl_comp = NULL; 101509e61dafSJames Morse guard(srcu)(&mpam_srcu); 101609e61dafSJames Morse list_for_each_entry_srcu(comp_iter, &class->components, class_list, 101709e61dafSJames Morse srcu_read_lock_held(&mpam_srcu)) { 101809e61dafSJames Morse if (cpumask_test_cpu(cpu, &comp_iter->affinity)) { 101909e61dafSJames Morse ctrl_comp = comp_iter; 102009e61dafSJames Morse break; 102109e61dafSJames Morse } 102209e61dafSJames Morse } 102309e61dafSJames Morse 102409e61dafSJames Morse /* class has no component for this CPU */ 102509e61dafSJames Morse if (WARN_ON_ONCE(!ctrl_comp)) 102609e61dafSJames Morse return ERR_PTR(-EINVAL); 102709e61dafSJames Morse 102809e61dafSJames Morse dom = kzalloc_node(sizeof(*dom), GFP_KERNEL, cpu_to_node(cpu)); 102909e61dafSJames Morse if (!dom) 103009e61dafSJames Morse return ERR_PTR(-ENOMEM); 103109e61dafSJames Morse 103209e61dafSJames Morse if (r->alloc_capable) { 103309e61dafSJames Morse dom->ctrl_comp = ctrl_comp; 103409e61dafSJames Morse 103509e61dafSJames Morse ctrl_d = &dom->resctrl_ctrl_dom; 103609e61dafSJames Morse mpam_resctrl_domain_hdr_init(cpu, ctrl_comp, r->rid, &ctrl_d->hdr); 103709e61dafSJames Morse ctrl_d->hdr.type = RESCTRL_CTRL_DOMAIN; 103809e61dafSJames Morse err = resctrl_online_ctrl_domain(r, ctrl_d); 103909e61dafSJames Morse if (err) 104009e61dafSJames Morse goto free_domain; 104109e61dafSJames Morse 104209e61dafSJames Morse mpam_resctrl_domain_insert(&r->ctrl_domains, &ctrl_d->hdr); 104309e61dafSJames Morse } else { 104409e61dafSJames Morse pr_debug("Skipped control domain online - no controls\n"); 104509e61dafSJames Morse } 1046*264c2859SBen Horgan 1047*264c2859SBen Horgan if (r->mon_capable) { 1048*264c2859SBen Horgan struct mpam_component *any_mon_comp; 1049*264c2859SBen Horgan struct mpam_resctrl_mon *mon; 1050*264c2859SBen Horgan enum resctrl_event_id eventid; 1051*264c2859SBen Horgan 1052*264c2859SBen Horgan /* 1053*264c2859SBen Horgan * Even if the monitor domain is backed by a different 1054*264c2859SBen Horgan * component, the L3 component IDs need to be used... only 1055*264c2859SBen Horgan * there may be no ctrl_comp for the L3. 1056*264c2859SBen Horgan * Search each event's class list for a component with 1057*264c2859SBen Horgan * overlapping CPUs and set up the dom->mon_comp array. 1058*264c2859SBen Horgan */ 1059*264c2859SBen Horgan 1060*264c2859SBen Horgan for_each_mpam_resctrl_mon(mon, eventid) { 1061*264c2859SBen Horgan struct mpam_component *mon_comp; 1062*264c2859SBen Horgan 1063*264c2859SBen Horgan if (!mon->class) 1064*264c2859SBen Horgan continue; // dummy resource 1065*264c2859SBen Horgan 1066*264c2859SBen Horgan mon_comp = find_component(mon->class, cpu); 1067*264c2859SBen Horgan dom->mon_comp[eventid] = mon_comp; 1068*264c2859SBen Horgan if (mon_comp) 1069*264c2859SBen Horgan any_mon_comp = mon_comp; 1070*264c2859SBen Horgan } 1071*264c2859SBen Horgan if (!any_mon_comp) { 1072*264c2859SBen Horgan WARN_ON_ONCE(0); 1073*264c2859SBen Horgan err = -EFAULT; 1074*264c2859SBen Horgan goto offline_ctrl_domain; 1075*264c2859SBen Horgan } 1076*264c2859SBen Horgan 1077*264c2859SBen Horgan mon_d = &dom->resctrl_mon_dom; 1078*264c2859SBen Horgan mpam_resctrl_domain_hdr_init(cpu, any_mon_comp, r->rid, &mon_d->hdr); 1079*264c2859SBen Horgan mon_d->hdr.type = RESCTRL_MON_DOMAIN; 1080*264c2859SBen Horgan err = resctrl_online_mon_domain(r, &mon_d->hdr); 1081*264c2859SBen Horgan if (err) 1082*264c2859SBen Horgan goto offline_ctrl_domain; 1083*264c2859SBen Horgan 1084*264c2859SBen Horgan mpam_resctrl_domain_insert(&r->mon_domains, &mon_d->hdr); 1085*264c2859SBen Horgan } else { 1086*264c2859SBen Horgan pr_debug("Skipped monitor domain online - no monitors\n"); 1087*264c2859SBen Horgan } 1088*264c2859SBen Horgan 108909e61dafSJames Morse return dom; 109009e61dafSJames Morse 1091*264c2859SBen Horgan offline_ctrl_domain: 1092*264c2859SBen Horgan if (r->alloc_capable) { 1093*264c2859SBen Horgan mpam_resctrl_offline_domain_hdr(cpu, &ctrl_d->hdr); 1094*264c2859SBen Horgan resctrl_offline_ctrl_domain(r, ctrl_d); 1095*264c2859SBen Horgan } 109609e61dafSJames Morse free_domain: 109709e61dafSJames Morse kfree(dom); 109809e61dafSJames Morse dom = ERR_PTR(err); 109909e61dafSJames Morse 110009e61dafSJames Morse return dom; 110109e61dafSJames Morse } 110209e61dafSJames Morse 1103*264c2859SBen Horgan /* 1104*264c2859SBen Horgan * We know all the monitors are associated with the L3, even if there are no 1105*264c2859SBen Horgan * controls and therefore no control component. Find the cache-id for the CPU 1106*264c2859SBen Horgan * and use that to search for existing resctrl domains. 1107*264c2859SBen Horgan * This relies on mpam_resctrl_pick_domain_id() using the L3 cache-id 1108*264c2859SBen Horgan * for anything that is not a cache. 1109*264c2859SBen Horgan */ 1110*264c2859SBen Horgan static struct mpam_resctrl_dom *mpam_resctrl_get_mon_domain_from_cpu(int cpu) 1111*264c2859SBen Horgan { 1112*264c2859SBen Horgan int cache_id; 1113*264c2859SBen Horgan struct mpam_resctrl_dom *dom; 1114*264c2859SBen Horgan struct mpam_resctrl_res *l3 = &mpam_resctrl_controls[RDT_RESOURCE_L3]; 1115*264c2859SBen Horgan 1116*264c2859SBen Horgan lockdep_assert_cpus_held(); 1117*264c2859SBen Horgan 1118*264c2859SBen Horgan if (!l3->class) 1119*264c2859SBen Horgan return NULL; 1120*264c2859SBen Horgan cache_id = get_cpu_cacheinfo_id(cpu, 3); 1121*264c2859SBen Horgan if (cache_id < 0) 1122*264c2859SBen Horgan return NULL; 1123*264c2859SBen Horgan 1124*264c2859SBen Horgan list_for_each_entry_rcu(dom, &l3->resctrl_res.mon_domains, resctrl_mon_dom.hdr.list) { 1125*264c2859SBen Horgan if (dom->resctrl_mon_dom.hdr.id == cache_id) 1126*264c2859SBen Horgan return dom; 1127*264c2859SBen Horgan } 1128*264c2859SBen Horgan 1129*264c2859SBen Horgan return NULL; 1130*264c2859SBen Horgan } 1131*264c2859SBen Horgan 113209e61dafSJames Morse static struct mpam_resctrl_dom * 113309e61dafSJames Morse mpam_resctrl_get_domain_from_cpu(int cpu, struct mpam_resctrl_res *res) 113409e61dafSJames Morse { 113509e61dafSJames Morse struct mpam_resctrl_dom *dom; 113609e61dafSJames Morse struct rdt_resource *r = &res->resctrl_res; 113709e61dafSJames Morse 113809e61dafSJames Morse lockdep_assert_cpus_held(); 113909e61dafSJames Morse 114009e61dafSJames Morse list_for_each_entry_rcu(dom, &r->ctrl_domains, resctrl_ctrl_dom.hdr.list) { 114109e61dafSJames Morse if (cpumask_test_cpu(cpu, &dom->ctrl_comp->affinity)) 114209e61dafSJames Morse return dom; 114309e61dafSJames Morse } 114409e61dafSJames Morse 1145*264c2859SBen Horgan if (r->rid != RDT_RESOURCE_L3) 114609e61dafSJames Morse return NULL; 1147*264c2859SBen Horgan 1148*264c2859SBen Horgan /* Search the mon domain list too - needed on monitor only platforms. */ 1149*264c2859SBen Horgan return mpam_resctrl_get_mon_domain_from_cpu(cpu); 115009e61dafSJames Morse } 115109e61dafSJames Morse 115209e61dafSJames Morse int mpam_resctrl_online_cpu(unsigned int cpu) 115309e61dafSJames Morse { 115409e61dafSJames Morse struct mpam_resctrl_res *res; 115509e61dafSJames Morse enum resctrl_res_level rid; 115609e61dafSJames Morse 115709e61dafSJames Morse guard(mutex)(&domain_list_lock); 115809e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 115909e61dafSJames Morse struct mpam_resctrl_dom *dom; 116009e61dafSJames Morse struct rdt_resource *r = &res->resctrl_res; 116109e61dafSJames Morse 116209e61dafSJames Morse if (!res->class) 116309e61dafSJames Morse continue; // dummy_resource; 116409e61dafSJames Morse 116509e61dafSJames Morse dom = mpam_resctrl_get_domain_from_cpu(cpu, res); 116609e61dafSJames Morse if (!dom) { 116709e61dafSJames Morse dom = mpam_resctrl_alloc_domain(cpu, res); 116809e61dafSJames Morse if (IS_ERR(dom)) 116909e61dafSJames Morse return PTR_ERR(dom); 117009e61dafSJames Morse } else { 117109e61dafSJames Morse if (r->alloc_capable) { 117209e61dafSJames Morse struct rdt_ctrl_domain *ctrl_d = &dom->resctrl_ctrl_dom; 117309e61dafSJames Morse 117409e61dafSJames Morse mpam_resctrl_online_domain_hdr(cpu, &ctrl_d->hdr); 117509e61dafSJames Morse } 1176*264c2859SBen Horgan if (r->mon_capable) { 1177*264c2859SBen Horgan struct rdt_l3_mon_domain *mon_d = &dom->resctrl_mon_dom; 1178*264c2859SBen Horgan 1179*264c2859SBen Horgan mpam_resctrl_online_domain_hdr(cpu, &mon_d->hdr); 1180*264c2859SBen Horgan } 118109e61dafSJames Morse } 118209e61dafSJames Morse } 118309e61dafSJames Morse 118409e61dafSJames Morse resctrl_online_cpu(cpu); 118509e61dafSJames Morse 118609e61dafSJames Morse return 0; 118709e61dafSJames Morse } 118809e61dafSJames Morse 118909e61dafSJames Morse void mpam_resctrl_offline_cpu(unsigned int cpu) 119009e61dafSJames Morse { 119109e61dafSJames Morse struct mpam_resctrl_res *res; 119209e61dafSJames Morse enum resctrl_res_level rid; 119309e61dafSJames Morse 119409e61dafSJames Morse resctrl_offline_cpu(cpu); 119509e61dafSJames Morse 119609e61dafSJames Morse guard(mutex)(&domain_list_lock); 119709e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 119809e61dafSJames Morse struct mpam_resctrl_dom *dom; 1199*264c2859SBen Horgan struct rdt_l3_mon_domain *mon_d; 120009e61dafSJames Morse struct rdt_ctrl_domain *ctrl_d; 1201*264c2859SBen Horgan bool ctrl_dom_empty, mon_dom_empty; 120209e61dafSJames Morse struct rdt_resource *r = &res->resctrl_res; 120309e61dafSJames Morse 120409e61dafSJames Morse if (!res->class) 120509e61dafSJames Morse continue; // dummy resource 120609e61dafSJames Morse 120709e61dafSJames Morse dom = mpam_resctrl_get_domain_from_cpu(cpu, res); 120809e61dafSJames Morse if (WARN_ON_ONCE(!dom)) 120909e61dafSJames Morse continue; 121009e61dafSJames Morse 121109e61dafSJames Morse if (r->alloc_capable) { 121209e61dafSJames Morse ctrl_d = &dom->resctrl_ctrl_dom; 121309e61dafSJames Morse ctrl_dom_empty = mpam_resctrl_offline_domain_hdr(cpu, &ctrl_d->hdr); 121409e61dafSJames Morse if (ctrl_dom_empty) 121509e61dafSJames Morse resctrl_offline_ctrl_domain(&res->resctrl_res, ctrl_d); 121609e61dafSJames Morse } else { 121709e61dafSJames Morse ctrl_dom_empty = true; 121809e61dafSJames Morse } 121909e61dafSJames Morse 1220*264c2859SBen Horgan if (r->mon_capable) { 1221*264c2859SBen Horgan mon_d = &dom->resctrl_mon_dom; 1222*264c2859SBen Horgan mon_dom_empty = mpam_resctrl_offline_domain_hdr(cpu, &mon_d->hdr); 1223*264c2859SBen Horgan if (mon_dom_empty) 1224*264c2859SBen Horgan resctrl_offline_mon_domain(&res->resctrl_res, &mon_d->hdr); 1225*264c2859SBen Horgan } else { 1226*264c2859SBen Horgan mon_dom_empty = true; 1227*264c2859SBen Horgan } 1228*264c2859SBen Horgan 1229*264c2859SBen Horgan if (ctrl_dom_empty && mon_dom_empty) 123009e61dafSJames Morse kfree(dom); 123109e61dafSJames Morse } 123209e61dafSJames Morse } 123309e61dafSJames Morse 123409e61dafSJames Morse int mpam_resctrl_setup(void) 123509e61dafSJames Morse { 123609e61dafSJames Morse int err = 0; 123709e61dafSJames Morse struct mpam_resctrl_res *res; 123809e61dafSJames Morse enum resctrl_res_level rid; 1239*264c2859SBen Horgan struct mpam_resctrl_mon *mon; 1240*264c2859SBen Horgan enum resctrl_event_id eventid; 124109e61dafSJames Morse 12421c1e2968SBen Horgan wait_event(wait_cacheinfo_ready, cacheinfo_ready); 12431c1e2968SBen Horgan 124409e61dafSJames Morse cpus_read_lock(); 124509e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 124609e61dafSJames Morse INIT_LIST_HEAD_RCU(&res->resctrl_res.ctrl_domains); 1247*264c2859SBen Horgan INIT_LIST_HEAD_RCU(&res->resctrl_res.mon_domains); 124809e61dafSJames Morse res->resctrl_res.rid = rid; 124909e61dafSJames Morse } 125009e61dafSJames Morse 125152a4edb1SJames Morse /* Find some classes to use for controls */ 125252a4edb1SJames Morse mpam_resctrl_pick_caches(); 125336528c76SJames Morse mpam_resctrl_pick_mba(); 125409e61dafSJames Morse 125509e61dafSJames Morse /* Initialise the resctrl structures from the classes */ 125609e61dafSJames Morse for_each_mpam_resctrl_control(res, rid) { 125709e61dafSJames Morse if (!res->class) 125809e61dafSJames Morse continue; // dummy resource 125909e61dafSJames Morse 126009e61dafSJames Morse err = mpam_resctrl_control_init(res); 126109e61dafSJames Morse if (err) { 126209e61dafSJames Morse pr_debug("Failed to initialise rid %u\n", rid); 1263*264c2859SBen Horgan goto internal_error; 126409e61dafSJames Morse } 126509e61dafSJames Morse } 1266*264c2859SBen Horgan 1267*264c2859SBen Horgan for_each_mpam_resctrl_mon(mon, eventid) { 1268*264c2859SBen Horgan if (!mon->class) 1269*264c2859SBen Horgan continue; // dummy resource 1270*264c2859SBen Horgan 1271*264c2859SBen Horgan err = mpam_resctrl_monitor_init(mon, eventid); 1272*264c2859SBen Horgan if (err) { 1273*264c2859SBen Horgan pr_debug("Failed to initialise event %u\n", eventid); 1274*264c2859SBen Horgan goto internal_error; 1275*264c2859SBen Horgan } 1276*264c2859SBen Horgan } 1277*264c2859SBen Horgan 127809e61dafSJames Morse cpus_read_unlock(); 127909e61dafSJames Morse 1280*264c2859SBen Horgan if (!resctrl_arch_alloc_capable() && !resctrl_arch_mon_capable()) { 1281*264c2859SBen Horgan pr_debug("No alloc(%u) or monitor(%u) found - resctrl not supported\n", 1282*264c2859SBen Horgan resctrl_arch_alloc_capable(), resctrl_arch_mon_capable()); 128309e61dafSJames Morse return -EOPNOTSUPP; 128409e61dafSJames Morse } 128509e61dafSJames Morse 128609e61dafSJames Morse /* TODO: call resctrl_init() */ 128709e61dafSJames Morse 128809e61dafSJames Morse return 0; 1289*264c2859SBen Horgan 1290*264c2859SBen Horgan internal_error: 1291*264c2859SBen Horgan cpus_read_unlock(); 1292*264c2859SBen Horgan pr_debug("Internal error %d - resctrl not supported\n", err); 1293*264c2859SBen Horgan return err; 129409e61dafSJames Morse } 12951c1e2968SBen Horgan 12961c1e2968SBen Horgan static int __init __cacheinfo_ready(void) 12971c1e2968SBen Horgan { 12981c1e2968SBen Horgan cacheinfo_ready = true; 12991c1e2968SBen Horgan wake_up(&wait_cacheinfo_ready); 13001c1e2968SBen Horgan 13011c1e2968SBen Horgan return 0; 13021c1e2968SBen Horgan } 13031c1e2968SBen Horgan device_initcall_sync(__cacheinfo_ready); 13045dc8f73eSDave Martin 13055dc8f73eSDave Martin #ifdef CONFIG_MPAM_KUNIT_TEST 13065dc8f73eSDave Martin #include "test_mpam_resctrl.c" 13075dc8f73eSDave Martin #endif 1308