Raul Rangel has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/56048 )
Change subject: lib/future: Implement Futures API ......................................................................
lib/future: Implement Futures API
This is implemented in coreboot instead of commonlib because commonlib doesn't have things like printk, die and stopwatch.
A later CL will add calls to poll_futures to hardwaremain.c.
BUG=b:177909625 TEST=boot guybrush and see payload read/decompress time drop 23ms.
Signed-off-by: Raul E Rangel rrangel@chromium.org Change-Id: I350aebcd07024a00b90495bf937f8bca5193d02f --- M src/lib/Kconfig M src/lib/Makefile.inc A src/lib/future.c 3 files changed, 134 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/48/56048/1
diff --git a/src/lib/Kconfig b/src/lib/Kconfig index 239f613..b556f7d 100644 --- a/src/lib/Kconfig +++ b/src/lib/Kconfig @@ -98,3 +98,11 @@ the associated CAR/SRAM size. In that case every single CBFS file lookup must re-read the same CBFS directory entries from flash to find the respective file. + +config FUTURES_EXECUTOR + bool + help + Enables the futures executor. The executor provides means to register + futures and poll them until completion. If this is disabled and + futures are in use, they must be polled manually. + diff --git a/src/lib/Makefile.inc b/src/lib/Makefile.inc index baf9c4d..0ae1304 100644 --- a/src/lib/Makefile.inc +++ b/src/lib/Makefile.inc @@ -271,6 +271,13 @@ ramstage-y += crc_byte.c smm-y += crc_byte.c
+bootblock-y += future.c +verstage-y += future.c +romstage-y += future.c +postcar-y += future.c +ramstage-y += future.c +smm-y += future.c + postcar-y += bootmode.c postcar-y += boot_device.c postcar-y += cbfs.c diff --git a/src/lib/future.c b/src/lib/future.c new file mode 100644 index 0000000..902f1e0 --- /dev/null +++ b/src/lib/future.c @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <commonlib/bsd/cb_err.h> +#include <commonlib/bsd/future.h> +#include <console/console.h> +#include <stdbool.h> +#include <timer.h> +#include <types.h> + +/* + * This is a very trivial implementation. If we ever need a very large number of + * concurrent futures we can implement a better data structure. + */ +static struct future *futures[8]; +size_t registered_futures; + +void register_future(struct future *future) +{ + if (!CONFIG(FUTURES_EXECUTOR)) + return; + + for (size_t i = 0; i < ARRAY_SIZE(futures); ++i) { + if (futures[i]) + continue; + futures[i] = future; + registered_futures++; + + /* + * We don't poll the future since new futures should already be + * running. + */ + return; + } + die("%s: Polling list is full\n"); +} + +void poll_futures(void) +{ + if (!CONFIG(FUTURES_EXECUTOR)) + return; + + enum future_state state; + + /* Check for re-entry */ + static bool polling = 0; + + /* + * We shouldn't have future produces polling the global list. This + * can cause a circular loop. We could break the loop by removing the + * current future from the list before calling it, but we should just + * avoid this pattern. + */ + if (polling) + die("%s: A futures produces tried to poll the global queue."); + + /* Optimization so we don't always traverse the list */ + if (!registered_futures) + return; + + for (size_t i = 0; i < ARRAY_SIZE(futures); ++i) { + if (!futures[i]) + continue; + + /* + * We only ever perform a non-blocking poll. We leave the + * blocking poll for the future consumer. + */ + state = futures[i]->poll(futures[i], false); + if (state == FUTURE_DONE) { + futures[i] = NULL; + registered_futures--; + } + } +} + +static void unregister_future(struct future *future) +{ + if (!CONFIG(FUTURES_EXECUTOR)) + return; + + for (size_t i = 0; i < ARRAY_SIZE(futures); ++i) { + if (futures[i] == future) { + futures[i] = NULL; + registered_futures--; + return; + } + } +} + +void wait_for_future(struct future *future) +{ + struct stopwatch sw; + + stopwatch_init(&sw); + + printk(BIOS_DEBUG, "waiting for future %p\n", future); + + /* + * Unregister the current future so we don't poll it twice while + * busy waiting. + */ + unregister_future(future); + + while (future->poll(future, true) != FUTURE_DONE) + poll_futures(); + + printk(BIOS_DEBUG, " took %lu us\n", stopwatch_duration_usecs(&sw)); +} + +static inline enum future_state future_complete(struct future *future, bool busy_loop) +{ + return FUTURE_DONE; +} + +void complete_future(struct future *future, cb_err_t err) +{ + future->error = err; + future->poll = future_complete; +}