diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -149,57 +149,106 @@ #endif /* - * This ensures that there is at least one entry so that the sysinit_set - * symbol is not undefined. A sybsystem ID of SI_SUB_DUMMY is never - * executed. + * Declare the list of function pointers, to init the kernel. */ -static void -placeholder(void) +SET_DECLARE(init_array, sysinit_func_t); + +/* + * Declare the list of function pointers, to shutdown the kernel. + */ +SET_DECLARE(fini_array, sysinit_func_t); + +/* + * Compare two sysinit structures. + */ +static int +sysinit_compare(const struct sysinit *pa, const struct sysinit *pb, int neg) { + if (pa->subsystem > pb->subsystem) + return (neg); + else if (pa->subsystem < pb->subsystem) + return (-neg); + else if (pa->order > pb->order) + return (neg); + else if (pa->order < pb->order) + return (-neg); + else + return (0); } -SYSINIT(placeholder, SI_SUB_DUMMY, SI_ORDER_ANY, placeholder); + +SYSINIT_DECLARE_HEAD(sysinit_head); +SYSINIT_DECLARE_HEAD(sysuninit_head); /* - * The sysinit table itself. Items are checked off as the are run. - * If we want to register new sysinit types, add them to newsysinit. + * Insert a sysinit structure into a list sorted by subsystem and order. + * + * @param ptr Pointer to the sysinit structure to be inserted. + * @param phead Pointer to the head structure for the sysinits. + * @param neg 1: Ascending order -1: Descending order */ -SET_DECLARE(sysinit_set, struct sysinit); -struct sysinit **sysinit, **sysinit_end; -struct sysinit **newsysinit, **newsysinit_end; +void +sysinit_insert_sorted(struct sysinit *ptr, sysinit_head_t *phead, int neg) +{ + /* don't insert reserved entries */ + if (ptr->subsystem == SI_SUB_DUMMY) + return; + + if (TAILQ_FIRST(&phead->h) == NULL) { + TAILQ_INSERT_TAIL(&phead->h, ptr, entry); + } else { + for (;;) { + struct sysinit *pnext; + int cmp; + + pnext = TAILQ_NEXT(phead->l, entry); + + cmp = sysinit_compare(ptr, phead->l, neg); + if (cmp < 0) { + /* search from beginning */ + phead->l = TAILQ_FIRST(&phead->h); + } else if (pnext == NULL || sysinit_compare(ptr, pnext, neg) < 0) { + /* insert after last element */ + TAILQ_INSERT_AFTER(&phead->h, phead->l, ptr, entry); + break; + } else { + /* advance to next element */ + phead->l = pnext; + } + } + } + phead->l = ptr; +} /* - * Merge a new sysinit set into the current set, reallocating it if - * necessary. This can only be called after malloc is running. + * Remove a sysinit structure from a list. + + * @param ptr Pointer to the sysinit structure to be removed + * @param phead Pointer to the head structure for the sysinits */ void -sysinit_add(struct sysinit **set, struct sysinit **set_end) +sysinit_remove(struct sysinit *ptr, sysinit_head_t *phead) { - 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; - for (sipp = set; sipp < set_end; sipp++) - *xipp++ = *sipp; - if (newsysinit) - free(newsysinit, M_TEMP); - newsysinit = newset; - newsysinit_end = newset + count; + /* update last pointer to next element, if it was removed */ + if (phead->l == ptr) + phead->l = TAILQ_NEXT(ptr, entry); + + TAILQ_REMOVE(&phead->h, ptr, entry); + + /* in case TAILQ_NEXT() returned NULL, reset the last pointer */ + if (phead->l == NULL) + phead->l = TAILQ_FIRST(&phead->h); +} + +void +sysinit_constructor_register(struct sysinit *ptr) +{ + sysinit_insert_sorted(ptr, &sysinit_head, 1); +} + +void +sysinit_destructor_register(struct sysinit *ptr) +{ + sysinit_insert_sorted(ptr, &sysuninit_head, -1); } #if defined (DDB) && defined(VERBOSE_SYSINIT) @@ -220,128 +269,88 @@ } #endif -/* - * System startup; initialize the world, create process 0, mount root - * filesystem, and fork to create init and pagedaemon. Most of the - * hard work is done in the lower-level initialization routines including - * startup(), which does memory initialization and autoconfiguration. - * - * This allows simple addition of new kernel subsystems that require - * boot time initialization. It also allows substitution of subsystem - * (for instance, a scheduler, kernel profiler, or VM system) by object - * module. Finally, it allows for optional "kernel threads". - */ void -mi_startup(void) +sysinit_execute(sysinit_head_t *phead, const char *fname) { - - struct sysinit **sipp; /* system initialization*/ - struct sysinit **xipp; /* interior loop of sort*/ - struct sysinit *save; /* bubble*/ - - int last; + struct sysinit *ptr; + enum sysinit_sub_id last = SI_SUB_DUMMY; #if defined(VERBOSE_SYSINIT) - int verbose; + bool verbose = false; #endif - TSENTER(); + while ((ptr = TAILQ_FIRST(&phead->h))) { + sysinit_remove(ptr, phead); - 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"); + if (last != ptr->subsystem) { + last = ptr->subsystem; + BOOTTRACE("%s: sysinit execute 0x%3X", fname, last); - last = SI_SUB_COPYRIGHT; #if defined(VERBOSE_SYSINIT) - verbose = 0; -#if !defined(DDB) - printf("VERBOSE_SYSINIT: DDB not enabled, symbol lookups disabled.\n"); + verbose = (last > SI_SUB_COPYRIGHT); + if (verbose) + printf("subsystem 0x%03X\n", last); #endif -#endif - - /* - * Traverse the (now) ordered list of system initialization tasks. - * Perform each task, and continue on to the next task. - */ - for (sipp = sysinit; sipp < sysinit_end; sipp++) { - if ((*sipp)->subsystem == SI_SUB_DUMMY) - continue; /* skip dummy task(s)*/ - - if ((*sipp)->subsystem == SI_SUB_DONE) - continue; - - if ((*sipp)->subsystem > last) - BOOTTRACE_INIT("sysinit 0x%7x", (*sipp)->subsystem); + } #if defined(VERBOSE_SYSINIT) - if ((*sipp)->subsystem > last && verbose_sysinit != 0) { - verbose = 1; - printf("subsystem %x\n", last); - } if (verbose) { #if defined(DDB) - const char *func, *data; + const char *func; - func = symbol_name((vm_offset_t)(*sipp)->func, + func = symbol_name((vm_offset_t)ptr->func, DB_STGY_PROC); - data = symbol_name((vm_offset_t)(*sipp)->udata, - DB_STGY_ANY); - if (func != NULL && data != NULL) - printf(" %s(&%s)... ", func, data); - else if (func != NULL) - printf(" %s(%p)... ", func, (*sipp)->udata); + if (func != NULL) + printf(" %s()... ", func); else #endif - printf(" %p(%p)... ", (*sipp)->func, - (*sipp)->udata); + printf(" %p()... ", ptr->func); } #endif - - /* Call function */ - (*((*sipp)->func))((*sipp)->udata); + /* execute sysinit */ + ptr->func(); #if defined(VERBOSE_SYSINIT) if (verbose) printf("done.\n"); #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; - } } +} + +/* + * System startup; initialize the world, create process 0, mount root + * filesystem, and fork to create init and pagedaemon. Most of the + * hard work is done in the lower-level initialization routines including + * startup(), which does memory initialization and autoconfiguration. + * + * This allows simple addition of new kernel subsystems that require + * boot time initialization. It also allows substitution of subsystem + * (for instance, a scheduler, kernel profiler, or VM system) by object + * module. Finally, it allows for optional "kernel threads". + */ +void +mi_startup(void) +{ + sysinit_func_t * const * si_start; + sysinit_func_t * const * si_stop; + + TSENTER(); + + if (boothowto & RB_VERBOSE) + bootverbose++; + + si_start = SET_BEGIN(init_array); + si_stop = SET_LIMIT(init_array); + +#if defined(VERBOSE_SYSINIT) && !defined(DDB) + printf("VERBOSE_SYSINIT: DDB not enabled, symbol lookups disabled.\n"); +#endif + + /* run all the kernel constructors */ + for (; si_start != si_stop; si_start++) + (*si_start)(); + + /* execute all the sysinits */ + sysinit_execute(&sysinit_head, "kernel"); TSEXIT(); /* Here so we don't overlap with start_init. */ BOOTTRACE("mi_startup done"); @@ -871,9 +880,9 @@ */ #ifdef DDB static void -db_show_print_syinit(struct sysinit *sip, bool ddb) +db_show_print_function(sysinit_func_t *func, bool ddb) { - const char *sname, *funcname; + const char *funcname; c_db_sym_t sym; db_expr_t offset; @@ -883,31 +892,44 @@ else \ printf(__VA_ARGS__) - if (sip == NULL) { - xprint("%s: no sysinit * given\n", __func__); + if (func == NULL) { + xprint("%s: no function given\n", __func__); return; } - sym = db_search_symbol((vm_offset_t)sip, DB_STGY_ANY, &offset); - db_symbol_values(sym, &sname, NULL); - sym = db_search_symbol((vm_offset_t)sip->func, DB_STGY_PROC, &offset); + sym = db_search_symbol((vm_offset_t)func, DB_STGY_PROC, &offset); db_symbol_values(sym, &funcname, NULL); - xprint("%s(%p)\n", (sname != NULL) ? sname : "", sip); - xprint(" %#08x %#08x\n", sip->subsystem, sip->order); - xprint(" %p(%s)(%p)\n", - sip->func, (funcname != NULL) ? funcname : "", sip->udata); + xprint("%s(%p)\n", funcname, func); #undef xprint } -DB_SHOW_COMMAND_FLAGS(sysinit, db_show_sysinit, DB_CMD_MEMSAFE) +DB_SHOW_COMMAND_FLAGS(constructors, db_show_constructors, DB_CMD_MEMSAFE) { - struct sysinit **sipp; + sysinit_func_t * const * si_start; + sysinit_func_t * const * si_stop; + + si_start = SET_BEGIN(init_array); + si_stop = SET_LIMIT(init_array); + + db_printf("CONSTRUCTORS vs Name(Ptr)\n"); + for (; si_start != si_stop; si_start++) { + db_show_print_function(*si_start, true); + if (db_pager_quit) + break; + } +} + +DB_SHOW_COMMAND_FLAGS(destructors, db_show_destructors, DB_CMD_MEMSAFE) +{ + sysinit_func_t * const * si_start; + sysinit_func_t * const * si_stop; + + si_start = SET_BEGIN(fini_array); + si_stop = SET_LIMIT(fini_array); - 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); + db_printf("DESTRUCTORS vs Name(Ptr)\n"); + for (; si_start != si_stop; si_start++) { + db_show_print_function(*si_start, true); if (db_pager_quit) break; } diff --git a/sys/kern/kern_linker.c b/sys/kern/kern_linker.c --- a/sys/kern/kern_linker.c +++ b/sys/kern/kern_linker.c @@ -195,54 +195,27 @@ static void linker_file_sysinit(linker_file_t lf) { - struct sysinit **start, **stop, **sipp, **xipp, *save; - int last; + sysinit_func_t * const * si_start; + sysinit_func_t * const * si_stop; - KLD_DPF(FILE, ("linker_file_sysinit: calling SYSINITs for %s\n", + KLD_DPF(FILE, ("linker_file_sysinit: calling constructors for %s\n", lf->filename)); sx_assert(&kld_sx, SA_XLOCKED); - if (linker_file_lookup_set(lf, "sysinit_set", &start, &stop, NULL) != 0) + if (linker_file_lookup_set(lf, ".init_array", &si_start, &si_stop, NULL) != 0) return; - /* - * Perform a bubble sort of the system initialization objects by - * their subsystem (primary key) and order (secondary key). - * - * Since some things care about execution order, this is the operation - * which ensures continued function. - */ - for (sipp = start; sipp < stop; sipp++) { - for (xipp = sipp + 1; xipp < stop; xipp++) { - if ((*sipp)->subsystem < (*xipp)->subsystem || - ((*sipp)->subsystem == (*xipp)->subsystem && - (*sipp)->order <= (*xipp)->order)) - continue; /* skip */ - save = *sipp; - *sipp = *xipp; - *xipp = save; - } - } - /* - * Traverse the (now) ordered list of system initialization tasks. - * Perform each task, and continue on to the next task. - */ - last = SI_SUB_DUMMY; sx_xunlock(&kld_sx); mtx_lock(&Giant); - for (sipp = start; sipp < stop; sipp++) { - if ((*sipp)->subsystem == SI_SUB_DUMMY) - continue; /* skip dummy task(s) */ - if ((*sipp)->subsystem > last) - BOOTTRACE("%s: sysinit 0x%7x", lf->filename, - (*sipp)->subsystem); + /* run all the constructors provided by the kernel module */ + for (; si_start != si_stop; si_start++) + (*si_start)(); + + /* run all the registered SYSINITs */ + sysinit_execute(&sysinit_head, lf->filename); - /* Call function */ - (*((*sipp)->func)) ((*sipp)->udata); - last = (*sipp)->subsystem; - } mtx_unlock(&Giant); sx_xlock(&kld_sx); } @@ -250,56 +223,27 @@ static void linker_file_sysuninit(linker_file_t lf) { - struct sysinit **start, **stop, **sipp, **xipp, *save; - int last; + sysinit_func_t * const * si_start; + sysinit_func_t * const * si_stop; - KLD_DPF(FILE, ("linker_file_sysuninit: calling SYSUNINITs for %s\n", + KLD_DPF(FILE, ("linker_file_sysuninit: calling destructors for %s\n", lf->filename)); sx_assert(&kld_sx, SA_XLOCKED); - if (linker_file_lookup_set(lf, "sysuninit_set", &start, &stop, - NULL) != 0) + if (linker_file_lookup_set(lf, ".fini_array", &si_start, &si_stop, NULL) != 0) return; - /* - * Perform a reverse bubble sort of the system initialization objects - * by their subsystem (primary key) and order (secondary key). - * - * Since some things care about execution order, this is the operation - * which ensures continued function. - */ - for (sipp = start; sipp < stop; sipp++) { - for (xipp = sipp + 1; xipp < stop; xipp++) { - if ((*sipp)->subsystem > (*xipp)->subsystem || - ((*sipp)->subsystem == (*xipp)->subsystem && - (*sipp)->order >= (*xipp)->order)) - continue; /* skip */ - save = *sipp; - *sipp = *xipp; - *xipp = save; - } - } - - /* - * Traverse the (now) ordered list of system initialization tasks. - * Perform each task, and continue on to the next task. - */ sx_xunlock(&kld_sx); mtx_lock(&Giant); - last = SI_SUB_DUMMY; - for (sipp = start; sipp < stop; sipp++) { - if ((*sipp)->subsystem == SI_SUB_DUMMY) - continue; /* skip dummy task(s) */ - - if ((*sipp)->subsystem > last) - BOOTTRACE("%s: sysuninit 0x%7x", lf->filename, - (*sipp)->subsystem); - - /* Call function */ - (*((*sipp)->func)) ((*sipp)->udata); - last = (*sipp)->subsystem; - } + + /* run all the destructors provided by the kernel module */ + for (; si_start != si_stop; si_start++) + (*si_start)(); + + /* run all the registered SYSUNINITs */ + sysinit_execute(&sysuninit_head, lf->filename); + mtx_unlock(&Giant); sx_xlock(&kld_sx); } @@ -1581,7 +1525,8 @@ int nver; int resolves; modlist_t mod; - struct sysinit **si_start, **si_stop; + sysinit_func_t * const * si_start; + sysinit_func_t * const * si_stop; TAILQ_INIT(&loaded_files); TAILQ_INIT(&depended_files); @@ -1762,9 +1707,10 @@ linker_file_register_modules(lf); if (!TAILQ_EMPTY(&lf->modules)) lf->flags |= LINKER_FILE_MODULES; - if (linker_file_lookup_set(lf, "sysinit_set", &si_start, - &si_stop, NULL) == 0) - sysinit_add(si_start, si_stop); + if (linker_file_lookup_set(lf, ".init_array", &si_start, &si_stop, NULL) == 0) { + for (; si_start != si_stop; si_start++) + (*si_start)(); + } linker_file_register_sysctls(lf, true); lf->flags |= LINKER_FILE_LINKED; continue; diff --git a/sys/net/vnet.h b/sys/net/vnet.h --- a/sys/net/vnet.h +++ b/sys/net/vnet.h @@ -320,6 +320,8 @@ */ #include +typedef void (*sysinit_cfunc_t)(const void *); + /* * SYSINIT/SYSUNINIT variants that provide per-vnet constructors and * destructors. diff --git a/sys/sys/kernel.h b/sys/sys/kernel.h --- a/sys/sys/kernel.h +++ b/sys/sys/kernel.h @@ -49,11 +49,10 @@ #include -#ifdef _KERNEL - -/* for intrhook below */ #include +#ifdef _KERNEL + /* for timestamping SYSINITs; other files may assume this is included here */ #include @@ -88,7 +87,6 @@ */ enum sysinit_sub_id { SI_SUB_DUMMY = 0x000, /* not executed; for linker */ - SI_SUB_DONE = 0x00C, /* processed */ SI_SUB_TUNABLES = 0x018, /* establish tunable values */ SI_SUB_COPYRIGHT = 0x024, /* first use of console */ SI_SUB_VM = 0x030, /* virtual memory system init */ @@ -192,37 +190,28 @@ SI_ORDER_ANY = 0x3f /* last */ }; -/* - * A system initialization call instance - * - * At the moment there is one instance of sysinit. We probably do not - * want two which is why this code is if'd out, but we definitely want - * to discern SYSINIT's which take non-constant data pointers and - * SYSINIT's which take constant data pointers, - * - * The C_* macros take functions expecting const void * arguments - * while the non-C_* macros take functions expecting just void * arguments. - * - * With -Wcast-qual on, the compiler issues warnings: - * - if we pass non-const data or functions taking non-const data - * to a C_* macro. - * - * - if we pass const data to the normal macros - * - * However, no warning is issued if we pass a function taking const data - * through a normal non-const macro. This is ok because the function is - * saying it won't modify the data so we don't care whether the data is - * modifiable or not. - */ +struct sysinit; + +typedef struct { + TAILQ_HEAD(, sysinit) h; + struct sysinit *l; /* last entry added - to optimise sorted insertion */ +} sysinit_head_t; + +#define SYSINIT_DECLARE_HEAD(name) \ + sysinit_head_t name = { .h = TAILQ_HEAD_INITIALIZER(name.h), .l = NULL } + +#define SYSINIT_FOREACH(ptr, head) \ + TAILQ_FOREACH(ptr, &(head)->h, entry) + +typedef TAILQ_ENTRY(sysinit) sysinit_entry_t; -typedef void (*sysinit_nfunc_t)(void *); -typedef void (*sysinit_cfunc_t)(const void *); +typedef void (sysinit_func_t)(void); struct sysinit { - enum sysinit_sub_id subsystem; /* subsystem identifier*/ - enum sysinit_elem_order order; /* init order within subsystem*/ - sysinit_cfunc_t func; /* function */ - const void *udata; /* multiplexer/argument */ + sysinit_entry_t entry; /* entry for list */ + sysinit_func_t *func; /* function pointer */ + enum sysinit_sub_id subsystem; /* subsystem identifier */ + enum sysinit_elem_order order; /* init order within subsystem */ }; /* @@ -243,11 +232,17 @@ do { } while (0) #endif +#define __SI_PRIORITY(sub, order) \ + (((sub) * (SI_ORDER_ANY + 1)) + (order)) + +_Static_assert(__SI_PRIORITY(SI_SUB_LAST, SI_ORDER_ANY) <= 65535, + "The maximum constructor priority exceeds 65535"); + /* * Explanation of arguments for the __SI_REGISTER() macro: * * @param uniq An identifier for the needed functions and structures. - * @param type sysinit_set or sysuninit_set, depending on use case. + * @param type constructor or destructor, depending on use case. * @param _sub SI_SUB_XXX enum. * @param _order SI_ORDER_XXX enum. * @param _func Pointer to callback function. @@ -259,41 +254,51 @@ * pointer casting between types is avoided entirely. */ #define __SI_REGISTER(uniq, type, _sub, _order, _func, ...) \ -_Static_assert((_sub) > SI_SUB_DONE && (_sub) <= SI_SUB_LAST, \ +_Static_assert((_sub) > SI_SUB_DUMMY && (_sub) <= SI_SUB_LAST, \ "Subsystem is out of range"); \ _Static_assert((_order) >= 0 && (_order) <= SI_ORDER_ANY, \ "Order is out of range"); \ +_Static_assert(__SI_PRIORITY(_sub, _order) >= 100, \ + "Priorities below 100 are reserved"); \ static void \ -type##_##uniq##_shim(const void *arg __unused) \ +type##_##uniq##_shim(void) \ { \ __SI_FUNCTION_PRE(type, _func); \ (_func)(__VA_ARGS__); \ __SI_FUNCTION_POST(type, _func); \ } \ -static struct sysinit type##_##uniq = { \ - .subsystem = _sub, \ - .order = _order, \ - .func = &type##_##uniq##_shim, \ - .udata = NULL, \ -}; \ -DATA_WSET(type, type##_##uniq); \ +static void __attribute__((type(__SI_PRIORITY(_sub, _order)))) \ + type##_##uniq##_register(void) { \ + static struct sysinit __data = { \ + .subsystem = _sub, \ + .order = _order, \ + .func = &type##_##uniq##_shim, \ + }; \ + sysinit_##type##_register(&__data); \ +} \ struct __hack /* * Sysinit API macro, called on kernel or module load: */ #define SYSINIT(uniq, sub, order, ...) \ - __SI_REGISTER(uniq, sysinit_set, sub, order, __VA_ARGS__) + __SI_REGISTER(uniq, constructor, sub, order, __VA_ARGS__) /* * Sysuninit API macros, called on kernel shutdown or module unload: */ #define SYSUNINIT(uniq, sub, order, ...) \ - __SI_REGISTER(uniq, sysuinit_set, sub, order, __VA_ARGS__) - -void sysinit_add(struct sysinit **set, struct sysinit **set_end); + __SI_REGISTER(uniq, destructor, sub, order, __VA_ARGS__) #ifdef _KERNEL +extern sysinit_head_t sysinit_head; +extern sysinit_head_t sysuninit_head; + +void sysinit_insert_sorted(struct sysinit *, sysinit_head_t *, int neg); +void sysinit_remove(struct sysinit *, sysinit_head_t *); +void sysinit_constructor_register(struct sysinit *); +void sysinit_destructor_register(struct sysinit *); +void sysinit_execute(sysinit_head_t *, const char *); /* * Infrastructure for tunable 'constants'. Value may be specified at compile