percpu.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. /*
  2. * Copyright (c) 2014-2017 Richard Braun.
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. #include <assert.h>
  18. #include <errno.h>
  19. #include <stddef.h>
  20. #include <stdint.h>
  21. #include <string.h>
  22. #include <kern/init.h>
  23. #include <kern/log.h>
  24. #include <kern/macros.h>
  25. #include <kern/panic.h>
  26. #include <kern/percpu.h>
  27. #include <kern/slist.h>
  28. #include <machine/cpu.h>
  29. #include <vm/kmem.h>
  30. #include <vm/page.h>
  31. void *percpu_areas[CONFIG_MAX_CPUS] __read_mostly;
  32. static void *percpu_area_content __initdata;
  33. static size_t percpu_area_size __initdata;
  34. static int percpu_skip_warning __initdata;
  35. static struct slist percpu_ops __initdata;
  36. static void __init
  37. percpu_op_run (const struct percpu_op *op)
  38. {
  39. op->fn ();
  40. }
  41. static int __init
  42. percpu_bootstrap (void)
  43. {
  44. percpu_areas[0] = &_percpu;
  45. return (0);
  46. }
  47. INIT_OP_DEFINE (percpu_bootstrap);
  48. static int __init
  49. percpu_setup (void)
  50. {
  51. slist_init (&percpu_ops);
  52. percpu_area_size = &_percpu_end - &_percpu;
  53. log_info ("percpu: max_cpus: %u, section size: %zuk",
  54. CONFIG_MAX_CPUS, percpu_area_size >> 10);
  55. assert (vm_page_aligned (percpu_area_size));
  56. if (! percpu_area_size)
  57. return (0);
  58. uint32_t order = vm_page_order (percpu_area_size);
  59. _Auto page = vm_page_alloc (order, VM_PAGE_SEL_DIRECTMAP, VM_PAGE_KERNEL, 0);
  60. if (! page)
  61. panic ("percpu: unable to allocate memory for percpu area content");
  62. percpu_area_content = vm_page_direct_ptr (page);
  63. memcpy (percpu_area_content, &_percpu, percpu_area_size);
  64. return (0);
  65. }
  66. INIT_OP_DEFINE (percpu_setup,
  67. INIT_OP_DEP (percpu_bootstrap, true),
  68. INIT_OP_DEP (vm_page_setup, true));
  69. void __init
  70. percpu_register_op (struct percpu_op *op)
  71. {
  72. slist_insert_tail (&percpu_ops, &op->node);
  73. // Run on BSP.
  74. percpu_op_run (op);
  75. }
  76. int __init
  77. percpu_add (uint32_t cpu)
  78. {
  79. if (cpu >= ARRAY_SIZE (percpu_areas))
  80. {
  81. if (!percpu_skip_warning)
  82. {
  83. log_warning ("percpu: ignoring processor beyond id %zu",
  84. ARRAY_SIZE (percpu_areas) - 1);
  85. percpu_skip_warning = 1;
  86. }
  87. return (EINVAL);
  88. }
  89. else if (percpu_areas[cpu])
  90. {
  91. log_err ("percpu: id %u ignored, already registered", cpu);
  92. return (EINVAL);
  93. }
  94. if (! percpu_area_size)
  95. goto out;
  96. unsigned int order = vm_page_order (percpu_area_size);
  97. _Auto page = vm_page_alloc (order, VM_PAGE_SEL_DIRECTMAP, VM_PAGE_KERNEL, 0);
  98. if (! page)
  99. {
  100. log_err ("percpu: unable to allocate percpu area");
  101. return (ENOMEM);
  102. }
  103. percpu_areas[cpu] = vm_page_direct_ptr (page);
  104. memcpy (percpu_area (cpu), percpu_area_content, percpu_area_size);
  105. out:
  106. return (0);
  107. }
  108. void __init
  109. percpu_ap_setup (void)
  110. {
  111. struct percpu_op *op;
  112. slist_for_each_entry (&percpu_ops, op, node)
  113. percpu_op_run (op);
  114. }
  115. static int __init
  116. percpu_cleanup (void)
  117. {
  118. uintptr_t va = (uintptr_t) percpu_area_content;
  119. _Auto page = vm_page_lookup (vm_page_direct_pa (va));
  120. vm_page_free (page, vm_page_order (percpu_area_size), 0);
  121. return (0);
  122. }
  123. INIT_OP_DEFINE (percpu_cleanup,
  124. INIT_OP_DEP (cpu_mp_probe, true),
  125. INIT_OP_DEP (percpu_setup, true),
  126. INIT_OP_DEP (vm_page_setup, true));