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