1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) 2021 Ahmad Fatoum, Pengutronix
4 * Copyright (C) 2025 Linaro Limited
5 *
6 * An implementation of cooperative multi-tasking inspired from barebox threads
7 * https://github.com/barebox/barebox/blob/master/common/bthread.c
8 */
9
10 #include <compiler.h>
11 #include <linux/errno.h>
12 #include <linux/kernel.h>
13 #include <linux/list.h>
14 #include <malloc.h>
15 #include <setjmp.h>
16 #include <stdint.h>
17 #include <uthread.h>
18
19 static struct uthread main_thread = {
20 .list = LIST_HEAD_INIT(main_thread.list),
21 };
22
23 static struct uthread *current = &main_thread;
24
25 /**
26 * uthread_trampoline() - Call the current thread's entry point then resume the
27 * main thread.
28 *
29 * This is a helper function which is used as the @func argument to the
30 * initjmp() function, and ultimately invoked via setjmp(). It does not return
31 * but instead longjmp()'s back to the main thread.
32 */
uthread_trampoline(void)33 static void __noreturn uthread_trampoline(void)
34 {
35 struct uthread *curr = current;
36
37 curr->fn(curr->arg);
38 curr->done = true;
39 current = &main_thread;
40 longjmp(current->ctx, 1);
41 /* Not reached */
42 while (true)
43 ;
44 }
45
46 /**
47 * uthread_free() - Free memory used by a uthread object.
48 */
uthread_free(struct uthread * uthread)49 static void uthread_free(struct uthread *uthread)
50 {
51 if (!uthread)
52 return;
53 free(uthread->stack);
54 free(uthread);
55 }
56
uthread_create(struct uthread * uthr,void (* fn)(void *),void * arg,size_t stack_sz,unsigned int grp_id)57 int uthread_create(struct uthread *uthr, void (*fn)(void *), void *arg,
58 size_t stack_sz, unsigned int grp_id)
59 {
60 bool user_allocated = false;
61
62 if (!stack_sz)
63 stack_sz = CONFIG_UTHREAD_STACK_SIZE;
64
65 if (uthr) {
66 user_allocated = true;
67 } else {
68 uthr = calloc(1, sizeof(*uthr));
69 if (!uthr)
70 return -1;
71 }
72
73 uthr->stack = memalign(16, stack_sz);
74 if (!uthr->stack)
75 goto err;
76
77 uthr->fn = fn;
78 uthr->arg = arg;
79 uthr->grp_id = grp_id;
80
81 list_add_tail(&uthr->list, ¤t->list);
82
83 initjmp(uthr->ctx, uthread_trampoline, uthr->stack, stack_sz);
84
85 return 0;
86 err:
87 if (!user_allocated)
88 free(uthr);
89 return -1;
90 }
91
92 /**
93 * uthread_resume() - switch execution to a given thread
94 *
95 * @uthread: the thread object that should be resumed
96 */
uthread_resume(struct uthread * uthread)97 static void uthread_resume(struct uthread *uthread)
98 {
99 if (!setjmp(current->ctx)) {
100 current = uthread;
101 longjmp(uthread->ctx, 1);
102 }
103 }
104
uthread_schedule(void)105 bool uthread_schedule(void)
106 {
107 struct uthread *next;
108 struct uthread *tmp;
109
110 list_for_each_entry_safe(next, tmp, ¤t->list, list) {
111 if (!next->done) {
112 uthread_resume(next);
113 return true;
114 }
115 /* Found a 'done' thread, free its resources */
116 list_del(&next->list);
117 uthread_free(next);
118 }
119 return false;
120 }
121
uthread_grp_new_id(void)122 unsigned int uthread_grp_new_id(void)
123 {
124 static unsigned int id;
125
126 return ++id;
127 }
128
uthread_grp_done(unsigned int grp_id)129 bool uthread_grp_done(unsigned int grp_id)
130 {
131 struct uthread *next;
132
133 list_for_each_entry(next, &main_thread.list, list) {
134 if (next->grp_id == grp_id && !next->done)
135 return false;
136 }
137
138 return true;
139 }
140
uthread_mutex_lock(struct uthread_mutex * mutex)141 int uthread_mutex_lock(struct uthread_mutex *mutex)
142 {
143 while (mutex->state == UTHREAD_MUTEX_LOCKED)
144 uthread_schedule();
145
146 mutex->state = UTHREAD_MUTEX_LOCKED;
147 return 0;
148 }
149
uthread_mutex_trylock(struct uthread_mutex * mutex)150 int uthread_mutex_trylock(struct uthread_mutex *mutex)
151 {
152 if (mutex->state == UTHREAD_MUTEX_UNLOCKED) {
153 mutex->state = UTHREAD_MUTEX_LOCKED;
154 return 0;
155 }
156
157 return -EBUSY;
158 }
159
uthread_mutex_unlock(struct uthread_mutex * mutex)160 int uthread_mutex_unlock(struct uthread_mutex *mutex)
161 {
162 mutex->state = UTHREAD_MUTEX_UNLOCKED;
163
164 return 0;
165 }
166