2017-10-27 05:42:28 +03:00
/*
* Housekeeping management . Manage the targets for routine code that can run on
* any CPU : unbound workqueues , timers , kthreads and any offloadable work .
*
* Copyright ( C ) 2017 Red Hat , Inc . , Frederic Weisbecker
*
*/
# include <linux/sched/isolation.h>
# include <linux/tick.h>
# include <linux/init.h>
# include <linux/kernel.h>
2017-10-27 05:42:32 +03:00
# include <linux/static_key.h>
2017-10-27 05:42:38 +03:00
# include <linux/ctype.h>
2017-10-27 05:42:28 +03:00
2017-10-27 05:42:32 +03:00
DEFINE_STATIC_KEY_FALSE ( housekeeping_overriden ) ;
EXPORT_SYMBOL_GPL ( housekeeping_overriden ) ;
2017-10-27 05:42:31 +03:00
static cpumask_var_t housekeeping_mask ;
2017-10-27 05:42:35 +03:00
static unsigned int housekeeping_flags ;
2017-10-27 05:42:31 +03:00
2017-10-27 05:42:35 +03:00
int housekeeping_any_cpu ( enum hk_flags flags )
2017-10-27 05:42:31 +03:00
{
2017-10-27 05:42:32 +03:00
if ( static_branch_unlikely ( & housekeeping_overriden ) )
2017-10-27 05:42:35 +03:00
if ( housekeeping_flags & flags )
return cpumask_any_and ( housekeeping_mask , cpu_online_mask ) ;
2017-10-27 05:42:31 +03:00
return smp_processor_id ( ) ;
}
EXPORT_SYMBOL_GPL ( housekeeping_any_cpu ) ;
2017-10-27 05:42:35 +03:00
const struct cpumask * housekeeping_cpumask ( enum hk_flags flags )
2017-10-27 05:42:31 +03:00
{
2017-10-27 05:42:32 +03:00
if ( static_branch_unlikely ( & housekeeping_overriden ) )
2017-10-27 05:42:35 +03:00
if ( housekeeping_flags & flags )
return housekeeping_mask ;
2017-10-27 05:42:31 +03:00
return cpu_possible_mask ;
}
EXPORT_SYMBOL_GPL ( housekeeping_cpumask ) ;
2017-10-27 05:42:35 +03:00
void housekeeping_affine ( struct task_struct * t , enum hk_flags flags )
2017-10-27 05:42:31 +03:00
{
2017-10-27 05:42:32 +03:00
if ( static_branch_unlikely ( & housekeeping_overriden ) )
2017-10-27 05:42:35 +03:00
if ( housekeeping_flags & flags )
set_cpus_allowed_ptr ( t , housekeeping_mask ) ;
2017-10-27 05:42:31 +03:00
}
EXPORT_SYMBOL_GPL ( housekeeping_affine ) ;
2017-10-27 05:42:35 +03:00
bool housekeeping_test_cpu ( int cpu , enum hk_flags flags )
2017-10-27 05:42:31 +03:00
{
2017-10-27 05:42:32 +03:00
if ( static_branch_unlikely ( & housekeeping_overriden ) )
2017-10-27 05:42:35 +03:00
if ( housekeeping_flags & flags )
return cpumask_test_cpu ( cpu , housekeeping_mask ) ;
2017-10-27 05:42:31 +03:00
return true ;
}
EXPORT_SYMBOL_GPL ( housekeeping_test_cpu ) ;
2017-10-27 05:42:28 +03:00
void __init housekeeping_init ( void )
{
2017-10-27 05:42:36 +03:00
if ( ! housekeeping_flags )
2017-10-27 05:42:28 +03:00
return ;
2017-10-27 05:42:36 +03:00
static_branch_enable ( & housekeeping_overriden ) ;
/* We need at least one CPU to handle housekeeping work */
WARN_ON_ONCE ( cpumask_empty ( housekeeping_mask ) ) ;
}
2017-10-27 05:42:37 +03:00
static int __init housekeeping_setup ( char * str , enum hk_flags flags )
2017-10-27 05:42:36 +03:00
{
cpumask_var_t non_housekeeping_mask ;
2017-10-27 05:42:37 +03:00
int err ;
2017-10-27 05:42:36 +03:00
alloc_bootmem_cpumask_var ( & non_housekeeping_mask ) ;
2017-10-27 05:42:37 +03:00
err = cpulist_parse ( str , non_housekeeping_mask ) ;
if ( err < 0 | | cpumask_last ( non_housekeeping_mask ) > = nr_cpu_ids ) {
pr_warn ( " Housekeeping: nohz_full= or isolcpus= incorrect CPU range \n " ) ;
2017-10-27 05:42:36 +03:00
free_bootmem_cpumask_var ( non_housekeeping_mask ) ;
return 0 ;
2017-10-27 05:42:28 +03:00
}
2017-10-27 05:42:37 +03:00
if ( ! housekeeping_flags ) {
alloc_bootmem_cpumask_var ( & housekeeping_mask ) ;
cpumask_andnot ( housekeeping_mask ,
cpu_possible_mask , non_housekeeping_mask ) ;
if ( cpumask_empty ( housekeeping_mask ) )
cpumask_set_cpu ( smp_processor_id ( ) , housekeeping_mask ) ;
} else {
cpumask_var_t tmp ;
alloc_bootmem_cpumask_var ( & tmp ) ;
cpumask_andnot ( tmp , cpu_possible_mask , non_housekeeping_mask ) ;
if ( ! cpumask_equal ( tmp , housekeeping_mask ) ) {
pr_warn ( " Housekeeping: nohz_full= must match isolcpus= \n " ) ;
free_bootmem_cpumask_var ( tmp ) ;
free_bootmem_cpumask_var ( non_housekeeping_mask ) ;
return 0 ;
}
free_bootmem_cpumask_var ( tmp ) ;
}
2017-10-27 05:42:35 +03:00
2017-10-27 05:42:37 +03:00
if ( ( flags & HK_FLAG_TICK ) & & ! ( housekeeping_flags & HK_FLAG_TICK ) ) {
if ( IS_ENABLED ( CONFIG_NO_HZ_FULL ) ) {
tick_nohz_full_setup ( non_housekeeping_mask ) ;
} else {
pr_warn ( " Housekeeping: nohz unsupported. "
" Build with CONFIG_NO_HZ_FULL \n " ) ;
free_bootmem_cpumask_var ( non_housekeeping_mask ) ;
return 0 ;
}
}
2017-10-27 05:42:32 +03:00
2017-10-27 05:42:37 +03:00
housekeeping_flags | = flags ;
2017-10-27 05:42:36 +03:00
free_bootmem_cpumask_var ( non_housekeeping_mask ) ;
return 1 ;
2017-10-27 05:42:28 +03:00
}
2017-10-27 05:42:37 +03:00
static int __init housekeeping_nohz_full_setup ( char * str )
{
unsigned int flags ;
flags = HK_FLAG_TICK | HK_FLAG_TIMER | HK_FLAG_RCU | HK_FLAG_MISC ;
return housekeeping_setup ( str , flags ) ;
}
2017-10-27 05:42:36 +03:00
__setup ( " nohz_full= " , housekeeping_nohz_full_setup ) ;
2017-10-27 05:42:37 +03:00
static int __init housekeeping_isolcpus_setup ( char * str )
{
2017-10-27 05:42:38 +03:00
unsigned int flags = 0 ;
while ( isalpha ( * str ) ) {
if ( ! strncmp ( str , " nohz, " , 5 ) ) {
str + = 5 ;
flags | = HK_FLAG_TICK ;
continue ;
}
if ( ! strncmp ( str , " domain, " , 7 ) ) {
str + = 7 ;
flags | = HK_FLAG_DOMAIN ;
continue ;
}
pr_warn ( " isolcpus: Error, unknown flag \n " ) ;
return 0 ;
}
/* Default behaviour for isolcpus without flags */
if ( ! flags )
flags | = HK_FLAG_DOMAIN ;
return housekeeping_setup ( str , flags ) ;
2017-10-27 05:42:37 +03:00
}
__setup ( " isolcpus= " , housekeeping_isolcpus_setup ) ;