xcall.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. /*
  2. * Copyright (c) 2014-2018 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 <stdalign.h>
  19. #include <stddef.h>
  20. #include <stdio.h>
  21. #include <kern/atomic.h>
  22. #include <kern/init.h>
  23. #include <kern/log.h>
  24. #include <kern/macros.h>
  25. #include <kern/percpu.h>
  26. #include <kern/spinlock.h>
  27. #include <kern/syscnt.h>
  28. #include <kern/thread.h>
  29. #include <kern/xcall.h>
  30. #include <machine/cpu.h>
  31. struct xcall {
  32. xcall_fn_t fn;
  33. void *arg;
  34. };
  35. /*
  36. * Per-CPU data.
  37. *
  38. * The lock is used to serialize cross-calls from different processors
  39. * to the same processor. It is held during the complete cross-call
  40. * sequence. Inside the critical section, accesses to the receive call
  41. * are used to enforce release-acquire ordering between the sending
  42. * and receiving processors.
  43. *
  44. * Locking keys :
  45. * (a) atomic
  46. * (c) cpu_data
  47. */
  48. struct xcall_cpu_data {
  49. alignas(CPU_L1_SIZE) struct spinlock lock;
  50. struct xcall *recv_call; /* (c) */
  51. struct syscnt sc_sent; /* (a) */
  52. struct syscnt sc_received; /* (a) */
  53. };
  54. static struct xcall_cpu_data xcall_cpu_data __percpu;
  55. static struct xcall_cpu_data *
  56. xcall_get_local_cpu_data(void)
  57. {
  58. return cpu_local_ptr(xcall_cpu_data);
  59. }
  60. static struct xcall_cpu_data *
  61. xcall_get_cpu_data(unsigned int cpu)
  62. {
  63. return percpu_ptr(xcall_cpu_data, cpu);
  64. }
  65. static void
  66. xcall_init(struct xcall *call, xcall_fn_t fn, void *arg)
  67. {
  68. call->fn = fn;
  69. call->arg = arg;
  70. }
  71. static void
  72. xcall_process(struct xcall *call)
  73. {
  74. call->fn(call->arg);
  75. }
  76. static void
  77. xcall_cpu_data_init(struct xcall_cpu_data *cpu_data, unsigned int cpu)
  78. {
  79. char name[SYSCNT_NAME_SIZE];
  80. snprintf(name, sizeof(name), "xcall_sent/%u", cpu);
  81. syscnt_register(&cpu_data->sc_sent, name);
  82. snprintf(name, sizeof(name), "xcall_received/%u", cpu);
  83. syscnt_register(&cpu_data->sc_received, name);
  84. cpu_data->recv_call = NULL;
  85. spinlock_init(&cpu_data->lock);
  86. }
  87. static struct xcall *
  88. xcall_cpu_data_get_recv_call(const struct xcall_cpu_data *cpu_data)
  89. {
  90. return atomic_load(&cpu_data->recv_call, ATOMIC_ACQUIRE);
  91. }
  92. static void
  93. xcall_cpu_data_set_recv_call(struct xcall_cpu_data *cpu_data,
  94. struct xcall *call)
  95. {
  96. atomic_store(&cpu_data->recv_call, call, ATOMIC_RELEASE);
  97. }
  98. static void
  99. xcall_cpu_data_clear_recv_call(struct xcall_cpu_data *cpu_data)
  100. {
  101. xcall_cpu_data_set_recv_call(cpu_data, NULL);
  102. }
  103. static int __init
  104. xcall_setup(void)
  105. {
  106. unsigned int i;
  107. for (i = 0; i < cpu_count(); i++) {
  108. xcall_cpu_data_init(xcall_get_cpu_data(i), i);
  109. }
  110. return 0;
  111. }
  112. INIT_OP_DEFINE(xcall_setup,
  113. INIT_OP_DEP(cpu_mp_probe, true),
  114. INIT_OP_DEP(thread_bootstrap, true),
  115. INIT_OP_DEP(spinlock_setup, true),
  116. INIT_OP_DEP(syscnt_setup, true));
  117. void
  118. xcall_call(xcall_fn_t fn, void *arg, unsigned int cpu)
  119. {
  120. struct xcall_cpu_data *cpu_data;
  121. struct xcall call;
  122. assert(cpu_intr_enabled());
  123. assert(fn);
  124. xcall_init(&call, fn, arg);
  125. cpu_data = xcall_get_cpu_data(cpu);
  126. spinlock_lock(&cpu_data->lock);
  127. /* Enforce release ordering on the receive call */
  128. xcall_cpu_data_set_recv_call(cpu_data, &call);
  129. cpu_send_xcall(cpu);
  130. /* Enforce acquire ordering on the receive call */
  131. while (xcall_cpu_data_get_recv_call(cpu_data) != NULL) {
  132. cpu_pause();
  133. }
  134. spinlock_unlock(&cpu_data->lock);
  135. syscnt_inc(&cpu_data->sc_sent);
  136. }
  137. void
  138. xcall_intr(void)
  139. {
  140. struct xcall_cpu_data *cpu_data;
  141. struct xcall *call;
  142. assert(thread_check_intr_context());
  143. cpu_data = xcall_get_local_cpu_data();
  144. /* Enforce acquire ordering on the receive call */
  145. call = xcall_cpu_data_get_recv_call(cpu_data);
  146. if (call) {
  147. xcall_process(call);
  148. } else {
  149. log_err("xcall: spurious interrupt on cpu%u", cpu_id());
  150. }
  151. syscnt_inc(&cpu_data->sc_received);
  152. /* Enforce release ordering on the receive call */
  153. xcall_cpu_data_clear_recv_call(cpu_data);
  154. }