| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * User interface for Resource Allocation in Resource Director Technology(RDT) |
| * |
| * Copyright (C) 2016 Intel Corporation |
| * |
| * Author: Fenghua Yu <fenghua.yu@intel.com> |
| * |
| * More information about RDT be found in the Intel (R) x86 Architecture |
| * Software Developer Manual. |
| */ |
| |
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| |
| #include <linux/cpu.h> |
| #include <linux/debugfs.h> |
| #include <linux/fs.h> |
| #include <linux/fs_parser.h> |
| #include <linux/sysfs.h> |
| #include <linux/kernfs.h> |
| #include <linux/resctrl.h> |
| #include <linux/seq_buf.h> |
| #include <linux/seq_file.h> |
| #include <linux/sched/signal.h> |
| #include <linux/sched/task.h> |
| #include <linux/slab.h> |
| #include <linux/task_work.h> |
| #include <linux/user_namespace.h> |
| |
| #include <uapi/linux/magic.h> |
| |
| #include <asm/msr.h> |
| #include "internal.h" |
| |
| DEFINE_STATIC_KEY_FALSE(rdt_enable_key); |
| |
| DEFINE_STATIC_KEY_FALSE(rdt_mon_enable_key); |
| |
| DEFINE_STATIC_KEY_FALSE(rdt_alloc_enable_key); |
| |
| /* |
| * This is safe against resctrl_arch_sched_in() called from __switch_to() |
| * because __switch_to() is executed with interrupts disabled. A local call |
| * from update_closid_rmid() is protected against __switch_to() because |
| * preemption is disabled. |
| */ |
| void resctrl_arch_sync_cpu_closid_rmid(void *info) |
| { |
| struct resctrl_cpu_defaults *r = info; |
| |
| if (r) { |
| this_cpu_write(pqr_state.default_closid, r->closid); |
| this_cpu_write(pqr_state.default_rmid, r->rmid); |
| } |
| |
| /* |
| * We cannot unconditionally write the MSR because the current |
| * executing task might have its own closid selected. Just reuse |
| * the context switch code. |
| */ |
| resctrl_arch_sched_in(current); |
| } |
| |
| #define INVALID_CONFIG_INDEX UINT_MAX |
| |
| /** |
| * mon_event_config_index_get - get the hardware index for the |
| * configurable event |
| * @evtid: event id. |
| * |
| * Return: 0 for evtid == QOS_L3_MBM_TOTAL_EVENT_ID |
| * 1 for evtid == QOS_L3_MBM_LOCAL_EVENT_ID |
| * INVALID_CONFIG_INDEX for invalid evtid |
| */ |
| static inline unsigned int mon_event_config_index_get(u32 evtid) |
| { |
| switch (evtid) { |
| case QOS_L3_MBM_TOTAL_EVENT_ID: |
| return 0; |
| case QOS_L3_MBM_LOCAL_EVENT_ID: |
| return 1; |
| default: |
| /* Should never reach here */ |
| return INVALID_CONFIG_INDEX; |
| } |
| } |
| |
| void resctrl_arch_mon_event_config_read(void *_config_info) |
| { |
| struct resctrl_mon_config_info *config_info = _config_info; |
| unsigned int index; |
| u64 msrval; |
| |
| index = mon_event_config_index_get(config_info->evtid); |
| if (index == INVALID_CONFIG_INDEX) { |
| pr_warn_once("Invalid event id %d\n", config_info->evtid); |
| return; |
| } |
| rdmsrq(MSR_IA32_EVT_CFG_BASE + index, msrval); |
| |
| /* Report only the valid event configuration bits */ |
| config_info->mon_config = msrval & MAX_EVT_CONFIG_BITS; |
| } |
| |
| void resctrl_arch_mon_event_config_write(void *_config_info) |
| { |
| struct resctrl_mon_config_info *config_info = _config_info; |
| unsigned int index; |
| |
| index = mon_event_config_index_get(config_info->evtid); |
| if (index == INVALID_CONFIG_INDEX) { |
| pr_warn_once("Invalid event id %d\n", config_info->evtid); |
| return; |
| } |
| wrmsrq(MSR_IA32_EVT_CFG_BASE + index, config_info->mon_config); |
| } |
| |
| static void l3_qos_cfg_update(void *arg) |
| { |
| bool *enable = arg; |
| |
| wrmsrq(MSR_IA32_L3_QOS_CFG, *enable ? L3_QOS_CDP_ENABLE : 0ULL); |
| } |
| |
| static void l2_qos_cfg_update(void *arg) |
| { |
| bool *enable = arg; |
| |
| wrmsrq(MSR_IA32_L2_QOS_CFG, *enable ? L2_QOS_CDP_ENABLE : 0ULL); |
| } |
| |
| static int set_cache_qos_cfg(int level, bool enable) |
| { |
| void (*update)(void *arg); |
| struct rdt_ctrl_domain *d; |
| struct rdt_resource *r_l; |
| cpumask_var_t cpu_mask; |
| int cpu; |
| |
| /* Walking r->domains, ensure it can't race with cpuhp */ |
| lockdep_assert_cpus_held(); |
| |
| if (level == RDT_RESOURCE_L3) |
| update = l3_qos_cfg_update; |
| else if (level == RDT_RESOURCE_L2) |
| update = l2_qos_cfg_update; |
| else |
| return -EINVAL; |
| |
| if (!zalloc_cpumask_var(&cpu_mask, GFP_KERNEL)) |
| return -ENOMEM; |
| |
| r_l = &rdt_resources_all[level].r_resctrl; |
| list_for_each_entry(d, &r_l->ctrl_domains, hdr.list) { |
| if (r_l->cache.arch_has_per_cpu_cfg) |
| /* Pick all the CPUs in the domain instance */ |
| for_each_cpu(cpu, &d->hdr.cpu_mask) |
| cpumask_set_cpu(cpu, cpu_mask); |
| else |
| /* Pick one CPU from each domain instance to update MSR */ |
| cpumask_set_cpu(cpumask_any(&d->hdr.cpu_mask), cpu_mask); |
| } |
| |
| /* Update QOS_CFG MSR on all the CPUs in cpu_mask */ |
| on_each_cpu_mask(cpu_mask, update, &enable, 1); |
| |
| free_cpumask_var(cpu_mask); |
| |
| return 0; |
| } |
| |
| /* Restore the qos cfg state when a domain comes online */ |
| void rdt_domain_reconfigure_cdp(struct rdt_resource *r) |
| { |
| struct rdt_hw_resource *hw_res = resctrl_to_arch_res(r); |
| |
| if (!r->cdp_capable) |
| return; |
| |
| if (r->rid == RDT_RESOURCE_L2) |
| l2_qos_cfg_update(&hw_res->cdp_enabled); |
| |
| if (r->rid == RDT_RESOURCE_L3) |
| l3_qos_cfg_update(&hw_res->cdp_enabled); |
| } |
| |
| static int cdp_enable(int level) |
| { |
| struct rdt_resource *r_l = &rdt_resources_all[level].r_resctrl; |
| int ret; |
| |
| if (!r_l->alloc_capable) |
| return -EINVAL; |
| |
| ret = set_cache_qos_cfg(level, true); |
| if (!ret) |
| rdt_resources_all[level].cdp_enabled = true; |
| |
| return ret; |
| } |
| |
| static void cdp_disable(int level) |
| { |
| struct rdt_hw_resource *r_hw = &rdt_resources_all[level]; |
| |
| if (r_hw->cdp_enabled) { |
| set_cache_qos_cfg(level, false); |
| r_hw->cdp_enabled = false; |
| } |
| } |
| |
| int resctrl_arch_set_cdp_enabled(enum resctrl_res_level l, bool enable) |
| { |
| struct rdt_hw_resource *hw_res = &rdt_resources_all[l]; |
| |
| if (!hw_res->r_resctrl.cdp_capable) |
| return -EINVAL; |
| |
| if (enable) |
| return cdp_enable(l); |
| |
| cdp_disable(l); |
| |
| return 0; |
| } |
| |
| bool resctrl_arch_get_cdp_enabled(enum resctrl_res_level l) |
| { |
| return rdt_resources_all[l].cdp_enabled; |
| } |
| |
| void resctrl_arch_reset_all_ctrls(struct rdt_resource *r) |
| { |
| struct rdt_hw_resource *hw_res = resctrl_to_arch_res(r); |
| struct rdt_hw_ctrl_domain *hw_dom; |
| struct msr_param msr_param; |
| struct rdt_ctrl_domain *d; |
| int i; |
| |
| /* Walking r->domains, ensure it can't race with cpuhp */ |
| lockdep_assert_cpus_held(); |
| |
| msr_param.res = r; |
| msr_param.low = 0; |
| msr_param.high = hw_res->num_closid; |
| |
| /* |
| * Disable resource control for this resource by setting all |
| * CBMs in all ctrl_domains to the maximum mask value. Pick one CPU |
| * from each domain to update the MSRs below. |
| */ |
| list_for_each_entry(d, &r->ctrl_domains, hdr.list) { |
| hw_dom = resctrl_to_arch_ctrl_dom(d); |
| |
| for (i = 0; i < hw_res->num_closid; i++) |
| hw_dom->ctrl_val[i] = resctrl_get_default_ctrl(r); |
| msr_param.dom = d; |
| smp_call_function_any(&d->hdr.cpu_mask, rdt_ctrl_update, &msr_param, 1); |
| } |
| |
| return; |
| } |