aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Percival <cperciva@FreeBSD.org>2023-07-18 02:29:20 +0000
committerColin Percival <cperciva@FreeBSD.org>2023-08-20 05:04:56 +0000
commit9a7add6d01f3c5f7eba811e794cf860d2bce131d (patch)
treeefddb214e7d568f4e53742209ef9652a9e60577d
parentcedc82c0466a5116d5a89ecd1f10a69be1a31ca0 (diff)
downloadsrc-9a7add6d01f3c5f7eba811e794cf860d2bce131d.tar.gz
src-9a7add6d01f3c5f7eba811e794cf860d2bce131d.zip
init_main: Switch from sysinit array to SLIST
This has two effects: 1. We can mergesort the sysinits instead of bubblesorting them, which shaves about 2 ms off the boot time in Firecracker. 2. Adding more sysinits (e.g. from a KLD) can be performed by sorting them and then merging the sorted lists, which is both faster than the previous "append and sort" approach and avoids needing malloc. Reviewed by: jhb (previous version) Sponsored by: https://www.patreon.com/cperciva Differential Revision: https://reviews.freebsd.org/D41075
-rw-r--r--sys/kern/init_main.c177
1 files changed, 86 insertions, 91 deletions
diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c
index d675e3538f67..aa82eafff9b0 100644
--- a/sys/kern/init_main.c
+++ b/sys/kern/init_main.c
@@ -73,6 +73,8 @@
#include <sys/racct.h>
#include <sys/reboot.h>
#include <sys/resourcevar.h>
+#include <sys/queue.h>
+#include <sys/queue_mergesort.h>
#include <sys/sched.h>
#include <sys/signalvar.h>
#include <sys/sx.h>
@@ -154,46 +156,73 @@ FEATURE(invariants, "Kernel compiled with INVARIANTS, may affect performance");
SYSINIT(placeholder, SI_SUB_DUMMY, SI_ORDER_ANY, NULL, NULL);
/*
- * The sysinit table itself. Items are checked off as the are run.
- * If we want to register new sysinit types, add them to newsysinit.
+ * The sysinit linker set compiled into the kernel. These are placed onto the
+ * sysinit list by mi_startup; sysinit_add can add (e.g., from klds) additional
+ * sysinits to the linked list but the linker set here does not change.
*/
SET_DECLARE(sysinit_set, struct sysinit);
-struct sysinit **sysinit, **sysinit_end;
-struct sysinit **newsysinit, **newsysinit_end;
/*
- * Merge a new sysinit set into the current set, reallocating it if
- * necessary. This can only be called after malloc is running.
+ * The sysinit list itself. Items are removed as they are run.
*/
-void
-sysinit_add(struct sysinit **set, struct sysinit **set_end)
+static SLIST_HEAD(sysinitlist, sysinit) sysinit_list;
+
+/*
+ * Compare two sysinits; return -1, 0, or 1 if a comes before, at the same time
+ * as, or after b.
+ */
+static int
+sysinit_compar(struct sysinit *a, struct sysinit *b, void *thunk __unused)
+{
+
+ if (a->subsystem < b->subsystem)
+ return (-1);
+ if (a->subsystem > b->subsystem)
+ return (1);
+ if (a->order < b->order)
+ return (-1);
+ if (a->order > b->order)
+ return (1);
+ return (0);
+}
+
+static void
+sysinit_mklist(struct sysinitlist *list, struct sysinit **set,
+ struct sysinit **set_end)
{
- struct sysinit **newset;
struct sysinit **sipp;
- struct sysinit **xipp;
- int count;
-
- count = set_end - set;
- if (newsysinit)
- count += newsysinit_end - newsysinit;
- else
- count += sysinit_end - sysinit;
- newset = malloc(count * sizeof(*sipp), M_TEMP, M_NOWAIT);
- if (newset == NULL)
- panic("cannot malloc for sysinit");
- xipp = newset;
- if (newsysinit)
- for (sipp = newsysinit; sipp < newsysinit_end; sipp++)
- *xipp++ = *sipp;
- else
- for (sipp = sysinit; sipp < sysinit_end; sipp++)
- *xipp++ = *sipp;
+
+ TSENTER();
+ TSENTER2("listify");
+ SLIST_INIT(list);
for (sipp = set; sipp < set_end; sipp++)
- *xipp++ = *sipp;
- if (newsysinit)
- free(newsysinit, M_TEMP);
- newsysinit = newset;
- newsysinit_end = newset + count;
+ SLIST_INSERT_HEAD(list, *sipp, next);
+ TSEXIT2("listify");
+ TSENTER2("mergesort");
+ SLIST_MERGESORT(list, NULL, sysinit_compar, sysinit, next);
+ TSEXIT2("mergesort");
+ TSEXIT();
+}
+
+/*
+ * Merge a new sysinit set into the sysinit list.
+ */
+void
+sysinit_add(struct sysinit **set, struct sysinit **set_end)
+{
+ struct sysinitlist new_list;
+
+ TSENTER();
+
+ /* Construct a sorted list from the new sysinits. */
+ sysinit_mklist(&new_list, set, set_end);
+
+ /* Merge the new list into the existing one. */
+ TSENTER2("SLIST_MERGE");
+ SLIST_MERGE(&sysinit_list, &new_list, NULL, sysinit_compar, sysinit, next);
+ TSEXIT2("SLIST_MERGE");
+
+ TSEXIT();
}
#if defined (DDB) && defined(VERBOSE_SYSINIT)
@@ -229,10 +258,7 @@ void
mi_startup(void)
{
- struct sysinit **sipp; /* system initialization*/
- struct sysinit **xipp; /* interior loop of sort*/
- struct sysinit *save; /* bubble*/
-
+ struct sysinit *sip;
int last;
#if defined(VERBOSE_SYSINIT)
int verbose;
@@ -243,29 +269,8 @@ mi_startup(void)
if (boothowto & RB_VERBOSE)
bootverbose++;
- if (sysinit == NULL) {
- sysinit = SET_BEGIN(sysinit_set);
- sysinit_end = SET_LIMIT(sysinit_set);
- }
-
-restart:
- /*
- * Perform a bubble sort of the system initialization objects by
- * their subsystem (primary key) and order (secondary key).
- */
- TSENTER2("bubblesort");
- for (sipp = sysinit; sipp < sysinit_end; sipp++) {
- for (xipp = sipp + 1; xipp < sysinit_end; xipp++) {
- if ((*sipp)->subsystem < (*xipp)->subsystem ||
- ((*sipp)->subsystem == (*xipp)->subsystem &&
- (*sipp)->order <= (*xipp)->order))
- continue; /* skip*/
- save = *sipp;
- *sipp = *xipp;
- *xipp = save;
- }
- }
- TSEXIT2("bubblesort");
+ /* Construct and sort sysinit list. */
+ sysinit_mklist(&sysinit_list, SET_BEGIN(sysinit_set), SET_LIMIT(sysinit_set));
last = SI_SUB_COPYRIGHT;
#if defined(VERBOSE_SYSINIT)
@@ -276,21 +281,23 @@ restart:
#endif
/*
- * Traverse the (now) ordered list of system initialization tasks.
- * Perform each task, and continue on to the next task.
+ * Perform each system initialization task from the ordered list. Note
+ * that if sysinit_list is modified (e.g. by a KLD) we will nonetheless
+ * always perform the earlist-sorted sysinit at each step; using the
+ * SLIST_FOREACH macro would result in items being skipped if inserted
+ * earlier than the "current item".
*/
- for (sipp = sysinit; sipp < sysinit_end; sipp++) {
- if ((*sipp)->subsystem == SI_SUB_DUMMY)
- continue; /* skip dummy task(s)*/
+ while ((sip = SLIST_FIRST(&sysinit_list)) != NULL) {
+ SLIST_REMOVE_HEAD(&sysinit_list, next);
- if ((*sipp)->subsystem == SI_SUB_DONE)
- continue;
+ if (sip->subsystem == SI_SUB_DUMMY)
+ continue; /* skip dummy task(s)*/
- if ((*sipp)->subsystem > last)
- BOOTTRACE_INIT("sysinit 0x%7x", (*sipp)->subsystem);
+ if (sip->subsystem > last)
+ BOOTTRACE_INIT("sysinit 0x%7x", sip->subsystem);
#if defined(VERBOSE_SYSINIT)
- if ((*sipp)->subsystem > last && verbose_sysinit != 0) {
+ if (sip->subsystem > last && verbose_sysinit != 0) {
verbose = 1;
printf("subsystem %x\n", last);
}
@@ -298,23 +305,23 @@ restart:
#if defined(DDB)
const char *func, *data;
- func = symbol_name((vm_offset_t)(*sipp)->func,
+ func = symbol_name((vm_offset_t)sip->func,
DB_STGY_PROC);
- data = symbol_name((vm_offset_t)(*sipp)->udata,
+ data = symbol_name((vm_offset_t)sip->udata,
DB_STGY_ANY);
if (func != NULL && data != NULL)
printf(" %s(&%s)... ", func, data);
else if (func != NULL)
- printf(" %s(%p)... ", func, (*sipp)->udata);
+ printf(" %s(%p)... ", func, sip->udata);
else
#endif
- printf(" %p(%p)... ", (*sipp)->func,
- (*sipp)->udata);
+ printf(" %p(%p)... ", sip->func,
+ sip->udata);
}
#endif
/* Call function */
- (*((*sipp)->func))((*sipp)->udata);
+ (*(sip->func))(sip->udata);
#if defined(VERBOSE_SYSINIT)
if (verbose)
@@ -322,19 +329,7 @@ restart:
#endif
/* Check off the one we're just done */
- last = (*sipp)->subsystem;
- (*sipp)->subsystem = SI_SUB_DONE;
-
- /* Check if we've installed more sysinit items via KLD */
- if (newsysinit != NULL) {
- if (sysinit != SET_BEGIN(sysinit_set))
- free(sysinit, M_TEMP);
- sysinit = newsysinit;
- sysinit_end = newsysinit_end;
- newsysinit = NULL;
- newsysinit_end = NULL;
- goto restart;
- }
+ last = sip->subsystem;
}
TSEXIT(); /* Here so we don't overlap with start_init. */
@@ -904,13 +899,13 @@ db_show_print_syinit(struct sysinit *sip, bool ddb)
DB_SHOW_COMMAND_FLAGS(sysinit, db_show_sysinit, DB_CMD_MEMSAFE)
{
- struct sysinit **sipp;
+ struct sysinit *sip;
db_printf("SYSINIT vs Name(Ptr)\n");
db_printf(" Subsystem Order\n");
db_printf(" Function(Name)(Arg)\n");
- for (sipp = sysinit; sipp < sysinit_end; sipp++) {
- db_show_print_syinit(*sipp, true);
+ SLIST_FOREACH(sip, &sysinit_list, next) {
+ db_show_print_syinit(sip, true);
if (db_pager_quit)
break;
}