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