[PATCH 00/12] poller: run pollers as proper coroutines (green threads)

Ahmad Fatoum a.fatoum at pengutronix.de
Mon Feb 15 05:36:53 EST 2021

barebox pollers were so far very limited coroutines that only yield at
the end of the function. This series leverages setjmp/longjmp to allow
pollers to yield at any point of their execution. The motivation behind
this is that it finally becomes feasible to port threaded code to barebox with
minimal modification: If your thread has a delay, just stick in a
poller_yield() and give other threads as well as the main thread a chance
to run before resuming execution again.

How that can look like is showing in "usbgadget: ums: run gadget loop in a
background coroutine if possible", which turns a blocking loop very
naturally into a thread.

The implementation is basically: setjmp() to store the original place of
execution, longjmp() to jump back on a context switch, initjmp() to start
a new poller the very first time. The reason why we need an extra initjmp()
beyond standard C setjmp and longjmp is because those two only save a
stack pointer, so coroutines can clobber each other's stack.
With initjmp, the new coroutine starts directly executing on a disjoint stack.
initjmp, like setjmp and longjmp should be implemented in assembly, but it
should be fairly straight forward.  e.g. the ARM implementation for initjmp is
a mere 4 instructions long.

For now, I only implemented this for ARM and sandbox. If this is accepted and
becomes available for other architectures as well, it'll likely result in
changes to other parts of barebox:

  - Workqueues will become obsolete. This series already has pollers
    doing file system access yielding until they can be run in command context

  - Slices will become obsolete. They are equivalent to a mutex_try_lock
    and we can implement blocking mutexes now

  - Functions calling poller_call can be changed to call poller_reschedule()
    to become available from calling from any context

  - Backtraces should indicate which coroutine is currently running

The execution tax on context switching is that for an idle i.MX8MM system
30% less pollers can be executed in a given time frame. The extra accounting
overhead for a yielding poller is in the range of a 100 byte or so, which
is negligible compared to the stack newly allocated on poller creation, which
is CONFIG_STACK_SIZE bytes long, usually 32K. Pollers probably don't need that
much stack space, but for now, we just use the same stack size everywhere.

This is series is based on:
 - <20210215102442.28731-1-a.fatoum at pengutronix.de> to avoid a conflict
 - <20210215102740.30418-1-a.fatoum at pengutronix.de> which adds blocking
   usb mass storage, which is made non-blocing here

Note: Despite the name green threads (or fibers), this is wholly cooperative.
There is no preemption and thus no new extra precautions that need to be taken
when writing pollers. On the contrary, if we make this available everywhere,
you can basically do anything in a poller.

Looking forward to feedback,
Ahmad Fatoum (12):
  common: add coroutine support
  poller: run pollers as proper coroutines if architecture supports it
  ARM: asm: setjmp: annotate setjmp/longjmp for GCC
  ARM: asm: setjmp: implement coroutine dependency initjmp()
  sandbox: asm: implement setjmp/longjmp/initjmp
  poller: command: add new coroutine check
  slice: have assert_command_context() yield until true if possible
  poller: implement basic Linux-like completion API
  include: add kthread wrappers for pollers
  usbgadget: ums: run gadget loop in a background coroutine if possible
  usbgadget: refactor usbgadget_register to accept array
  usbgadget: multi: wire mass storage gadget into composite gadget

 Documentation/user/usb.rst          |   2 +
 arch/arm/Kconfig                    |   1 +
 arch/arm/include/asm/setjmp.h       |   6 +-
 arch/arm/lib32/setjmp.S             |   8 ++
 arch/arm/lib64/setjmp.S             |   9 ++
 arch/sandbox/Kconfig                |   1 +
 arch/sandbox/Makefile               |   5 +-
 arch/sandbox/include/asm/setjmp.h   |  17 +++
 arch/sandbox/os/Makefile            |   5 +-
 arch/sandbox/os/setjmp.c            | 180 ++++++++++++++++++++++++++++
 commands/Kconfig                    |  10 ++
 commands/Makefile                   |   2 +-
 commands/usbgadget.c                |  29 +++--
 common/Kconfig                      |  17 +++
 common/Makefile                     |   1 +
 common/coroutine.c                  | 178 +++++++++++++++++++++++++++
 common/poller.c                     | 111 ++++++++++++++---
 common/usbgadget.c                  |  55 +++++++--
 drivers/usb/gadget/Kconfig          |  12 +-
 drivers/usb/gadget/f_mass_storage.c |  14 +++
 drivers/usb/gadget/multi.c          |  36 ++++++
 include/coroutine.h                 |  43 +++++++
 include/linux/completion.h          |  61 ++++++++++
 include/linux/kthread.h             |  72 +++++++++++
 include/poller.h                    |  16 ++-
 include/slice.h                     |  14 ++-
 include/usb/gadget-multi.h          |  19 ++-
 27 files changed, 869 insertions(+), 55 deletions(-)
 create mode 100644 arch/sandbox/include/asm/setjmp.h
 create mode 100644 arch/sandbox/os/setjmp.c
 create mode 100644 common/coroutine.c
 create mode 100644 include/coroutine.h
 create mode 100644 include/linux/completion.h
 create mode 100644 include/linux/kthread.h


More information about the barebox mailing list