mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
riscv: Add support for srmcfg CSR from Ssqosid extension
Add support for the srmcfg CSR defined in the Ssqosid ISA extension. The CSR contains two fields: - Resource Control ID (RCID) for resource allocation - Monitoring Counter ID (MCID) for tracking resource usage Requests from a hart to shared resources are tagged with these IDs, allowing resource usage to be associated with the running task. Add a srmcfg field to thread_struct with the same format as the CSR. The context-switch path writes the field to the CSR, and resctrl_arch_set_closid_rmid() updates it when a task is assigned to a resctrl control or monitoring group. A per-cpu cpu_srmcfg_default holds the default srmcfg for each CPU, set by resctrl_arch_set_cpu_default_closid_rmid() on CPU group assignment. On context switch, RCID and MCID inherit from the CPU default independently: a task whose thread RCID field is zero takes the CPU default's RCID, and likewise for MCID. A per-cpu cpu_srmcfg variable mirrors the CSR state to avoid redundant writes. L1D-hot memory access is faster than a CSR read and avoids traps under virtualization. Link: https://github.com/riscv/riscv-ssqosid/releases/tag/v1.0 Assisted-by: Claude:claude-opus-4-7 Co-developed-by: Kornel Dulęba <mindal@semihalf.com> Signed-off-by: Kornel Dulęba <mindal@semihalf.com> Signed-off-by: Drew Fustini <fustini@kernel.org> Link: https://patch.msgid.link/20260729-dfustini-atl-sc-cbqri-dt-v6-3-7c22b05d461b@kernel.org Signed-off-by: Paul Walmsley <pjw@kernel.org>
This commit is contained in:
parent
5a17eb5f1c
commit
326ba66db9
|
|
@ -23296,6 +23296,14 @@ F: drivers/perf/riscv_pmu.c
|
|||
F: drivers/perf/riscv_pmu_legacy.c
|
||||
F: drivers/perf/riscv_pmu_sbi.c
|
||||
|
||||
RISC-V QOS RESCTRL SUPPORT
|
||||
M: Drew Fustini <fustini@kernel.org>
|
||||
R: yunhui cui <cuiyunhui@bytedance.com>
|
||||
L: linux-riscv@lists.infradead.org
|
||||
S: Supported
|
||||
F: arch/riscv/include/asm/qos.h
|
||||
F: arch/riscv/kernel/qos.c
|
||||
|
||||
RISC-V RPMI AND MPXY DRIVERS
|
||||
M: Rahul Pathak <rahul@summations.net>
|
||||
M: Anup Patel <anup@brainfault.org>
|
||||
|
|
|
|||
|
|
@ -592,6 +592,24 @@ config RISCV_ISA_SVNAPOT
|
|||
|
||||
If you don't know what to do here, say Y.
|
||||
|
||||
config RISCV_ISA_SSQOSID
|
||||
bool "Ssqosid extension support for supervisor mode Quality of Service ID"
|
||||
depends on 64BIT
|
||||
default n
|
||||
help
|
||||
Adds support for the Ssqosid ISA extension (Supervisor-mode
|
||||
Quality of Service ID).
|
||||
|
||||
Ssqosid defines the srmcfg CSR which allows the system to tag the
|
||||
running process with an RCID (Resource Control ID) and MCID
|
||||
(Monitoring Counter ID). The RCID is used to determine resource
|
||||
allocation. The MCID is used to track resource usage in event
|
||||
counters.
|
||||
|
||||
For example, a cache controller may use the RCID to apply a
|
||||
cache partitioning scheme and use the MCID to track how much
|
||||
cache a process, or a group of processes, is using.
|
||||
|
||||
config RISCV_ISA_SVPBMT
|
||||
bool "Svpbmt extension support for supervisor mode page-based memory types"
|
||||
depends on 64BIT && MMU
|
||||
|
|
|
|||
|
|
@ -84,6 +84,10 @@
|
|||
#define SATP_ASID_MASK _AC(0xFFFF, UL)
|
||||
#endif
|
||||
|
||||
/* SRMCFG fields */
|
||||
#define SRMCFG_RCID_MASK GENMASK(11, 0)
|
||||
#define SRMCFG_MCID_MASK GENMASK(27, 16)
|
||||
|
||||
/* Exception cause high bit - is an interrupt if set */
|
||||
#define CAUSE_IRQ_FLAG (_AC(1, UL) << (__riscv_xlen - 1))
|
||||
|
||||
|
|
@ -346,6 +350,7 @@
|
|||
#define CSR_STVAL 0x143
|
||||
#define CSR_SIP 0x144
|
||||
#define CSR_SATP 0x180
|
||||
#define CSR_SRMCFG 0x181
|
||||
|
||||
#define CSR_STIMECMP 0x14D
|
||||
#define CSR_STIMECMPH 0x15D
|
||||
|
|
|
|||
|
|
@ -123,6 +123,9 @@ struct thread_struct {
|
|||
/* A forced icache flush is not needed if migrating to the previous cpu. */
|
||||
unsigned int prev_cpu;
|
||||
#endif
|
||||
#ifdef CONFIG_RISCV_ISA_SSQOSID
|
||||
u32 srmcfg;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Whitelist the fstate from the task_struct for hardened usercopy */
|
||||
|
|
|
|||
74
arch/riscv/include/asm/qos.h
Normal file
74
arch/riscv/include/asm/qos.h
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _ASM_RISCV_QOS_H
|
||||
#define _ASM_RISCV_QOS_H
|
||||
|
||||
#include <linux/percpu-defs.h>
|
||||
|
||||
#ifdef CONFIG_RISCV_ISA_SSQOSID
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/cpufeature.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include <asm/csr.h>
|
||||
#include <asm/hwcap.h>
|
||||
|
||||
/* cached value of srmcfg csr for each cpu */
|
||||
DECLARE_PER_CPU(u32, cpu_srmcfg);
|
||||
|
||||
/* default srmcfg value for each cpu, set via resctrl cpu assignment */
|
||||
DECLARE_PER_CPU(u32, cpu_srmcfg_default);
|
||||
|
||||
static inline void __switch_to_srmcfg(struct task_struct *next)
|
||||
{
|
||||
u32 thread_srmcfg, default_srmcfg;
|
||||
|
||||
thread_srmcfg = READ_ONCE(next->thread.srmcfg);
|
||||
default_srmcfg = __this_cpu_read(cpu_srmcfg_default);
|
||||
|
||||
/*
|
||||
* RCID and MCID inherit from cpu_srmcfg_default independently.
|
||||
* RESCTRL_RESERVED_CLOSID and RESCTRL_RESERVED_RMID are both 0, so a
|
||||
* zero field means "unassigned" and takes the CPU default.
|
||||
*/
|
||||
if (thread_srmcfg == 0) {
|
||||
thread_srmcfg = default_srmcfg;
|
||||
} else {
|
||||
u32 rcid = FIELD_GET(SRMCFG_RCID_MASK, thread_srmcfg);
|
||||
u32 mcid = FIELD_GET(SRMCFG_MCID_MASK, thread_srmcfg);
|
||||
|
||||
if (rcid == 0 || mcid == 0) {
|
||||
if (rcid == 0)
|
||||
rcid = FIELD_GET(SRMCFG_RCID_MASK, default_srmcfg);
|
||||
if (mcid == 0)
|
||||
mcid = FIELD_GET(SRMCFG_MCID_MASK, default_srmcfg);
|
||||
thread_srmcfg = FIELD_PREP(SRMCFG_RCID_MASK, rcid) |
|
||||
FIELD_PREP(SRMCFG_MCID_MASK, mcid);
|
||||
}
|
||||
}
|
||||
|
||||
if (thread_srmcfg != __this_cpu_read(cpu_srmcfg)) {
|
||||
/*
|
||||
* No fence around the csrw. Ssqosid is silent on srmcfg
|
||||
* ordering versus memory accesses, so a few accesses at the
|
||||
* switch boundary may carry the previous RCID/MCID. The
|
||||
* tagging inaccuracy is bounded and acceptable for QoS.
|
||||
*/
|
||||
__this_cpu_write(cpu_srmcfg, thread_srmcfg);
|
||||
csr_write(CSR_SRMCFG, thread_srmcfg);
|
||||
}
|
||||
}
|
||||
|
||||
static __always_inline bool has_srmcfg(void)
|
||||
{
|
||||
return riscv_has_extension_unlikely(RISCV_ISA_EXT_SSQOSID);
|
||||
}
|
||||
|
||||
#else /* ! CONFIG_RISCV_ISA_SSQOSID */
|
||||
|
||||
struct task_struct;
|
||||
static __always_inline bool has_srmcfg(void) { return false; }
|
||||
static inline void __switch_to_srmcfg(struct task_struct *next) { }
|
||||
|
||||
#endif /* CONFIG_RISCV_ISA_SSQOSID */
|
||||
#endif /* _ASM_RISCV_QOS_H */
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
#include <asm/processor.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/csr.h>
|
||||
#include <asm/qos.h>
|
||||
|
||||
#ifdef CONFIG_FPU
|
||||
extern void __fstate_save(struct task_struct *save_to);
|
||||
|
|
@ -119,6 +120,8 @@ do { \
|
|||
__switch_to_fpu(__prev, __next); \
|
||||
if (has_vector() || has_xtheadvector()) \
|
||||
__switch_to_vector(__prev, __next); \
|
||||
if (has_srmcfg()) \
|
||||
__switch_to_srmcfg(__next); \
|
||||
if (switch_to_should_flush_icache(__next)) \
|
||||
local_flush_icache_all(); \
|
||||
__switch_to_envcfg(__next); \
|
||||
|
|
|
|||
|
|
@ -128,3 +128,5 @@ obj-$(CONFIG_ACPI_NUMA) += acpi_numa.o
|
|||
|
||||
obj-$(CONFIG_GENERIC_CPU_VULNERABILITIES) += bugs.o
|
||||
obj-$(CONFIG_RISCV_USER_CFI) += usercfi.o
|
||||
|
||||
obj-$(CONFIG_RISCV_ISA_SSQOSID) += qos.o
|
||||
|
|
|
|||
101
arch/riscv/kernel/qos.c
Normal file
101
arch/riscv/kernel/qos.c
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/cpuhotplug.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/percpu-defs.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <asm/cpufeature-macros.h>
|
||||
#include <asm/hwcap.h>
|
||||
#include <asm/qos.h>
|
||||
|
||||
/*
|
||||
* Cached value of srmcfg csr for each cpu. Seeded to U32_MAX so the next
|
||||
* __switch_to_srmcfg() unconditionally writes the CSR. The encoding
|
||||
* MCID << 16 | RCID with both fields well under 16 bits can never
|
||||
* produce this sentinel. This covers early-boot context switches that
|
||||
* happen before riscv_srmcfg_init() runs as an arch_initcall.
|
||||
*/
|
||||
DEFINE_PER_CPU(u32, cpu_srmcfg) = U32_MAX;
|
||||
|
||||
/* default srmcfg value for each cpu, set via resctrl cpu assignment */
|
||||
DEFINE_PER_CPU(u32, cpu_srmcfg_default);
|
||||
|
||||
/*
|
||||
* Invalidate the per-CPU srmcfg cache. Used as both the cpuhp startup
|
||||
* and teardown callback. U32_MAX is not a valid srmcfg value
|
||||
* (MCID << 16 | RCID, both fields under 16 bits), so the next
|
||||
* __switch_to_srmcfg() always writes the CSR.
|
||||
*
|
||||
* Ssqosid leaves the CSR implementation-defined across hart stop/start,
|
||||
* so the cached value cannot be trusted after online. The startup
|
||||
* callback runs at CPUHP_AP_ONLINE_DYN, before CPUHP_AP_ACTIVE makes
|
||||
* the CPU schedulable, so the cache is invalidated before any normal
|
||||
* task runs and the CSR is written on that task's first switch.
|
||||
* The teardown callback is not relied on. Idle and per-CPU kthreads keep
|
||||
* switching as the CPU goes down and overwrite the sentinel with the CPU
|
||||
* default, so it does not survive the offline period.
|
||||
*/
|
||||
static int riscv_srmcfg_reset_cache(unsigned int cpu)
|
||||
{
|
||||
per_cpu(cpu_srmcfg, cpu) = U32_MAX;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* CPU PM notifier: invalidate the cached srmcfg on resume from a deep
|
||||
* idle / suspend. Ssqosid leaves CSR_SRMCFG state across low-power
|
||||
* transitions implementation-defined, and the boot CPU never goes
|
||||
* through the cpuhp online callback during system suspend, so without
|
||||
* this hook __switch_to_srmcfg() would skip the CSR write when the
|
||||
* outgoing task happens to share its srmcfg with the pre-suspend cache.
|
||||
*/
|
||||
static int riscv_srmcfg_pm_notify(struct notifier_block *nb,
|
||||
unsigned long action, void *unused)
|
||||
{
|
||||
switch (action) {
|
||||
case CPU_PM_EXIT:
|
||||
case CPU_PM_ENTER_FAILED:
|
||||
/*
|
||||
* The CSR is implementation-defined across the low-power
|
||||
* transition. Invalidate the cache and eagerly rewrite the
|
||||
* CSR for the current task so it does not run mis-tagged
|
||||
* until the next context switch.
|
||||
*/
|
||||
__this_cpu_write(cpu_srmcfg, U32_MAX);
|
||||
__switch_to_srmcfg(current);
|
||||
break;
|
||||
}
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block riscv_srmcfg_pm_nb = {
|
||||
.notifier_call = riscv_srmcfg_pm_notify,
|
||||
};
|
||||
|
||||
static int __init riscv_srmcfg_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!riscv_has_extension_unlikely(RISCV_ISA_EXT_SSQOSID))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* cpuhp_setup_state() invokes the startup callback locally on every
|
||||
* already-online CPU, so no separate seed loop is needed here.
|
||||
*/
|
||||
err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "riscv/srmcfg:online",
|
||||
riscv_srmcfg_reset_cache, riscv_srmcfg_reset_cache);
|
||||
if (err < 0)
|
||||
pr_warn("srmcfg: cpuhp setup failed (%d), cache not invalidated on CPU online\n",
|
||||
err);
|
||||
|
||||
/*
|
||||
* Register the PM notifier even if the cpuhp setup failed. It is
|
||||
* independent of the cpuhp state and guards suspend/resume.
|
||||
*/
|
||||
cpu_pm_register_notifier(&riscv_srmcfg_pm_nb);
|
||||
return 0;
|
||||
}
|
||||
arch_initcall(riscv_srmcfg_init);
|
||||
Loading…
Reference in New Issue
Block a user