2016-10-22 06:19:53 -07:00
/*
* Resource Director Technology ( RDT )
* - Cache Allocation code .
*
* Copyright ( C ) 2016 Intel Corporation
*
* Authors :
* Fenghua Yu < fenghua . yu @ intel . com >
* Tony Luck < tony . luck @ intel . com >
* Vikas Shivappa < vikas . shivappa @ intel . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* More information about RDT be found in the Intel ( R ) x86 Architecture
* Software Developer Manual June 2016 , volume 3 , section 17.17 .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/slab.h>
# include <linux/err.h>
2016-10-28 15:04:41 -07:00
# include <linux/cacheinfo.h>
# include <linux/cpuhotplug.h>
2016-10-22 06:19:53 -07:00
2016-10-22 06:19:54 -07:00
# include <asm/intel-family.h>
2017-07-25 14:14:23 -07:00
# include <asm/intel_rdt_sched.h>
# include "intel_rdt.h"
2016-10-22 06:19:54 -07:00
2017-04-07 17:33:53 -07:00
# define MBA_IS_LINEAR 0x4
2018-04-20 15:36:18 -07:00
# define MBA_MAX_MBPS U32_MAX
2017-04-07 17:33:53 -07:00
2016-10-28 15:04:41 -07:00
/* Mutex to protect rdtgroup access. */
DEFINE_MUTEX ( rdtgroup_mutex ) ;
2017-07-25 14:14:20 -07:00
/*
* The cached intel_pqr_state is strictly per CPU and can never be
* updated from a remote CPU . Functions which modify the state
* are called with interrupts disabled and no preemption , which
* is sufficient for the protection .
*/
DEFINE_PER_CPU ( struct intel_pqr_state , pqr_state ) ;
2017-04-03 14:44:17 -07:00
/*
* Used to store the max resource name width and max resource data width
* to display the schemata in a tabular format
*/
int max_name_width , max_data_width ;
2017-07-25 14:14:27 -07:00
/*
* Global boolean for rdt_alloc which is true if any
* resource allocation is enabled .
*/
bool rdt_alloc_capable ;
2017-04-07 17:33:53 -07:00
static void
mba_wrmsr ( struct rdt_domain * d , struct msr_param * m , struct rdt_resource * r ) ;
2017-04-14 14:14:31 +02:00
static void
cat_wrmsr ( struct rdt_domain * d , struct msr_param * m , struct rdt_resource * r ) ;
2017-04-14 13:00:36 +02:00
# define domain_init(id) LIST_HEAD_INIT(rdt_resources_all[id].domains)
2016-10-22 06:19:55 -07:00
struct rdt_resource rdt_resources_all [ ] = {
2017-07-25 14:14:26 -07:00
[ RDT_RESOURCE_L3 ] =
2016-10-22 06:19:55 -07:00
{
2017-07-25 14:14:38 -07:00
. rid = RDT_RESOURCE_L3 ,
2017-04-14 13:00:36 +02:00
. name = " L3 " ,
. domains = domain_init ( RDT_RESOURCE_L3 ) ,
. msr_base = IA32_L3_CBM_BASE ,
2017-04-14 14:14:31 +02:00
. msr_update = cat_wrmsr ,
2017-04-14 13:00:36 +02:00
. cache_level = 3 ,
. cache = {
. min_cbm_bits = 1 ,
. cbm_idx_mult = 1 ,
. cbm_idx_offset = 0 ,
} ,
2017-04-07 17:33:56 -07:00
. parse_ctrlval = parse_cbm ,
. format_str = " %d=%0*x " ,
2017-07-25 14:14:29 -07:00
. fflags = RFTYPE_RES_CACHE ,
2016-10-22 06:19:55 -07:00
} ,
2017-07-25 14:14:26 -07:00
[ RDT_RESOURCE_L3DATA ] =
2016-10-22 06:19:55 -07:00
{
2017-07-25 14:14:38 -07:00
. rid = RDT_RESOURCE_L3DATA ,
2017-04-14 13:00:36 +02:00
. name = " L3DATA " ,
. domains = domain_init ( RDT_RESOURCE_L3DATA ) ,
. msr_base = IA32_L3_CBM_BASE ,
2017-04-14 14:14:31 +02:00
. msr_update = cat_wrmsr ,
2017-04-14 13:00:36 +02:00
. cache_level = 3 ,
. cache = {
. min_cbm_bits = 1 ,
. cbm_idx_mult = 2 ,
. cbm_idx_offset = 0 ,
} ,
2017-04-07 17:33:56 -07:00
. parse_ctrlval = parse_cbm ,
. format_str = " %d=%0*x " ,
2017-07-25 14:14:29 -07:00
. fflags = RFTYPE_RES_CACHE ,
2016-10-22 06:19:55 -07:00
} ,
2017-07-25 14:14:26 -07:00
[ RDT_RESOURCE_L3CODE ] =
2016-10-22 06:19:55 -07:00
{
2017-07-25 14:14:38 -07:00
. rid = RDT_RESOURCE_L3CODE ,
2017-04-14 13:00:36 +02:00
. name = " L3CODE " ,
. domains = domain_init ( RDT_RESOURCE_L3CODE ) ,
. msr_base = IA32_L3_CBM_BASE ,
2017-04-14 14:14:31 +02:00
. msr_update = cat_wrmsr ,
2017-04-14 13:00:36 +02:00
. cache_level = 3 ,
. cache = {
. min_cbm_bits = 1 ,
. cbm_idx_mult = 2 ,
. cbm_idx_offset = 1 ,
} ,
2017-04-07 17:33:56 -07:00
. parse_ctrlval = parse_cbm ,
. format_str = " %d=%0*x " ,
2017-07-25 14:14:29 -07:00
. fflags = RFTYPE_RES_CACHE ,
2016-10-22 06:19:55 -07:00
} ,
2017-07-25 14:14:26 -07:00
[ RDT_RESOURCE_L2 ] =
2016-10-22 06:19:55 -07:00
{
2017-07-25 14:14:38 -07:00
. rid = RDT_RESOURCE_L2 ,
2017-04-14 13:00:36 +02:00
. name = " L2 " ,
. domains = domain_init ( RDT_RESOURCE_L2 ) ,
. msr_base = IA32_L2_CBM_BASE ,
2017-04-14 14:14:31 +02:00
. msr_update = cat_wrmsr ,
2017-04-14 13:00:36 +02:00
. cache_level = 2 ,
. cache = {
. min_cbm_bits = 1 ,
. cbm_idx_mult = 1 ,
. cbm_idx_offset = 0 ,
} ,
2017-04-07 17:33:56 -07:00
. parse_ctrlval = parse_cbm ,
. format_str = " %d=%0*x " ,
2017-07-25 14:14:29 -07:00
. fflags = RFTYPE_RES_CACHE ,
2016-10-22 06:19:55 -07:00
} ,
2017-12-20 14:57:22 -08:00
[ RDT_RESOURCE_L2DATA ] =
{
. rid = RDT_RESOURCE_L2DATA ,
. name = " L2DATA " ,
. domains = domain_init ( RDT_RESOURCE_L2DATA ) ,
. msr_base = IA32_L2_CBM_BASE ,
. msr_update = cat_wrmsr ,
. cache_level = 2 ,
. cache = {
. min_cbm_bits = 1 ,
. cbm_idx_mult = 2 ,
. cbm_idx_offset = 0 ,
} ,
. parse_ctrlval = parse_cbm ,
. format_str = " %d=%0*x " ,
. fflags = RFTYPE_RES_CACHE ,
} ,
[ RDT_RESOURCE_L2CODE ] =
{
. rid = RDT_RESOURCE_L2CODE ,
. name = " L2CODE " ,
. domains = domain_init ( RDT_RESOURCE_L2CODE ) ,
. msr_base = IA32_L2_CBM_BASE ,
. msr_update = cat_wrmsr ,
. cache_level = 2 ,
. cache = {
. min_cbm_bits = 1 ,
. cbm_idx_mult = 2 ,
. cbm_idx_offset = 1 ,
} ,
. parse_ctrlval = parse_cbm ,
. format_str = " %d=%0*x " ,
. fflags = RFTYPE_RES_CACHE ,
} ,
2017-07-25 14:14:26 -07:00
[ RDT_RESOURCE_MBA ] =
2017-04-07 17:33:53 -07:00
{
2017-07-25 14:14:38 -07:00
. rid = RDT_RESOURCE_MBA ,
2017-04-07 17:33:53 -07:00
. name = " MB " ,
. domains = domain_init ( RDT_RESOURCE_MBA ) ,
. msr_base = IA32_MBA_THRTL_BASE ,
. msr_update = mba_wrmsr ,
. cache_level = 3 ,
2017-04-07 17:33:57 -07:00
. parse_ctrlval = parse_bw ,
2018-04-20 15:36:19 -07:00
. format_str = " %d=%*u " ,
2017-07-25 14:14:29 -07:00
. fflags = RFTYPE_RES_MB ,
2017-04-07 17:33:53 -07:00
} ,
2016-10-22 06:19:55 -07:00
} ;
2017-04-14 13:00:36 +02:00
static unsigned int cbm_idx ( struct rdt_resource * r , unsigned int closid )
2016-10-28 15:04:41 -07:00
{
2017-04-14 13:00:36 +02:00
return closid * r - > cache . cbm_idx_mult + r - > cache . cbm_idx_offset ;
2016-10-28 15:04:41 -07:00
}
2016-10-22 06:19:54 -07:00
/*
* cache_alloc_hsw_probe ( ) - Have to probe for Intel haswell server CPUs
* as they do not have CPUID enumeration support for Cache allocation .
* The check for Vendor / Family / Model is not enough to guarantee that
* the MSRs won ' t # GP fault because only the following SKUs support
* CAT :
* Intel ( R ) Xeon ( R ) CPU E5 - 2658 v3 @ 2.20 GHz
* Intel ( R ) Xeon ( R ) CPU E5 - 2648L v3 @ 1.80 GHz
* Intel ( R ) Xeon ( R ) CPU E5 - 2628L v3 @ 2.00 GHz
* Intel ( R ) Xeon ( R ) CPU E5 - 2618L v3 @ 2.30 GHz
* Intel ( R ) Xeon ( R ) CPU E5 - 2608L v3 @ 2.00 GHz
* Intel ( R ) Xeon ( R ) CPU E5 - 2658 A v3 @ 2.20 GHz
*
* Probe by trying to write the first of the L3 cach mask registers
* and checking that the bits stick . Max CLOSids is always 4 and max cbm length
* is always 20 on hsw server parts . The minimum cache bitmask length
* allowed for HSW server is always 2 bits . Hardcode all of them .
*/
2017-08-24 09:26:50 -07:00
static inline void cache_alloc_hsw_probe ( void )
2016-10-22 06:19:54 -07:00
{
2017-08-24 09:26:50 -07:00
struct rdt_resource * r = & rdt_resources_all [ RDT_RESOURCE_L3 ] ;
u32 l , h , max_cbm = BIT_MASK ( 20 ) - 1 ;
2016-10-22 06:19:54 -07:00
2017-08-24 09:26:50 -07:00
if ( wrmsr_safe ( IA32_L3_CBM_BASE , max_cbm , 0 ) )
return ;
rdmsr ( IA32_L3_CBM_BASE , l , h ) ;
2016-10-22 06:19:55 -07:00
2017-08-24 09:26:50 -07:00
/* If all the bits were set in MSR, return success */
if ( l ! = max_cbm )
return ;
2016-10-22 06:19:55 -07:00
2017-08-24 09:26:50 -07:00
r - > num_closid = 4 ;
r - > default_ctrl = max_cbm ;
r - > cache . cbm_len = 20 ;
r - > cache . shareable_bits = 0xc0000 ;
r - > cache . min_cbm_bits = 2 ;
r - > alloc_capable = true ;
r - > alloc_enabled = true ;
2016-10-22 06:19:54 -07:00
2017-08-24 09:26:50 -07:00
rdt_alloc_capable = true ;
2016-10-22 06:19:54 -07:00
}
2018-04-20 15:36:17 -07:00
bool is_mba_sc ( struct rdt_resource * r )
{
if ( ! r )
return rdt_resources_all [ RDT_RESOURCE_MBA ] . membw . mba_sc ;
return r - > membw . mba_sc ;
}
2017-04-07 17:33:53 -07:00
/*
* rdt_get_mb_table ( ) - get a mapping of bandwidth ( b / w ) percentage values
* exposed to user interface and the h / w understandable delay values .
*
* The non - linear delay values have the granularity of power of two
* and also the h / w does not guarantee a curve for configured delay
* values vs . actual b / w enforced .
* Hence we need a mapping that is pre calibrated so the user can
* express the memory b / w as a percentage value .
*/
static inline bool rdt_get_mb_table ( struct rdt_resource * r )
{
/*
* There are no Intel SKUs as of now to support non - linear delay .
*/
pr_info ( " MBA b/w map not implemented for cpu:%d, model:%d " ,
boot_cpu_data . x86 , boot_cpu_data . x86_model ) ;
return false ;
}
static bool rdt_get_mem_config ( struct rdt_resource * r )
{
union cpuid_0x10_3_eax eax ;
union cpuid_0x10_x_edx edx ;
u32 ebx , ecx ;
cpuid_count ( 0x00000010 , 3 , & eax . full , & ebx , & ecx , & edx . full ) ;
r - > num_closid = edx . split . cos_max + 1 ;
r - > membw . max_delay = eax . split . max_delay + 1 ;
r - > default_ctrl = MAX_MBA_BW ;
if ( ecx & MBA_IS_LINEAR ) {
r - > membw . delay_linear = true ;
r - > membw . min_bw = MAX_MBA_BW - r - > membw . max_delay ;
r - > membw . bw_gran = MAX_MBA_BW - r - > membw . max_delay ;
} else {
if ( ! rdt_get_mb_table ( r ) )
return false ;
}
r - > data_width = 3 ;
2017-07-25 14:14:25 -07:00
r - > alloc_capable = true ;
r - > alloc_enabled = true ;
2017-04-07 17:33:53 -07:00
return true ;
}
2017-07-25 14:14:27 -07:00
static void rdt_get_cache_alloc_cfg ( int idx , struct rdt_resource * r )
2016-10-22 06:19:55 -07:00
{
union cpuid_0x10_1_eax eax ;
2017-04-07 17:33:51 -07:00
union cpuid_0x10_x_edx edx ;
2016-10-22 06:19:55 -07:00
u32 ebx , ecx ;
cpuid_count ( 0x00000010 , idx , & eax . full , & ebx , & ecx , & edx . full ) ;
r - > num_closid = edx . split . cos_max + 1 ;
2017-04-14 13:00:36 +02:00
r - > cache . cbm_len = eax . split . cbm_len + 1 ;
2017-04-07 17:33:51 -07:00
r - > default_ctrl = BIT_MASK ( eax . split . cbm_len + 1 ) - 1 ;
2017-07-25 15:39:04 -07:00
r - > cache . shareable_bits = ebx & r - > default_ctrl ;
2017-04-14 13:00:36 +02:00
r - > data_width = ( r - > cache . cbm_len + 3 ) / 4 ;
2017-07-25 14:14:25 -07:00
r - > alloc_capable = true ;
r - > alloc_enabled = true ;
2016-10-22 06:19:55 -07:00
}
2017-12-20 14:57:22 -08:00
static void rdt_get_cdp_config ( int level , int type )
2016-10-22 06:19:55 -07:00
{
2017-12-20 14:57:22 -08:00
struct rdt_resource * r_l = & rdt_resources_all [ level ] ;
2016-10-22 06:19:55 -07:00
struct rdt_resource * r = & rdt_resources_all [ type ] ;
2017-12-20 14:57:22 -08:00
r - > num_closid = r_l - > num_closid / 2 ;
r - > cache . cbm_len = r_l - > cache . cbm_len ;
r - > default_ctrl = r_l - > default_ctrl ;
r - > cache . shareable_bits = r_l - > cache . shareable_bits ;
2017-04-14 13:00:36 +02:00
r - > data_width = ( r - > cache . cbm_len + 3 ) / 4 ;
2017-07-25 14:14:25 -07:00
r - > alloc_capable = true ;
2016-10-22 06:19:55 -07:00
/*
* By default , CDP is disabled . CDP can be enabled by mount parameter
* " cdp " during resctrl file system mount time .
*/
2017-07-25 14:14:25 -07:00
r - > alloc_enabled = false ;
2016-10-22 06:19:55 -07:00
}
2017-12-20 14:57:22 -08:00
static void rdt_get_cdp_l3_config ( void )
{
rdt_get_cdp_config ( RDT_RESOURCE_L3 , RDT_RESOURCE_L3DATA ) ;
rdt_get_cdp_config ( RDT_RESOURCE_L3 , RDT_RESOURCE_L3CODE ) ;
}
static void rdt_get_cdp_l2_config ( void )
{
rdt_get_cdp_config ( RDT_RESOURCE_L2 , RDT_RESOURCE_L2DATA ) ;
rdt_get_cdp_config ( RDT_RESOURCE_L2 , RDT_RESOURCE_L2CODE ) ;
}
2016-10-28 15:04:41 -07:00
static int get_cache_id ( int cpu , int level )
{
struct cpu_cacheinfo * ci = get_cpu_cacheinfo ( cpu ) ;
int i ;
for ( i = 0 ; i < ci - > num_leaves ; i + + ) {
if ( ci - > info_list [ i ] . level = = level )
return ci - > info_list [ i ] . id ;
}
return - 1 ;
}
2017-04-07 17:33:53 -07:00
/*
* Map the memory b / w percentage value to delay values
* that can be written to QOS_MSRs .
* There are currently no SKUs which support non linear delay values .
*/
2018-04-20 15:36:21 -07:00
u32 delay_bw_map ( unsigned long bw , struct rdt_resource * r )
2017-04-07 17:33:53 -07:00
{
if ( r - > membw . delay_linear )
return MAX_MBA_BW - bw ;
pr_warn_once ( " Non Linear delay-bw map not supported but queried \n " ) ;
return r - > default_ctrl ;
}
static void
mba_wrmsr ( struct rdt_domain * d , struct msr_param * m , struct rdt_resource * r )
{
unsigned int i ;
/* Write the delay values for mba. */
for ( i = m - > low ; i < m - > high ; i + + )
wrmsrl ( r - > msr_base + i , delay_bw_map ( d - > ctrl_val [ i ] , r ) ) ;
}
2017-04-14 14:14:31 +02:00
static void
cat_wrmsr ( struct rdt_domain * d , struct msr_param * m , struct rdt_resource * r )
{
unsigned int i ;
for ( i = m - > low ; i < m - > high ; i + + )
wrmsrl ( r - > msr_base + cbm_idx ( r , i ) , d - > ctrl_val [ i ] ) ;
}
2017-07-25 14:14:28 -07:00
struct rdt_domain * get_domain_from_cpu ( int cpu , struct rdt_resource * r )
{
struct rdt_domain * d ;
list_for_each_entry ( d , & r - > domains , list ) {
/* Find the domain that contains this CPU */
if ( cpumask_test_cpu ( cpu , & d - > cpu_mask ) )
return d ;
}
return NULL ;
}
2017-04-07 17:33:51 -07:00
void rdt_ctrl_update ( void * arg )
2016-10-28 15:04:41 -07:00
{
2017-04-14 14:14:31 +02:00
struct msr_param * m = arg ;
2016-10-28 15:04:41 -07:00
struct rdt_resource * r = m - > res ;
2017-04-14 14:14:31 +02:00
int cpu = smp_processor_id ( ) ;
2016-10-28 15:04:41 -07:00
struct rdt_domain * d ;
2017-07-25 14:14:47 -07:00
d = get_domain_from_cpu ( cpu , r ) ;
if ( d ) {
r - > msr_update ( d , m , r ) ;
return ;
2016-10-28 15:04:41 -07:00
}
2017-04-14 14:14:31 +02:00
pr_warn_once ( " cpu %d not found in any domain for resource %s \n " ,
2016-10-28 15:04:41 -07:00
cpu , r - > name ) ;
}
/*
* rdt_find_domain - Find a domain in a resource that matches input resource id
*
* Search resource r ' s domain list to find the resource id . If the resource
* id is found in a domain , return the domain . Otherwise , if requested by
* caller , return the first domain whose id is bigger than the input id .
* The domain list is sorted by id in ascending order .
*/
2017-07-25 14:14:38 -07:00
struct rdt_domain * rdt_find_domain ( struct rdt_resource * r , int id ,
struct list_head * * pos )
2016-10-28 15:04:41 -07:00
{
struct rdt_domain * d ;
struct list_head * l ;
if ( id < 0 )
return ERR_PTR ( id ) ;
list_for_each ( l , & r - > domains ) {
d = list_entry ( l , struct rdt_domain , list ) ;
/* When id is found, return its domain. */
if ( id = = d - > id )
return d ;
/* Stop searching when finding id's position in sorted list. */
if ( id < d - > id )
break ;
}
if ( pos )
* pos = l ;
return NULL ;
}
2018-04-20 15:36:18 -07:00
void setup_default_ctrlval ( struct rdt_resource * r , u32 * dc , u32 * dm )
{
int i ;
/*
* Initialize the Control MSRs to having no control .
* For Cache Allocation : Set all bits in cbm
* For Memory Allocation : Set b / w requested to 100 %
* and the bandwidth in MBps to U32_MAX
*/
for ( i = 0 ; i < r - > num_closid ; i + + , dc + + , dm + + ) {
* dc = r - > default_ctrl ;
* dm = MBA_MAX_MBPS ;
}
}
2017-04-14 14:14:31 +02:00
static int domain_setup_ctrlval ( struct rdt_resource * r , struct rdt_domain * d )
{
struct msr_param m ;
2018-04-20 15:36:18 -07:00
u32 * dc , * dm ;
2017-04-14 14:14:31 +02:00
dc = kmalloc_array ( r - > num_closid , sizeof ( * d - > ctrl_val ) , GFP_KERNEL ) ;
if ( ! dc )
return - ENOMEM ;
2018-04-20 15:36:18 -07:00
dm = kmalloc_array ( r - > num_closid , sizeof ( * d - > mbps_val ) , GFP_KERNEL ) ;
if ( ! dm ) {
kfree ( dc ) ;
return - ENOMEM ;
}
2017-04-14 14:14:31 +02:00
2018-04-20 15:36:18 -07:00
d - > ctrl_val = dc ;
d - > mbps_val = dm ;
setup_default_ctrlval ( r , dc , dm ) ;
2017-04-14 14:14:31 +02:00
m . low = 0 ;
m . high = r - > num_closid ;
r - > msr_update ( d , & m , r ) ;
return 0 ;
}
2017-07-25 14:14:28 -07:00
static int domain_setup_mon_state ( struct rdt_resource * r , struct rdt_domain * d )
{
2017-07-25 14:14:45 -07:00
size_t tsize ;
2017-07-25 14:14:28 -07:00
if ( is_llc_occupancy_enabled ( ) ) {
d - > rmid_busy_llc = kcalloc ( BITS_TO_LONGS ( r - > num_rmid ) ,
sizeof ( unsigned long ) ,
GFP_KERNEL ) ;
if ( ! d - > rmid_busy_llc )
return - ENOMEM ;
2017-08-15 18:00:43 -07:00
INIT_DELAYED_WORK ( & d - > cqm_limbo , cqm_handle_limbo ) ;
2017-07-25 14:14:28 -07:00
}
2017-07-25 14:14:45 -07:00
if ( is_mbm_total_enabled ( ) ) {
tsize = sizeof ( * d - > mbm_total ) ;
d - > mbm_total = kcalloc ( r - > num_rmid , tsize , GFP_KERNEL ) ;
if ( ! d - > mbm_total ) {
kfree ( d - > rmid_busy_llc ) ;
return - ENOMEM ;
}
}
if ( is_mbm_local_enabled ( ) ) {
tsize = sizeof ( * d - > mbm_local ) ;
d - > mbm_local = kcalloc ( r - > num_rmid , tsize , GFP_KERNEL ) ;
if ( ! d - > mbm_local ) {
kfree ( d - > rmid_busy_llc ) ;
kfree ( d - > mbm_total ) ;
return - ENOMEM ;
}
}
2017-07-25 14:14:28 -07:00
2017-07-25 14:14:47 -07:00
if ( is_mbm_enabled ( ) ) {
INIT_DELAYED_WORK ( & d - > mbm_over , mbm_handle_overflow ) ;
2017-08-15 18:00:42 -07:00
mbm_setup_overflow_handler ( d , MBM_OVERFLOW_INTERVAL ) ;
2017-07-25 14:14:47 -07:00
}
2017-07-25 14:14:28 -07:00
return 0 ;
}
2016-10-28 15:04:41 -07:00
/*
* domain_add_cpu - Add a cpu to a resource ' s domain list .
*
* If an existing domain in the resource r ' s domain list matches the cpu ' s
* resource id , add the cpu in the domain .
*
* Otherwise , a new domain is allocated and inserted into the right position
* in the domain list sorted by id in ascending order .
*
* The order in the domain list is visible to users when we print entries
* in the schemata file and schemata input is validated to have the same order
* as this list .
*/
static void domain_add_cpu ( int cpu , struct rdt_resource * r )
{
2017-04-14 14:14:31 +02:00
int id = get_cache_id ( cpu , r - > cache_level ) ;
2016-10-28 15:04:41 -07:00
struct list_head * add_pos = NULL ;
struct rdt_domain * d ;
d = rdt_find_domain ( r , id , & add_pos ) ;
if ( IS_ERR ( d ) ) {
pr_warn ( " Could't find cache id for cpu %d \n " , cpu ) ;
return ;
}
if ( d ) {
cpumask_set_cpu ( cpu , & d - > cpu_mask ) ;
return ;
}
d = kzalloc_node ( sizeof ( * d ) , GFP_KERNEL , cpu_to_node ( cpu ) ) ;
if ( ! d )
return ;
d - > id = id ;
2017-07-25 14:14:45 -07:00
cpumask_set_cpu ( cpu , & d - > cpu_mask ) ;
2016-10-28 15:04:41 -07:00
2017-07-25 14:14:25 -07:00
if ( r - > alloc_capable & & domain_setup_ctrlval ( r , d ) ) {
2016-10-28 15:04:41 -07:00
kfree ( d ) ;
return ;
}
2017-07-25 14:14:28 -07:00
if ( r - > mon_capable & & domain_setup_mon_state ( r , d ) ) {
kfree ( d ) ;
return ;
}
2016-10-28 15:04:41 -07:00
list_add_tail ( & d - > list , add_pos ) ;
2017-07-25 14:14:44 -07:00
/*
* If resctrl is mounted , add
* per domain monitor data directories .
*/
if ( static_branch_unlikely ( & rdt_mon_enable_key ) )
mkdir_mondata_subdir_allrdtgrp ( r , d ) ;
2016-10-28 15:04:41 -07:00
}
static void domain_remove_cpu ( int cpu , struct rdt_resource * r )
{
int id = get_cache_id ( cpu , r - > cache_level ) ;
struct rdt_domain * d ;
d = rdt_find_domain ( r , id , NULL ) ;
if ( IS_ERR_OR_NULL ( d ) ) {
pr_warn ( " Could't find cache id for cpu %d \n " , cpu ) ;
return ;
}
cpumask_clear_cpu ( cpu , & d - > cpu_mask ) ;
if ( cpumask_empty ( & d - > cpu_mask ) ) {
2017-07-25 14:14:44 -07:00
/*
* If resctrl is mounted , remove all the
* per domain monitor data directories .
*/
if ( static_branch_unlikely ( & rdt_mon_enable_key ) )
rmdir_mondata_subdir_allrdtgrp ( r , d - > id ) ;
2016-10-28 15:04:41 -07:00
list_del ( & d - > list ) ;
2017-07-25 14:14:47 -07:00
if ( is_mbm_enabled ( ) )
cancel_delayed_work ( & d - > mbm_over ) ;
2017-08-15 18:00:43 -07:00
if ( is_llc_occupancy_enabled ( ) & & has_busy_rmid ( r , d ) ) {
/*
* When a package is going down , forcefully
* decrement rmid - > ebusy . There is no way to know
* that the L3 was flushed and hence may lead to
* incorrect counts in rare scenarios , but leaving
* the RMID as busy creates RMID leaks if the
* package never comes back .
*/
__check_limbo ( d , true ) ;
cancel_delayed_work ( & d - > cqm_limbo ) ;
}
2018-01-16 19:59:59 +01:00
kfree ( d - > ctrl_val ) ;
2018-04-20 15:36:18 -07:00
kfree ( d - > mbps_val ) ;
2018-01-16 19:59:59 +01:00
kfree ( d - > rmid_busy_llc ) ;
kfree ( d - > mbm_total ) ;
kfree ( d - > mbm_local ) ;
2016-10-28 15:04:41 -07:00
kfree ( d ) ;
2017-08-15 18:00:43 -07:00
return ;
}
if ( r = = & rdt_resources_all [ RDT_RESOURCE_L3 ] ) {
if ( is_mbm_enabled ( ) & & cpu = = d - > mbm_work_cpu ) {
cancel_delayed_work ( & d - > mbm_over ) ;
mbm_setup_overflow_handler ( d , 0 ) ;
}
if ( is_llc_occupancy_enabled ( ) & & cpu = = d - > cqm_work_cpu & &
has_busy_rmid ( r , d ) ) {
cancel_delayed_work ( & d - > cqm_limbo ) ;
cqm_setup_limbo_handler ( d , 0 ) ;
}
2016-10-28 15:04:41 -07:00
}
}
2017-07-25 14:14:44 -07:00
static void clear_closid_rmid ( int cpu )
2016-10-28 15:04:41 -07:00
{
struct intel_pqr_state * state = this_cpu_ptr ( & pqr_state ) ;
2016-10-28 15:04:45 -07:00
2017-08-09 11:46:34 -07:00
state - > default_closid = 0 ;
state - > default_rmid = 0 ;
state - > cur_closid = 0 ;
state - > cur_rmid = 0 ;
2017-07-25 14:14:44 -07:00
wrmsr ( IA32_PQR_ASSOC , 0 , 0 ) ;
2016-10-28 15:04:45 -07:00
}
static int intel_rdt_online_cpu ( unsigned int cpu )
{
2016-10-28 15:04:41 -07:00
struct rdt_resource * r ;
mutex_lock ( & rdtgroup_mutex ) ;
2017-07-25 14:14:44 -07:00
for_each_capable_rdt_resource ( r )
2016-10-28 15:04:41 -07:00
domain_add_cpu ( cpu , r ) ;
2016-10-28 15:04:45 -07:00
/* The cpu is set in default rdtgroup after online. */
cpumask_set_cpu ( cpu , & rdtgroup_default . cpu_mask ) ;
2017-07-25 14:14:44 -07:00
clear_closid_rmid ( cpu ) ;
2016-10-28 15:04:41 -07:00
mutex_unlock ( & rdtgroup_mutex ) ;
return 0 ;
}
2017-07-25 14:14:44 -07:00
static void clear_childcpus ( struct rdtgroup * r , unsigned int cpu )
{
struct rdtgroup * cr ;
list_for_each_entry ( cr , & r - > mon . crdtgrp_list , mon . crdtgrp_list ) {
if ( cpumask_test_and_clear_cpu ( cpu , & cr - > cpu_mask ) ) {
break ;
}
}
}
2016-10-28 15:04:41 -07:00
static int intel_rdt_offline_cpu ( unsigned int cpu )
{
2016-10-28 15:04:45 -07:00
struct rdtgroup * rdtgrp ;
2016-10-28 15:04:41 -07:00
struct rdt_resource * r ;
mutex_lock ( & rdtgroup_mutex ) ;
2017-07-25 14:14:44 -07:00
for_each_capable_rdt_resource ( r )
2016-10-28 15:04:41 -07:00
domain_remove_cpu ( cpu , r ) ;
2016-10-28 15:04:45 -07:00
list_for_each_entry ( rdtgrp , & rdt_all_groups , rdtgroup_list ) {
2017-07-25 14:14:44 -07:00
if ( cpumask_test_and_clear_cpu ( cpu , & rdtgrp - > cpu_mask ) ) {
clear_childcpus ( rdtgrp , cpu ) ;
2016-10-28 15:04:45 -07:00
break ;
2017-07-25 14:14:44 -07:00
}
2016-10-28 15:04:45 -07:00
}
2017-07-25 14:14:44 -07:00
clear_closid_rmid ( cpu ) ;
2016-10-28 15:04:41 -07:00
mutex_unlock ( & rdtgroup_mutex ) ;
return 0 ;
}
2017-04-14 14:07:47 +02:00
/*
* Choose a width for the resource name and resource data based on the
* resource that has widest name and cbm .
*/
static __init void rdt_init_padding ( void )
{
struct rdt_resource * r ;
int cl ;
2017-07-25 14:14:25 -07:00
for_each_alloc_capable_rdt_resource ( r ) {
2017-04-14 14:07:47 +02:00
cl = strlen ( r - > name ) ;
if ( cl > max_name_width )
max_name_width = cl ;
if ( r - > data_width > max_data_width )
max_data_width = r - > data_width ;
}
}
2017-08-24 09:26:51 -07:00
enum {
RDT_FLAG_CMT ,
RDT_FLAG_MBM_TOTAL ,
RDT_FLAG_MBM_LOCAL ,
RDT_FLAG_L3_CAT ,
RDT_FLAG_L3_CDP ,
RDT_FLAG_L2_CAT ,
2017-12-20 14:57:24 -08:00
RDT_FLAG_L2_CDP ,
2017-08-24 09:26:51 -07:00
RDT_FLAG_MBA ,
} ;
# define RDT_OPT(idx, n, f) \
[ idx ] = { \
. name = n , \
. flag = f \
}
struct rdt_options {
char * name ;
int flag ;
bool force_off , force_on ;
} ;
static struct rdt_options rdt_options [ ] __initdata = {
RDT_OPT ( RDT_FLAG_CMT , " cmt " , X86_FEATURE_CQM_OCCUP_LLC ) ,
RDT_OPT ( RDT_FLAG_MBM_TOTAL , " mbmtotal " , X86_FEATURE_CQM_MBM_TOTAL ) ,
RDT_OPT ( RDT_FLAG_MBM_LOCAL , " mbmlocal " , X86_FEATURE_CQM_MBM_LOCAL ) ,
RDT_OPT ( RDT_FLAG_L3_CAT , " l3cat " , X86_FEATURE_CAT_L3 ) ,
RDT_OPT ( RDT_FLAG_L3_CDP , " l3cdp " , X86_FEATURE_CDP_L3 ) ,
RDT_OPT ( RDT_FLAG_L2_CAT , " l2cat " , X86_FEATURE_CAT_L2 ) ,
2017-12-20 14:57:24 -08:00
RDT_OPT ( RDT_FLAG_L2_CDP , " l2cdp " , X86_FEATURE_CDP_L2 ) ,
2017-08-24 09:26:51 -07:00
RDT_OPT ( RDT_FLAG_MBA , " mba " , X86_FEATURE_MBA ) ,
} ;
# define NUM_RDT_OPTIONS ARRAY_SIZE(rdt_options)
static int __init set_rdt_options ( char * str )
{
struct rdt_options * o ;
bool force_off ;
char * tok ;
if ( * str = = ' = ' )
str + + ;
while ( ( tok = strsep ( & str , " , " ) ) ! = NULL ) {
force_off = * tok = = ' ! ' ;
if ( force_off )
tok + + ;
for ( o = rdt_options ; o < & rdt_options [ NUM_RDT_OPTIONS ] ; o + + ) {
if ( strcmp ( tok , o - > name ) = = 0 ) {
if ( force_off )
o - > force_off = true ;
else
o - > force_on = true ;
break ;
}
}
}
return 1 ;
}
__setup ( " rdt " , set_rdt_options ) ;
static bool __init rdt_cpu_has ( int flag )
{
bool ret = boot_cpu_has ( flag ) ;
struct rdt_options * o ;
if ( ! ret )
return ret ;
for ( o = rdt_options ; o < & rdt_options [ NUM_RDT_OPTIONS ] ; o + + ) {
if ( flag = = o - > flag ) {
if ( o - > force_off )
ret = false ;
if ( o - > force_on )
ret = true ;
break ;
}
}
return ret ;
}
2017-07-25 14:14:27 -07:00
static __init bool get_rdt_alloc_resources ( void )
2017-04-14 14:07:47 +02:00
{
bool ret = false ;
2017-08-24 09:26:50 -07:00
if ( rdt_alloc_capable )
2017-04-14 14:07:47 +02:00
return true ;
if ( ! boot_cpu_has ( X86_FEATURE_RDT_A ) )
return false ;
2017-08-24 09:26:51 -07:00
if ( rdt_cpu_has ( X86_FEATURE_CAT_L3 ) ) {
2017-07-25 14:14:27 -07:00
rdt_get_cache_alloc_cfg ( 1 , & rdt_resources_all [ RDT_RESOURCE_L3 ] ) ;
2017-12-20 14:57:22 -08:00
if ( rdt_cpu_has ( X86_FEATURE_CDP_L3 ) )
rdt_get_cdp_l3_config ( ) ;
2017-04-14 14:07:47 +02:00
ret = true ;
}
2017-08-24 09:26:51 -07:00
if ( rdt_cpu_has ( X86_FEATURE_CAT_L2 ) ) {
2017-04-14 14:07:47 +02:00
/* CPUID 0x10.2 fields are same format at 0x10.1 */
2017-07-25 14:14:27 -07:00
rdt_get_cache_alloc_cfg ( 2 , & rdt_resources_all [ RDT_RESOURCE_L2 ] ) ;
2017-12-20 14:57:22 -08:00
if ( rdt_cpu_has ( X86_FEATURE_CDP_L2 ) )
rdt_get_cdp_l2_config ( ) ;
2017-04-14 14:07:47 +02:00
ret = true ;
}
2017-04-07 17:33:52 -07:00
2017-08-24 09:26:51 -07:00
if ( rdt_cpu_has ( X86_FEATURE_MBA ) ) {
2017-04-07 17:33:53 -07:00
if ( rdt_get_mem_config ( & rdt_resources_all [ RDT_RESOURCE_MBA ] ) )
ret = true ;
}
2017-04-14 14:07:47 +02:00
return ret ;
}
2017-07-25 14:14:27 -07:00
static __init bool get_rdt_mon_resources ( void )
{
2017-08-24 09:26:51 -07:00
if ( rdt_cpu_has ( X86_FEATURE_CQM_OCCUP_LLC ) )
2017-07-25 14:14:27 -07:00
rdt_mon_features | = ( 1 < < QOS_L3_OCCUP_EVENT_ID ) ;
2017-08-24 09:26:51 -07:00
if ( rdt_cpu_has ( X86_FEATURE_CQM_MBM_TOTAL ) )
2017-07-25 14:14:27 -07:00
rdt_mon_features | = ( 1 < < QOS_L3_MBM_TOTAL_EVENT_ID ) ;
2017-08-24 09:26:51 -07:00
if ( rdt_cpu_has ( X86_FEATURE_CQM_MBM_LOCAL ) )
2017-07-25 14:14:27 -07:00
rdt_mon_features | = ( 1 < < QOS_L3_MBM_LOCAL_EVENT_ID ) ;
if ( ! rdt_mon_features )
return false ;
return ! rdt_get_mon_l3_config ( & rdt_resources_all [ RDT_RESOURCE_L3 ] ) ;
}
2017-08-24 09:26:50 -07:00
static __init void rdt_quirks ( void )
{
switch ( boot_cpu_data . x86_model ) {
case INTEL_FAM6_HASWELL_X :
2017-08-24 09:26:51 -07:00
if ( ! rdt_options [ RDT_FLAG_L3_CAT ] . force_off )
cache_alloc_hsw_probe ( ) ;
2017-08-24 09:26:50 -07:00
break ;
2017-08-24 09:26:52 -07:00
case INTEL_FAM6_SKYLAKE_X :
2018-01-01 09:52:10 +08:00
if ( boot_cpu_data . x86_stepping < = 4 )
2017-08-24 09:26:52 -07:00
set_rdt_options ( " !cmt,!mbmtotal,!mbmlocal,!l3cat " ) ;
2018-06-08 09:07:32 -07:00
else
set_rdt_options ( " !l3cat " ) ;
2017-08-24 09:26:50 -07:00
}
}
2017-07-25 14:14:27 -07:00
static __init bool get_rdt_resources ( void )
{
2017-08-24 09:26:50 -07:00
rdt_quirks ( ) ;
2017-07-25 14:14:27 -07:00
rdt_alloc_capable = get_rdt_alloc_resources ( ) ;
rdt_mon_capable = get_rdt_mon_resources ( ) ;
return ( rdt_mon_capable | | rdt_alloc_capable ) ;
}
2016-10-22 06:19:53 -07:00
static int __init intel_rdt_late_init ( void )
{
2016-10-22 06:19:55 -07:00
struct rdt_resource * r ;
2016-10-28 15:04:42 -07:00
int state , ret ;
2016-10-22 06:19:55 -07:00
2016-10-22 06:19:53 -07:00
if ( ! get_rdt_resources ( ) )
return - ENODEV ;
2017-04-14 14:06:26 +02:00
rdt_init_padding ( ) ;
2016-10-28 15:04:41 -07:00
state = cpuhp_setup_state ( CPUHP_AP_ONLINE_DYN ,
" x86/rdt/cat:online: " ,
intel_rdt_online_cpu , intel_rdt_offline_cpu ) ;
if ( state < 0 )
return state ;
2016-10-28 15:04:42 -07:00
ret = rdtgroup_init ( ) ;
if ( ret ) {
cpuhp_remove_state ( state ) ;
return ret ;
}
2017-07-25 14:14:25 -07:00
for_each_alloc_capable_rdt_resource ( r )
2016-10-22 06:19:55 -07:00
pr_info ( " Intel RDT %s allocation detected \n " , r - > name ) ;
2016-10-22 06:19:53 -07:00
2017-07-25 14:14:27 -07:00
for_each_mon_capable_rdt_resource ( r )
pr_info ( " Intel RDT %s monitoring detected \n " , r - > name ) ;
2016-10-22 06:19:53 -07:00
return 0 ;
}
late_initcall ( intel_rdt_late_init ) ;