xref: /openbmc/linux/kernel/sched/isolation.c (revision 5927145e)
1 /*
2  *  Housekeeping management. Manage the targets for routine code that can run on
3  *  any CPU: unbound workqueues, timers, kthreads and any offloadable work.
4  *
5  * Copyright (C) 2017 Red Hat, Inc., Frederic Weisbecker
6  *
7  */
8 
9 #include <linux/sched/isolation.h>
10 #include <linux/tick.h>
11 #include <linux/init.h>
12 #include <linux/kernel.h>
13 #include <linux/static_key.h>
14 #include <linux/ctype.h>
15 
16 DEFINE_STATIC_KEY_FALSE(housekeeping_overriden);
17 EXPORT_SYMBOL_GPL(housekeeping_overriden);
18 static cpumask_var_t housekeeping_mask;
19 static unsigned int housekeeping_flags;
20 
21 int housekeeping_any_cpu(enum hk_flags flags)
22 {
23 	if (static_branch_unlikely(&housekeeping_overriden))
24 		if (housekeeping_flags & flags)
25 			return cpumask_any_and(housekeeping_mask, cpu_online_mask);
26 	return smp_processor_id();
27 }
28 EXPORT_SYMBOL_GPL(housekeeping_any_cpu);
29 
30 const struct cpumask *housekeeping_cpumask(enum hk_flags flags)
31 {
32 	if (static_branch_unlikely(&housekeeping_overriden))
33 		if (housekeeping_flags & flags)
34 			return housekeeping_mask;
35 	return cpu_possible_mask;
36 }
37 EXPORT_SYMBOL_GPL(housekeeping_cpumask);
38 
39 void housekeeping_affine(struct task_struct *t, enum hk_flags flags)
40 {
41 	if (static_branch_unlikely(&housekeeping_overriden))
42 		if (housekeeping_flags & flags)
43 			set_cpus_allowed_ptr(t, housekeeping_mask);
44 }
45 EXPORT_SYMBOL_GPL(housekeeping_affine);
46 
47 bool housekeeping_test_cpu(int cpu, enum hk_flags flags)
48 {
49 	if (static_branch_unlikely(&housekeeping_overriden))
50 		if (housekeeping_flags & flags)
51 			return cpumask_test_cpu(cpu, housekeeping_mask);
52 	return true;
53 }
54 EXPORT_SYMBOL_GPL(housekeeping_test_cpu);
55 
56 void __init housekeeping_init(void)
57 {
58 	if (!housekeeping_flags)
59 		return;
60 
61 	static_branch_enable(&housekeeping_overriden);
62 
63 	/* We need at least one CPU to handle housekeeping work */
64 	WARN_ON_ONCE(cpumask_empty(housekeeping_mask));
65 }
66 
67 static int __init housekeeping_setup(char *str, enum hk_flags flags)
68 {
69 	cpumask_var_t non_housekeeping_mask;
70 	int err;
71 
72 	alloc_bootmem_cpumask_var(&non_housekeeping_mask);
73 	err = cpulist_parse(str, non_housekeeping_mask);
74 	if (err < 0 || cpumask_last(non_housekeeping_mask) >= nr_cpu_ids) {
75 		pr_warn("Housekeeping: nohz_full= or isolcpus= incorrect CPU range\n");
76 		free_bootmem_cpumask_var(non_housekeeping_mask);
77 		return 0;
78 	}
79 
80 	if (!housekeeping_flags) {
81 		alloc_bootmem_cpumask_var(&housekeeping_mask);
82 		cpumask_andnot(housekeeping_mask,
83 			       cpu_possible_mask, non_housekeeping_mask);
84 		if (cpumask_empty(housekeeping_mask))
85 			cpumask_set_cpu(smp_processor_id(), housekeeping_mask);
86 	} else {
87 		cpumask_var_t tmp;
88 
89 		alloc_bootmem_cpumask_var(&tmp);
90 		cpumask_andnot(tmp, cpu_possible_mask, non_housekeeping_mask);
91 		if (!cpumask_equal(tmp, housekeeping_mask)) {
92 			pr_warn("Housekeeping: nohz_full= must match isolcpus=\n");
93 			free_bootmem_cpumask_var(tmp);
94 			free_bootmem_cpumask_var(non_housekeeping_mask);
95 			return 0;
96 		}
97 		free_bootmem_cpumask_var(tmp);
98 	}
99 
100 	if ((flags & HK_FLAG_TICK) && !(housekeeping_flags & HK_FLAG_TICK)) {
101 		if (IS_ENABLED(CONFIG_NO_HZ_FULL)) {
102 			tick_nohz_full_setup(non_housekeeping_mask);
103 		} else {
104 			pr_warn("Housekeeping: nohz unsupported."
105 				" Build with CONFIG_NO_HZ_FULL\n");
106 			free_bootmem_cpumask_var(non_housekeeping_mask);
107 			return 0;
108 		}
109 	}
110 
111 	housekeeping_flags |= flags;
112 
113 	free_bootmem_cpumask_var(non_housekeeping_mask);
114 
115 	return 1;
116 }
117 
118 static int __init housekeeping_nohz_full_setup(char *str)
119 {
120 	unsigned int flags;
121 
122 	flags = HK_FLAG_TICK | HK_FLAG_TIMER | HK_FLAG_RCU | HK_FLAG_MISC;
123 
124 	return housekeeping_setup(str, flags);
125 }
126 __setup("nohz_full=", housekeeping_nohz_full_setup);
127 
128 static int __init housekeeping_isolcpus_setup(char *str)
129 {
130 	unsigned int flags = 0;
131 
132 	while (isalpha(*str)) {
133 		if (!strncmp(str, "nohz,", 5)) {
134 			str += 5;
135 			flags |= HK_FLAG_TICK;
136 			continue;
137 		}
138 
139 		if (!strncmp(str, "domain,", 7)) {
140 			str += 7;
141 			flags |= HK_FLAG_DOMAIN;
142 			continue;
143 		}
144 
145 		pr_warn("isolcpus: Error, unknown flag\n");
146 		return 0;
147 	}
148 
149 	/* Default behaviour for isolcpus without flags */
150 	if (!flags)
151 		flags |= HK_FLAG_DOMAIN;
152 
153 	return housekeeping_setup(str, flags);
154 }
155 __setup("isolcpus=", housekeeping_isolcpus_setup);
156