xref: /openbmc/linux/kernel/sched/isolation.c (revision b181f7029bd71238ac2754ce7052dffd69432085)
1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
278634061SFrederic Weisbecker /*
378634061SFrederic Weisbecker  *  Housekeeping management. Manage the targets for routine code that can run on
478634061SFrederic Weisbecker  *  any CPU: unbound workqueues, timers, kthreads and any offloadable work.
578634061SFrederic Weisbecker  *
678634061SFrederic Weisbecker  * Copyright (C) 2017 Red Hat, Inc., Frederic Weisbecker
71bda3f80SFrederic Weisbecker  * Copyright (C) 2017-2018 SUSE, Frederic Weisbecker
878634061SFrederic Weisbecker  *
978634061SFrederic Weisbecker  */
1078634061SFrederic Weisbecker 
1104d4e665SFrederic Weisbecker enum hk_flags {
1204d4e665SFrederic Weisbecker 	HK_FLAG_TIMER		= BIT(HK_TYPE_TIMER),
1304d4e665SFrederic Weisbecker 	HK_FLAG_RCU		= BIT(HK_TYPE_RCU),
1404d4e665SFrederic Weisbecker 	HK_FLAG_MISC		= BIT(HK_TYPE_MISC),
1504d4e665SFrederic Weisbecker 	HK_FLAG_SCHED		= BIT(HK_TYPE_SCHED),
1604d4e665SFrederic Weisbecker 	HK_FLAG_TICK		= BIT(HK_TYPE_TICK),
1704d4e665SFrederic Weisbecker 	HK_FLAG_DOMAIN		= BIT(HK_TYPE_DOMAIN),
1804d4e665SFrederic Weisbecker 	HK_FLAG_WQ		= BIT(HK_TYPE_WQ),
1904d4e665SFrederic Weisbecker 	HK_FLAG_MANAGED_IRQ	= BIT(HK_TYPE_MANAGED_IRQ),
2004d4e665SFrederic Weisbecker 	HK_FLAG_KTHREAD		= BIT(HK_TYPE_KTHREAD),
2104d4e665SFrederic Weisbecker };
2204d4e665SFrederic Weisbecker 
23dfcb245eSIngo Molnar DEFINE_STATIC_KEY_FALSE(housekeeping_overridden);
24dfcb245eSIngo Molnar EXPORT_SYMBOL_GPL(housekeeping_overridden);
25ed3b362dSFrederic Weisbecker 
26ed3b362dSFrederic Weisbecker struct housekeeping {
27ed3b362dSFrederic Weisbecker 	cpumask_var_t cpumasks[HK_TYPE_MAX];
28ed3b362dSFrederic Weisbecker 	unsigned long flags;
29ed3b362dSFrederic Weisbecker };
30ed3b362dSFrederic Weisbecker 
31ed3b362dSFrederic Weisbecker static struct housekeeping housekeeping;
327e56a1cfSFrederic Weisbecker 
housekeeping_enabled(enum hk_type type)3304d4e665SFrederic Weisbecker bool housekeeping_enabled(enum hk_type type)
340c5f81daSWanpeng Li {
35ed3b362dSFrederic Weisbecker 	return !!(housekeeping.flags & BIT(type));
360c5f81daSWanpeng Li }
370c5f81daSWanpeng Li EXPORT_SYMBOL_GPL(housekeeping_enabled);
380c5f81daSWanpeng Li 
housekeeping_any_cpu(enum hk_type type)3904d4e665SFrederic Weisbecker int housekeeping_any_cpu(enum hk_type type)
407e56a1cfSFrederic Weisbecker {
41e0e8d491SWanpeng Li 	int cpu;
42e0e8d491SWanpeng Li 
43e0e8d491SWanpeng Li 	if (static_branch_unlikely(&housekeeping_overridden)) {
44ed3b362dSFrederic Weisbecker 		if (housekeeping.flags & BIT(type)) {
45ed3b362dSFrederic Weisbecker 			cpu = sched_numa_find_closest(housekeeping.cpumasks[type], smp_processor_id());
46e0e8d491SWanpeng Li 			if (cpu < nr_cpu_ids)
47e0e8d491SWanpeng Li 				return cpu;
48e0e8d491SWanpeng Li 
49ed3b362dSFrederic Weisbecker 			return cpumask_any_and(housekeeping.cpumasks[type], cpu_online_mask);
50e0e8d491SWanpeng Li 		}
51e0e8d491SWanpeng Li 	}
527e56a1cfSFrederic Weisbecker 	return smp_processor_id();
537e56a1cfSFrederic Weisbecker }
547e56a1cfSFrederic Weisbecker EXPORT_SYMBOL_GPL(housekeeping_any_cpu);
557e56a1cfSFrederic Weisbecker 
housekeeping_cpumask(enum hk_type type)5604d4e665SFrederic Weisbecker const struct cpumask *housekeeping_cpumask(enum hk_type type)
577e56a1cfSFrederic Weisbecker {
58dfcb245eSIngo Molnar 	if (static_branch_unlikely(&housekeeping_overridden))
59ed3b362dSFrederic Weisbecker 		if (housekeeping.flags & BIT(type))
60ed3b362dSFrederic Weisbecker 			return housekeeping.cpumasks[type];
617e56a1cfSFrederic Weisbecker 	return cpu_possible_mask;
627e56a1cfSFrederic Weisbecker }
637e56a1cfSFrederic Weisbecker EXPORT_SYMBOL_GPL(housekeeping_cpumask);
647e56a1cfSFrederic Weisbecker 
housekeeping_affine(struct task_struct * t,enum hk_type type)6504d4e665SFrederic Weisbecker void housekeeping_affine(struct task_struct *t, enum hk_type type)
667e56a1cfSFrederic Weisbecker {
67dfcb245eSIngo Molnar 	if (static_branch_unlikely(&housekeeping_overridden))
68ed3b362dSFrederic Weisbecker 		if (housekeeping.flags & BIT(type))
69ed3b362dSFrederic Weisbecker 			set_cpus_allowed_ptr(t, housekeeping.cpumasks[type]);
707e56a1cfSFrederic Weisbecker }
717e56a1cfSFrederic Weisbecker EXPORT_SYMBOL_GPL(housekeeping_affine);
727e56a1cfSFrederic Weisbecker 
housekeeping_test_cpu(int cpu,enum hk_type type)7304d4e665SFrederic Weisbecker bool housekeeping_test_cpu(int cpu, enum hk_type type)
747e56a1cfSFrederic Weisbecker {
75dfcb245eSIngo Molnar 	if (static_branch_unlikely(&housekeeping_overridden))
76ed3b362dSFrederic Weisbecker 		if (housekeeping.flags & BIT(type))
77ed3b362dSFrederic Weisbecker 			return cpumask_test_cpu(cpu, housekeeping.cpumasks[type]);
787e56a1cfSFrederic Weisbecker 	return true;
797e56a1cfSFrederic Weisbecker }
807e56a1cfSFrederic Weisbecker EXPORT_SYMBOL_GPL(housekeeping_test_cpu);
8178634061SFrederic Weisbecker 
housekeeping_init(void)8278634061SFrederic Weisbecker void __init housekeeping_init(void)
8378634061SFrederic Weisbecker {
84ed3b362dSFrederic Weisbecker 	enum hk_type type;
85ed3b362dSFrederic Weisbecker 
86ed3b362dSFrederic Weisbecker 	if (!housekeeping.flags)
8778634061SFrederic Weisbecker 		return;
8878634061SFrederic Weisbecker 
89dfcb245eSIngo Molnar 	static_branch_enable(&housekeeping_overridden);
90e179f5a0SFrederic Weisbecker 
91ed3b362dSFrederic Weisbecker 	if (housekeeping.flags & HK_FLAG_TICK)
92d84b3131SFrederic Weisbecker 		sched_tick_offload_init();
93d84b3131SFrederic Weisbecker 
94ed3b362dSFrederic Weisbecker 	for_each_set_bit(type, &housekeeping.flags, HK_TYPE_MAX) {
9578634061SFrederic Weisbecker 		/* We need at least one CPU to handle housekeeping work */
96ed3b362dSFrederic Weisbecker 		WARN_ON_ONCE(cpumask_empty(housekeeping.cpumasks[type]));
97ed3b362dSFrederic Weisbecker 	}
9878634061SFrederic Weisbecker }
996f1982feSFrederic Weisbecker 
housekeeping_setup_type(enum hk_type type,cpumask_var_t housekeeping_staging)100ed3b362dSFrederic Weisbecker static void __init housekeeping_setup_type(enum hk_type type,
101ed3b362dSFrederic Weisbecker 					   cpumask_var_t housekeeping_staging)
102ed3b362dSFrederic Weisbecker {
103ed3b362dSFrederic Weisbecker 
104ed3b362dSFrederic Weisbecker 	alloc_bootmem_cpumask_var(&housekeeping.cpumasks[type]);
105ed3b362dSFrederic Weisbecker 	cpumask_copy(housekeeping.cpumasks[type],
106ed3b362dSFrederic Weisbecker 		     housekeeping_staging);
107ed3b362dSFrederic Weisbecker }
108ed3b362dSFrederic Weisbecker 
housekeeping_setup(char * str,unsigned long flags)109ed3b362dSFrederic Weisbecker static int __init housekeeping_setup(char *str, unsigned long flags)
1106f1982feSFrederic Weisbecker {
1116367b600SFrederic Weisbecker 	cpumask_var_t non_housekeeping_mask, housekeeping_staging;
112*02580c6aSOleg Nesterov 	unsigned int first_cpu;
1130cd3e59dSFrederic Weisbecker 	int err = 0;
1146f1982feSFrederic Weisbecker 
115ed3b362dSFrederic Weisbecker 	if ((flags & HK_FLAG_TICK) && !(housekeeping.flags & HK_FLAG_TICK)) {
11665e53f86SFrederic Weisbecker 		if (!IS_ENABLED(CONFIG_NO_HZ_FULL)) {
11765e53f86SFrederic Weisbecker 			pr_warn("Housekeeping: nohz unsupported."
11865e53f86SFrederic Weisbecker 				" Build with CONFIG_NO_HZ_FULL\n");
11965e53f86SFrederic Weisbecker 			return 0;
12065e53f86SFrederic Weisbecker 		}
12165e53f86SFrederic Weisbecker 	}
12265e53f86SFrederic Weisbecker 
1236f1982feSFrederic Weisbecker 	alloc_bootmem_cpumask_var(&non_housekeeping_mask);
124915a2bc3SPaul Gortmaker 	if (cpulist_parse(str, non_housekeeping_mask) < 0) {
125edb93821SFrederic Weisbecker 		pr_warn("Housekeeping: nohz_full= or isolcpus= incorrect CPU range\n");
1260cd3e59dSFrederic Weisbecker 		goto free_non_housekeeping_mask;
1276f1982feSFrederic Weisbecker 	}
1286f1982feSFrederic Weisbecker 
1296367b600SFrederic Weisbecker 	alloc_bootmem_cpumask_var(&housekeeping_staging);
1306367b600SFrederic Weisbecker 	cpumask_andnot(housekeeping_staging,
131edb93821SFrederic Weisbecker 		       cpu_possible_mask, non_housekeeping_mask);
1326f1982feSFrederic Weisbecker 
133*02580c6aSOleg Nesterov 	first_cpu = cpumask_first_and(cpu_present_mask, housekeeping_staging);
134*02580c6aSOleg Nesterov 	if (first_cpu >= nr_cpu_ids || first_cpu >= setup_max_cpus) {
1356367b600SFrederic Weisbecker 		__cpumask_set_cpu(smp_processor_id(), housekeeping_staging);
1366367b600SFrederic Weisbecker 		__cpumask_clear_cpu(smp_processor_id(), non_housekeeping_mask);
137ed3b362dSFrederic Weisbecker 		if (!housekeeping.flags) {
1389219565aSNicholas Piggin 			pr_warn("Housekeeping: must include one present CPU, "
1399219565aSNicholas Piggin 				"using boot CPU:%d\n", smp_processor_id());
1409219565aSNicholas Piggin 		}
1416367b600SFrederic Weisbecker 	}
1426367b600SFrederic Weisbecker 
143*02580c6aSOleg Nesterov 	if (cpumask_empty(non_housekeeping_mask))
144*02580c6aSOleg Nesterov 		goto free_housekeeping_staging;
145*02580c6aSOleg Nesterov 
146ed3b362dSFrederic Weisbecker 	if (!housekeeping.flags) {
147ed3b362dSFrederic Weisbecker 		/* First setup call ("nohz_full=" or "isolcpus=") */
148ed3b362dSFrederic Weisbecker 		enum hk_type type;
149ed3b362dSFrederic Weisbecker 
150ed3b362dSFrederic Weisbecker 		for_each_set_bit(type, &flags, HK_TYPE_MAX)
151ed3b362dSFrederic Weisbecker 			housekeeping_setup_type(type, housekeeping_staging);
1529219565aSNicholas Piggin 	} else {
153ed3b362dSFrederic Weisbecker 		/* Second setup call ("nohz_full=" after "isolcpus=" or the reverse) */
154ed3b362dSFrederic Weisbecker 		enum hk_type type;
155ed3b362dSFrederic Weisbecker 		unsigned long iter_flags = flags & housekeeping.flags;
156ed3b362dSFrederic Weisbecker 
157ed3b362dSFrederic Weisbecker 		for_each_set_bit(type, &iter_flags, HK_TYPE_MAX) {
158ed3b362dSFrederic Weisbecker 			if (!cpumask_equal(housekeeping_staging,
159ed3b362dSFrederic Weisbecker 					   housekeeping.cpumasks[type])) {
160edb93821SFrederic Weisbecker 				pr_warn("Housekeeping: nohz_full= must match isolcpus=\n");
1610cd3e59dSFrederic Weisbecker 				goto free_housekeeping_staging;
162edb93821SFrederic Weisbecker 			}
163edb93821SFrederic Weisbecker 		}
1646367b600SFrederic Weisbecker 
165ed3b362dSFrederic Weisbecker 		iter_flags = flags & ~housekeeping.flags;
166ed3b362dSFrederic Weisbecker 
167ed3b362dSFrederic Weisbecker 		for_each_set_bit(type, &iter_flags, HK_TYPE_MAX)
168ed3b362dSFrederic Weisbecker 			housekeeping_setup_type(type, housekeeping_staging);
169ed3b362dSFrederic Weisbecker 	}
170ed3b362dSFrederic Weisbecker 
171ed3b362dSFrederic Weisbecker 	if ((flags & HK_FLAG_TICK) && !(housekeeping.flags & HK_FLAG_TICK))
1726f1982feSFrederic Weisbecker 		tick_nohz_full_setup(non_housekeeping_mask);
173edb93821SFrederic Weisbecker 
174ed3b362dSFrederic Weisbecker 	housekeeping.flags |= flags;
1750cd3e59dSFrederic Weisbecker 	err = 1;
1766f1982feSFrederic Weisbecker 
1770cd3e59dSFrederic Weisbecker free_housekeeping_staging:
1780cd3e59dSFrederic Weisbecker 	free_bootmem_cpumask_var(housekeeping_staging);
1790cd3e59dSFrederic Weisbecker free_non_housekeeping_mask:
1806f1982feSFrederic Weisbecker 	free_bootmem_cpumask_var(non_housekeeping_mask);
1816f1982feSFrederic Weisbecker 
1820cd3e59dSFrederic Weisbecker 	return err;
1836f1982feSFrederic Weisbecker }
184edb93821SFrederic Weisbecker 
housekeeping_nohz_full_setup(char * str)185edb93821SFrederic Weisbecker static int __init housekeeping_nohz_full_setup(char *str)
186edb93821SFrederic Weisbecker {
187ed3b362dSFrederic Weisbecker 	unsigned long flags;
188edb93821SFrederic Weisbecker 
1899cc5b865SMarcelo Tosatti 	flags = HK_FLAG_TICK | HK_FLAG_WQ | HK_FLAG_TIMER | HK_FLAG_RCU |
1909cc5b865SMarcelo Tosatti 		HK_FLAG_MISC | HK_FLAG_KTHREAD;
191edb93821SFrederic Weisbecker 
192edb93821SFrederic Weisbecker 	return housekeeping_setup(str, flags);
193edb93821SFrederic Weisbecker }
1946f1982feSFrederic Weisbecker __setup("nohz_full=", housekeeping_nohz_full_setup);
195edb93821SFrederic Weisbecker 
housekeeping_isolcpus_setup(char * str)196edb93821SFrederic Weisbecker static int __init housekeeping_isolcpus_setup(char *str)
197edb93821SFrederic Weisbecker {
198ed3b362dSFrederic Weisbecker 	unsigned long flags = 0;
1993662daf0SPeter Xu 	bool illegal = false;
2003662daf0SPeter Xu 	char *par;
2013662daf0SPeter Xu 	int len;
202150dfee9SFrederic Weisbecker 
203150dfee9SFrederic Weisbecker 	while (isalpha(*str)) {
204150dfee9SFrederic Weisbecker 		if (!strncmp(str, "nohz,", 5)) {
205150dfee9SFrederic Weisbecker 			str += 5;
206150dfee9SFrederic Weisbecker 			flags |= HK_FLAG_TICK;
207150dfee9SFrederic Weisbecker 			continue;
208150dfee9SFrederic Weisbecker 		}
209150dfee9SFrederic Weisbecker 
210150dfee9SFrederic Weisbecker 		if (!strncmp(str, "domain,", 7)) {
211150dfee9SFrederic Weisbecker 			str += 7;
212150dfee9SFrederic Weisbecker 			flags |= HK_FLAG_DOMAIN;
213150dfee9SFrederic Weisbecker 			continue;
214150dfee9SFrederic Weisbecker 		}
215150dfee9SFrederic Weisbecker 
21611ea68f5SMing Lei 		if (!strncmp(str, "managed_irq,", 12)) {
21711ea68f5SMing Lei 			str += 12;
21811ea68f5SMing Lei 			flags |= HK_FLAG_MANAGED_IRQ;
21911ea68f5SMing Lei 			continue;
22011ea68f5SMing Lei 		}
22111ea68f5SMing Lei 
2223662daf0SPeter Xu 		/*
2233662daf0SPeter Xu 		 * Skip unknown sub-parameter and validate that it is not
2243662daf0SPeter Xu 		 * containing an invalid character.
2253662daf0SPeter Xu 		 */
2263662daf0SPeter Xu 		for (par = str, len = 0; *str && *str != ','; str++, len++) {
2273662daf0SPeter Xu 			if (!isalpha(*str) && *str != '_')
2283662daf0SPeter Xu 				illegal = true;
2293662daf0SPeter Xu 		}
2303662daf0SPeter Xu 
2313662daf0SPeter Xu 		if (illegal) {
2323662daf0SPeter Xu 			pr_warn("isolcpus: Invalid flag %.*s\n", len, par);
233150dfee9SFrederic Weisbecker 			return 0;
234150dfee9SFrederic Weisbecker 		}
235150dfee9SFrederic Weisbecker 
2363662daf0SPeter Xu 		pr_info("isolcpus: Skipped unknown flag %.*s\n", len, par);
2373662daf0SPeter Xu 		str++;
2383662daf0SPeter Xu 	}
2393662daf0SPeter Xu 
240150dfee9SFrederic Weisbecker 	/* Default behaviour for isolcpus without flags */
241150dfee9SFrederic Weisbecker 	if (!flags)
242150dfee9SFrederic Weisbecker 		flags |= HK_FLAG_DOMAIN;
243150dfee9SFrederic Weisbecker 
244150dfee9SFrederic Weisbecker 	return housekeeping_setup(str, flags);
245edb93821SFrederic Weisbecker }
246edb93821SFrederic Weisbecker __setup("isolcpus=", housekeeping_isolcpus_setup);
247