![Frederic Weisbecker](/assets/img/avatar_default.png)
Add flags to control NOHZ and domain isolation from "isolcpus=", in order to centralize the isolation features to a common interface. Domain isolation remains the default so not to break the existing isolcpus boot paramater behaviour. Further flags in the future may include 0hz (1hz tick offload) and timers, workqueue, RCU, kthread, watchdog, likely all merged together in a common flag ("async"?). In any case, this will have to be modifiable by cpusets. Signed-off-by: Frederic Weisbecker <frederic@kernel.org> Acked-by: Thomas Gleixner <tglx@linutronix.de> Cc: Chris Metcalf <cmetcalf@mellanox.com> Cc: Christoph Lameter <cl@linux.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Luiz Capitulino <lcapitulino@redhat.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Rik van Riel <riel@redhat.com> Cc: Wanpeng Li <kernellwp@gmail.com> Link: http://lkml.kernel.org/r/1509072159-31808-12-git-send-email-frederic@kernel.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
156 lines
4 KiB
C
156 lines
4 KiB
C
/*
|
|
* 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>
|
|
#include <linux/static_key.h>
|
|
#include <linux/ctype.h>
|
|
|
|
DEFINE_STATIC_KEY_FALSE(housekeeping_overriden);
|
|
EXPORT_SYMBOL_GPL(housekeeping_overriden);
|
|
static cpumask_var_t housekeeping_mask;
|
|
static unsigned int housekeeping_flags;
|
|
|
|
int housekeeping_any_cpu(enum hk_flags flags)
|
|
{
|
|
if (static_branch_unlikely(&housekeeping_overriden))
|
|
if (housekeeping_flags & flags)
|
|
return cpumask_any_and(housekeeping_mask, cpu_online_mask);
|
|
return smp_processor_id();
|
|
}
|
|
EXPORT_SYMBOL_GPL(housekeeping_any_cpu);
|
|
|
|
const struct cpumask *housekeeping_cpumask(enum hk_flags flags)
|
|
{
|
|
if (static_branch_unlikely(&housekeeping_overriden))
|
|
if (housekeeping_flags & flags)
|
|
return housekeeping_mask;
|
|
return cpu_possible_mask;
|
|
}
|
|
EXPORT_SYMBOL_GPL(housekeeping_cpumask);
|
|
|
|
void housekeeping_affine(struct task_struct *t, enum hk_flags flags)
|
|
{
|
|
if (static_branch_unlikely(&housekeeping_overriden))
|
|
if (housekeeping_flags & flags)
|
|
set_cpus_allowed_ptr(t, housekeeping_mask);
|
|
}
|
|
EXPORT_SYMBOL_GPL(housekeeping_affine);
|
|
|
|
bool housekeeping_test_cpu(int cpu, enum hk_flags flags)
|
|
{
|
|
if (static_branch_unlikely(&housekeeping_overriden))
|
|
if (housekeeping_flags & flags)
|
|
return cpumask_test_cpu(cpu, housekeeping_mask);
|
|
return true;
|
|
}
|
|
EXPORT_SYMBOL_GPL(housekeeping_test_cpu);
|
|
|
|
void __init housekeeping_init(void)
|
|
{
|
|
if (!housekeeping_flags)
|
|
return;
|
|
|
|
static_branch_enable(&housekeeping_overriden);
|
|
|
|
/* We need at least one CPU to handle housekeeping work */
|
|
WARN_ON_ONCE(cpumask_empty(housekeeping_mask));
|
|
}
|
|
|
|
static int __init housekeeping_setup(char *str, enum hk_flags flags)
|
|
{
|
|
cpumask_var_t non_housekeeping_mask;
|
|
int err;
|
|
|
|
alloc_bootmem_cpumask_var(&non_housekeeping_mask);
|
|
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");
|
|
free_bootmem_cpumask_var(non_housekeeping_mask);
|
|
return 0;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
housekeeping_flags |= flags;
|
|
|
|
free_bootmem_cpumask_var(non_housekeeping_mask);
|
|
|
|
return 1;
|
|
}
|
|
|
|
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);
|
|
}
|
|
__setup("nohz_full=", housekeeping_nohz_full_setup);
|
|
|
|
static int __init housekeeping_isolcpus_setup(char *str)
|
|
{
|
|
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);
|
|
}
|
|
__setup("isolcpus=", housekeeping_isolcpus_setup);
|