net/enic: avoid error message when no advanced filtering
[dpdk.git] / examples / performance-thread / common / lthread.c
1 /*
2  * SPDX-License-Identifier: BSD-3-Clause
3  * Copyright 2015 Intel Corporation.
4  * Copyright 2012 Hasan Alayli <halayli@gmail.com>
5  */
6
7 #define RTE_MEM 1
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <stdint.h>
13 #include <stddef.h>
14 #include <limits.h>
15 #include <inttypes.h>
16 #include <unistd.h>
17 #include <pthread.h>
18 #include <fcntl.h>
19 #include <sys/time.h>
20 #include <sys/mman.h>
21
22 #include <rte_log.h>
23 #include <rte_string_fns.h>
24 #include <ctx.h>
25 #include <stack.h>
26
27 #include "lthread_api.h"
28 #include "lthread.h"
29 #include "lthread_timer.h"
30 #include "lthread_tls.h"
31 #include "lthread_objcache.h"
32 #include "lthread_diag.h"
33
34
35 /*
36  * This function gets called after an lthread function has returned.
37  */
38 void _lthread_exit_handler(struct lthread *lt)
39 {
40
41         lt->state |= BIT(ST_LT_EXITED);
42
43         if (!(lt->state & BIT(ST_LT_DETACH))) {
44                 /* thread is this not explicitly detached
45                  * it must be joinable, so we call lthread_exit().
46                  */
47                 lthread_exit(NULL);
48         }
49
50         /* if we get here the thread is detached so we can reschedule it,
51          * allowing the scheduler to free it
52          */
53         _reschedule();
54 }
55
56
57 /*
58  * Free resources allocated to an lthread
59  */
60 void _lthread_free(struct lthread *lt)
61 {
62
63         DIAG_EVENT(lt, LT_DIAG_LTHREAD_FREE, lt, 0);
64
65         /* invoke any user TLS destructor functions */
66         _lthread_tls_destroy(lt);
67
68         /* free memory allocated for TLS defined using RTE_PER_LTHREAD macros */
69         if (sizeof(void *) < (uint64_t)RTE_PER_LTHREAD_SECTION_SIZE)
70                 _lthread_objcache_free(lt->tls->root_sched->per_lthread_cache,
71                                         lt->per_lthread_data);
72
73         /* free pthread style TLS memory */
74         _lthread_objcache_free(lt->tls->root_sched->tls_cache, lt->tls);
75
76         /* free the stack */
77         _lthread_objcache_free(lt->stack_container->root_sched->stack_cache,
78                                 lt->stack_container);
79
80         /* now free the thread */
81         _lthread_objcache_free(lt->root_sched->lthread_cache, lt);
82
83 }
84
85 /*
86  * Allocate a stack and maintain a cache of stacks
87  */
88 struct lthread_stack *_stack_alloc(void)
89 {
90         struct lthread_stack *s;
91
92         s = _lthread_objcache_alloc((THIS_SCHED)->stack_cache);
93         RTE_ASSERT(s != NULL);
94
95         s->root_sched = THIS_SCHED;
96         s->stack_size = LTHREAD_MAX_STACK_SIZE;
97         return s;
98 }
99
100 /*
101  * Execute a ctx by invoking the start function
102  * On return call an exit handler if the user has provided one
103  */
104 static void _lthread_exec(void *arg)
105 {
106         struct lthread *lt = (struct lthread *)arg;
107
108         /* invoke the contexts function */
109         lt->fun(lt->arg);
110         /* do exit handling */
111         if (lt->exit_handler != NULL)
112                 lt->exit_handler(lt);
113 }
114
115 /*
116  *      Initialize an lthread
117  *      Set its function, args, and exit handler
118  */
119 void
120 _lthread_init(struct lthread *lt,
121         lthread_func_t fun, void *arg, lthread_exit_func exit_handler)
122 {
123
124         /* set ctx func and args */
125         lt->fun = fun;
126         lt->arg = arg;
127         lt->exit_handler = exit_handler;
128
129         /* set initial state */
130         lt->birth = _sched_now();
131         lt->state = BIT(ST_LT_INIT);
132         lt->join = LT_JOIN_INITIAL;
133 }
134
135 /*
136  *      set the lthread stack
137  */
138 void _lthread_set_stack(struct lthread *lt, void *stack, size_t stack_size)
139 {
140         /* set stack */
141         lt->stack = stack;
142         lt->stack_size = stack_size;
143
144         arch_set_stack(lt, _lthread_exec);
145 }
146
147 /*
148  * Create an lthread on the current scheduler
149  * If there is no current scheduler on this pthread then first create one
150  */
151 int
152 lthread_create(struct lthread **new_lt, int lcore_id,
153                 lthread_func_t fun, void *arg)
154 {
155         if ((new_lt == NULL) || (fun == NULL))
156                 return POSIX_ERRNO(EINVAL);
157
158         if (lcore_id < 0)
159                 lcore_id = rte_lcore_id();
160         else if (lcore_id > LTHREAD_MAX_LCORES)
161                 return POSIX_ERRNO(EINVAL);
162
163         struct lthread *lt = NULL;
164
165         if (THIS_SCHED == NULL) {
166                 THIS_SCHED = _lthread_sched_create(0);
167                 if (THIS_SCHED == NULL) {
168                         perror("Failed to create scheduler");
169                         return POSIX_ERRNO(EAGAIN);
170                 }
171         }
172
173         /* allocate a thread structure */
174         lt = _lthread_objcache_alloc((THIS_SCHED)->lthread_cache);
175         if (lt == NULL)
176                 return POSIX_ERRNO(EAGAIN);
177
178         bzero(lt, sizeof(struct lthread));
179         lt->root_sched = THIS_SCHED;
180
181         /* set the function args and exit handlder */
182         _lthread_init(lt, fun, arg, _lthread_exit_handler);
183
184         /* put it in the ready queue */
185         *new_lt = lt;
186
187         if (lcore_id < 0)
188                 lcore_id = rte_lcore_id();
189
190         DIAG_CREATE_EVENT(lt, LT_DIAG_LTHREAD_CREATE);
191
192         rte_wmb();
193         _ready_queue_insert(_lthread_sched_get(lcore_id), lt);
194         return 0;
195 }
196
197 /*
198  * Schedules lthread to sleep for `nsecs`
199  * setting the lthread state to LT_ST_SLEEPING.
200  * lthread state is cleared upon resumption or expiry.
201  */
202 static inline void _lthread_sched_sleep(struct lthread *lt, uint64_t nsecs)
203 {
204         uint64_t state = lt->state;
205         uint64_t clks = _ns_to_clks(nsecs);
206
207         if (clks) {
208                 _timer_start(lt, clks);
209                 lt->state = state | BIT(ST_LT_SLEEPING);
210         }
211         DIAG_EVENT(lt, LT_DIAG_LTHREAD_SLEEP, clks, 0);
212         _suspend();
213 }
214
215
216
217 /*
218  * Cancels any running timer.
219  * This can be called multiple times on the same lthread regardless if it was
220  * sleeping or not.
221  */
222 int _lthread_desched_sleep(struct lthread *lt)
223 {
224         uint64_t state = lt->state;
225
226         if (state & BIT(ST_LT_SLEEPING)) {
227                 _timer_stop(lt);
228                 state &= (CLEARBIT(ST_LT_SLEEPING) & CLEARBIT(ST_LT_EXPIRED));
229                 lt->state = state | BIT(ST_LT_READY);
230                 return 1;
231         }
232         return 0;
233 }
234
235 /*
236  * set user data pointer in an lthread
237  */
238 void lthread_set_data(void *data)
239 {
240         if (sizeof(void *) == RTE_PER_LTHREAD_SECTION_SIZE)
241                 THIS_LTHREAD->per_lthread_data = data;
242 }
243
244 /*
245  * Retrieve user data pointer from an lthread
246  */
247 void *lthread_get_data(void)
248 {
249         return THIS_LTHREAD->per_lthread_data;
250 }
251
252 /*
253  * Return the current lthread handle
254  */
255 struct lthread *lthread_current(void)
256 {
257         struct lthread_sched *sched = THIS_SCHED;
258
259         if (sched)
260                 return sched->current_lthread;
261         return NULL;
262 }
263
264
265
266 /*
267  * Tasklet to cancel a thread
268  */
269 static void *
270 _cancel(void *arg)
271 {
272         struct lthread *lt = (struct lthread *) arg;
273
274         lt->state |= BIT(ST_LT_CANCELLED);
275         lthread_detach();
276         return NULL;
277 }
278
279
280 /*
281  * Mark the specified as canceled
282  */
283 int lthread_cancel(struct lthread *cancel_lt)
284 {
285         struct lthread *lt;
286
287         if ((cancel_lt == NULL) || (cancel_lt == THIS_LTHREAD))
288                 return POSIX_ERRNO(EINVAL);
289
290         DIAG_EVENT(cancel_lt, LT_DIAG_LTHREAD_CANCEL, cancel_lt, 0);
291
292         if (cancel_lt->sched != THIS_SCHED) {
293
294                 /* spawn task-let to cancel the thread */
295                 lthread_create(&lt,
296                                 cancel_lt->sched->lcore_id,
297                                 _cancel,
298                                 cancel_lt);
299                 return 0;
300         }
301         cancel_lt->state |= BIT(ST_LT_CANCELLED);
302         return 0;
303 }
304
305 /*
306  * Suspend the current lthread for specified time
307  */
308 void lthread_sleep(uint64_t nsecs)
309 {
310         struct lthread *lt = THIS_LTHREAD;
311
312         _lthread_sched_sleep(lt, nsecs);
313
314 }
315
316 /*
317  * Suspend the current lthread for specified time
318  */
319 void lthread_sleep_clks(uint64_t clks)
320 {
321         struct lthread *lt = THIS_LTHREAD;
322         uint64_t state = lt->state;
323
324         if (clks) {
325                 _timer_start(lt, clks);
326                 lt->state = state | BIT(ST_LT_SLEEPING);
327         }
328         DIAG_EVENT(lt, LT_DIAG_LTHREAD_SLEEP, clks, 0);
329         _suspend();
330 }
331
332 /*
333  * Requeue the current thread to the back of the ready queue
334  */
335 void lthread_yield(void)
336 {
337         struct lthread *lt = THIS_LTHREAD;
338
339         DIAG_EVENT(lt, LT_DIAG_LTHREAD_YIELD, 0, 0);
340
341         _ready_queue_insert(THIS_SCHED, lt);
342         ctx_switch(&(THIS_SCHED)->ctx, &lt->ctx);
343 }
344
345 /*
346  * Exit the current lthread
347  * If a thread is joining pass the user pointer to it
348  */
349 void lthread_exit(void *ptr)
350 {
351         struct lthread *lt = THIS_LTHREAD;
352
353         /* if thread is detached (this is not valid) just exit */
354         if (lt->state & BIT(ST_LT_DETACH))
355                 return;
356
357         /* There is a race between lthread_join() and lthread_exit()
358          *  - if exit before join then we suspend and resume on join
359          *  - if join before exit then we resume the joining thread
360          */
361         uint64_t join_initial = LT_JOIN_INITIAL;
362         if ((lt->join == LT_JOIN_INITIAL)
363             && __atomic_compare_exchange_n(&lt->join, &join_initial,
364                 LT_JOIN_EXITING, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
365
366                 DIAG_EVENT(lt, LT_DIAG_LTHREAD_EXIT, 1, 0);
367                 _suspend();
368                 /* set the exit value */
369                 if ((ptr != NULL) && (lt->lt_join->lt_exit_ptr != NULL))
370                         *(lt->lt_join->lt_exit_ptr) = ptr;
371
372                 /* let the joining thread know we have set the exit value */
373                 lt->join = LT_JOIN_EXIT_VAL_SET;
374         } else {
375
376                 DIAG_EVENT(lt, LT_DIAG_LTHREAD_EXIT, 0, 0);
377                 /* set the exit value */
378                 if ((ptr != NULL) && (lt->lt_join->lt_exit_ptr != NULL))
379                         *(lt->lt_join->lt_exit_ptr) = ptr;
380                 /* let the joining thread know we have set the exit value */
381                 lt->join = LT_JOIN_EXIT_VAL_SET;
382                 _ready_queue_insert(lt->lt_join->sched,
383                                     (struct lthread *)lt->lt_join);
384         }
385
386
387         /* wait until the joinging thread has collected the exit value */
388         while (lt->join != LT_JOIN_EXIT_VAL_READ)
389                 _reschedule();
390
391         /* reset join state */
392         lt->join = LT_JOIN_INITIAL;
393
394         /* detach it so its resources can be released */
395         lt->state |= (BIT(ST_LT_DETACH) | BIT(ST_LT_EXITED));
396 }
397
398 /*
399  * Join an lthread
400  * Suspend until the joined thread returns
401  */
402 int lthread_join(struct lthread *lt, void **ptr)
403 {
404         if (lt == NULL)
405                 return POSIX_ERRNO(EINVAL);
406
407         struct lthread *current = THIS_LTHREAD;
408         uint64_t lt_state = lt->state;
409
410         /* invalid to join a detached thread, or a thread that is joined */
411         if ((lt_state & BIT(ST_LT_DETACH)) || (lt->join == LT_JOIN_THREAD_SET))
412                 return POSIX_ERRNO(EINVAL);
413         /* pointer to the joining thread and a poingter to return a value */
414         lt->lt_join = current;
415         current->lt_exit_ptr = ptr;
416         /* There is a race between lthread_join() and lthread_exit()
417          *  - if join before exit we suspend and will resume when exit is called
418          *  - if exit before join we resume the exiting thread
419          */
420         uint64_t join_initial = LT_JOIN_INITIAL;
421         if ((lt->join == LT_JOIN_INITIAL)
422             && __atomic_compare_exchange_n(&lt->join, &join_initial,
423                 LT_JOIN_THREAD_SET, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
424
425                 DIAG_EVENT(current, LT_DIAG_LTHREAD_JOIN, lt, 1);
426                 _suspend();
427         } else {
428                 DIAG_EVENT(current, LT_DIAG_LTHREAD_JOIN, lt, 0);
429                 _ready_queue_insert(lt->sched, lt);
430         }
431
432         /* wait for exiting thread to set return value */
433         while (lt->join != LT_JOIN_EXIT_VAL_SET)
434                 _reschedule();
435
436         /* collect the return value */
437         if (ptr != NULL)
438                 *ptr = *current->lt_exit_ptr;
439
440         /* let the exiting thread proceed to exit */
441         lt->join = LT_JOIN_EXIT_VAL_READ;
442         return 0;
443 }
444
445
446 /*
447  * Detach current lthread
448  * A detached thread cannot be joined
449  */
450 void lthread_detach(void)
451 {
452         struct lthread *lt = THIS_LTHREAD;
453
454         DIAG_EVENT(lt, LT_DIAG_LTHREAD_DETACH, 0, 0);
455
456         uint64_t state = lt->state;
457
458         lt->state = state | BIT(ST_LT_DETACH);
459 }
460
461 /*
462  * Set function name of an lthread
463  * this is a debug aid
464  */
465 void lthread_set_funcname(const char *f)
466 {
467         struct lthread *lt = THIS_LTHREAD;
468
469         strlcpy(lt->funcname, f, sizeof(lt->funcname));
470 }