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>
# include <asm/intel_rdt.h>
2016-10-28 15:04:41 -07:00
/* Mutex to protect rdtgroup access. */
DEFINE_MUTEX ( rdtgroup_mutex ) ;
2016-10-28 15:04:45 -07:00
DEFINE_PER_CPU_READ_MOSTLY ( int , cpu_closid ) ;
2016-10-22 06:19:55 -07:00
# define domain_init(id) LIST_HEAD_INIT(rdt_resources_all[id].domains)
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 ;
2016-10-22 06:19:55 -07:00
struct rdt_resource rdt_resources_all [ ] = {
{
. name = " L3 " ,
. domains = domain_init ( RDT_RESOURCE_L3 ) ,
. msr_base = IA32_L3_CBM_BASE ,
. min_cbm_bits = 1 ,
. cache_level = 3 ,
. cbm_idx_multi = 1 ,
. cbm_idx_offset = 0
} ,
{
. name = " L3DATA " ,
. domains = domain_init ( RDT_RESOURCE_L3DATA ) ,
. msr_base = IA32_L3_CBM_BASE ,
. min_cbm_bits = 1 ,
. cache_level = 3 ,
. cbm_idx_multi = 2 ,
. cbm_idx_offset = 0
} ,
{
. name = " L3CODE " ,
. domains = domain_init ( RDT_RESOURCE_L3CODE ) ,
. msr_base = IA32_L3_CBM_BASE ,
. min_cbm_bits = 1 ,
. cache_level = 3 ,
. cbm_idx_multi = 2 ,
. cbm_idx_offset = 1
} ,
{
. name = " L2 " ,
. domains = domain_init ( RDT_RESOURCE_L2 ) ,
. msr_base = IA32_L2_CBM_BASE ,
. min_cbm_bits = 1 ,
. cache_level = 2 ,
. cbm_idx_multi = 1 ,
. cbm_idx_offset = 0
} ,
} ;
2016-10-28 15:04:41 -07:00
static int cbm_idx ( struct rdt_resource * r , int closid )
{
return closid * r - > cbm_idx_multi + r - > cbm_idx_offset ;
}
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 .
*/
static inline bool cache_alloc_hsw_probe ( void )
{
if ( boot_cpu_data . x86_vendor = = X86_VENDOR_INTEL & &
boot_cpu_data . x86 = = 6 & &
boot_cpu_data . x86_model = = INTEL_FAM6_HASWELL_X ) {
2016-10-22 06:19:55 -07:00
struct rdt_resource * r = & rdt_resources_all [ RDT_RESOURCE_L3 ] ;
2016-10-22 06:19:54 -07:00
u32 l , h , max_cbm = BIT_MASK ( 20 ) - 1 ;
if ( wrmsr_safe ( IA32_L3_CBM_BASE , max_cbm , 0 ) )
return false ;
rdmsr ( IA32_L3_CBM_BASE , l , h ) ;
/* If all the bits were set in MSR, return success */
2016-10-22 06:19:55 -07:00
if ( l ! = max_cbm )
return false ;
r - > num_closid = 4 ;
r - > cbm_len = 20 ;
2017-04-07 17:33:51 -07:00
r - > default_ctrl = max_cbm ;
2016-10-22 06:19:55 -07:00
r - > min_cbm_bits = 2 ;
r - > capable = true ;
r - > enabled = true ;
return true ;
2016-10-22 06:19:54 -07:00
}
return false ;
}
2017-04-07 17:33:51 -07:00
static void rdt_get_cache_config ( 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 ;
r - > 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-04-03 14:44:17 -07:00
r - > data_width = ( r - > cbm_len + 3 ) / 4 ;
2016-10-22 06:19:55 -07:00
r - > capable = true ;
r - > enabled = true ;
}
static void rdt_get_cdp_l3_config ( int type )
{
struct rdt_resource * r_l3 = & rdt_resources_all [ RDT_RESOURCE_L3 ] ;
struct rdt_resource * r = & rdt_resources_all [ type ] ;
r - > num_closid = r_l3 - > num_closid / 2 ;
r - > cbm_len = r_l3 - > cbm_len ;
2017-04-07 17:33:51 -07:00
r - > default_ctrl = r_l3 - > default_ctrl ;
2017-04-03 14:44:17 -07:00
r - > data_width = ( r - > cbm_len + 3 ) / 4 ;
2016-10-22 06:19:55 -07:00
r - > capable = true ;
/*
* By default , CDP is disabled . CDP can be enabled by mount parameter
* " cdp " during resctrl file system mount time .
*/
r - > enabled = false ;
}
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:51 -07:00
void rdt_ctrl_update ( void * arg )
2016-10-28 15:04:41 -07:00
{
struct msr_param * m = ( struct msr_param * ) arg ;
struct rdt_resource * r = m - > res ;
int i , cpu = smp_processor_id ( ) ;
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 ) )
goto found ;
}
pr_info_once ( " cpu %d not found in any domain for resource %s \n " ,
cpu , r - > name ) ;
return ;
found :
for ( i = m - > low ; i < m - > high ; i + + ) {
int idx = cbm_idx ( r , i ) ;
2017-04-07 17:33:51 -07:00
wrmsrl ( r - > msr_base + idx , d - > ctrl_val [ i ] ) ;
2016-10-28 15:04:41 -07:00
}
}
/*
* 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 .
*/
static struct rdt_domain * rdt_find_domain ( struct rdt_resource * r , int id ,
struct list_head * * pos )
{
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 ;
}
/*
* 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 )
{
int i , id = get_cache_id ( cpu , r - > cache_level ) ;
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-04-07 17:33:51 -07:00
d - > ctrl_val = kmalloc_array ( r - > num_closid , sizeof ( * d - > ctrl_val ) , GFP_KERNEL ) ;
if ( ! d - > ctrl_val ) {
2016-10-28 15:04:41 -07:00
kfree ( d ) ;
return ;
}
for ( i = 0 ; i < r - > num_closid ; i + + ) {
int idx = cbm_idx ( r , i ) ;
2017-04-07 17:33:51 -07:00
d - > ctrl_val [ i ] = r - > default_ctrl ;
wrmsrl ( r - > msr_base + idx , d - > ctrl_val [ i ] ) ;
2016-10-28 15:04:41 -07:00
}
cpumask_set_cpu ( cpu , & d - > cpu_mask ) ;
list_add_tail ( & d - > list , add_pos ) ;
}
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-04-07 17:33:51 -07:00
kfree ( d - > ctrl_val ) ;
2016-10-28 15:04:41 -07:00
list_del ( & d - > list ) ;
kfree ( d ) ;
}
}
2016-10-28 15:04:45 -07:00
static void clear_closid ( 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
per_cpu ( cpu_closid , cpu ) = 0 ;
state - > closid = 0 ;
wrmsr ( MSR_IA32_PQR_ASSOC , state - > rmid , 0 ) ;
}
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 ) ;
for_each_capable_rdt_resource ( r )
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 ) ;
clear_closid ( cpu ) ;
2016-10-28 15:04:41 -07:00
mutex_unlock ( & rdtgroup_mutex ) ;
return 0 ;
}
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 ) ;
for_each_capable_rdt_resource ( r )
domain_remove_cpu ( cpu , r ) ;
2016-10-28 15:04:45 -07:00
list_for_each_entry ( rdtgrp , & rdt_all_groups , rdtgroup_list ) {
if ( cpumask_test_and_clear_cpu ( cpu , & rdtgrp - > cpu_mask ) )
break ;
}
clear_closid ( 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 ;
for_each_enabled_rdt_resource ( r ) {
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 ;
}
}
static __init bool get_rdt_resources ( void )
{
bool ret = false ;
if ( cache_alloc_hsw_probe ( ) )
return true ;
if ( ! boot_cpu_has ( X86_FEATURE_RDT_A ) )
return false ;
if ( boot_cpu_has ( X86_FEATURE_CAT_L3 ) ) {
2017-04-07 17:33:51 -07:00
rdt_get_cache_config ( 1 , & rdt_resources_all [ RDT_RESOURCE_L3 ] ) ;
2017-04-14 14:07:47 +02:00
if ( boot_cpu_has ( X86_FEATURE_CDP_L3 ) ) {
rdt_get_cdp_l3_config ( RDT_RESOURCE_L3DATA ) ;
rdt_get_cdp_l3_config ( RDT_RESOURCE_L3CODE ) ;
}
ret = true ;
}
if ( boot_cpu_has ( X86_FEATURE_CAT_L2 ) ) {
/* CPUID 0x10.2 fields are same format at 0x10.1 */
2017-04-07 17:33:51 -07:00
rdt_get_cache_config ( 2 , & rdt_resources_all [ RDT_RESOURCE_L2 ] ) ;
2017-04-14 14:07:47 +02:00
ret = true ;
}
return ret ;
}
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 ;
}
2016-10-22 06:19:55 -07:00
for_each_capable_rdt_resource ( r )
pr_info ( " Intel RDT %s allocation detected \n " , r - > name ) ;
2016-10-22 06:19:53 -07:00
return 0 ;
}
late_initcall ( intel_rdt_late_init ) ;