* [RFC PATCH 01/10] arch: introduce symbol HAVE_INITJMP
2025-02-14 14:00 [RFC PATCH 00/10] Uthreads Jerome Forissier
@ 2025-02-14 14:00 ` Jerome Forissier
2025-02-14 14:41 ` Heinrich Schuchardt
2025-02-14 14:00 ` [RFC PATCH 02/10] arm: add initjmp() Jerome Forissier
` (8 subsequent siblings)
9 siblings, 1 reply; 20+ messages in thread
From: Jerome Forissier @ 2025-02-14 14:00 UTC (permalink / raw)
To: u-boot
Cc: Ilias Apalodimas, Jerome Forissier, Tom Rini, Simon Glass,
Heinrich Schuchardt, Dan Carpenter, Andrew Goodbody,
Yu-Chien Peter Lin, Jiaxun Yang
HAVE_INIJMP will be set by architectures that support initjmp(), a
non-standard extension to setjmp()/longjmp() allowing to initialize a
jump buffer with a function pointer and a stack pointer. This will be
useful to later introduce threads.
Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
---
arch/Kconfig | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/Kconfig b/arch/Kconfig
index 35b19f9bfdc..8d5b54031b3 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -13,6 +13,12 @@ config HAVE_SETJMP
help
The architecture supports setjmp() and longjmp().
+config HAVE_INITJMP
+ bool
+ help
+ The architecture supports initjmp(), a non-standard companion to
+ setjmp() and longjmp().
+
config SUPPORT_BIG_ENDIAN
bool
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread* Re: [RFC PATCH 01/10] arch: introduce symbol HAVE_INITJMP
2025-02-14 14:00 ` [RFC PATCH 01/10] arch: introduce symbol HAVE_INITJMP Jerome Forissier
@ 2025-02-14 14:41 ` Heinrich Schuchardt
2025-02-14 15:03 ` Jerome Forissier
0 siblings, 1 reply; 20+ messages in thread
From: Heinrich Schuchardt @ 2025-02-14 14:41 UTC (permalink / raw)
To: Jerome Forissier
Cc: Ilias Apalodimas, Tom Rini, Simon Glass, Dan Carpenter,
Andrew Goodbody, Yu-Chien Peter Lin, Jiaxun Yang, u-boot
On 14.02.25 15:00, Jerome Forissier wrote:
> HAVE_INIJMP will be set by architectures that support initjmp(), a
> non-standard extension to setjmp()/longjmp() allowing to initialize a
> jump buffer with a function pointer and a stack pointer. This will be
> useful to later introduce threads.
How is initjmp() used in threading?
A cover-letter for the series would have been helpful.
>
> Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
> ---
> arch/Kconfig | 6 ++++++
> 1 file changed, 6 insertions(+)
>
> diff --git a/arch/Kconfig b/arch/Kconfig
> index 35b19f9bfdc..8d5b54031b3 100644
> --- a/arch/Kconfig
> +++ b/arch/Kconfig
> @@ -13,6 +13,12 @@ config HAVE_SETJMP
> help
> The architecture supports setjmp() and longjmp().
>
> +config HAVE_INITJMP
> + bool
> + help
> + The architecture supports initjmp(), a non-standard companion to
> + setjmp() and longjmp().
If this required for thread support, I would have expected this to be in
the help text.
Best regards
Heinrich
> +
> config SUPPORT_BIG_ENDIAN
> bool
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFC PATCH 01/10] arch: introduce symbol HAVE_INITJMP
2025-02-14 14:41 ` Heinrich Schuchardt
@ 2025-02-14 15:03 ` Jerome Forissier
0 siblings, 0 replies; 20+ messages in thread
From: Jerome Forissier @ 2025-02-14 15:03 UTC (permalink / raw)
To: Heinrich Schuchardt
Cc: Ilias Apalodimas, Tom Rini, Simon Glass, Dan Carpenter,
Andrew Goodbody, Yu-Chien Peter Lin, Jiaxun Yang, u-boot
Hi Heinrich,
On 2/14/25 15:41, Heinrich Schuchardt wrote:
> On 14.02.25 15:00, Jerome Forissier wrote:
>> HAVE_INIJMP will be set by architectures that support initjmp(), a
>> non-standard extension to setjmp()/longjmp() allowing to initialize a
>> jump buffer with a function pointer and a stack pointer. This will be
>> useful to later introduce threads.
>
> How is initjmp() used in threading?
It's used so that we can longjmp() to a particular function pointer
(rather than to a point previously reached during program execution
as is the case with setjmp()), and with a custom stack. Both things
are needed to spin off a thread.
I will update the description.
>
> A cover-letter for the series would have been helpful.
It's here [1].
[1] https://lists.denx.de/pipermail/u-boot/2025-February/580550.html
>
>>
>> Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
>> ---
>> arch/Kconfig | 6 ++++++
>> 1 file changed, 6 insertions(+)
>>
>> diff --git a/arch/Kconfig b/arch/Kconfig
>> index 35b19f9bfdc..8d5b54031b3 100644
>> --- a/arch/Kconfig
>> +++ b/arch/Kconfig
>> @@ -13,6 +13,12 @@ config HAVE_SETJMP
>> help
>> The architecture supports setjmp() and longjmp().
>>
>> +config HAVE_INITJMP
>> + bool
>> + help
>> + The architecture supports initjmp(), a non-standard companion to
>> + setjmp() and longjmp().
>
> If this required for thread support, I would have expected this to be in
> the help text.
>
> Best regards
>
> Heinrich
>
>> +
>> config SUPPORT_BIG_ENDIAN
>> bool
>>
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* [RFC PATCH 02/10] arm: add initjmp()
2025-02-14 14:00 [RFC PATCH 00/10] Uthreads Jerome Forissier
2025-02-14 14:00 ` [RFC PATCH 01/10] arch: introduce symbol HAVE_INITJMP Jerome Forissier
@ 2025-02-14 14:00 ` Jerome Forissier
2025-02-14 14:35 ` Heinrich Schuchardt
2025-02-14 14:00 ` [RFC PATCH 03/10] riscv: " Jerome Forissier
` (7 subsequent siblings)
9 siblings, 1 reply; 20+ messages in thread
From: Jerome Forissier @ 2025-02-14 14:00 UTC (permalink / raw)
To: u-boot
Cc: Ilias Apalodimas, Jerome Forissier, Tom Rini, Dan Carpenter,
Simon Glass, Heinrich Schuchardt, Jiaxun Yang, Yu-Chien Peter Lin,
Andrew Goodbody
Implement initjmp() for Arm.
Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
---
arch/Kconfig | 1 +
arch/arm/include/asm/setjmp.h | 1 +
arch/arm/lib/setjmp.S | 11 +++++++++++
arch/arm/lib/setjmp_aarch64.S | 9 +++++++++
4 files changed, 22 insertions(+)
diff --git a/arch/Kconfig b/arch/Kconfig
index 8d5b54031b3..57695fada8d 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -94,6 +94,7 @@ config ARC
config ARM
bool "ARM architecture"
select HAVE_SETJMP
+ select HAVE_INITJMP
select ARCH_SUPPORTS_LTO
select CREATE_ARCH_SYMLINK
select HAVE_PRIVATE_LIBGCC if !ARM64
diff --git a/arch/arm/include/asm/setjmp.h b/arch/arm/include/asm/setjmp.h
index 662bec86321..1ad5b500f2a 100644
--- a/arch/arm/include/asm/setjmp.h
+++ b/arch/arm/include/asm/setjmp.h
@@ -23,5 +23,6 @@ typedef struct jmp_buf_data jmp_buf[1];
int setjmp(jmp_buf jmp);
void longjmp(jmp_buf jmp, int ret);
+int initjmp(jmp_buf jmp, void __noreturn (*func)(void), void *stack_top);
#endif /* _SETJMP_H_ */
diff --git a/arch/arm/lib/setjmp.S b/arch/arm/lib/setjmp.S
index 2f041aeef01..320ddea85f9 100644
--- a/arch/arm/lib/setjmp.S
+++ b/arch/arm/lib/setjmp.S
@@ -34,3 +34,14 @@ ENTRY(longjmp)
ret lr
ENDPROC(longjmp)
.popsection
+
+.pushsection .text.initjmp, "ax"
+ENTRY(initjmp)
+ stm a1, {v1-v8}
+ /* a2: entry point address, a3: stack top */
+ str a3, [a1, #32] /* where setjmp would save sp */
+ str a2, [a1, #36] /* where setjmp would save lr */
+ mov a1, #0
+ ret lr
+ENDPROC(initjmp)
+.popsection
diff --git a/arch/arm/lib/setjmp_aarch64.S b/arch/arm/lib/setjmp_aarch64.S
index 1b8d000eb48..074320d25fb 100644
--- a/arch/arm/lib/setjmp_aarch64.S
+++ b/arch/arm/lib/setjmp_aarch64.S
@@ -39,3 +39,12 @@ ENTRY(longjmp)
ret
ENDPROC(longjmp)
.popsection
+
+.pushsection .text.initjmp, "ax"
+ENTRY(initjmp)
+ /* x1: entry point address, x2: stack top */
+ stp x1, x2, [x0,#88]
+ mov x0, #0
+ ret
+ENDPROC(initjmp)
+.popsection
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread* Re: [RFC PATCH 02/10] arm: add initjmp()
2025-02-14 14:00 ` [RFC PATCH 02/10] arm: add initjmp() Jerome Forissier
@ 2025-02-14 14:35 ` Heinrich Schuchardt
2025-02-17 13:10 ` Jerome Forissier
0 siblings, 1 reply; 20+ messages in thread
From: Heinrich Schuchardt @ 2025-02-14 14:35 UTC (permalink / raw)
To: Jerome Forissier
Cc: Ilias Apalodimas, Tom Rini, Dan Carpenter, Simon Glass,
Jiaxun Yang, Yu-Chien Peter Lin, Andrew Goodbody, u-boot
On 14.02.25 15:00, Jerome Forissier wrote:
> Implement initjmp() for Arm.
Could you, please, describe in the commit message what initjmp() good
for and why it is needed and why it would not be needed for any other
architectures but ARM.
>
> Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
> ---
> arch/Kconfig | 1 +
> arch/arm/include/asm/setjmp.h | 1 +
> arch/arm/lib/setjmp.S | 11 +++++++++++
> arch/arm/lib/setjmp_aarch64.S | 9 +++++++++
> 4 files changed, 22 insertions(+)
>
> diff --git a/arch/Kconfig b/arch/Kconfig
> index 8d5b54031b3..57695fada8d 100644
> --- a/arch/Kconfig
> +++ b/arch/Kconfig
> @@ -94,6 +94,7 @@ config ARC
> config ARM
> bool "ARM architecture"
> select HAVE_SETJMP
> + select HAVE_INITJMP
> select ARCH_SUPPORTS_LTO
> select CREATE_ARCH_SYMLINK
> select HAVE_PRIVATE_LIBGCC if !ARM64
> diff --git a/arch/arm/include/asm/setjmp.h b/arch/arm/include/asm/setjmp.h
> index 662bec86321..1ad5b500f2a 100644
> --- a/arch/arm/include/asm/setjmp.h
> +++ b/arch/arm/include/asm/setjmp.h
> @@ -23,5 +23,6 @@ typedef struct jmp_buf_data jmp_buf[1];
>
> int setjmp(jmp_buf jmp);
> void longjmp(jmp_buf jmp, int ret);
Please, always provide a Sphinx description for any new function.
Best regards
Heinrich
> +int initjmp(jmp_buf jmp, void __noreturn (*func)(void), void *stack_top);
>
> #endif /* _SETJMP_H_ */
> diff --git a/arch/arm/lib/setjmp.S b/arch/arm/lib/setjmp.S
> index 2f041aeef01..320ddea85f9 100644
> --- a/arch/arm/lib/setjmp.S
> +++ b/arch/arm/lib/setjmp.S
> @@ -34,3 +34,14 @@ ENTRY(longjmp)
> ret lr
> ENDPROC(longjmp)
> .popsection
> +
> +.pushsection .text.initjmp, "ax"
> +ENTRY(initjmp)
> + stm a1, {v1-v8}
> + /* a2: entry point address, a3: stack top */
> + str a3, [a1, #32] /* where setjmp would save sp */
> + str a2, [a1, #36] /* where setjmp would save lr */
> + mov a1, #0
> + ret lr
> +ENDPROC(initjmp)
> +.popsection
> diff --git a/arch/arm/lib/setjmp_aarch64.S b/arch/arm/lib/setjmp_aarch64.S
> index 1b8d000eb48..074320d25fb 100644
> --- a/arch/arm/lib/setjmp_aarch64.S
> +++ b/arch/arm/lib/setjmp_aarch64.S
> @@ -39,3 +39,12 @@ ENTRY(longjmp)
> ret
> ENDPROC(longjmp)
> .popsection
> +
> +.pushsection .text.initjmp, "ax"
> +ENTRY(initjmp)
> + /* x1: entry point address, x2: stack top */
> + stp x1, x2, [x0,#88]
> + mov x0, #0
> + ret
> +ENDPROC(initjmp)
> +.popsection
^ permalink raw reply [flat|nested] 20+ messages in thread* Re: [RFC PATCH 02/10] arm: add initjmp()
2025-02-14 14:35 ` Heinrich Schuchardt
@ 2025-02-17 13:10 ` Jerome Forissier
0 siblings, 0 replies; 20+ messages in thread
From: Jerome Forissier @ 2025-02-17 13:10 UTC (permalink / raw)
To: Heinrich Schuchardt
Cc: Ilias Apalodimas, Tom Rini, Dan Carpenter, Simon Glass,
Jiaxun Yang, Yu-Chien Peter Lin, Andrew Goodbody, u-boot
On 2/14/25 15:35, Heinrich Schuchardt wrote:
> On 14.02.25 15:00, Jerome Forissier wrote:
>> Implement initjmp() for Arm.
>
> Could you, please, describe in the commit message what initjmp() good
> for and why it is needed and why it would not be needed for any other
> architectures but ARM.
Sure. If you don't mind, I will update the commit message of "arch:
introduce symbol HAVE_INITJMP" rather than this one, to avoid duplication
for each architecture. I mean, if I update "arm: add initjmp()" then why
not "riscv: add initjmp()".
>
>>
>> Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
>> ---
>> arch/Kconfig | 1 +
>> arch/arm/include/asm/setjmp.h | 1 +
>> arch/arm/lib/setjmp.S | 11 +++++++++++
>> arch/arm/lib/setjmp_aarch64.S | 9 +++++++++
>> 4 files changed, 22 insertions(+)
>>
>> diff --git a/arch/Kconfig b/arch/Kconfig
>> index 8d5b54031b3..57695fada8d 100644
>> --- a/arch/Kconfig
>> +++ b/arch/Kconfig
>> @@ -94,6 +94,7 @@ config ARC
>> config ARM
>> bool "ARM architecture"
>> select HAVE_SETJMP
>> + select HAVE_INITJMP
>> select ARCH_SUPPORTS_LTO
>> select CREATE_ARCH_SYMLINK
>> select HAVE_PRIVATE_LIBGCC if !ARM64
>> diff --git a/arch/arm/include/asm/setjmp.h b/arch/arm/include/asm/setjmp.h
>> index 662bec86321..1ad5b500f2a 100644
>> --- a/arch/arm/include/asm/setjmp.h
>> +++ b/arch/arm/include/asm/setjmp.h
>> @@ -23,5 +23,6 @@ typedef struct jmp_buf_data jmp_buf[1];
>>
>> int setjmp(jmp_buf jmp);
>> void longjmp(jmp_buf jmp, int ret);
>
> Please, always provide a Sphinx description for any new function.
Will do in v2.
>
> Best regards
>
> Heinrich
>
>> +int initjmp(jmp_buf jmp, void __noreturn (*func)(void), void *stack_top);
>>
>> #endif /* _SETJMP_H_ */
>> diff --git a/arch/arm/lib/setjmp.S b/arch/arm/lib/setjmp.S
>> index 2f041aeef01..320ddea85f9 100644
>> --- a/arch/arm/lib/setjmp.S
>> +++ b/arch/arm/lib/setjmp.S
>> @@ -34,3 +34,14 @@ ENTRY(longjmp)
>> ret lr
>> ENDPROC(longjmp)
>> .popsection
>> +
>> +.pushsection .text.initjmp, "ax"
>> +ENTRY(initjmp)
>> + stm a1, {v1-v8}
>> + /* a2: entry point address, a3: stack top */
>> + str a3, [a1, #32] /* where setjmp would save sp */
>> + str a2, [a1, #36] /* where setjmp would save lr */
>> + mov a1, #0
>> + ret lr
>> +ENDPROC(initjmp)
>> +.popsection
>> diff --git a/arch/arm/lib/setjmp_aarch64.S b/arch/arm/lib/setjmp_aarch64.S
>> index 1b8d000eb48..074320d25fb 100644
>> --- a/arch/arm/lib/setjmp_aarch64.S
>> +++ b/arch/arm/lib/setjmp_aarch64.S
>> @@ -39,3 +39,12 @@ ENTRY(longjmp)
>> ret
>> ENDPROC(longjmp)
>> .popsection
>> +
>> +.pushsection .text.initjmp, "ax"
>> +ENTRY(initjmp)
>> + /* x1: entry point address, x2: stack top */
>> + stp x1, x2, [x0,#88]
>> + mov x0, #0
>> + ret
>> +ENDPROC(initjmp)
>> +.popsection
>
Thanks,
--
Jerome
^ permalink raw reply [flat|nested] 20+ messages in thread
* [RFC PATCH 03/10] riscv: add initjmp()
2025-02-14 14:00 [RFC PATCH 00/10] Uthreads Jerome Forissier
2025-02-14 14:00 ` [RFC PATCH 01/10] arch: introduce symbol HAVE_INITJMP Jerome Forissier
2025-02-14 14:00 ` [RFC PATCH 02/10] arm: add initjmp() Jerome Forissier
@ 2025-02-14 14:00 ` Jerome Forissier
2025-02-14 14:00 ` [RFC PATCH 04/10] sandbox: " Jerome Forissier
` (6 subsequent siblings)
9 siblings, 0 replies; 20+ messages in thread
From: Jerome Forissier @ 2025-02-14 14:00 UTC (permalink / raw)
To: u-boot
Cc: Ilias Apalodimas, Jerome Forissier, Tom Rini, Rick Chen, Leo,
Dan Carpenter, Simon Glass, Heinrich Schuchardt, Jiaxun Yang,
Andrew Goodbody, Yu-Chien Peter Lin
Implement initjmp() for RISC-V.
Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
---
arch/Kconfig | 1 +
arch/riscv/include/asm/setjmp.h | 1 +
arch/riscv/lib/setjmp.S | 10 ++++++++++
3 files changed, 12 insertions(+)
diff --git a/arch/Kconfig b/arch/Kconfig
index 57695fada8d..b745222bfbe 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -152,6 +152,7 @@ config RISCV
bool "RISC-V architecture"
select CREATE_ARCH_SYMLINK
select HAVE_SETJMP
+ select HAVE_INITJMP
select SUPPORT_ACPI
select SUPPORT_LITTLE_ENDIAN
select SUPPORT_OF_CONTROL
diff --git a/arch/riscv/include/asm/setjmp.h b/arch/riscv/include/asm/setjmp.h
index 72383d43303..98ea157eb3b 100644
--- a/arch/riscv/include/asm/setjmp.h
+++ b/arch/riscv/include/asm/setjmp.h
@@ -21,5 +21,6 @@ typedef struct jmp_buf_data jmp_buf[1];
int setjmp(jmp_buf jmp);
void longjmp(jmp_buf jmp, int ret);
+int initjmp(jmp_buf jmp, void __noreturn (*func)(void), void *stack_top);
#endif /* _SETJMP_H_ */
diff --git a/arch/riscv/lib/setjmp.S b/arch/riscv/lib/setjmp.S
index 99d6195827e..6f952a16eee 100644
--- a/arch/riscv/lib/setjmp.S
+++ b/arch/riscv/lib/setjmp.S
@@ -59,3 +59,13 @@ ENTRY(longjmp)
ret
ENDPROC(longjmp)
.popsection
+
+.pushsection .text.initjmp, "ax"
+ENTRY(initjmp)
+ /* a1: entry point address, a2: stack top */
+ STORE_IDX(a1, 12)
+ STORE_IDX(a2, 13)
+ li a0, 0
+ ret
+ENDPROC(initjmp)
+.popsection
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread* [RFC PATCH 04/10] sandbox: add initjmp()
2025-02-14 14:00 [RFC PATCH 00/10] Uthreads Jerome Forissier
` (2 preceding siblings ...)
2025-02-14 14:00 ` [RFC PATCH 03/10] riscv: " Jerome Forissier
@ 2025-02-14 14:00 ` Jerome Forissier
2025-02-14 14:00 ` [RFC PATCH 05/10] test: lib: add initjmp() test Jerome Forissier
` (5 subsequent siblings)
9 siblings, 0 replies; 20+ messages in thread
From: Jerome Forissier @ 2025-02-14 14:00 UTC (permalink / raw)
To: u-boot; +Cc: Ilias Apalodimas, Jerome Forissier, Simon Glass, Tom Rini
Add ininijmp() to sandbox. The implementation is taken verbatim from
barebox [1]. It is quite complex because contrary to U-Boot platform
code we don't know how the system's C library implements the jump
buffer, so we can't just write the function and stack pointers into it.
FIXME: this patch should make SANDBOX select HAVE_INITJMP (in
arch/Kconfig). It does not due to the following error detected by CI:
_________________________ test_ut[ut_lib_lib_initjmp] __________________________
test/py/tests/test_ut.py:608: in test_ut
output = u_boot_console.run_command('ut ' + ut_subtest)
test/py/u_boot_console_base.py:334: in run_command
m = self.p.expect([self.prompt_compiled] + self.bad_patterns)
test/py/u_boot_spawn.py:296: in expect
c = self.receive(1024)
test/py/u_boot_spawn.py:235: in receive
raise err
test/py/u_boot_spawn.py:227: in receive
c = os.read(self.fd, num_bytes).decode(errors='replace')
E OSError: [Errno 5] Input/output error
----------------------------- Captured stdout call -----------------------------
=> ut lib lib_initjmp
Test: lib_initjmp: initjmp.c
Failures: 0
common/dlmalloc.c:796: do_check_free_chunk: Assertion `next == top || inuse(next)' failed.common/dlmalloc.c:796: do_check_free_chunk: Assertion `next == top || inuse(next)' failed.
---------------- generated xml file: /tmp/sandbox64/results.xml ----------------
On x86 the dmalloc error is not printed but the I/O error is still there.
[1] https://github.com/barebox/barebox/blob/b2a15c383ddc/arch/sandbox/os/setjmp.c
Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
---
arch/sandbox/cpu/Makefile | 11 +-
arch/sandbox/cpu/initjmp.c | 172 ++++++++++++++++++++++++++++++
arch/sandbox/include/asm/setjmp.h | 5 +
3 files changed, 187 insertions(+), 1 deletion(-)
create mode 100644 arch/sandbox/cpu/initjmp.c
diff --git a/arch/sandbox/cpu/Makefile b/arch/sandbox/cpu/Makefile
index bfcdc335d32..038ad78accc 100644
--- a/arch/sandbox/cpu/Makefile
+++ b/arch/sandbox/cpu/Makefile
@@ -5,7 +5,7 @@
# (C) Copyright 2000-2003
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
-obj-y := cache.o cpu.o state.o
+obj-y := cache.o cpu.o state.o initjmp.o
extra-y := start.o os.o
extra-$(CONFIG_SANDBOX_SDL) += sdl.o
obj-$(CONFIG_XPL_BUILD) += spl.o
@@ -29,6 +29,15 @@ cmd_cc_eth-raw-os.o = $(CC) $(filter-out -nostdinc, \
$(obj)/eth-raw-os.o: $(src)/eth-raw-os.c FORCE
$(call if_changed_dep,cc_eth-raw-os.o)
+# initjmp.c is build in the system environment, so needs standard includes
+# CFLAGS_REMOVE_initjmp.o cannot be used to drop header include path
+quiet_cmd_cc_initjmp.o = CC $(quiet_modtag) $@
+cmd_cc_initjmp.o = $(CC) $(filter-out -nostdinc, \
+ $(patsubst -I%,-idirafter%,$(c_flags))) -c -o $@ $<
+
+$(obj)/initjmp.o: $(src)/initjmp.c FORCE
+ $(call if_changed_dep,cc_initjmp.o)
+
# sdl.c fails to build with -fshort-wchar using musl
cmd_cc_sdl.o = $(CC) $(filter-out -nostdinc -fshort-wchar, \
$(patsubst -I%,-idirafter%,$(c_flags))) -fno-lto -c -o $@ $<
diff --git a/arch/sandbox/cpu/initjmp.c b/arch/sandbox/cpu/initjmp.c
new file mode 100644
index 00000000000..c99721423c5
--- /dev/null
+++ b/arch/sandbox/cpu/initjmp.c
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ * An implementation of initjmp() in C, that plays well with the system's
+ * setjmp() and longjmp() functions.
+ * Taken verbatim from arch/sandbox/os/setjmp.c in the barebox project.
+ *
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
+ * Copyright (C) 2011 Kevin Wolf <kwolf@redhat.com>
+ * Copyright (C) 2012 Alex Barcelo <abarcelo@ac.upc.edu>
+ * Copyright (C) 2021 Ahmad Fatoum, Pengutronix
+ * This file is partly based on pth_mctx.c, from the GNU Portable Threads
+ * Copyright (c) 1999-2006 Ralf S. Engelschall <rse@engelschall.com>
+ */
+
+/* XXX Is there a nicer way to disable glibc's stack check for longjmp? */
+#ifdef _FORTIFY_SOURCE
+#undef _FORTIFY_SOURCE
+#endif
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <signal.h>
+
+typedef sigjmp_buf _jmp_buf __attribute__((aligned((16))));
+_Static_assert(sizeof(_jmp_buf) <= 512, "sigjmp_buf size exceeds expectation");
+
+/*
+ * Information for the signal handler (trampoline)
+ */
+static struct {
+ _jmp_buf *reenter;
+ void (*entry)(void);
+ volatile sig_atomic_t called;
+} tr_state;
+
+/*
+ * "boot" function
+ * This is what starts the coroutine, is called from the trampoline
+ * (from the signal handler when it is not signal handling, read ahead
+ * for more information).
+ */
+static void __attribute__((noinline, noreturn))
+coroutine_bootstrap(void (*entry)(void))
+{
+ for (;;)
+ entry();
+}
+
+/*
+ * This is used as the signal handler. This is called with the brand new stack
+ * (thanks to sigaltstack). We have to return, given that this is a signal
+ * handler and the sigmask and some other things are changed.
+ */
+static void coroutine_trampoline(int signal)
+{
+ /* Get the thread specific information */
+ tr_state.called = 1;
+
+ /*
+ * Here we have to do a bit of a ping pong between the caller, given that
+ * this is a signal handler and we have to do a return "soon". Then the
+ * caller can reestablish everything and do a siglongjmp here again.
+ */
+ if (!sigsetjmp(*tr_state.reenter, 0)) {
+ return;
+ }
+
+ /*
+ * Ok, the caller has siglongjmp'ed back to us, so now prepare
+ * us for the real machine state switching. We have to jump
+ * into another function here to get a new stack context for
+ * the auto variables (which have to be auto-variables
+ * because the start of the thread happens later). Else with
+ * PIC (i.e. Position Independent Code which is used when PTH
+ * is built as a shared library) most platforms would
+ * horrible core dump as experience showed.
+ */
+ coroutine_bootstrap(tr_state.entry);
+}
+
+int __attribute__((weak)) initjmp(_jmp_buf jmp, void (*func)(void), void *stack_top)
+{
+ struct sigaction sa;
+ struct sigaction osa;
+ stack_t ss;
+ stack_t oss;
+ sigset_t sigs;
+ sigset_t osigs;
+
+ /* The way to manipulate stack is with the sigaltstack function. We
+ * prepare a stack, with it delivering a signal to ourselves and then
+ * put sigsetjmp/siglongjmp where needed.
+ * This has been done keeping coroutine-ucontext (from the QEMU project)
+ * as a model and with the pth ideas (GNU Portable Threads).
+ * See coroutine-ucontext for the basics of the coroutines and see
+ * pth_mctx.c (from the pth project) for the
+ * sigaltstack way of manipulating stacks.
+ */
+
+ tr_state.entry = func;
+ tr_state.reenter = (void *)jmp;
+
+ /*
+ * Preserve the SIGUSR2 signal state, block SIGUSR2,
+ * and establish our signal handler. The signal will
+ * later transfer control onto the signal stack.
+ */
+ sigemptyset(&sigs);
+ sigaddset(&sigs, SIGUSR2);
+ pthread_sigmask(SIG_BLOCK, &sigs, &osigs);
+ sa.sa_handler = coroutine_trampoline;
+ sigfillset(&sa.sa_mask);
+ sa.sa_flags = SA_ONSTACK;
+ if (sigaction(SIGUSR2, &sa, &osa) != 0) {
+ return -1;
+ }
+
+ /*
+ * Set the new stack.
+ */
+ ss.ss_sp = stack_top - CONFIG_STACK_SIZE;
+ ss.ss_size = CONFIG_STACK_SIZE;
+ ss.ss_flags = 0;
+ if (sigaltstack(&ss, &oss) < 0) {
+ return -1;
+ }
+
+ /*
+ * Now transfer control onto the signal stack and set it up.
+ * It will return immediately via "return" after the sigsetjmp()
+ * was performed. Be careful here with race conditions. The
+ * signal can be delivered the first time sigsuspend() is
+ * called.
+ */
+ tr_state.called = 0;
+ pthread_kill(pthread_self(), SIGUSR2);
+ sigfillset(&sigs);
+ sigdelset(&sigs, SIGUSR2);
+ while (!tr_state.called) {
+ sigsuspend(&sigs);
+ }
+
+ /*
+ * Inform the system that we are back off the signal stack by
+ * removing the alternative signal stack. Be careful here: It
+ * first has to be disabled, before it can be removed.
+ */
+ sigaltstack(NULL, &ss);
+ ss.ss_flags = SS_DISABLE;
+ if (sigaltstack(&ss, NULL) < 0) {
+ return -1;
+ }
+ sigaltstack(NULL, &ss);
+ if (!(oss.ss_flags & SS_DISABLE)) {
+ sigaltstack(&oss, NULL);
+ }
+
+ /*
+ * Restore the old SIGUSR2 signal handler and mask
+ */
+ sigaction(SIGUSR2, &osa, NULL);
+ pthread_sigmask(SIG_SETMASK, &osigs, NULL);
+
+ /*
+ * jmp can now be used to enter the trampoline again, but not as a
+ * signal handler. Instead it's longjmp'd to directly.
+ */
+ return 0;
+}
+
diff --git a/arch/sandbox/include/asm/setjmp.h b/arch/sandbox/include/asm/setjmp.h
index 001c7ea322d..d708e6da3fc 100644
--- a/arch/sandbox/include/asm/setjmp.h
+++ b/arch/sandbox/include/asm/setjmp.h
@@ -31,5 +31,10 @@ typedef struct jmp_buf_data jmp_buf[1];
*/
int setjmp(jmp_buf jmp);
__noreturn void longjmp(jmp_buf jmp, int ret);
+/*
+ * initjmp() is non-standard, still it has to play well with the system versions
+ * of setjmp()/longjmp().
+ */
+int initjmp(jmp_buf jmp, void __noreturn (*func)(void), void *stack_top);
#endif /* _SETJMP_H_ */
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread* [RFC PATCH 05/10] test: lib: add initjmp() test
2025-02-14 14:00 [RFC PATCH 00/10] Uthreads Jerome Forissier
` (3 preceding siblings ...)
2025-02-14 14:00 ` [RFC PATCH 04/10] sandbox: " Jerome Forissier
@ 2025-02-14 14:00 ` Jerome Forissier
2025-02-14 14:00 ` [RFC PATCH 06/10] uthread: add cooperative multi-tasking interface Jerome Forissier
` (4 subsequent siblings)
9 siblings, 0 replies; 20+ messages in thread
From: Jerome Forissier @ 2025-02-14 14:00 UTC (permalink / raw)
To: u-boot
Cc: Ilias Apalodimas, Jerome Forissier, Tom Rini, Simon Glass,
Heinrich Schuchardt, Philippe Reynes, Raymond Mao
Test the initjmp() function when HAVE_INITJMP is set.
Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
---
test/lib/Makefile | 1 +
test/lib/initjmp.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 73 insertions(+)
create mode 100644 test/lib/initjmp.c
diff --git a/test/lib/Makefile b/test/lib/Makefile
index 0e4cb8e3dfd..bf04685dae1 100644
--- a/test/lib/Makefile
+++ b/test/lib/Makefile
@@ -14,6 +14,7 @@ obj-y += hexdump.o
obj-$(CONFIG_SANDBOX) += kconfig.o
obj-y += lmb.o
obj-$(CONFIG_HAVE_SETJMP) += longjmp.o
+obj-$(CONFIG_HAVE_INITJMP) += initjmp.o
obj-$(CONFIG_CONSOLE_RECORD) += test_print.o
obj-$(CONFIG_SSCANF) += sscanf.o
obj-$(CONFIG_$(PHASE_)STRTO) += str.o
diff --git a/test/lib/initjmp.c b/test/lib/initjmp.c
new file mode 100644
index 00000000000..52bbda8cc60
--- /dev/null
+++ b/test/lib/initjmp.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2025 Linaro Limited
+ *
+ * Unit test for initjmp()
+ */
+
+#include <compiler.h>
+#include <asm/setjmp.h>
+#include <stdbool.h>
+#include <test/lib.h>
+#include <test/ut.h>
+#include <vsprintf.h>
+
+static bool ep_entered;
+static jmp_buf return_buf;
+
+static void __noreturn entrypoint(void)
+{
+ ep_entered = true;
+
+ /* Jump back to the main routine */
+ longjmp(return_buf, 1);
+
+ /* Not reached */
+ panic("longjmp failed\n");
+}
+
+static int lib_initjmp(struct unit_test_state *uts)
+{
+ int ret;
+ void *stack;
+ jmp_buf buf;
+ int stack_sz = 1024;
+
+ ep_entered = false;
+
+ stack = malloc(stack_sz);
+ ut_assertnonnull(stack);
+
+ ut_assert(!ep_entered);
+
+ /*
+ * Prepare return_buf so that entrypoint may jump back just after the
+ * if()
+ */
+ if (!setjmp(return_buf)) {
+ /* return_buf initialized, entrypoint not yet called */
+
+ /*
+ * Prepare another jump buffer to jump into entrypoint with the
+ * given stack
+ */
+ ret = initjmp(buf, entrypoint, stack + stack_sz);
+ ut_assertok(ret);
+
+ /* Jump into entrypoint */
+ longjmp(buf, 1);
+ /*
+ * Not reached since entrypoint is expected to branch after
+ * the if()
+ */
+ ut_assert(false);
+ }
+
+ ut_assert(ep_entered);
+
+ free(stack);
+
+ return 0;
+}
+LIB_TEST(lib_initjmp, 0);
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread* [RFC PATCH 06/10] uthread: add cooperative multi-tasking interface
2025-02-14 14:00 [RFC PATCH 00/10] Uthreads Jerome Forissier
` (4 preceding siblings ...)
2025-02-14 14:00 ` [RFC PATCH 05/10] test: lib: add initjmp() test Jerome Forissier
@ 2025-02-14 14:00 ` Jerome Forissier
2025-02-14 14:47 ` Heinrich Schuchardt
2025-02-14 18:25 ` Yao Zi
2025-02-14 14:00 ` [RFC PATCH 07/10] lib: time: hook uthread_schedule() into udelay() Jerome Forissier
` (3 subsequent siblings)
9 siblings, 2 replies; 20+ messages in thread
From: Jerome Forissier @ 2025-02-14 14:00 UTC (permalink / raw)
To: u-boot
Cc: Ilias Apalodimas, Jerome Forissier, Tom Rini, Simon Glass,
Sughosh Ganu, Raymond Mao, Patrick Rudolph, Michal Simek
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);
+ }
+}
+
+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;
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread* Re: [RFC PATCH 06/10] uthread: add cooperative multi-tasking interface
2025-02-14 14:00 ` [RFC PATCH 06/10] uthread: add cooperative multi-tasking interface Jerome Forissier
@ 2025-02-14 14:47 ` Heinrich Schuchardt
2025-02-14 18:25 ` Yao Zi
1 sibling, 0 replies; 20+ messages in thread
From: Heinrich Schuchardt @ 2025-02-14 14:47 UTC (permalink / raw)
To: Jerome Forissier
Cc: Ilias Apalodimas, Tom Rini, Simon Glass, Sughosh Ganu,
Raymond Mao, Patrick Rudolph, Michal Simek, u-boot
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;
> +}
^ permalink raw reply [flat|nested] 20+ messages in thread* Re: [RFC PATCH 06/10] uthread: add cooperative multi-tasking interface
2025-02-14 14:00 ` [RFC PATCH 06/10] uthread: add cooperative multi-tasking interface Jerome Forissier
2025-02-14 14:47 ` Heinrich Schuchardt
@ 2025-02-14 18:25 ` Yao Zi
2025-02-17 9:48 ` Jerome Forissier
1 sibling, 1 reply; 20+ messages in thread
From: Yao Zi @ 2025-02-14 18:25 UTC (permalink / raw)
To: Jerome Forissier, u-boot
Cc: Ilias Apalodimas, Tom Rini, Simon Glass, Sughosh Ganu,
Raymond Mao, Patrick Rudolph, Michal Simek
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
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFC PATCH 06/10] uthread: add cooperative multi-tasking interface
2025-02-14 18:25 ` Yao Zi
@ 2025-02-17 9:48 ` Jerome Forissier
0 siblings, 0 replies; 20+ messages in thread
From: Jerome Forissier @ 2025-02-17 9:48 UTC (permalink / raw)
To: Yao Zi, u-boot
Cc: Ilias Apalodimas, Tom Rini, Simon Glass, Sughosh Ganu,
Raymond Mao, Patrick Rudolph, Michal Simek
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,
--
Jerome
^ permalink raw reply [flat|nested] 20+ messages in thread
* [RFC PATCH 07/10] lib: time: hook uthread_schedule() into udelay()
2025-02-14 14:00 [RFC PATCH 00/10] Uthreads Jerome Forissier
` (5 preceding siblings ...)
2025-02-14 14:00 ` [RFC PATCH 06/10] uthread: add cooperative multi-tasking interface Jerome Forissier
@ 2025-02-14 14:00 ` Jerome Forissier
2025-02-14 14:00 ` [RFC PATCH 08/10] dm: usb: move bus initialization into new static function usb_init_bus() Jerome Forissier
` (2 subsequent siblings)
9 siblings, 0 replies; 20+ messages in thread
From: Jerome Forissier @ 2025-02-14 14:00 UTC (permalink / raw)
To: u-boot; +Cc: Ilias Apalodimas, Jerome Forissier, Tom Rini, Simon Glass
Introduce a uthread scheduling call into udelay() when CONFIG_UTHREAD
is enabled. This means that any uthread calling into udelay() may yield
to uthread and be scheduled again later.
TBD: check if udelay_yield is really necessary.
Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
---
lib/time.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/lib/time.c b/lib/time.c
index d88edafb196..55e41c92210 100644
--- a/lib/time.c
+++ b/lib/time.c
@@ -17,6 +17,7 @@
#include <asm/global_data.h>
#include <asm/io.h>
#include <linux/delay.h>
+#include <uthread.h>
#ifndef CFG_WD_PERIOD
# define CFG_WD_PERIOD (10 * 1000 * 1000) /* 10 seconds default */
@@ -190,6 +191,10 @@ void __weak __udelay(unsigned long usec)
/* ------------------------------------------------------------------------- */
+#if CONFIG_IS_ENABLED(UTHREAD)
+int udelay_yield;
+#endif
+
void udelay(unsigned long usec)
{
ulong kv;
@@ -197,7 +202,17 @@ void udelay(unsigned long usec)
do {
schedule();
kv = usec > CFG_WD_PERIOD ? CFG_WD_PERIOD : usec;
- __udelay(kv);
+#if CONFIG_IS_ENABLED(UTHREAD)
+ if (udelay_yield == 0xCAFEDECA) {
+ ulong t0 = timer_get_us();
+ while (timer_get_us() < t0 + kv)
+ uthread_schedule();
+ } else {
+#endif
+ __udelay(kv);
+#if CONFIG_IS_ENABLED(UTHREAD)
+ }
+#endif
usec -= kv;
} while(usec);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread* [RFC PATCH 08/10] dm: usb: move bus initialization into new static function usb_init_bus()
2025-02-14 14:00 [RFC PATCH 00/10] Uthreads Jerome Forissier
` (6 preceding siblings ...)
2025-02-14 14:00 ` [RFC PATCH 07/10] lib: time: hook uthread_schedule() into udelay() Jerome Forissier
@ 2025-02-14 14:00 ` Jerome Forissier
2025-02-14 14:00 ` [RFC PATCH 09/10] dm: usb: initialize and scan multiple buses simultaneously with uthread Jerome Forissier
2025-02-14 14:00 ` [RFC PATCH 10/10] test: lib: add uthread test Jerome Forissier
9 siblings, 0 replies; 20+ messages in thread
From: Jerome Forissier @ 2025-02-14 14:00 UTC (permalink / raw)
To: u-boot
Cc: Ilias Apalodimas, Jerome Forissier, Marek Vasut, Tom Rini,
Mattijs Korpershoek, Caleb Connolly, Dragan Simic,
Heinrich Schuchardt
To prepare for the introduction of coroutines in the USB initialization
sequence, move code out of usb_init() into a new helper function:
usb_init_bus(). No functional change.
Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
---
drivers/usb/host/usb-uclass.c | 88 +++++++++++++++++++----------------
1 file changed, 48 insertions(+), 40 deletions(-)
diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c
index bfec303e7af..cc803241461 100644
--- a/drivers/usb/host/usb-uclass.c
+++ b/drivers/usb/host/usb-uclass.c
@@ -287,9 +287,55 @@ static int usb_probe_companion(struct udevice *bus)
return 0;
}
+static int controllers_initialized;
+
+static void usb_init_bus(struct udevice *bus)
+{
+ int ret;
+
+ /* init low_level USB */
+ printf("Bus %s: ", bus->name);
+
+ /*
+ * For Sandbox, we need scan the device tree each time when we
+ * start the USB stack, in order to re-create the emulated USB
+ * devices and bind drivers for them before we actually do the
+ * driver probe.
+ *
+ * For USB onboard HUB, we need to do some non-trivial init
+ * like enabling a power regulator, before enumeration.
+ */
+ if (IS_ENABLED(CONFIG_SANDBOX) ||
+ IS_ENABLED(CONFIG_USB_ONBOARD_HUB)) {
+ ret = dm_scan_fdt_dev(bus);
+ if (ret) {
+ printf("USB device scan from fdt failed (%d)", ret);
+ return;
+ }
+ }
+
+ ret = device_probe(bus);
+ if (ret == -ENODEV) { /* No such device. */
+ puts("Port not available.\n");
+ controllers_initialized++;
+ return;
+ }
+
+ if (ret) { /* Other error. */
+ printf("probe failed, error %d\n", ret);
+ return;
+ }
+
+ ret = usb_probe_companion(bus);
+ if (ret)
+ return;
+
+ controllers_initialized++;
+ usb_started = true;
+}
+
int usb_init(void)
{
- int controllers_initialized = 0;
struct usb_uclass_priv *uc_priv;
struct usb_bus_priv *priv;
struct udevice *bus;
@@ -305,45 +351,7 @@ int usb_init(void)
uc_priv = uclass_get_priv(uc);
uclass_foreach_dev(bus, uc) {
- /* init low_level USB */
- printf("Bus %s: ", bus->name);
-
- /*
- * For Sandbox, we need scan the device tree each time when we
- * start the USB stack, in order to re-create the emulated USB
- * devices and bind drivers for them before we actually do the
- * driver probe.
- *
- * For USB onboard HUB, we need to do some non-trivial init
- * like enabling a power regulator, before enumeration.
- */
- if (IS_ENABLED(CONFIG_SANDBOX) ||
- IS_ENABLED(CONFIG_USB_ONBOARD_HUB)) {
- ret = dm_scan_fdt_dev(bus);
- if (ret) {
- printf("USB device scan from fdt failed (%d)", ret);
- continue;
- }
- }
-
- ret = device_probe(bus);
- if (ret == -ENODEV) { /* No such device. */
- puts("Port not available.\n");
- controllers_initialized++;
- continue;
- }
-
- if (ret) { /* Other error. */
- printf("probe failed, error %d\n", ret);
- continue;
- }
-
- ret = usb_probe_companion(bus);
- if (ret)
- continue;
-
- controllers_initialized++;
- usb_started = true;
+ usb_init_bus(bus);
}
/*
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread* [RFC PATCH 09/10] dm: usb: initialize and scan multiple buses simultaneously with uthread
2025-02-14 14:00 [RFC PATCH 00/10] Uthreads Jerome Forissier
` (7 preceding siblings ...)
2025-02-14 14:00 ` [RFC PATCH 08/10] dm: usb: move bus initialization into new static function usb_init_bus() Jerome Forissier
@ 2025-02-14 14:00 ` Jerome Forissier
2025-02-14 14:00 ` [RFC PATCH 10/10] test: lib: add uthread test Jerome Forissier
9 siblings, 0 replies; 20+ messages in thread
From: Jerome Forissier @ 2025-02-14 14:00 UTC (permalink / raw)
To: u-boot
Cc: Ilias Apalodimas, Jerome Forissier, Marek Vasut, Tom Rini,
Simon Glass, Mattijs Korpershoek, Dragan Simic, Caleb Connolly,
Heinrich Schuchardt, Julius Lehmann, Guillaume La Roque
Use the uthread framework to initialize and scan USB buses in parallel
for better performance. Tested on two platforms:
1. arm64 QEMU on a somewhat contrived example (4 USB buses, each with
one audio device, one keyboard, one mouse and one tablet)
$ make qemu_arm64_defconfig
$ make -j$(nproc) CROSS_COMPILE="ccache aarch64-linux-gnu-"
$ qemu-system-aarch64 -M virt -nographic -cpu max -bios u-boot.bin \
$(for i in {1..4}; do echo -device qemu-xhci,id=xhci$i \
-device\ usb-{audio,kbd,mouse,tablet},bus=xhci$i.0; \
done)
2. i.MX93 EVK (imx93_11x11_evk_defconfig) with two USB hubs, each with
one webcam and one ethernet adapter, resulting in the following device
tree:
USB device tree:
1 Hub (480 Mb/s, 0mA)
| u-boot EHCI Host Controller
|
+-2 Hub (480 Mb/s, 100mA)
| GenesysLogic USB2.1 Hub
|
+-3 Vendor specific (480 Mb/s, 350mA)
| Realtek USB 10/100/1000 LAN 001000001
|
+-4 (480 Mb/s, 500mA)
HD Pro Webcam C920 8F7CD51F
1 Hub (480 Mb/s, 0mA)
| u-boot EHCI Host Controller
|
+-2 Hub (480 Mb/s, 100mA)
| USB 2.0 Hub
|
+-3 Vendor specific (480 Mb/s, 200mA)
| Realtek USB 10/100/1000 LAN 000001
|
+-4 (480 Mb/s, 500mA)
Generic OnLan-CS30 201801010008
Note that i.MX was tested on top of the downstream repository [1] since
USB doesn't work in the upstream master branch.
[1] https://github.com/nxp-imx/uboot-imx/tree/lf-6.6.52-2.2.0
commit 6c4545203d12 ("LF-13928 update key for capsule")
The time spent in usb_init() ("usb start" command) is reported on
the console. Here are the results:
| CONFIG_UTHREAD=n | CONFIG_UTHREAD=y
--------+------------------+-----------------
QEMU | 5628 ms | 2212 ms
i.MX93 | 4591 ms | 2441 ms
Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
---
drivers/usb/host/usb-uclass.c | 89 ++++++++++++++++++++++++++++-------
test/boot/bootdev.c | 14 +++---
test/boot/bootflow.c | 3 +-
3 files changed, 82 insertions(+), 24 deletions(-)
diff --git a/drivers/usb/host/usb-uclass.c b/drivers/usb/host/usb-uclass.c
index cc803241461..eedf4e1c3fb 100644
--- a/drivers/usb/host/usb-uclass.c
+++ b/drivers/usb/host/usb-uclass.c
@@ -9,6 +9,7 @@
#define LOG_CATEGORY UCLASS_USB
#include <bootdev.h>
+#include <uthread.h>
#include <dm.h>
#include <errno.h>
#include <log.h>
@@ -17,6 +18,7 @@
#include <dm/device-internal.h>
#include <dm/lists.h>
#include <dm/uclass-internal.h>
+#include <time.h>
static bool asynch_allowed;
@@ -221,25 +223,18 @@ int usb_stop(void)
return err;
}
-static void usb_scan_bus(struct udevice *bus, bool recurse)
+static void _usb_scan_bus(void *arg)
{
+ struct udevice *bus = (struct udevice *)arg;
struct usb_bus_priv *priv;
struct udevice *dev;
int ret;
priv = dev_get_uclass_priv(bus);
- assert(recurse); /* TODO: Support non-recusive */
-
- printf("scanning bus %s for devices... ", bus->name);
- debug("\n");
ret = usb_scan_device(bus, 0, USB_SPEED_FULL, &dev);
if (ret)
- printf("failed, error %d\n", ret);
- else if (priv->next_addr == 0)
- printf("No USB Device found\n");
- else
- printf("%d USB Device(s) found\n", priv->next_addr);
+ printf("Scanning bus %s failed, error %d\n", bus->name, ret);
}
static void remove_inactive_children(struct uclass *uc, struct udevice *bus)
@@ -289,12 +284,12 @@ static int usb_probe_companion(struct udevice *bus)
static int controllers_initialized;
-static void usb_init_bus(struct udevice *bus)
+static void _usb_init_bus(void *arg)
{
+ struct udevice *bus = (struct udevice *)arg;
int ret;
/* init low_level USB */
- printf("Bus %s: ", bus->name);
/*
* For Sandbox, we need scan the device tree each time when we
@@ -309,20 +304,21 @@ static void usb_init_bus(struct udevice *bus)
IS_ENABLED(CONFIG_USB_ONBOARD_HUB)) {
ret = dm_scan_fdt_dev(bus);
if (ret) {
- printf("USB device scan from fdt failed (%d)", ret);
+ printf("Bus %s: USB device scan from fdt failed (%d)\n",
+ bus->name, ret);
return;
}
}
ret = device_probe(bus);
if (ret == -ENODEV) { /* No such device. */
- puts("Port not available.\n");
+ printf("Bus %s: Port not available.\n", bus->name);
controllers_initialized++;
return;
}
if (ret) { /* Other error. */
- printf("probe failed, error %d\n", ret);
+ printf("Bus %s: probe failed, error %d\n", bus->name, ret);
return;
}
@@ -334,8 +330,57 @@ static void usb_init_bus(struct udevice *bus)
usb_started = true;
}
+#if CONFIG_IS_ENABLED(UTHREAD)
+extern int udelay_yield;
+#endif
+static int nthr;
+
+static void usb_init_bus(struct udevice *bus)
+{
+ if (!uthread_create(_usb_init_bus, (void *)bus, 0))
+ nthr++;
+}
+
+static void usb_scan_bus(struct udevice *bus, bool recurse)
+{
+ if (!uthread_create(_usb_scan_bus, (void *)bus, 0))
+ nthr++;
+}
+
+static void usb_report_devices(struct uclass *uc)
+{
+ struct usb_bus_priv *priv;
+ struct udevice *bus;
+
+ uclass_foreach_dev(bus, uc) {
+ if (!device_active(bus))
+ continue;
+ priv = dev_get_uclass_priv(bus);
+ printf("Bus %s: ", bus->name);
+ if (priv->next_addr == 0)
+ printf("No USB Device found\n");
+ else
+ printf("%d USB Device(s) found\n", priv->next_addr);
+ }
+}
+
+static void run_threads(void)
+{
+#if CONFIG_IS_ENABLED(UTHREAD)
+ if (!nthr)
+ return;
+ udelay_yield = 0xCAFEDECA;
+ while (uthread_schedule())
+ /* Nothing */;
+ udelay_yield = 0;
+ uthread_free_all();
+ nthr = 0;
+#endif
+}
+
int usb_init(void)
{
+ unsigned long t0 = timer_get_us();
struct usb_uclass_priv *uc_priv;
struct usb_bus_priv *priv;
struct udevice *bus;
@@ -354,6 +399,9 @@ int usb_init(void)
usb_init_bus(bus);
}
+ if (CONFIG_IS_ENABLED(UTHREAD))
+ run_threads();
+
/*
* lowlevel init done, now scan the bus for devices i.e. search HUBs
* and configure them, first scan primary controllers.
@@ -367,6 +415,9 @@ int usb_init(void)
usb_scan_bus(bus, true);
}
+ if (CONFIG_IS_ENABLED(UTHREAD))
+ run_threads();
+
/*
* Now that the primary controllers have been scanned and have handed
* over any devices they do not understand to their companions, scan
@@ -383,7 +434,10 @@ int usb_init(void)
}
}
- debug("scan end\n");
+ if (CONFIG_IS_ENABLED(UTHREAD))
+ run_threads();
+
+ usb_report_devices(uc);
/* Remove any devices that were not found on this scan */
remove_inactive_children(uc, bus);
@@ -397,6 +451,9 @@ int usb_init(void)
if (controllers_initialized == 0)
printf("No USB controllers found\n");
+ debug("USB initialized in %ld ms\n",
+ (timer_get_us() - t0) / 1000);
+
return usb_started ? 0 : -ENOENT;
}
diff --git a/test/boot/bootdev.c b/test/boot/bootdev.c
index 8c44afd9297..956320f1daa 100644
--- a/test/boot/bootdev.c
+++ b/test/boot/bootdev.c
@@ -393,8 +393,8 @@ static int bootdev_test_hunter(struct unit_test_state *uts)
ut_assert_console_end();
ut_assertok(bootdev_hunt("usb1", false));
- ut_assert_nextline(
- "Bus usb@1: scanning bus usb@1 for devices... 5 USB Device(s) found");
+ ut_assert_skip_to_line(
+ "Bus usb@1: 5 USB Device(s) found");
ut_assert_console_end();
/* USB is 7th in the list, so bit 8 */
@@ -449,8 +449,8 @@ static int bootdev_test_cmd_hunt(struct unit_test_state *uts)
ut_assert_nextline("scanning bus for devices...");
ut_assert_skip_to_line("Hunting with: spi_flash");
ut_assert_nextline("Hunting with: usb");
- ut_assert_nextline(
- "Bus usb@1: scanning bus usb@1 for devices... 5 USB Device(s) found");
+ ut_assert_skip_to_line(
+ "Bus usb@1: 5 USB Device(s) found");
ut_assert_nextline("Hunting with: virtio");
ut_assert_console_end();
@@ -551,8 +551,8 @@ static int bootdev_test_hunt_prio(struct unit_test_state *uts)
ut_assertok(bootdev_hunt_prio(BOOTDEVP_5_SCAN_SLOW, true));
ut_assert_nextline("Hunting with: ide");
ut_assert_nextline("Hunting with: usb");
- ut_assert_nextline(
- "Bus usb@1: scanning bus usb@1 for devices... 5 USB Device(s) found");
+ ut_assert_skip_to_line(
+ "Bus usb@1: 5 USB Device(s) found");
ut_assert_console_end();
return 0;
@@ -604,7 +604,7 @@ static int bootdev_test_hunt_label(struct unit_test_state *uts)
ut_assertnonnull(dev);
ut_asserteq_str("usb_mass_storage.lun0.bootdev", dev->name);
ut_asserteq(BOOTFLOW_METHF_SINGLE_UCLASS, mflags);
- ut_assert_nextlinen("Bus usb@1: scanning bus usb@1");
+ ut_assert_nextline("Bus usb@1: 5 USB Device(s) found");
ut_assert_console_end();
return 0;
diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c
index a8735c1c23d..67ee1777d01 100644
--- a/test/boot/bootflow.c
+++ b/test/boot/bootflow.c
@@ -1289,8 +1289,9 @@ static int bootflow_efi(struct unit_test_state *uts)
bootstd_reset_usb();
ut_assertok(run_command("bootflow scan", 0));
+
ut_assert_skip_to_line(
- "Bus usb@1: scanning bus usb@1 for devices... 5 USB Device(s) found");
+ "Bus usb@1: 5 USB Device(s) found");
ut_assertok(run_command("bootflow list", 0));
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread* [RFC PATCH 10/10] test: lib: add uthread test
2025-02-14 14:00 [RFC PATCH 00/10] Uthreads Jerome Forissier
` (8 preceding siblings ...)
2025-02-14 14:00 ` [RFC PATCH 09/10] dm: usb: initialize and scan multiple buses simultaneously with uthread Jerome Forissier
@ 2025-02-14 14:00 ` Jerome Forissier
2025-02-14 18:41 ` Yao Zi
9 siblings, 1 reply; 20+ messages in thread
From: Jerome Forissier @ 2025-02-14 14:00 UTC (permalink / raw)
To: u-boot
Cc: Ilias Apalodimas, Jerome Forissier, Tom Rini, Simon Glass,
Heinrich Schuchardt, Raymond Mao, Philippe Reynes
Test uthread scheduling.
Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
---
lib/uthread.c | 3 ++-
test/lib/Makefile | 1 +
test/lib/uthread.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 61 insertions(+), 1 deletion(-)
create mode 100644 test/lib/uthread.c
diff --git a/lib/uthread.c b/lib/uthread.c
index bb132001fb6..1f8e6d0fa80 100644
--- a/lib/uthread.c
+++ b/lib/uthread.c
@@ -76,10 +76,11 @@ err:
void uthread_free_all(void)
{
+ struct uthread *main = &main_thread;
struct uthread *next;
struct uthread *tmp;
- list_for_each_entry_safe(next, tmp, ¤t->list, list) {
+ list_for_each_entry_safe(next, tmp, &main->list, list) {
list_del(&next->list);
uthread_free(next);
}
diff --git a/test/lib/Makefile b/test/lib/Makefile
index bf04685dae1..c991dff1c63 100644
--- a/test/lib/Makefile
+++ b/test/lib/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_CRC8) += test_crc8.o
obj-$(CONFIG_UT_LIB_CRYPT) += test_crypt.o
obj-$(CONFIG_UT_TIME) += time.o
obj-$(CONFIG_$(XPL_)UT_UNICODE) += unicode.o
+obj-$(CONFIG_UTHREAD) += uthread.o
obj-$(CONFIG_LIB_UUID) += uuid.o
else
obj-$(CONFIG_SANDBOX) += kconfig_spl.o
diff --git a/test/lib/uthread.c b/test/lib/uthread.c
new file mode 100644
index 00000000000..c9d030dc778
--- /dev/null
+++ b/test/lib/uthread.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2025 Linaro Limited
+ *
+ * Unit test for uthread
+ */
+
+#include <stdbool.h>
+#include <test/lib.h>
+#include <test/ut.h>
+#include <uthread.h>
+
+static int count;
+
+static void worker(void *arg)
+{
+ int loops = (int)(unsigned long)arg;
+ int i;
+
+ for (i = 0; i < loops; i++) {
+ count++;
+ uthread_schedule();
+ }
+}
+
+static int lib_uthread(struct unit_test_state *uts)
+{
+ int i;
+
+ count = 0;
+ ut_assertok(uthread_create(worker, (void *)5, 0));
+ ut_assertok(uthread_create(worker, (void *)10, 0));
+ /*
+ * The first call is expected to schedule the first worker, which will
+ * schedule the second one, which will schedule back to the main thread
+ * (here). Therefore count should be 2.
+ */
+ ut_assert(uthread_schedule());
+ ut_asserteq(2, count);
+ /* Four more calls should bring the count to 10 */
+ for (i = 0; i < 4; i++)
+ ut_assert(uthread_schedule());
+ ut_asserteq(10, count);
+ /* This one allows the first worker to exit */
+ ut_assert(uthread_schedule());
+ /* Five more calls for the second worker to finish incrementing */
+ for (i = 0; i < 5; i++)
+ ut_assert(uthread_schedule());
+ ut_asserteq(15, count);
+ /* Plus one call to let the second worker return from its entry point */
+ ut_assert(uthread_schedule());
+ /* Now both tasks should be done, schedule should return false */
+ ut_assert(!uthread_schedule());
+ uthread_free_all();
+
+ return 0;
+}
+LIB_TEST(lib_uthread, 0);
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread* Re: [RFC PATCH 10/10] test: lib: add uthread test
2025-02-14 14:00 ` [RFC PATCH 10/10] test: lib: add uthread test Jerome Forissier
@ 2025-02-14 18:41 ` Yao Zi
2025-02-17 9:49 ` Jerome Forissier
0 siblings, 1 reply; 20+ messages in thread
From: Yao Zi @ 2025-02-14 18:41 UTC (permalink / raw)
To: Jerome Forissier, u-boot
Cc: Ilias Apalodimas, Tom Rini, Simon Glass, Heinrich Schuchardt,
Raymond Mao, Philippe Reynes
On Fri, Feb 14, 2025 at 03:00:25PM +0100, Jerome Forissier wrote:
> Test uthread scheduling.
>
> Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
> ---
> lib/uthread.c | 3 ++-
> test/lib/Makefile | 1 +
> test/lib/uthread.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 61 insertions(+), 1 deletion(-)
> create mode 100644 test/lib/uthread.c
>
> diff --git a/lib/uthread.c b/lib/uthread.c
> index bb132001fb6..1f8e6d0fa80 100644
> --- a/lib/uthread.c
> +++ b/lib/uthread.c
> @@ -76,10 +76,11 @@ err:
>
> void uthread_free_all(void)
> {
> + struct uthread *main = &main_thread;
> struct uthread *next;
> struct uthread *tmp;
>
> - list_for_each_entry_safe(next, tmp, ¤t->list, list) {
> + list_for_each_entry_safe(next, tmp, &main->list, list) {
> list_del(&next->list);
> uthread_free(next);
> }
I think this should belong to the sixth patch.
> diff --git a/test/lib/Makefile b/test/lib/Makefile
> index bf04685dae1..c991dff1c63 100644
> --- a/test/lib/Makefile
> +++ b/test/lib/Makefile
> ...
Best regards,
Yao Zi
^ permalink raw reply [flat|nested] 20+ messages in thread* Re: [RFC PATCH 10/10] test: lib: add uthread test
2025-02-14 18:41 ` Yao Zi
@ 2025-02-17 9:49 ` Jerome Forissier
0 siblings, 0 replies; 20+ messages in thread
From: Jerome Forissier @ 2025-02-17 9:49 UTC (permalink / raw)
To: Yao Zi, u-boot
Cc: Ilias Apalodimas, Tom Rini, Simon Glass, Heinrich Schuchardt,
Raymond Mao, Philippe Reynes
On 2/14/25 19:41, Yao Zi wrote:
> On Fri, Feb 14, 2025 at 03:00:25PM +0100, Jerome Forissier wrote:
>> Test uthread scheduling.
>>
>> Signed-off-by: Jerome Forissier <jerome.forissier@linaro.org>
>> ---
>> lib/uthread.c | 3 ++-
>> test/lib/Makefile | 1 +
>> test/lib/uthread.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 61 insertions(+), 1 deletion(-)
>> create mode 100644 test/lib/uthread.c
>>
>> diff --git a/lib/uthread.c b/lib/uthread.c
>> index bb132001fb6..1f8e6d0fa80 100644
>> --- a/lib/uthread.c
>> +++ b/lib/uthread.c
>> @@ -76,10 +76,11 @@ err:
>>
>> void uthread_free_all(void)
>> {
>> + struct uthread *main = &main_thread;
>> struct uthread *next;
>> struct uthread *tmp;
>>
>> - list_for_each_entry_safe(next, tmp, ¤t->list, list) {
>> + list_for_each_entry_safe(next, tmp, &main->list, list) {
>> list_del(&next->list);
>> uthread_free(next);
>> }
>
> I think this should belong to the sixth patch.
Absolutely. I'll fix in v2.
>
>> diff --git a/test/lib/Makefile b/test/lib/Makefile
>> index bf04685dae1..c991dff1c63 100644
>> --- a/test/lib/Makefile
>> +++ b/test/lib/Makefile
>> ...
>
> Best regards,
> Yao Zi
Thanks,
--
Jerome
^ permalink raw reply [flat|nested] 20+ messages in thread