percpu.c 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  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/vm_kmem.h>
  30. #include <vm/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. struct vm_page *page;
  52. unsigned int order;
  53. slist_init(&percpu_ops);
  54. percpu_area_size = &_percpu_end - &_percpu;
  55. log_info("percpu: max_cpus: %u, section size: %zuk", CONFIG_MAX_CPUS,
  56. percpu_area_size >> 10);
  57. assert(vm_page_aligned(percpu_area_size));
  58. if (percpu_area_size == 0) {
  59. return 0;
  60. }
  61. order = vm_page_order(percpu_area_size);
  62. page = vm_page_alloc(order, VM_PAGE_SEL_DIRECTMAP, VM_PAGE_KERNEL);
  63. if (page == NULL) {
  64. panic("percpu: unable to allocate memory for percpu area content");
  65. }
  66. percpu_area_content = vm_page_direct_ptr(page);
  67. memcpy(percpu_area_content, &_percpu, percpu_area_size);
  68. return 0;
  69. }
  70. INIT_OP_DEFINE(percpu_setup,
  71. INIT_OP_DEP(percpu_bootstrap, true),
  72. INIT_OP_DEP(vm_page_setup, true));
  73. void __init
  74. percpu_register_op(struct percpu_op *op)
  75. {
  76. slist_insert_tail(&percpu_ops, &op->node);
  77. /* Run on BSP */
  78. percpu_op_run(op);
  79. }
  80. int __init
  81. percpu_add(unsigned int cpu)
  82. {
  83. struct vm_page *page;
  84. unsigned int order;
  85. if (cpu >= ARRAY_SIZE(percpu_areas)) {
  86. if (!percpu_skip_warning) {
  87. log_warning("percpu: ignoring processor beyond id %zu",
  88. ARRAY_SIZE(percpu_areas) - 1);
  89. percpu_skip_warning = 1;
  90. }
  91. return EINVAL;
  92. }
  93. if (percpu_areas[cpu] != NULL) {
  94. log_err("percpu: id %u ignored, already registered", cpu);
  95. return EINVAL;
  96. }
  97. if (percpu_area_size == 0) {
  98. goto out;
  99. }
  100. order = vm_page_order(percpu_area_size);
  101. page = vm_page_alloc(order, VM_PAGE_SEL_DIRECTMAP, VM_PAGE_KERNEL);
  102. if (page == NULL) {
  103. log_err("percpu: unable to allocate percpu area");
  104. return ENOMEM;
  105. }
  106. percpu_areas[cpu] = vm_page_direct_ptr(page);
  107. memcpy(percpu_area(cpu), percpu_area_content, percpu_area_size);
  108. out:
  109. return 0;
  110. }
  111. void __init
  112. percpu_ap_setup(void)
  113. {
  114. struct percpu_op *op;
  115. slist_for_each_entry(&percpu_ops, op, node) {
  116. percpu_op_run(op);
  117. }
  118. }
  119. static int __init
  120. percpu_cleanup(void)
  121. {
  122. struct vm_page *page;
  123. uintptr_t va;
  124. va = (uintptr_t)percpu_area_content;
  125. page = vm_page_lookup(vm_page_direct_pa(va));
  126. vm_page_free(page, vm_page_order(percpu_area_size));
  127. return 0;
  128. }
  129. INIT_OP_DEFINE(percpu_cleanup,
  130. INIT_OP_DEP(cpu_mp_probe, true),
  131. INIT_OP_DEP(percpu_setup, true),
  132. INIT_OP_DEP(vm_page_setup, true));