1 /*
2  * Copyright (c) 2016-2017 Nordic Semiconductor ASA
3  * Copyright (c) 2016 Vinayak Kariappa Chettimada
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <stddef.h>
9 #include <zephyr/types.h>
10 #include <sys/printk.h>
11 #include "memq.h"
12 #include "mayfly.h"
13 
14 static struct {
15 	memq_link_t *head;
16 	memq_link_t *tail;
17 	uint8_t        enable_req;
18 	uint8_t        enable_ack;
19 	uint8_t        disable_req;
20 	uint8_t        disable_ack;
21 } mft[MAYFLY_CALLEE_COUNT][MAYFLY_CALLER_COUNT];
22 
23 static memq_link_t mfl[MAYFLY_CALLEE_COUNT][MAYFLY_CALLER_COUNT];
24 static uint8_t mfp[MAYFLY_CALLEE_COUNT];
25 
26 #if defined(MAYFLY_UT)
27 static uint8_t _state;
28 #endif /* MAYFLY_UT */
29 
mayfly_init(void)30 void mayfly_init(void)
31 {
32 	uint8_t callee_id;
33 
34 	callee_id = MAYFLY_CALLEE_COUNT;
35 	while (callee_id--) {
36 		uint8_t caller_id;
37 
38 		caller_id = MAYFLY_CALLER_COUNT;
39 		while (caller_id--) {
40 			memq_init(&mfl[callee_id][caller_id],
41 				  &mft[callee_id][caller_id].head,
42 				  &mft[callee_id][caller_id].tail);
43 		}
44 	}
45 }
46 
mayfly_enable(uint8_t caller_id,uint8_t callee_id,uint8_t enable)47 void mayfly_enable(uint8_t caller_id, uint8_t callee_id, uint8_t enable)
48 {
49 	if (enable) {
50 		if (mft[callee_id][caller_id].enable_req ==
51 		    mft[callee_id][caller_id].enable_ack) {
52 			mft[callee_id][caller_id].enable_req++;
53 		}
54 
55 		mayfly_enable_cb(caller_id, callee_id, enable);
56 	} else {
57 		if (mft[callee_id][caller_id].disable_req ==
58 		    mft[callee_id][caller_id].disable_ack) {
59 			mft[callee_id][caller_id].disable_req++;
60 
61 			/* set mayfly callee pending */
62 			mfp[callee_id] = 1U;
63 
64 			/* pend the callee for execution */
65 			mayfly_pend(caller_id, callee_id);
66 		}
67 	}
68 }
69 
mayfly_enqueue(uint8_t caller_id,uint8_t callee_id,uint8_t chain,struct mayfly * m)70 uint32_t mayfly_enqueue(uint8_t caller_id, uint8_t callee_id, uint8_t chain,
71 			struct mayfly *m)
72 {
73 	uint8_t state;
74 	uint8_t ack;
75 
76 	chain = chain || !mayfly_prio_is_equal(caller_id, callee_id) ||
77 		!mayfly_is_enabled(caller_id, callee_id) ||
78 		(mft[callee_id][caller_id].disable_req !=
79 		 mft[callee_id][caller_id].disable_ack);
80 
81 	/* shadow the ack */
82 	ack = m->_ack;
83 
84 	/* already in queue */
85 	state = (m->_req - ack) & 0x03;
86 	if (state != 0U) {
87 		if (chain) {
88 			if (state != 1U) {
89 				/* mark as ready in queue */
90 				m->_req = ack + 1;
91 
92 				goto mayfly_enqueue_pend;
93 			}
94 
95 			/* already ready */
96 			return 1;
97 		}
98 
99 		/* mark as done in queue, and fall thru */
100 		m->_req = ack + 2;
101 	}
102 
103 	/* handle mayfly(s) that can be inline */
104 	if (!chain) {
105 		/* call fp */
106 		m->fp(m->param);
107 
108 		return 0;
109 	}
110 
111 	/* new, add as ready in the queue */
112 	m->_req = ack + 1;
113 	memq_enqueue(m->_link, m, &mft[callee_id][caller_id].tail);
114 
115 mayfly_enqueue_pend:
116 	/* set mayfly callee pending */
117 	mfp[callee_id] = 1U;
118 
119 	/* pend the callee for execution */
120 	mayfly_pend(caller_id, callee_id);
121 
122 	return 0;
123 }
124 
dequeue(uint8_t callee_id,uint8_t caller_id,memq_link_t * link,struct mayfly * m)125 static void dequeue(uint8_t callee_id, uint8_t caller_id, memq_link_t *link,
126 		    struct mayfly *m)
127 {
128 	uint8_t req;
129 
130 	req = m->_req;
131 	if (((req - m->_ack) & 0x03) != 1U) {
132 		uint8_t ack;
133 
134 #if defined(MAYFLY_UT)
135 		uint32_t mayfly_ut_run_test(void);
136 		void mayfly_ut_mfy(void *param);
137 
138 		if (_state && m->fp == mayfly_ut_mfy) {
139 			static uint8_t single;
140 
141 			if (!single) {
142 				single = 1U;
143 				mayfly_ut_run_test();
144 			}
145 		}
146 #endif /* MAYFLY_UT */
147 
148 		/* dequeue mayfly struct */
149 		memq_dequeue(mft[callee_id][caller_id].tail,
150 			     &mft[callee_id][caller_id].head,
151 			     0);
152 
153 		/* release link into dequeued mayfly struct */
154 		m->_link = link;
155 
156 		/* reset mayfly state to idle */
157 		ack = m->_ack;
158 		m->_ack = req;
159 
160 		/* re-insert, if re-pended by interrupt */
161 		if (((m->_req - ack) & 0x03) == 1U) {
162 #if defined(MAYFLY_UT)
163 			printk("%s: RACE\n", __func__);
164 #endif /* MAYFLY_UT */
165 
166 			m->_ack = ack;
167 			memq_enqueue(link, m, &mft[callee_id][callee_id].tail);
168 		}
169 	}
170 }
171 
mayfly_run(uint8_t callee_id)172 void mayfly_run(uint8_t callee_id)
173 {
174 	uint8_t disable = 0U;
175 	uint8_t enable = 0U;
176 	uint8_t caller_id;
177 
178 	if (!mfp[callee_id]) {
179 		return;
180 	}
181 	mfp[callee_id] = 0U;
182 
183 	/* iterate through each caller queue to this callee_id */
184 	caller_id = MAYFLY_CALLER_COUNT;
185 	while (caller_id--) {
186 		memq_link_t *link;
187 		struct mayfly *m = 0;
188 
189 		/* fetch mayfly in callee queue, if any */
190 		link = memq_peek(mft[callee_id][caller_id].head,
191 				 mft[callee_id][caller_id].tail,
192 				 (void **)&m);
193 		while (link) {
194 			uint8_t state;
195 
196 #if defined(MAYFLY_UT)
197 			_state = 0U;
198 #endif /* MAYFLY_UT */
199 
200 			/* execute work if ready */
201 			state = (m->_req - m->_ack) & 0x03;
202 			if (state == 1U) {
203 #if defined(MAYFLY_UT)
204 				_state = 1U;
205 #endif /* MAYFLY_UT */
206 
207 				/* mark mayfly as ran */
208 				m->_ack--;
209 
210 				/* call the mayfly function */
211 				m->fp(m->param);
212 			}
213 
214 			/* dequeue if not re-pended */
215 			dequeue(callee_id, caller_id, link, m);
216 
217 			/* fetch next mayfly in callee queue, if any */
218 			link = memq_peek(mft[callee_id][caller_id].head,
219 					 mft[callee_id][caller_id].tail,
220 					 (void **)&m);
221 
222 /**
223  * When using cooperative thread implementation, an issue has been seen where
224  * pended mayflies are never executed in certain scenarios.
225  * This happens when mayflies with higher caller_id are constantly pended, in
226  * which case lower value caller ids never get to be executed.
227  * By allowing complete traversal of mayfly queues for all caller_ids, this
228  * does not happen, however this means that more than one mayfly function is
229  * potentially executed in a mayfly_run(), with added execution time as
230  * consequence.
231  */
232 #if defined(CONFIG_BT_MAYFLY_YIELD_AFTER_CALL)
233 			/* yield out of mayfly_run if a mayfly function was
234 			 * called.
235 			 */
236 			if (state == 1U) {
237 				/* pend callee (tailchain) if mayfly queue is
238 				 * not empty or all caller queues are not
239 				 * processed.
240 				 */
241 				if (caller_id || link) {
242 					/* set mayfly callee pending */
243 					mfp[callee_id] = 1U;
244 
245 					/* pend the callee for execution */
246 					mayfly_pend(callee_id, callee_id);
247 
248 					return;
249 				}
250 			}
251 #endif
252 		}
253 
254 		if (mft[callee_id][caller_id].disable_req !=
255 		    mft[callee_id][caller_id].disable_ack) {
256 			disable = 1U;
257 
258 			mft[callee_id][caller_id].disable_ack =
259 				mft[callee_id][caller_id].disable_req;
260 		}
261 
262 		if (mft[callee_id][caller_id].enable_req !=
263 		    mft[callee_id][caller_id].enable_ack) {
264 			enable = 1U;
265 
266 			mft[callee_id][caller_id].enable_ack =
267 				mft[callee_id][caller_id].enable_req;
268 		}
269 	}
270 
271 	if (disable && !enable) {
272 		mayfly_enable_cb(callee_id, callee_id, 0);
273 	}
274 }
275 
276 #if defined(MAYFLY_UT)
277 #define MAYFLY_CALL_ID_CALLER MAYFLY_CALL_ID_0
278 #define MAYFLY_CALL_ID_CALLEE MAYFLY_CALL_ID_2
279 
mayfly_ut_mfy(void * param)280 void mayfly_ut_mfy(void *param)
281 {
282 	printk("%s: ran.\n", __func__);
283 
284 	(*((uint32_t *)param))++;
285 }
286 
mayfly_ut_test(void * param)287 void mayfly_ut_test(void *param)
288 {
289 	static uint32_t *count;
290 	static memq_link_t link;
291 	static struct mayfly mfy = {0, 0, &link, NULL, mayfly_ut_mfy};
292 	uint32_t err;
293 
294 	printk("%s: req= %u, ack= %u\n", __func__, mfy._req, mfy._ack);
295 
296 	if (param) {
297 		count = param;
298 	}
299 
300 	mfy.param = count;
301 
302 	err = mayfly_enqueue(MAYFLY_CALL_ID_CALLER, MAYFLY_CALL_ID_CALLEE, 1,
303 			     &mfy);
304 	if (err) {
305 		printk("%s: FAILED (%u).\n", __func__, err);
306 	} else {
307 		printk("%s: SUCCESS.\n", __func__);
308 	}
309 }
310 
mayfly_ut_run_test(void)311 uint32_t mayfly_ut_run_test(void)
312 {
313 	static memq_link_t link;
314 	static struct mayfly mfy = {0, 0, &link, NULL, mayfly_ut_test};
315 	uint32_t err;
316 
317 	printk("%s: req= %u, ack= %u\n", __func__, mfy._req, mfy._ack);
318 
319 	err = mayfly_enqueue(MAYFLY_CALL_ID_CALLEE, MAYFLY_CALL_ID_CALLER, 0,
320 			     &mfy);
321 
322 	if (err) {
323 		printk("%s: FAILED.\n", __func__);
324 		return err;
325 	}
326 
327 	printk("%s: SUCCESS.\n", __func__);
328 
329 	return 0;
330 }
331 
mayfly_ut(void)332 uint32_t mayfly_ut(void)
333 {
334 	static uint32_t count;
335 	static memq_link_t link;
336 	static struct mayfly mfy = {0, 0, &link, &count, mayfly_ut_test};
337 	uint32_t err;
338 
339 	printk("%s: req= %u, ack= %u\n", __func__, mfy._req, mfy._ack);
340 
341 	err = mayfly_enqueue(MAYFLY_CALL_ID_PROGRAM, MAYFLY_CALL_ID_CALLER, 0,
342 			     &mfy);
343 
344 	if (err) {
345 		printk("%s: FAILED.\n", __func__);
346 		return err;
347 	}
348 
349 	printk("%s: count = %u.\n", __func__, count);
350 	printk("%s: SUCCESS.\n", __func__);
351 	return 0;
352 }
353 #endif /* MAYFLY_UT */
354