diff --git a/fs/proc/root.c b/fs/proc/root.c index 361ab4e..9f26ea1 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -161,6 +161,36 @@ static struct file_system_type proc_fs_type = { .fs_flags = FS_USERNS_VISIBLE | FS_USERNS_MOUNT, }; +extern void do_deferred_initcalls(void); + +static ssize_t deferred_initcalls_read_proc(struct file *file, char __user *buf, + size_t nbytes, loff_t *ppos) +{ + static int deferred_initcalls_done = 0; + int len, ret; + char tmp[3] = "1\n"; + + if (*ppos >= 3) + return 0; + + if ((! deferred_initcalls_done) && ! (*ppos)) { + tmp[0] = '0'; + do_deferred_initcalls(); + deferred_initcalls_done = 1; + } + + len = min(nbytes, (size_t)3); + ret = copy_to_user(buf, tmp, len); + if (ret) + return -EFAULT; + *ppos += len; + return len; +} + +static const struct file_operations deferred_initcalls_fops = { + .read = deferred_initcalls_read_proc, +}; + void __init proc_root_init(void) { int err; @@ -172,6 +202,7 @@ void __init proc_root_init(void) proc_self_init(); proc_thread_self_init(); + proc_create("deferred_initcalls", 0, NULL, &deferred_initcalls_fops); proc_symlink("mounts", NULL, "self/mounts"); proc_net_init(); diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index c4bd0e2..8d649ba 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -656,6 +656,11 @@ *(.initcall##level##.init) \ *(.initcall##level##s.init) \ +#define DEFERRED_INITCALLS \ + VMLINUX_SYMBOL(__deferred_initcall_start) = .; \ + *(.deferred_initcall.init) \ + VMLINUX_SYMBOL(__deferred_initcall_end) = .; + #define INIT_CALLS \ VMLINUX_SYMBOL(__initcall_start) = .; \ *(.initcallearly.init) \ @@ -668,7 +673,8 @@ INIT_CALLS_LEVEL(rootfs) \ INIT_CALLS_LEVEL(6) \ INIT_CALLS_LEVEL(7) \ - VMLINUX_SYMBOL(__initcall_end) = .; + VMLINUX_SYMBOL(__initcall_end) = .; \ + DEFERRED_INITCALLS #define CON_INITCALL \ VMLINUX_SYMBOL(__con_initcall_start) = .; \ diff --git a/include/linux/init.h b/include/linux/init.h index b449f37..36e5da9 100644 --- a/include/linux/init.h +++ b/include/linux/init.h @@ -229,6 +229,10 @@ extern bool initcall_debug; static initcall_t __initcall_##fn \ __used __section(.security_initcall.init) = fn +#define deferred_initcall(fn) \ + static initcall_t __initcall_##fn \ + __used __section(.deferred_initcall.init) = fn + struct obs_kernel_param { const char *str; int (*setup_func)(char *); diff --git a/include/linux/module.h b/include/linux/module.h index 3a19c79..7a49244 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -82,6 +82,7 @@ extern void cleanup_module(void); * be one per module. */ #define module_init(x) __initcall(x); +#define deferred_module_init(x) deferred_initcall(x); /** * module_exit() - driver exit entry point diff --git a/init/main.c b/init/main.c index 9e64d70..77064bd 100644 --- a/init/main.c +++ b/init/main.c @@ -936,7 +936,7 @@ static int __ref kernel_init(void *unused) kernel_init_freeable(); /* need to finish all async __init code before freeing the memory */ async_synchronize_full(); - free_initmem(); + //free_initmem(); mark_rodata_ro(); system_state = SYSTEM_RUNNING; numa_default_policy(); @@ -1038,3 +1038,33 @@ static noinline void __init kernel_init_freeable(void) integrity_load_keys(); load_default_modules(); } + +extern initcall_t __deferred_initcall_start[], __deferred_initcall_end[]; + +/* call deferred init routines */ +void do_deferred_initcalls(void) +{ + initcall_t *call; + static int already_run=0; + + if (already_run) { + printk("do_deferred_initcalls() has already run\n"); + return; + } + + already_run=1; + + printk("Running do_deferred_initcalls()\n"); + +// lock_kernel(); /* make environment similar to early boot */ + + for(call = __deferred_initcall_start; + call < __deferred_initcall_end; call++) + do_one_initcall(*call); + + flush_scheduled_work(); + + free_initmem(); +// unlock_kernel(); +} +