Raul Rangel has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/56047 )
Change subject: commonlib/bsd/future: Introduce future API ......................................................................
commonlib/bsd/future: Introduce future API
This CL introduces a futures API. It allows modeling asynchronous operations. This will make it possible to model DMA transfers, I2C transactions, EC operations, etc.
It was loosely modeled off the Rust Futures API: https://rust-lang.github.io/async-book/02_execution/02_future.html
Since we don't use interrupts while booting, the only option we have is to poll. For this reason I omitted the `wake` callback since there is nothing to call it.
I evaluated the existing threads.h API and it has the following down sides: * Each thread requires its own stack. This means that there is a pretty low limit to the number of concurrent async operations, and a lot of overhead. * There are no thread synchronization primitives (e.g., thread handles, join, etc), so it's difficult to wait for an asynchronous operation to complete. * It currently only works in ramstage. * It requires code to be reentrant since we can now have multiple threads of executing going through the same code. This will require adding support for mutexes to make sure critical sections are protected. Auditing this is complicated anid error prone. * It makes it hard to tell from looking at the code if something is running in a different thread. * Timestamp tracking becomes difficult because we can context switch to a task that hogs the CPU for a while.
The futures API has the following advantages: * We can easily scale the number of async operations since each operation only needs to store the context to run its state machine. * It provides a synchronization primitive so it's possible to block until the operation is complete. * It works in all stages. * There is no need for mutexes since there is only 1 stack and thread of execution. * Async operations are explicitly modeled so it's clear if the code is asynchronous. * The busy_loop parameter allows putting off CPU intensive operations until they are required. This will allow for proper timestamp accounting. * Creating futures that depend on other futures is pretty simple.
BUG=b:179699789 TEST=Implemented the API for rdev and cbfs, it seems pretty ergonomic
Signed-off-by: Raul E Rangel rrangel@chromium.org Change-Id: I125a946233f122c2848704296efbcb3ca3c96079 --- A src/commonlib/bsd/include/commonlib/bsd/future.h 1 file changed, 86 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/47/56047/1
diff --git a/src/commonlib/bsd/include/commonlib/bsd/future.h b/src/commonlib/bsd/include/commonlib/bsd/future.h new file mode 100644 index 0000000..1ec30c3 --- /dev/null +++ b/src/commonlib/bsd/include/commonlib/bsd/future.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later */ + +#ifndef COMMONLIB_BSD_FUTURE_H +#define COMMONLIB_BSD_FUTURE_H + +#include <commonlib/bsd/cb_err.h> +#include <stdbool.h> + +enum future_state { + FUTURE_PENDING, + FUTURE_IN_PROGRESS, + FUTURE_DONE, +}; + +/** + * A future is a representation of an asynchronous operation. + * + * e.g., Using a DMA controller to move memory, performing an I2C transaction, + * starting an expensive EC operation, etc + * + * An output value is omitted from this structure. If an output value is + * required, it is expected this structure will be embedded inside another + * structure that contains the output. + */ +struct future { + /** + * Polls the state of the future. + * + * If the future has not terminated, it will advance the state of the + * future's state machine as much as possible. + * + * e.g., If a DMA controller has been asked to transfer 100 KiB of data + * but it has a hardware limit of 64 KiB, then the transfer needs to be + * split up into two transactions. We need to periodically poll the + * hardware to check if the transaction is done. Once it has completed + * the second transaction can then be started. We continue to + * periodically poll until the second transaction completes. Once the + * second transaction has completed, `FUTURE_DONE` will be returned. + * + * By only polling periodically, we allow the CPU is to continue + * performing other work. + * + * If this function pointer is NULL, the future is uninitialized. + * + * @param busy_loop if the consumer has reached a point where it can no + * longer proceed without the future completing, then this value + * should be set to true. It is an indication to the producer that it + * can now perform any CPU intensive tasks (e.g., hashing, + * decompression, etc). + */ + enum future_state (*poll)(struct future *future, bool busy_loop); + + /* Status of the future once `poll` has returned `FUTURE_DONE`. */ + enum cb_err error; +}; + +static inline bool future_is_valid(struct future *future) +{ + return !!future->poll; +} + +/** + * Registers the future with the executor so the future's poll method will be + * periodically called. Once the future completes it will automatically be + * unregistered. + * + * You should only register top level futures. If a future has dependent futures + * those dependents should be polled by the containing future and not the + * executor. + */ +void register_future(struct future *future); + +/** + * Polls all the registered futures. + * + * This method should not be called by any future producers. + */ +void poll_futures(void); + +/* Wait until the future completes */ +void wait_for_future(struct future *future); + +/* Marks the future as complete */ +void complete_future(struct future *future, cb_err_t err); + +#endif /* COMMONLIB_BSD_FUTURE_H */