Message ID | 20250214140031.484344-7-jerome.forissier@linaro.org |
---|---|
State | New |
Headers | show |
Series | Uthreads | expand |
On 14.02.25 15:00, Jerome Forissier wrote: > Add an new internal API called uthread (Kconfig symbol: UTHREAD) which > provides cooperative multi-tasking. The goal is to be able to improve > the performance of some parts of U-Boot by overlapping lengthy > operations. Each uthread has its own stack allocated on the heap. The > default stack size is defined by the UTHREAD_STACK_SIZE symbol and is > used when uthread_create() is passed zero for the stack_sz argument. > > The implementation is based on context-switching via initjmp()/setjmp()/ > longjmp() and is inspired from barebox threads [1]. > > [1] https://github.com/barebox/barebox/blob/master/common/bthread.c > > Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org> > --- > include/uthread.h | 31 ++++++++++++++ > lib/Kconfig | 19 ++++++++ > lib/Makefile | 2 + > lib/uthread.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 159 insertions(+) > create mode 100644 include/uthread.h > create mode 100644 lib/uthread.c > > diff --git a/include/uthread.h b/include/uthread.h > new file mode 100644 > index 00000000000..ea1389e82c6 > --- /dev/null > +++ b/include/uthread.h > @@ -0,0 +1,31 @@ > +/* SPDX-License-Identifier: GPL-2.0+ */ > +/* > + * Copyright 2025 Linaro Limited > + */ > + > +#include <linux/types.h> > + > +#ifndef _UTHREAD_H_ > +#define _UTHREAD_H_ > + > +#ifdef CONFIG_UTHREAD > + > +int uthread_create(void (*fn)(void *), void *arg, size_t stack_sz); > +void uthread_free_all(void); > +/* Returns false when all threads are done */ > +bool uthread_schedule(void); > + > +#else > + > +static inline int uthread_create(void (*fn)(void *), void *arg, size_t stack_sz) > +{ > + fn(arg); > + return 0; > +} > + > +static inline void uthread_free_all(void) { } > + > +static inline bool uthread_schedule(void) { return false; } > + > +#endif /* CONFIG_UTHREAD */ > +#endif /* _UTHREAD_H_ */ > diff --git a/lib/Kconfig b/lib/Kconfig > index 1a683dea670..c3416bbd0be 100644 > --- a/lib/Kconfig > +++ b/lib/Kconfig > @@ -1255,6 +1255,25 @@ config PHANDLE_CHECK_SEQ > enable this config option to distinguish them using > phandles in fdtdec_get_alias_seq() function. > > +config UTHREAD > + bool "Enable thread support" > + depends on HAVE_INITJMP > + help > + Implement a simple form of cooperative multi-tasking based on > + context-switching via initjmp(), setjmp() and longjmp(). The > + uthread_ interface enables the main thread of execution to create > + one or more secondary threads and schedule them until they all have > + returned. At any point a thread may suspend its execution and > + schedule another thread, which allows for the efficient multiplexing > + of leghthy operations. > + > +config UTHREAD_STACK_SIZE > + int "Default uthread stack size" > + depends on UTHREAD > + default 32168 > + help > + The default stask size for uthreads. Each uthread has its own stack. > + > endmenu > > source "lib/fwu_updates/Kconfig" > diff --git a/lib/Makefile b/lib/Makefile > index a7bc2f3134a..3610694de7a 100644 > --- a/lib/Makefile > +++ b/lib/Makefile > @@ -164,6 +164,8 @@ obj-$(CONFIG_LIB_ELF) += elf.o > > obj-$(CONFIG_$(PHASE_)SEMIHOSTING) += semihosting.o > > +obj-$(CONFIG_UTHREAD) += uthread.o > + > # > # Build a fast OID lookup registry from include/linux/oid_registry.h > # > diff --git a/lib/uthread.c b/lib/uthread.c > new file mode 100644 > index 00000000000..bb132001fb6 > --- /dev/null > +++ b/lib/uthread.c > @@ -0,0 +1,107 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +// > +// Copyright (C) 2021 Ahmad Fatoum, Pengutronix > +// Copyright (C) 2025 Linaro Limited > +// > +// An implementation of cooperative multi-tasking inspired from barebox threads > +// https://github.com/barebox/barebox/blob/master/common/bthread.c > + > +#include <compiler.h> > +#include <asm/setjmp.h> > +#include <linux/kernel.h> > +#include <linux/list.h> > +#include <malloc.h> > +#include <stdint.h> > +#include <uthread.h> > + > +static struct uthread { > + void (*fn)(void *); > + void *arg; > + jmp_buf ctx; > + void *stack; > + bool done; > + struct list_head list; > +} main_thread = { > + .list = LIST_HEAD_INIT(main_thread.list), > +}; > + > +static struct uthread *current = &main_thread; > + > +static void __noreturn uthread_trampoline(void) > +{ > + current->fn(current->arg); > + current->done = true; > + current = &main_thread; > + longjmp(current->ctx, 1); > + /* Not reached */ > + while (true) > + ; > +} > + > +static void uthread_free(struct uthread *uthread) > +{ > + if (!uthread) > + return; > + free(uthread->stack); > + free(uthread); > +} > + > +int uthread_create(void (*fn)(void *), void *arg, size_t stack_sz) > +{ > + struct uthread *uthread; > + > + if (!stack_sz) > + stack_sz = CONFIG_UTHREAD_STACK_SIZE; > + > + uthread = calloc(1, sizeof(*uthread)); > + if (!uthread) > + return -1; > + > + uthread->stack = memalign(16, stack_sz); > + if (!uthread->stack) > + goto err; > + > + uthread->fn = fn; > + uthread->arg = arg; > + > + list_add_tail(&uthread->list, ¤t->list); > + > + initjmp(uthread->ctx, uthread_trampoline, uthread->stack + stack_sz); > + > + return 0; > +err: > + uthread_free(uthread); > + return -1; > +} > + > +void uthread_free_all(void) > +{ > + struct uthread *next; > + struct uthread *tmp; > + > + list_for_each_entry_safe(next, tmp, ¤t->list, list) { > + list_del(&next->list); > + uthread_free(next); > + } > +} > + For every function, please, create a Sphinx style documentation. For the whole uthread library, please, create a document in /doc/develop/ explaining how to use the API functions. Best regards Heinrich > +static void uthread_resume(struct uthread *uthread) > +{ > + if (!setjmp(current->ctx)) { > + current = uthread; > + longjmp(uthread->ctx, 1); > + } > +} > + > +bool uthread_schedule(void) > +{ > + struct uthread *next; > + > + list_for_each_entry(next, ¤t->list, list) { > + if (!next->done) { > + uthread_resume(next); > + return true; > + } > + } > + return false; > +}
On Fri, Feb 14, 2025 at 03:00:21PM +0100, Jerome Forissier wrote: > Add an new internal API called uthread (Kconfig symbol: UTHREAD) which > provides cooperative multi-tasking. The goal is to be able to improve > the performance of some parts of U-Boot by overlapping lengthy > operations. Each uthread has its own stack allocated on the heap. The > default stack size is defined by the UTHREAD_STACK_SIZE symbol and is > used when uthread_create() is passed zero for the stack_sz argument. > > The implementation is based on context-switching via initjmp()/setjmp()/ > longjmp() and is inspired from barebox threads [1]. > > [1] https://github.com/barebox/barebox/blob/master/common/bthread.c > > Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org> > --- > include/uthread.h | 31 ++++++++++++++ > lib/Kconfig | 19 ++++++++ > lib/Makefile | 2 + > lib/uthread.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 159 insertions(+) > create mode 100644 include/uthread.h > create mode 100644 lib/uthread.c > > diff --git a/lib/Kconfig b/lib/Kconfig > index 1a683dea670..c3416bbd0be 100644 > --- a/lib/Kconfig > +++ b/lib/Kconfig > @@ -1255,6 +1255,25 @@ config PHANDLE_CHECK_SEQ > enable this config option to distinguish them using > phandles in fdtdec_get_alias_seq() function. > > +config UTHREAD > + bool "Enable thread support" > + depends on HAVE_INITJMP > + help > + Implement a simple form of cooperative multi-tasking based on > + context-switching via initjmp(), setjmp() and longjmp(). The > + uthread_ interface enables the main thread of execution to create > + one or more secondary threads and schedule them until they all have > + returned. At any point a thread may suspend its execution and > + schedule another thread, which allows for the efficient multiplexing > + of leghthy operations. > + > +config UTHREAD_STACK_SIZE > + int "Default uthread stack size" > + depends on UTHREAD > + default 32168 Why choose 32168? It's even not a power of two. Typo for 32768? > + help > + The default stask size for uthreads. Each uthread has its own stack. > + > endmenu > > source "lib/fwu_updates/Kconfig" Thanks, Yao Zi
On 2/14/25 19:25, Yao Zi wrote: > On Fri, Feb 14, 2025 at 03:00:21PM +0100, Jerome Forissier wrote: >> Add an new internal API called uthread (Kconfig symbol: UTHREAD) which >> provides cooperative multi-tasking. The goal is to be able to improve >> the performance of some parts of U-Boot by overlapping lengthy >> operations. Each uthread has its own stack allocated on the heap. The >> default stack size is defined by the UTHREAD_STACK_SIZE symbol and is >> used when uthread_create() is passed zero for the stack_sz argument. >> >> The implementation is based on context-switching via initjmp()/setjmp()/ >> longjmp() and is inspired from barebox threads [1]. >> >> [1] https://github.com/barebox/barebox/blob/master/common/bthread.c >> >> Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org> >> --- >> include/uthread.h | 31 ++++++++++++++ >> lib/Kconfig | 19 ++++++++ >> lib/Makefile | 2 + >> lib/uthread.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++ >> 4 files changed, 159 insertions(+) >> create mode 100644 include/uthread.h >> create mode 100644 lib/uthread.c >> >> diff --git a/lib/Kconfig b/lib/Kconfig >> index 1a683dea670..c3416bbd0be 100644 >> --- a/lib/Kconfig >> +++ b/lib/Kconfig >> @@ -1255,6 +1255,25 @@ config PHANDLE_CHECK_SEQ >> enable this config option to distinguish them using >> phandles in fdtdec_get_alias_seq() function. >> >> +config UTHREAD >> + bool "Enable thread support" >> + depends on HAVE_INITJMP >> + help >> + Implement a simple form of cooperative multi-tasking based on >> + context-switching via initjmp(), setjmp() and longjmp(). The >> + uthread_ interface enables the main thread of execution to create >> + one or more secondary threads and schedule them until they all have >> + returned. At any point a thread may suspend its execution and >> + schedule another thread, which allows for the efficient multiplexing >> + of leghthy operations. >> + >> +config UTHREAD_STACK_SIZE >> + int "Default uthread stack size" >> + depends on UTHREAD >> + default 32168 > > Why choose 32168? It's even not a power of two. Typo for 32768? It is indeed a typo which I will fix in v2. 32 KiB is quite arbitrary and this value is used only when the stack_sz argument to uthread_create() is zero (hence the wording "default stack size"). I believe it is large enough for most needs, and not too large to not exhaust the heap when a few threads are created. > >> + help >> + The default stask size for uthreads. Each uthread has its own stack. >> + >> endmenu >> >> source "lib/fwu_updates/Kconfig" > > Thanks, > Yao Zi Thanks,
diff --git a/include/uthread.h b/include/uthread.h new file mode 100644 index 00000000000..ea1389e82c6 --- /dev/null +++ b/include/uthread.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2025 Linaro Limited + */ + +#include <linux/types.h> + +#ifndef _UTHREAD_H_ +#define _UTHREAD_H_ + +#ifdef CONFIG_UTHREAD + +int uthread_create(void (*fn)(void *), void *arg, size_t stack_sz); +void uthread_free_all(void); +/* Returns false when all threads are done */ +bool uthread_schedule(void); + +#else + +static inline int uthread_create(void (*fn)(void *), void *arg, size_t stack_sz) +{ + fn(arg); + return 0; +} + +static inline void uthread_free_all(void) { } + +static inline bool uthread_schedule(void) { return false; } + +#endif /* CONFIG_UTHREAD */ +#endif /* _UTHREAD_H_ */ diff --git a/lib/Kconfig b/lib/Kconfig index 1a683dea670..c3416bbd0be 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -1255,6 +1255,25 @@ config PHANDLE_CHECK_SEQ enable this config option to distinguish them using phandles in fdtdec_get_alias_seq() function. +config UTHREAD + bool "Enable thread support" + depends on HAVE_INITJMP + help + Implement a simple form of cooperative multi-tasking based on + context-switching via initjmp(), setjmp() and longjmp(). The + uthread_ interface enables the main thread of execution to create + one or more secondary threads and schedule them until they all have + returned. At any point a thread may suspend its execution and + schedule another thread, which allows for the efficient multiplexing + of leghthy operations. + +config UTHREAD_STACK_SIZE + int "Default uthread stack size" + depends on UTHREAD + default 32168 + help + The default stask size for uthreads. Each uthread has its own stack. + endmenu source "lib/fwu_updates/Kconfig" diff --git a/lib/Makefile b/lib/Makefile index a7bc2f3134a..3610694de7a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -164,6 +164,8 @@ obj-$(CONFIG_LIB_ELF) += elf.o obj-$(CONFIG_$(PHASE_)SEMIHOSTING) += semihosting.o +obj-$(CONFIG_UTHREAD) += uthread.o + # # Build a fast OID lookup registry from include/linux/oid_registry.h # diff --git a/lib/uthread.c b/lib/uthread.c new file mode 100644 index 00000000000..bb132001fb6 --- /dev/null +++ b/lib/uthread.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright (C) 2021 Ahmad Fatoum, Pengutronix +// Copyright (C) 2025 Linaro Limited +// +// An implementation of cooperative multi-tasking inspired from barebox threads +// https://github.com/barebox/barebox/blob/master/common/bthread.c + +#include <compiler.h> +#include <asm/setjmp.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <malloc.h> +#include <stdint.h> +#include <uthread.h> + +static struct uthread { + void (*fn)(void *); + void *arg; + jmp_buf ctx; + void *stack; + bool done; + struct list_head list; +} main_thread = { + .list = LIST_HEAD_INIT(main_thread.list), +}; + +static struct uthread *current = &main_thread; + +static void __noreturn uthread_trampoline(void) +{ + current->fn(current->arg); + current->done = true; + current = &main_thread; + longjmp(current->ctx, 1); + /* Not reached */ + while (true) + ; +} + +static void uthread_free(struct uthread *uthread) +{ + if (!uthread) + return; + free(uthread->stack); + free(uthread); +} + +int uthread_create(void (*fn)(void *), void *arg, size_t stack_sz) +{ + struct uthread *uthread; + + if (!stack_sz) + stack_sz = CONFIG_UTHREAD_STACK_SIZE; + + uthread = calloc(1, sizeof(*uthread)); + if (!uthread) + return -1; + + uthread->stack = memalign(16, stack_sz); + if (!uthread->stack) + goto err; + + uthread->fn = fn; + uthread->arg = arg; + + list_add_tail(&uthread->list, ¤t->list); + + initjmp(uthread->ctx, uthread_trampoline, uthread->stack + stack_sz); + + return 0; +err: + uthread_free(uthread); + return -1; +} + +void uthread_free_all(void) +{ + struct uthread *next; + struct uthread *tmp; + + list_for_each_entry_safe(next, tmp, ¤t->list, list) { + list_del(&next->list); + uthread_free(next); + } +} + +static void uthread_resume(struct uthread *uthread) +{ + if (!setjmp(current->ctx)) { + current = uthread; + longjmp(uthread->ctx, 1); + } +} + +bool uthread_schedule(void) +{ + struct uthread *next; + + list_for_each_entry(next, ¤t->list, list) { + if (!next->done) { + uthread_resume(next); + return true; + } + } + return false; +}
Add an new internal API called uthread (Kconfig symbol: UTHREAD) which provides cooperative multi-tasking. The goal is to be able to improve the performance of some parts of U-Boot by overlapping lengthy operations. Each uthread has its own stack allocated on the heap. The default stack size is defined by the UTHREAD_STACK_SIZE symbol and is used when uthread_create() is passed zero for the stack_sz argument. The implementation is based on context-switching via initjmp()/setjmp()/ longjmp() and is inspired from barebox threads [1]. [1] https://github.com/barebox/barebox/blob/master/common/bthread.c Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org> --- include/uthread.h | 31 ++++++++++++++ lib/Kconfig | 19 ++++++++ lib/Makefile | 2 + lib/uthread.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 159 insertions(+) create mode 100644 include/uthread.h create mode 100644 lib/uthread.c