2019-05-19 15:08:55 +03:00
// SPDX-License-Identifier: GPL-2.0-only
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
2018-02-21 07:17:26 +03:00
* Copyright ( C ) 2017 - 2018 SUSE , Frederic Weisbecker
2017-10-27 05:42:28 +03:00
*
*/
2018-02-21 07:17:27 +03:00
# include "sched.h"
2017-10-27 05:42:28 +03:00
2018-12-03 12:05:56 +03:00
DEFINE_STATIC_KEY_FALSE ( housekeeping_overridden ) ;
EXPORT_SYMBOL_GPL ( housekeeping_overridden ) ;
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
2019-07-06 04:26:51 +03:00
bool housekeeping_enabled ( enum hk_flags flags )
{
return ! ! ( housekeeping_flags & flags ) ;
}
EXPORT_SYMBOL_GPL ( housekeeping_enabled ) ;
2017-10-27 05:42:35 +03:00
int housekeeping_any_cpu ( enum hk_flags flags )
2017-10-27 05:42:31 +03:00
{
2019-06-28 11:51:41 +03:00
int cpu ;
if ( static_branch_unlikely ( & housekeeping_overridden ) ) {
if ( housekeeping_flags & flags ) {
cpu = sched_numa_find_closest ( housekeeping_mask , smp_processor_id ( ) ) ;
if ( cpu < nr_cpu_ids )
return cpu ;
2017-10-27 05:42:35 +03:00
return cpumask_any_and ( housekeeping_mask , cpu_online_mask ) ;
2019-06-28 11:51:41 +03:00
}
}
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
{
2018-12-03 12:05:56 +03:00
if ( static_branch_unlikely ( & housekeeping_overridden ) )
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
{
2018-12-03 12:05:56 +03:00
if ( static_branch_unlikely ( & housekeeping_overridden ) )
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
{
2018-12-03 12:05:56 +03:00
if ( static_branch_unlikely ( & housekeeping_overridden ) )
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 ;
2018-12-03 12:05:56 +03:00
static_branch_enable ( & housekeeping_overridden ) ;
2017-10-27 05:42:36 +03:00
2018-02-21 07:17:27 +03:00
if ( housekeeping_flags & HK_FLAG_TICK )
sched_tick_offload_init ( ) ;
2017-10-27 05:42:36 +03:00
/* 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 ;
2019-04-11 06:34:47 +03:00
cpumask_var_t tmp ;
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
}
2019-04-11 06:34:47 +03:00
alloc_bootmem_cpumask_var ( & tmp ) ;
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 ) ;
2019-04-11 06:34:47 +03:00
cpumask_andnot ( tmp , cpu_present_mask , non_housekeeping_mask ) ;
if ( cpumask_empty ( tmp ) ) {
pr_warn ( " Housekeeping: must include one present CPU, "
" using boot CPU:%d \n " , smp_processor_id ( ) ) ;
2019-02-12 12:27:01 +03:00
__cpumask_set_cpu ( smp_processor_id ( ) , housekeeping_mask ) ;
2019-04-11 06:34:47 +03:00
__cpumask_clear_cpu ( smp_processor_id ( ) , non_housekeeping_mask ) ;
}
2017-10-27 05:42:37 +03:00
} else {
2019-04-11 06:34:47 +03:00
cpumask_andnot ( tmp , cpu_present_mask , non_housekeeping_mask ) ;
if ( cpumask_empty ( tmp ) )
__cpumask_clear_cpu ( smp_processor_id ( ) , non_housekeeping_mask ) ;
2017-10-27 05:42:37 +03:00
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 ;
}
}
2019-04-11 06:34:47 +03:00
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 ;
2020-05-27 17:29:09 +03:00
flags = HK_FLAG_TICK | HK_FLAG_WQ | HK_FLAG_TIMER | HK_FLAG_RCU |
HK_FLAG_MISC | HK_FLAG_KTHREAD ;
2017-10-27 05:42:37 +03:00
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 ;
2020-04-04 01:35:17 +03:00
bool illegal = false ;
char * par ;
int len ;
2017-10-27 05:42:38 +03:00
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 ;
}
2020-01-20 12:16:25 +03:00
if ( ! strncmp ( str , " managed_irq, " , 12 ) ) {
str + = 12 ;
flags | = HK_FLAG_MANAGED_IRQ ;
continue ;
}
2020-04-04 01:35:17 +03:00
/*
* Skip unknown sub - parameter and validate that it is not
* containing an invalid character .
*/
for ( par = str , len = 0 ; * str & & * str ! = ' , ' ; str + + , len + + ) {
if ( ! isalpha ( * str ) & & * str ! = ' _ ' )
illegal = true ;
}
if ( illegal ) {
pr_warn ( " isolcpus: Invalid flag %.*s \n " , len , par ) ;
return 0 ;
}
pr_info ( " isolcpus: Skipped unknown flag %.*s \n " , len , par ) ;
str + + ;
2017-10-27 05:42:38 +03:00
}
/* 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 ) ;