isolation.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Housekeeping management. Manage the targets for routine code that can run on
  4. * any CPU: unbound workqueues, timers, kthreads and any offloadable work.
  5. *
  6. * Copyright (C) 2017 Red Hat, Inc., Frederic Weisbecker
  7. * Copyright (C) 2017-2018 SUSE, Frederic Weisbecker
  8. *
  9. */
  10. enum hk_flags {
  11. HK_FLAG_TIMER = BIT(HK_TYPE_TIMER),
  12. HK_FLAG_RCU = BIT(HK_TYPE_RCU),
  13. HK_FLAG_MISC = BIT(HK_TYPE_MISC),
  14. HK_FLAG_SCHED = BIT(HK_TYPE_SCHED),
  15. HK_FLAG_TICK = BIT(HK_TYPE_TICK),
  16. HK_FLAG_DOMAIN = BIT(HK_TYPE_DOMAIN),
  17. HK_FLAG_WQ = BIT(HK_TYPE_WQ),
  18. HK_FLAG_MANAGED_IRQ = BIT(HK_TYPE_MANAGED_IRQ),
  19. HK_FLAG_KTHREAD = BIT(HK_TYPE_KTHREAD),
  20. };
  21. DEFINE_STATIC_KEY_FALSE(housekeeping_overridden);
  22. EXPORT_SYMBOL_GPL(housekeeping_overridden);
  23. struct housekeeping {
  24. cpumask_var_t cpumasks[HK_TYPE_MAX];
  25. unsigned long flags;
  26. };
  27. static struct housekeeping housekeeping;
  28. bool housekeeping_enabled(enum hk_type type)
  29. {
  30. return !!(housekeeping.flags & BIT(type));
  31. }
  32. EXPORT_SYMBOL_GPL(housekeeping_enabled);
  33. int housekeeping_any_cpu(enum hk_type type)
  34. {
  35. int cpu;
  36. if (static_branch_unlikely(&housekeeping_overridden)) {
  37. if (housekeeping.flags & BIT(type)) {
  38. cpu = sched_numa_find_closest(housekeeping.cpumasks[type], smp_processor_id());
  39. if (cpu < nr_cpu_ids)
  40. return cpu;
  41. cpu = cpumask_any_and(housekeeping.cpumasks[type], cpu_online_mask);
  42. if (likely(cpu < nr_cpu_ids))
  43. return cpu;
  44. /*
  45. * Unless we have another problem this can only happen
  46. * at boot time before start_secondary() brings the 1st
  47. * housekeeping CPU up.
  48. */
  49. WARN_ON_ONCE(system_state == SYSTEM_RUNNING ||
  50. type != HK_TYPE_TIMER);
  51. }
  52. }
  53. return smp_processor_id();
  54. }
  55. EXPORT_SYMBOL_GPL(housekeeping_any_cpu);
  56. const struct cpumask *housekeeping_cpumask(enum hk_type type)
  57. {
  58. if (static_branch_unlikely(&housekeeping_overridden))
  59. if (housekeeping.flags & BIT(type))
  60. return housekeeping.cpumasks[type];
  61. return cpu_possible_mask;
  62. }
  63. EXPORT_SYMBOL_GPL(housekeeping_cpumask);
  64. void housekeeping_affine(struct task_struct *t, enum hk_type type)
  65. {
  66. if (static_branch_unlikely(&housekeeping_overridden))
  67. if (housekeeping.flags & BIT(type))
  68. set_cpus_allowed_ptr(t, housekeeping.cpumasks[type]);
  69. }
  70. EXPORT_SYMBOL_GPL(housekeeping_affine);
  71. bool housekeeping_test_cpu(int cpu, enum hk_type type)
  72. {
  73. if (static_branch_unlikely(&housekeeping_overridden))
  74. if (housekeeping.flags & BIT(type))
  75. return cpumask_test_cpu(cpu, housekeeping.cpumasks[type]);
  76. return true;
  77. }
  78. EXPORT_SYMBOL_GPL(housekeeping_test_cpu);
  79. void __init housekeeping_init(void)
  80. {
  81. enum hk_type type;
  82. if (!housekeeping.flags)
  83. return;
  84. static_branch_enable(&housekeeping_overridden);
  85. if (housekeeping.flags & HK_FLAG_TICK)
  86. sched_tick_offload_init();
  87. for_each_set_bit(type, &housekeeping.flags, HK_TYPE_MAX) {
  88. /* We need at least one CPU to handle housekeeping work */
  89. WARN_ON_ONCE(cpumask_empty(housekeeping.cpumasks[type]));
  90. }
  91. }
  92. static void __init housekeeping_setup_type(enum hk_type type,
  93. cpumask_var_t housekeeping_staging)
  94. {
  95. alloc_bootmem_cpumask_var(&housekeeping.cpumasks[type]);
  96. cpumask_copy(housekeeping.cpumasks[type],
  97. housekeeping_staging);
  98. }
  99. static int __init housekeeping_setup(char *str, unsigned long flags)
  100. {
  101. cpumask_var_t non_housekeeping_mask, housekeeping_staging;
  102. unsigned int first_cpu;
  103. int err = 0;
  104. if ((flags & HK_FLAG_TICK) && !(housekeeping.flags & HK_FLAG_TICK)) {
  105. if (!IS_ENABLED(CONFIG_NO_HZ_FULL)) {
  106. pr_warn("Housekeeping: nohz unsupported."
  107. " Build with CONFIG_NO_HZ_FULL\n");
  108. return 0;
  109. }
  110. }
  111. alloc_bootmem_cpumask_var(&non_housekeeping_mask);
  112. if (cpulist_parse(str, non_housekeeping_mask) < 0) {
  113. pr_warn("Housekeeping: nohz_full= or isolcpus= incorrect CPU range\n");
  114. goto free_non_housekeeping_mask;
  115. }
  116. alloc_bootmem_cpumask_var(&housekeeping_staging);
  117. cpumask_andnot(housekeeping_staging,
  118. cpu_possible_mask, non_housekeeping_mask);
  119. first_cpu = cpumask_first_and(cpu_present_mask, housekeeping_staging);
  120. if (first_cpu >= nr_cpu_ids || first_cpu >= setup_max_cpus) {
  121. __cpumask_set_cpu(smp_processor_id(), housekeeping_staging);
  122. __cpumask_clear_cpu(smp_processor_id(), non_housekeeping_mask);
  123. if (!housekeeping.flags) {
  124. pr_warn("Housekeeping: must include one present CPU, "
  125. "using boot CPU:%d\n", smp_processor_id());
  126. }
  127. }
  128. if (cpumask_empty(non_housekeeping_mask))
  129. goto free_housekeeping_staging;
  130. if (!housekeeping.flags) {
  131. /* First setup call ("nohz_full=" or "isolcpus=") */
  132. enum hk_type type;
  133. for_each_set_bit(type, &flags, HK_TYPE_MAX)
  134. housekeeping_setup_type(type, housekeeping_staging);
  135. } else {
  136. /* Second setup call ("nohz_full=" after "isolcpus=" or the reverse) */
  137. enum hk_type type;
  138. unsigned long iter_flags = flags & housekeeping.flags;
  139. for_each_set_bit(type, &iter_flags, HK_TYPE_MAX) {
  140. if (!cpumask_equal(housekeeping_staging,
  141. housekeeping.cpumasks[type])) {
  142. pr_warn("Housekeeping: nohz_full= must match isolcpus=\n");
  143. goto free_housekeeping_staging;
  144. }
  145. }
  146. iter_flags = flags & ~housekeeping.flags;
  147. for_each_set_bit(type, &iter_flags, HK_TYPE_MAX)
  148. housekeeping_setup_type(type, housekeeping_staging);
  149. }
  150. if ((flags & HK_FLAG_TICK) && !(housekeeping.flags & HK_FLAG_TICK))
  151. tick_nohz_full_setup(non_housekeeping_mask);
  152. housekeeping.flags |= flags;
  153. err = 1;
  154. free_housekeeping_staging:
  155. free_bootmem_cpumask_var(housekeeping_staging);
  156. free_non_housekeeping_mask:
  157. free_bootmem_cpumask_var(non_housekeeping_mask);
  158. return err;
  159. }
  160. static int __init housekeeping_nohz_full_setup(char *str)
  161. {
  162. unsigned long flags;
  163. flags = HK_FLAG_TICK | HK_FLAG_WQ | HK_FLAG_TIMER | HK_FLAG_RCU |
  164. HK_FLAG_MISC | HK_FLAG_KTHREAD;
  165. return housekeeping_setup(str, flags);
  166. }
  167. __setup("nohz_full=", housekeeping_nohz_full_setup);
  168. static int __init housekeeping_isolcpus_setup(char *str)
  169. {
  170. unsigned long flags = 0;
  171. bool illegal = false;
  172. char *par;
  173. int len;
  174. while (isalpha(*str)) {
  175. if (!strncmp(str, "nohz,", 5)) {
  176. str += 5;
  177. flags |= HK_FLAG_TICK;
  178. continue;
  179. }
  180. if (!strncmp(str, "domain,", 7)) {
  181. str += 7;
  182. flags |= HK_FLAG_DOMAIN;
  183. continue;
  184. }
  185. if (!strncmp(str, "managed_irq,", 12)) {
  186. str += 12;
  187. flags |= HK_FLAG_MANAGED_IRQ;
  188. continue;
  189. }
  190. /*
  191. * Skip unknown sub-parameter and validate that it is not
  192. * containing an invalid character.
  193. */
  194. for (par = str, len = 0; *str && *str != ','; str++, len++) {
  195. if (!isalpha(*str) && *str != '_')
  196. illegal = true;
  197. }
  198. if (illegal) {
  199. pr_warn("isolcpus: Invalid flag %.*s\n", len, par);
  200. return 0;
  201. }
  202. pr_info("isolcpus: Skipped unknown flag %.*s\n", len, par);
  203. str++;
  204. }
  205. /* Default behaviour for isolcpus without flags */
  206. if (!flags)
  207. flags |= HK_FLAG_DOMAIN;
  208. return housekeeping_setup(str, flags);
  209. }
  210. __setup("isolcpus=", housekeeping_isolcpus_setup);