x86: Intel Cache Allocation Technology support
PATCH v2 1/1 x86: Intel Cache Allocation Technology support
What is Cache Allocation Technology ( CAT )
-------------------------------------------
Cache Allocation Technology provides a way for the Software (OS/VMM) to restrict cache allocation to a defined 'subset' of cache which may be overlapping with other 'subsets'. This feature is used when allocating a line in cache ie when pulling new data into the cache. The programming of the h/w is done via programming MSRs.
The different cache subsets are identified by CLOS identifier (class of service) and each CLOS has a CBM (cache bit mask). The CBM is a contiguous set of bits which defines the amount of cache resource that is available for each 'subset'.
Why is CAT (cache allocation technology) needed
------------------------------------------------
The CAT enables more cache resources to be made available for higher priority applications based on guidance from the execution environment.
The architecture also allows dynamically changing these subsets during runtime to further optimize the performance of the higher priority application with minimal degradation to the low priority app. Additionally, resources can be rebalanced for system throughput benefit.
This technique may be useful in managing large computer systems which large LLC. Examples may be large servers running instances of webservers or database servers. In such complex systems, these subsets can be used for more careful placing of the available cache resources.
The CAT kernel patch would provide a basic kernel framework for users to be able to implement such cache subsets.
Kernel Implementation
---------------------
This patch implements a cgroup subsystem to support cache allocation. Each cgroup has a CLOSid <-> CBM(cache bit mask) mapping. A CLOS(Class of service) is represented by a CLOSid.CLOSid is internal to the kernel and not exposed to user. Each cgroup would have one CBM and would just represent one cache 'subset'.
The cgroup follows cgroup hierarchy ,mkdir and adding tasks to the cgroup never fails. When a child cgroup is created it inherits the CLOSid and the CBM from its parent. When a user changes the default CBM for a cgroup, a new CLOSid may be allocated if the CBM was not used before. The changing of 'cbm' may fail with -ERRNOSPC once the kernel runs out of maximum CLOSids it can support.
User can create as many cgroups as he wants but having different CBMs at the same time is restricted by the maximum number of CLOSids (multiple cgroups can have the same CBM).
Kernel maintains a CLOSid<->cbm mapping which keeps reference counter for each cgroup using a CLOSid.
The tasks in the cgroup would get to fill the LLC cache represented by the cgroup's 'cbm' file.
Root directory would have all available bits set in 'cbm' file by default.
Assignment of CBM,CLOS
---------------------------------
The 'cbm' needs to be a subset of the parent node's 'cbm'. Any contiguous subset of these bits(with a minimum of 2 bits) maybe set to indicate the cache mapping desired. The 'cbm' between 2 directories can overlap. The 'cbm' would represent the cache 'subset' of the CAT cgroup. For ex: on a system with 16 bits of max cbm bits, if the directory has the least significant 4 bits set in its 'cbm' file(meaning the 'cbm' is just 0xf), it would be allocated the right quarter of the Last level cache which means the tasks belonging to this CAT cgroup can use the right quarter of the cache to fill. If it has the most significant 8 bits set ,it would be allocated the left half of the cache(8 bits out of 16 represents 50%).
The cache portion defined in the CBM file is available to all tasks within the cgroup to fill and these task are not allowed to allocate space in other parts of the cache.
Scheduling and Context Switch
------------------------------
During context switch kernel implements this by writing the CLOSid (internally maintained by kernel) of the cgroup to which the task belongs to the CPU's IA32_PQR_ASSOC MSR.
---
Thanks to feedback from tglx, Boris, Dave Hansen and Matt. Changes in v2:
- Removed HSW specific enumeration changes. Plan to include it later as a seperate patch.
- Fixed the code in prep_arch_switch to be specific for x86 and removed x86 defines.
- Fixed cbm_write to not write all 1s when a cgroup is freed.
- fixed one possible memory leak in init.
- Changed some of manual bitmap
manipulation to use the predefined bitmap APIs to make code more readable
- Changed name in sources from cqe to cat
- Global cat enable flag changed to static_key and disabled cgroup early_init
This patch will apply on 3.18-rc7
arch/x86/include/asm/cat.h | 106 +++++++++++++ arch/x86/include/asm/cpufeature.h | 6 +- arch/x86/include/asm/processor.h | 3 + arch/x86/include/asm/switch_to.h | 5 + arch/x86/kernel/cpu/Makefile | 1 + arch/x86/kernel/cpu/cat.c | 310 ++++++++++++++++++++++++++++++++++++++ arch/x86/kernel/cpu/common.c | 16 ++ include/linux/cgroup_subsys.h | 4 + init/Kconfig | 11 ++ kernel/sched/core.c | 1 + kernel/sched/sched.h | 3 + 11 files changed, 465 insertions(+), 1 deletion(-) create mode 100644 arch/x86/include/asm/cat.h create mode 100644 arch/x86/kernel/cpu/cat.c
arch/x86/include/asm/cat.h new file +106 -0
@@ -0,0 +1,106 @@ +#ifndef _CAT_H_ +#define _CAT_H_ + +#ifdef CONFIG_CGROUP_CAT + +#include <linux/cgroup.h> +#include <linux/slab.h> +#include <linux/percpu.h> +#include <linux/spinlock.h> +#include <linux/seq_file.h> +#include <linux/err.h> + +#define IA32_PQR_ASSOC 0xc8f +#define MAX_CBM_LENGTH 32 +#define IA32_CBM_MASK 0xffffffff +#define IA32_L3_CBM_BASE 0xc90 +#define CBMFROMINDEX (x) (IA32_L3_CBM_BASE + x) + +DECLARE_PER_CPU(unsigned int, x86_cpu_clos); +extern struct static_key cat_enable_key; +extern struct cat root_cat_group; + +struct cat_subsys_info { +/* Clos Bitmap to keep track of available CLOSids.*/ +unsigned long *closmap; +}; + +struct cat { +struct cgroup_subsys_state css; +/* Class of service for the cgroup.*/ +unsigned int clos; +/* Corresponding cache bit mask.*/ +unsigned long *cbm; +}; + +struct closcbm_map { +unsigned long cbm; +unsigned int ref; +}; + +static inline bool cat_enabled (void ) +{ +return static_key_false (&cat_enable_key); +} + +/* +* Return cat group corresponding to this container. +*/ +static inline struct cat *css_cat (struct cgroup_subsys_state *css) +{ + return css ? container_of(css, struct cat, css) : NULL; +} + +static inline struct cat *parent_cat (struct cat *cq) +{ +return css_cat (cq->css.parent); +} + +/* +* Return cat group to which this task belongs. +*/ +static inline struct cat *task_cat (struct task_struct *task) +{ +return css_cat (task_css (task, cat_cgrp_id)); +} + +/* +* cat_sched_in() - Writes the task's CLOSid to IA32_PQR_MSR +* if the current Closid is different than the new one. +*/ + +static inline void cat_sched_in (struct task_struct *task) +{ +struct cat *cq; +unsigned int clos; + +if (!cat_enabled ()) +return ; + +/* +* This needs to be fixed after CQM code stabilizes +* to cache the whole PQR instead of just CLOSid. +* PQR has closid in high 32 bits and CQM-RMID in low 10 bits. +* Should not write a 0 to the low 10 bits of PQR +* and corrupt RMID. +*/ + clos =this_cpu_read (x86_cpu_clos); + +rcu_read_lock (); + cq =task_cat (task); +if (cq->clos == clos) { +rcu_read_unlock (); +return ; + } + +wrmsr (IA32_PQR_ASSOC ,0 , cq->clos); +this_cpu_write (x86_cpu_clos, cq->clos); +rcu_read_unlock (); +} + +#else + +static inline void cat_sched_in (struct task_struct *task) {} + +#endif +#endif
arch/x86/include/asm/cpufeature.h +5 -1
@@ -12,7 +12,7 @@#include <asm/disabled-features.h> #endif -#define NCAPINTS 11/* N 32-bit words worth of info */ +#define NCAPINTS 12/* N 32-bit words worth of info */ #define NBUGINTS 1/* N 32-bit bug flags */ /*
@@ -221,6 +221,7 @@#define X86_FEATURE_INVPCID ( 9*32+10)/* Invalidate Processor Context ID */ #define X86_FEATURE_RTM ( 9*32+11)/* Restricted Transactional Memory */ #define X86_FEATURE_MPX ( 9*32+14)/* Memory Protection Extension */ +#define X86_FEATURE_CAT ( 9*32+15)/* Cache QOS Enforcement */ #define X86_FEATURE_AVX512F ( 9*32+16)/* AVX-512 Foundation */ #define X86_FEATURE_RDSEED ( 9*32+18)/* The RDSEED instruction */ #define X86_FEATURE_ADX ( 9*32+19)/* The ADCX and ADOX instructions */
@@ -236,6 +237,9 @@#define X86_FEATURE_XGETBV1 (10*32+ 2)/* XGETBV with ECX = 1 */ #define X86_FEATURE_XSAVES (10*32+ 3)/* XSAVES/XRSTORS */ +/* Intel-defined CPU features, CPUID level 0x00000010:0 (ebx), word 11 */ +#define X86_FEATURE_CAT_L3 (11*32 + 1)/*Cache QOS Enforcement L3*/ +/* * BUG word(s) */
arch/x86/include/asm/processor.h +3 -0
@@ -111,6 +111,9 @@ struct cpuinfo_x86 {int x86_cache_alignment;/* In bytes */ int x86_power;unsigned long loops_per_jiffy; +/* Cache Allocation Technology values */ +int x86_cat_cbmlength; +int x86_cat_closs;/* cpuid returned max cores value: */ u16 x86_max_cores;u16 apicid;
arch/x86/include/asm/switch_to.h +5 -0
@@ -8,6 +8,11 @@ struct tss_struct;void __switch_to_xtra (struct task_struct *prev_p,struct task_struct *next_p,struct tss_struct *tss); +#ifdef CONFIG_CGROUP_CAT +#include <asm/cat.h> +#define post_arch_switch (current) cat_sched_in(current) +#endif +#ifdef CONFIG_X86_32 #ifdef CONFIG_CC_STACKPROTECTOR
arch/x86/kernel/cpu/Makefile +1 -0
@@ -47,6 +47,7 @@ obj-$(CONFIG_PERF_EVENTS_INTEL_UNCORE) += perf_event_intel_uncore.o \ perf_event_intel_uncore_nhmex.o endif +obj-$(CONFIG_CGROUP_CAT) += cat.o obj-$(CONFIG_X86_MCE) += mcheck/ obj-$(CONFIG_MTRR) += mtrr/
arch/x86/kernel/cpu/cat.c new file +310 -0
310 lines not on this page · raw message
arch/x86/kernel/cpu/common.c +16 -0
@@ -644,6 +644,22 @@ void get_cpu_cap(struct cpuinfo_x86 *c) c->x86_capability[10 ] = eax; } +/* Additional Intel-defined flags: level 0x00000010 */ +if (c->cpuid_level >=0x00000010 ) { +u32 eax, ebx, ecx, edx; + +cpuid_count (0x00000010 ,0 , &eax, &ebx, &ecx, &edx); + c->x86_capability[11 ] = ebx; + +if (cpu_has (c,X86_FEATURE_CAT ) && +cpu_has (c,X86_FEATURE_CAT_L3 )) { + +cpuid_count (0x00000010 ,1 , &eax, &ebx, &ecx, &edx); + c->x86_cat_closs = (edx &0xffff ) +1 ; + c->x86_cat_cbmlength = (eax &0xf ) +1 ; + } + } +/* AMD-defined flags: level 0x80000001 */ xlvl =cpuid_eax (0x80000000 ); c->extended_cpuid_level = xlvl;
include/linux/cgroup_subsys.h +4 -0
@@ -47,6 +47,10 @@ SUBSYS(net_prio) SUBSYS(hugetlb)#endif +#if IS_ENABLED (CONFIG_CGROUP_CAT ) +SUBSYS(cat) +#endif +/* * The following subsystems are not supported on the default hierarchy. */
init/Kconfig +11 -0
@@ -968,6 +968,17 @@ config CPUSETS Say N if unsure. +config CGROUP_CAT + bool "Cache Allocation Technology croup subsystem" + depends on X86 || X86_64 + help + This option provides framework to allocate cache lines when + applications fill cache. + This can be used by users to configure how much cache that can be + allocated to different PIDs. + + Say N if unsure. + config PROC_PID_CPUSET bool "Include legacy /proc/<pid>/cpuset file" depends on CPUSETS
kernel/sched/core.c +1 -0
@@ -2255,6 +2255,7 @@ static void finish_task_switch(struct rq *rq, struct task_struct *prev) prev_state = prev->state;vtime_task_switch (prev);finish_arch_switch (prev); +post_arch_switch (current);perf_event_task_sched_in (prev, current);finish_lock_switch (rq, prev);finish_arch_post_lock_switch ();
kernel/sched/sched.h +3 -0
@@ -971,6 +971,9 @@ static inline int task_on_rq_migrating(struct task_struct *p)#ifndef finish_arch_switch# define finish_arch_switch (prev) do { } while (0)#endif +#ifndef post_arch_switch +# define post_arch_switch (current) do { } while (0) +#endif #ifndef finish_arch_post_lock_switch# define finish_arch_post_lock_switch () do { } while (0)#endif
Signature · 1 line
1.9.1