When COROUTINES is enabled, schedule efi_disks_register() and
efi_tcg2_register() as two coroutines in efi_init_obj_list() instead of
invoking them sequentially. The voluntary yield point is introduced
inside udelay() which is called frequently as a result of each function
polling the hardware. This allows the two coroutines to make progress
simultaneously and reduce the wall clock time required by
efi_init_obj_list().

Tested on Kria KV260 with a microSD card inserted with the "printenv
-e" command. With COROUTINES disabled, efi_init_obj_list() completes in
2821 ms on average (2825, 2822, 2817). With COROUTINES enabled, it
takes 2265 ms (2262, 2260, 2272). That is a reduction of 556 ms which
is not bad at all considering that measured separately,
efi_tcg2_register() takes ~825 ms and efi_disks_register() needs ~600
ms, so assuming they would overlap perfectly one can expect a 600 ms
improvement at best.

The code size penalty for this improvement is 1340 bytes.

Signed-off-by: Jerome Forissier <[email protected]>
---
 lib/efi_loader/efi_setup.c | 113 +++++++++++++++++++++++++++++++++++--
 lib/time.c                 |  14 ++++-
 2 files changed, 122 insertions(+), 5 deletions(-)

diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
index aa59bc7779d..94160f4bd86 100644
--- a/lib/efi_loader/efi_setup.c
+++ b/lib/efi_loader/efi_setup.c
@@ -7,10 +7,12 @@
 
 #define LOG_CATEGORY LOGC_EFI
 
+#include <coroutines.h>
 #include <efi_loader.h>
 #include <efi_variable.h>
 #include <log.h>
 #include <asm-generic/unaligned.h>
+#include <stdlib.h>
 
 #define OBJ_LIST_NOT_INITIALIZED 1
 
@@ -208,6 +210,46 @@ out:
        return -1;
 }
 
+#if CONFIG_IS_ENABLED(COROUTINES)
+
+static void efi_disks_register_co(void)
+{
+       efi_status_t ret;
+
+       if (efi_obj_list_initialized != OBJ_LIST_NOT_INITIALIZED)
+               goto out;
+
+       /*
+        * Probe block devices to find the ESP.
+        * efi_disks_register() must be called before efi_init_variables().
+        */
+       ret = efi_disks_register();
+       if (ret != EFI_SUCCESS)
+               efi_obj_list_initialized = ret;
+out:
+       co_exit();
+}
+
+static void efi_tcg2_register_co(void)
+{
+       efi_status_t ret = EFI_SUCCESS;
+
+       if (efi_obj_list_initialized != OBJ_LIST_NOT_INITIALIZED)
+               goto out;
+
+       if (IS_ENABLED(CONFIG_EFI_TCG2_PROTOCOL)) {
+               ret = efi_tcg2_register();
+               if (ret != EFI_SUCCESS)
+                       efi_obj_list_initialized = ret;
+       }
+out:
+       co_exit();
+}
+
+extern int udelay_yield;
+
+#endif /* COROUTINES */
+
 /**
  * efi_init_obj_list() - Initialize and populate EFI object list
  *
@@ -216,6 +258,12 @@ out:
 efi_status_t efi_init_obj_list(void)
 {
        efi_status_t ret = EFI_SUCCESS;
+#if CONFIG_IS_ENABLED(COROUTINES)
+       struct co_stack *stk = NULL;
+       struct co *main_co = NULL;
+       struct co *co1 = NULL;
+       struct co *co2 = NULL;
+#endif
 
        /* Initialize once only */
        if (efi_obj_list_initialized != OBJ_LIST_NOT_INITIALIZED)
@@ -224,6 +272,53 @@ efi_status_t efi_init_obj_list(void)
        /* Set up console modes */
        efi_setup_console_size();
 
+#if CONFIG_IS_ENABLED(COROUTINES)
+       main_co = co_create(NULL, NULL, 0, NULL, NULL);
+       if (!main_co) {
+               ret = EFI_OUT_OF_RESOURCES;
+               goto out;
+       }
+
+       stk = co_stack_new(8192);
+       if (!stk) {
+               ret = EFI_OUT_OF_RESOURCES;
+               goto out;
+       }
+
+       co1 = co_create(main_co, stk, 0, efi_disks_register_co, NULL);
+       if (!co1) {
+               ret = EFI_OUT_OF_RESOURCES;
+               goto out;
+       }
+
+       co2 = co_create(main_co, stk, 0, efi_tcg2_register_co, NULL);
+       if (!co2) {
+               ret = EFI_OUT_OF_RESOURCES;
+               goto out;
+       }
+
+       udelay_yield = 0xCAFEDECA;
+       do {
+               if (!co1->done)
+                       co_resume(co1);
+               if (!co2->done)
+                       co_resume(co2);
+       } while (!(co1->done && co2->done));
+       udelay_yield = 0;
+
+       co_stack_destroy(stk);
+       co_destroy(main_co);
+       co_destroy(co1);
+       co_destroy(co2);
+       stk = NULL;
+       main_co = co1 = co2 = NULL;
+
+       if (efi_obj_list_initialized != OBJ_LIST_NOT_INITIALIZED) {
+               /* Some kind of error was saved by a coroutine */
+               ret = efi_obj_list_initialized;
+               goto out;
+       }
+#else
        /*
         * Probe block devices to find the ESP.
         * efi_disks_register() must be called before efi_init_variables().
@@ -232,6 +327,13 @@ efi_status_t efi_init_obj_list(void)
        if (ret != EFI_SUCCESS)
                goto out;
 
+       if (IS_ENABLED(CONFIG_EFI_TCG2_PROTOCOL)) {
+               ret = efi_tcg2_register();
+               if (ret != EFI_SUCCESS)
+                       efi_obj_list_initialized = ret;
+       }
+#endif
+
        /* Initialize variable services */
        ret = efi_init_variables();
        if (ret != EFI_SUCCESS)
@@ -272,10 +374,6 @@ efi_status_t efi_init_obj_list(void)
        }
 
        if (IS_ENABLED(CONFIG_EFI_TCG2_PROTOCOL)) {
-               ret = efi_tcg2_register();
-               if (ret != EFI_SUCCESS)
-                       goto out;
-
                ret = efi_tcg2_do_initial_measurement();
                if (ret == EFI_SECURITY_VIOLATION)
                        goto out;
@@ -350,6 +448,13 @@ efi_status_t efi_init_obj_list(void)
            !IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK_EARLY))
                ret = efi_launch_capsules();
 out:
+#if CONFIG_IS_ENABLED(COROUTINES)
+       co_stack_destroy(stk);
+       co_destroy(main_co);
+       co_destroy(co1);
+       co_destroy(co2);
        efi_obj_list_initialized = ret;
+#endif
+
        return ret;
 }
diff --git a/lib/time.c b/lib/time.c
index d88edafb196..c11288102fe 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 <coroutines.h>
 
 #ifndef CFG_WD_PERIOD
 # define CFG_WD_PERIOD (10 * 1000 * 1000)      /* 10 seconds default */
@@ -190,6 +191,8 @@ void __weak __udelay(unsigned long usec)
 
 /* ------------------------------------------------------------------------- */
 
+int udelay_yield;
+
 void udelay(unsigned long usec)
 {
        ulong kv;
@@ -197,7 +200,16 @@ void udelay(unsigned long usec)
        do {
                schedule();
                kv = usec > CFG_WD_PERIOD ? CFG_WD_PERIOD : usec;
-               __udelay(kv);
+               if (CONFIG_IS_ENABLED(COROUTINES) &&
+                   udelay_yield == 0xCAFEDECA) {
+                       ulong t0 = timer_get_us();
+                       do {
+                               co_yield();
+                               __udelay(10);
+                       } while (timer_get_us() < t0 + kv);
+               } else {
+                       __udelay(kv);
+               }
                usec -= kv;
        } while(usec);
 }
-- 
2.43.0

Reply via email to