smp_call.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. /*
  2. * Copyright (c) 2006-2024 RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2024/9/12 zhujiale the first version
  9. * 2024/10/24 Shell added non-blocking IPI calling method;
  10. * fixup data racing
  11. */
  12. #include "smp_call.h"
  13. #define DBG_TAG "SMP"
  14. #define DBG_LVL DBG_INFO
  15. #include <rtdbg.h>
  16. static struct smp_data
  17. {
  18. /* call request data to each cores */
  19. struct rt_smp_call_req call_req_cores[RT_CPUS_NR];
  20. /* call queue of this core */
  21. rt_ll_slist_t call_queue;
  22. } _smp_data_cores[RT_CPUS_NR];
  23. #define _CALL_REQ_USAGE_FREED 0
  24. #define _CALL_REQ_USAGE_BUSY 1
  25. static void _call_req_take(struct rt_smp_call_req *req)
  26. {
  27. rt_base_t exp;
  28. do
  29. {
  30. exp = _CALL_REQ_USAGE_FREED;
  31. }
  32. while (!rt_atomic_compare_exchange_strong(&req->event.typed.usage_tracer, &exp, _CALL_REQ_USAGE_BUSY));
  33. }
  34. static void _call_req_release(struct rt_smp_call_req *req)
  35. {
  36. rt_atomic_store(&req->event.typed.usage_tracer, _CALL_REQ_USAGE_FREED);
  37. }
  38. void rt_smp_request_wait_freed(struct rt_smp_call_req *req)
  39. {
  40. rt_base_t usage_tracer;
  41. RT_DEBUG_IN_THREAD_CONTEXT;
  42. usage_tracer = rt_atomic_load(&req->event.typed.usage_tracer);
  43. while (usage_tracer != _CALL_REQ_USAGE_FREED)
  44. {
  45. rt_thread_yield();
  46. usage_tracer = rt_atomic_load(&req->event.typed.usage_tracer);
  47. }
  48. }
  49. static void _mask_out_cpu(struct rt_smp_event *event, int oncpu)
  50. {
  51. rt_base_t new_mask, old_mask;
  52. rt_atomic_t *maskp = event->typed.calling_cpu_mask;
  53. do
  54. {
  55. old_mask = rt_atomic_load(maskp);
  56. new_mask = old_mask & ~(1ul << oncpu);
  57. } while (!rt_atomic_compare_exchange_strong(maskp, &old_mask, new_mask));
  58. }
  59. static void _do_glob_request(struct rt_smp_call_req *req_global,
  60. struct rt_smp_call_req *req_local)
  61. {
  62. struct rt_smp_event *event;
  63. /* release the global request data */
  64. rt_memcpy(req_local, req_global, sizeof(struct rt_smp_call_req));
  65. rt_hw_spin_unlock(&req_global->freed_lock);
  66. event = &req_local->event;
  67. RT_ASSERT(!!event->func);
  68. event->func(event->data);
  69. return ;
  70. }
  71. static void _do_request(struct rt_smp_call_req *req)
  72. {
  73. struct rt_smp_event *event;
  74. event = &req->event;
  75. RT_ASSERT(!!event->func);
  76. event->func(event->data);
  77. _call_req_release(req);
  78. return ;
  79. }
  80. static rt_err_t _smp_call_handler(struct rt_smp_call_req *req, int oncpu)
  81. {
  82. switch (req->event.event_id)
  83. {
  84. case SMP_CALL_EVENT_GLOB_SYNC:
  85. {
  86. struct rt_smp_call_req req_local;
  87. _do_glob_request(req, &req_local);
  88. _mask_out_cpu(&req_local.event, oncpu);
  89. break;
  90. }
  91. case SMP_CALL_EVENT_GLOB_ASYNC:
  92. {
  93. struct rt_smp_call_req req_local;
  94. _do_glob_request(req, &req_local);
  95. break;
  96. }
  97. case SMP_CALL_EVENT_REQUEST:
  98. {
  99. _do_request(req);
  100. break;
  101. }
  102. default:
  103. LOG_E("error event id\n");
  104. return -RT_ERROR;
  105. }
  106. return RT_EOK;
  107. }
  108. void rt_smp_call_ipi_handler(int vector, void *param)
  109. {
  110. int oncpu = rt_hw_cpu_id();
  111. struct rt_smp_call_req *request;
  112. RT_ASSERT(rt_interrupt_get_nest());
  113. while (1)
  114. {
  115. rt_ll_slist_t *node = rt_ll_slist_dequeue(&_smp_data_cores[oncpu].call_queue);
  116. if (node)
  117. {
  118. request = rt_list_entry(node, struct rt_smp_call_req, slist_node);
  119. _smp_call_handler(request, oncpu);
  120. }
  121. else
  122. {
  123. break;
  124. }
  125. }
  126. }
  127. static void _smp_call_remote_request(int callcpu, rt_smp_call_cb_t func,
  128. void *data, rt_uint8_t flags,
  129. struct rt_smp_call_req *call_req)
  130. {
  131. rt_base_t cpu_mask = 1ul << callcpu;
  132. _call_req_take(call_req);
  133. rt_ll_slist_enqueue(&_smp_data_cores[callcpu].call_queue, &call_req->slist_node);
  134. rt_hw_ipi_send(RT_SMP_CALL_IPI, cpu_mask);
  135. }
  136. /**
  137. * @brief SMP call request with user provided @call_req. Compare to
  138. * rt_smp_call_func* family, you can call it in ISR or IRQ-masked
  139. * environment.
  140. *
  141. * @param callcpu the logical core id of the target
  142. * @param flags control flags of your request
  143. * @param call_req the pre-initialized request data
  144. * @return rt_err_t RT_EOK on succeed, otherwise the errno to failure
  145. */
  146. rt_err_t rt_smp_call_request(int callcpu, rt_uint8_t flags, struct rt_smp_call_req *call_req)
  147. {
  148. rt_ubase_t clvl;
  149. int oncpu;
  150. if (rt_atomic_load(&call_req->event.typed.usage_tracer) ==
  151. _CALL_REQ_USAGE_BUSY)
  152. {
  153. return -RT_EBUSY;
  154. }
  155. if (flags & SMP_CALL_WAIT_ALL)
  156. {
  157. return -RT_EINVAL;
  158. }
  159. clvl = rt_enter_critical();
  160. oncpu = rt_hw_cpu_id();
  161. if (oncpu == callcpu && !(flags & SMP_CALL_NO_LOCAL))
  162. {
  163. rt_ubase_t level;
  164. /* handle IPI on irq-masked environment */
  165. level = rt_hw_local_irq_disable();
  166. call_req->event.func(call_req->event.data);
  167. rt_hw_local_irq_enable(level);
  168. }
  169. else if (callcpu < RT_CPUS_NR)
  170. {
  171. _smp_call_remote_request(callcpu, call_req->event.func, call_req->event.data, flags, call_req);
  172. }
  173. rt_exit_critical_safe(clvl);
  174. return RT_EOK;
  175. }
  176. void rt_smp_call_req_init(struct rt_smp_call_req *call_req,
  177. rt_smp_call_cb_t func, void *data)
  178. {
  179. call_req->event.typed.usage_tracer = 0;
  180. call_req->event.data = data;
  181. call_req->event.func = func;
  182. call_req->event.event_id = SMP_CALL_EVENT_REQUEST;
  183. }
  184. static void _smp_call_func_cond(int oncpu, rt_ubase_t cpu_mask,
  185. rt_smp_call_cb_t func, void *data,
  186. rt_uint8_t flags, rt_smp_cond_t cond)
  187. {
  188. rt_ubase_t tmp_mask;
  189. rt_bool_t sync_call = RT_FALSE;
  190. rt_ubase_t oncpu_mask = 1 << oncpu;
  191. rt_atomic_t calling_cpu_mask, *maskp;
  192. int tmp_id = 0, rcpu_cnt = 0, event_id, call_local;
  193. if (!(flags & SMP_CALL_NO_LOCAL) && (oncpu_mask & cpu_mask))
  194. {
  195. call_local = RT_TRUE;
  196. cpu_mask = cpu_mask & (~oncpu_mask);
  197. }
  198. else
  199. {
  200. call_local = RT_FALSE;
  201. }
  202. if (cpu_mask)
  203. {
  204. tmp_mask = cpu_mask;
  205. if (flags & SMP_CALL_WAIT_ALL)
  206. {
  207. sync_call = RT_TRUE;
  208. maskp = &calling_cpu_mask;
  209. event_id = SMP_CALL_EVENT_GLOB_SYNC;
  210. rt_atomic_store(maskp, cpu_mask);
  211. }
  212. else
  213. {
  214. event_id = SMP_CALL_EVENT_GLOB_ASYNC;
  215. maskp = RT_NULL;
  216. }
  217. while (tmp_mask)
  218. {
  219. struct rt_smp_call_req *call_req;
  220. struct rt_smp_event *event;
  221. int lz_bit = __rt_ffsl(tmp_mask);
  222. tmp_id = lz_bit - 1;
  223. tmp_mask &= ~(1ul << tmp_id);
  224. if (cond && !cond(tmp_id, data))
  225. {
  226. cpu_mask &= ~(1ul << tmp_id);
  227. continue;
  228. }
  229. /* need to wait one more */
  230. rcpu_cnt++;
  231. call_req = &_smp_data_cores[oncpu].call_req_cores[tmp_id];
  232. /* very careful here, spinning wait on previous occupation */
  233. rt_hw_spin_lock(&call_req->freed_lock);
  234. event = &call_req->event;
  235. event->event_id = event_id;
  236. event->func = func;
  237. event->data = data;
  238. event->typed.calling_cpu_mask = maskp;
  239. rt_ll_slist_enqueue(&_smp_data_cores[tmp_id].call_queue, &call_req->slist_node);
  240. }
  241. if (cpu_mask)
  242. {
  243. RT_ASSERT(rcpu_cnt);
  244. rt_hw_ipi_send(RT_SMP_CALL_IPI, cpu_mask);
  245. }
  246. }
  247. if (call_local && (!cond || cond(tmp_id, data)))
  248. {
  249. rt_ubase_t level;
  250. /* callback on local with sims ISR */
  251. level = rt_hw_local_irq_disable();
  252. func(data);
  253. rt_hw_local_irq_enable(level);
  254. }
  255. if (sync_call && rcpu_cnt)
  256. {
  257. while (rt_atomic_load(maskp) & cpu_mask)
  258. ;
  259. }
  260. }
  261. /**
  262. * @brief call function on specified CPU ,
  263. *
  264. * @param cpu_mask cpu mask for call
  265. * @param func the function pointer
  266. * @param data the data pointer
  267. * @param flag call flag if you set SMP_CALL_WAIT_ALL
  268. * then it will wait all cpu call finish and return
  269. * else it will call function on specified CPU and return immediately
  270. * @param cond the condition function pointer,if you set it then it will call function only when cond return true
  271. */
  272. void rt_smp_call_func_cond(rt_ubase_t cpu_mask, rt_smp_call_cb_t func, void *data, rt_uint8_t flag, rt_smp_cond_t cond)
  273. {
  274. int oncpu;
  275. rt_ubase_t clvl;
  276. RT_ASSERT(!rt_hw_interrupt_is_disabled());
  277. clvl = rt_enter_critical();
  278. oncpu = rt_hw_cpu_id();
  279. if (cpu_mask <= RT_ALL_CPU)
  280. {
  281. _smp_call_func_cond(oncpu, cpu_mask, func, data, flag, cond);
  282. }
  283. rt_exit_critical_safe(clvl);
  284. }
  285. void rt_smp_call_each_cpu(rt_smp_call_cb_t func, void *data, rt_uint8_t flag)
  286. {
  287. rt_smp_call_func_cond(RT_ALL_CPU, func, data, flag, RT_NULL);
  288. }
  289. void rt_smp_call_each_cpu_cond(rt_smp_call_cb_t func, void *data, rt_uint8_t flag, rt_smp_cond_t cond_func)
  290. {
  291. rt_smp_call_func_cond(RT_ALL_CPU, func, data, flag, cond_func);
  292. }
  293. void rt_smp_call_cpu_mask(rt_ubase_t cpu_mask, rt_smp_call_cb_t func, void *data, rt_uint8_t flag)
  294. {
  295. rt_smp_call_func_cond(cpu_mask, func, data, flag, RT_NULL);
  296. }
  297. void rt_smp_call_cpu_mask_cond(rt_ubase_t cpu_mask, rt_smp_call_cb_t func, void *data, rt_uint8_t flag, rt_smp_cond_t cond_func)
  298. {
  299. rt_smp_call_func_cond(cpu_mask, func, data, flag, cond_func);
  300. }
  301. void rt_smp_call_init(void)
  302. {
  303. rt_memset(&_smp_data_cores, 0, sizeof(_smp_data_cores));
  304. for (int i = 0; i < RT_CPUS_NR; i++)
  305. {
  306. for (int j = 0; j < RT_CPUS_NR; j++)
  307. {
  308. rt_hw_spin_lock_init(&_smp_data_cores[i].call_req_cores[j].freed_lock);
  309. }
  310. }
  311. }