Thanks for the notice!
I am a new kernel developer(also a kernel newbie :-) ) and working on a multi-threaded initcall patch in order to speed up the boot stage now. This idea provided by my mentor.
The basic idea is using kthread with initcall and register a wait_for_initcall() with xxx_initcall_sync() which introduced by Andrew Morton for multi-threaded probing used as barrier for each level.
I supposed the initcalls in same level can be run in parallel. I know this maybe not true, but I wrote this patch and tried.
I ignored preempt_count() check and irq_disabled() check which introduced with initcall_debug at first, for I think these may caused in chaos like temporary disable irq by some initcall.
I don't know if there is policy should by followed by initcall, I found something but seems too old. I'd like to know if some policy can be followed after layered initcall was introduced.
----
Patch's situation:
It can work, but by luck. I add initcall_debug for kernel parameters.
Some time it boots well(with some faults metioned below), but other time it stopped to wait for a subsystem call to complete and never returns. It looks as if some initcall in subsystem level affect others caused one initcall can't return. But I don't know how to find it - the output in screen is not completed, dmesg and syslog only contained successful boot.
Another obvious fault caused by ide_xxx initcalls.
[ 1.572000] ide1: I/O resource 0x376-0x376 not free.
[ 1.576000] hdc: ERROR, PORTS ALREADY IN USE
[ 1.576000] ide1 at 0x170-0x177,0x376 on irq 15
[ 1.576000] Call to ide_init+0x0/0x7d() finished.
[ 1.896000] Unbalanced enable for IRQ 15
[ 1.896000] BUG: at kernel/irq/manage.c:158 enable_irq()
[ 1.896000] [<c013578a>] enable_irq+0x6e/0xa2
[ 1.896000] [<c02c96fe>] probe_hwif+0x5d4/0x6b4
[ 1.896000] [<c02c9f4e>] ideprobe_init+0x4c/0x11d
[ 1.896000] [<c0595d10>] initcall_run+0x0/0x87
[ 1.896000] [<c05b3824>] ide_generic_init+0x5/0xd
[ 1.896000] [<c0595d20>] initcall_run+0x10/0x87
[ 1.896000] [<c0129624>] kthread+0xb0/0xd8
[ 1.896000] [<c0129574>] kthread+0x0/0xd8
[ 1.896000] [<c0103cab>] kernel_thread_helper+0x7/0x10
[ 1.896000] =======================
[ 1.896000] register_blkdev: cannot get major 3 for ide0
[ 1.896000] register_blkdev: cannot get major 22 for ide1
I think if we can introduce a mechanism like waitqueue with exclusive flag to deal with this kind of thing.
------
Doubt:
Is it necessary to make initcall run in parallel? I tested the result of successful boot with my old T41(PM1.6G), and reduce the boot time for initcall about 50%, but only 2 seconds...
Any suggest and correction are most welcome!
Thanks!
------
Patch:
--- /opt/kernel/linux-2.6.21.orig/init/main.c 2007-05-01 13:19:27.000000000 +0800
+++ ./init/main.c 2007-05-03 12:19: 31.000000000 +0800
@@ -54,6 +54,7 @@
#include <linux/lockdep.h>
#include <linux/pid_namespace.h>
#include <linux/device.h>
+#include <linux/kthread.h>
#include <asm/io.h>
#include <asm/bugs.h>
@@ -641,7 +642,7 @@ asmlinkage void __init start_kernel(void
rest_init();
}
-static int __initdata initcall_debug;
+static int __initdata initcall_debug;
static int __init initcall_debug_setup(char *str)
{
@@ -650,17 +651,87 @@ static int __init initcall_debug_setup(c
}
__setup("initcall_debug", initcall_debug_setup);
+static atomic_t initcall_count = ATOMIC_INIT(0);
+static DECLARE_WAIT_QUEUE_HEAD(initcall_waitqueue);
+
+static int __init initcall_run(void *void_call)
+{
+ int result;
+ initcall_t *call = void_call;
+
+ result = (*call)();
+ print_fn_descriptor_symbol("%s() ",
+ (unsigned long) *call);
+ printk("returns %d", result);
+ atomic_dec(&initcall_count);
+
+ if (result && result != -ENODEV && initcall_debug) {
+ printk(KERN_WARNING "initcall at 0x%p", *call);
+ print_fn_descriptor_symbol(": %s()",
+ (unsigned long) *call);
+ printk(": returned with error code %d\n", result);
+ }
+ /* if (preempt_count() != count) {
+ printk(KERN_WARNING "initcall at 0x%p", *call);
+ print_fn_descriptor_symbol(": %s()",
+ (unsigned long) *call);
+ printk(": returned with error code preemption imbalance\n");
+ preempt_count() = count;
+ }
+ if (irqs_disabled()) {
+ printk(KERN_WARNING "initcall at 0x%p", *call);
+ print_fn_descriptor_symbol(": %s()",
+ (unsigned long) *call);
+ printk(": returned with error code disabled interrupts\n");
+ local_irq_enable();
+ } */
+
+ wake_up(&initcall_waitqueue);
+
+ printk(" and thread finished.\n");
+
+ return 0;
+}
+
+static int __init wait_for_initcall(void)
+{
+ DEFINE_WAIT(wait);
+
+ printk("%s wait for %d initcall threads...\n",
+ __FUNCTION__, atomic_read(&initcall_count));
+ if (!atomic_read(&initcall_count)) {
+ printk("No need to wait...\n");
+ return 0;
+ }
+ while (atomic_read(&initcall_count)) {
+ printk("to be prepare_to_wait() %d initcalls...\n",
+ atomic_read(&initcall_count));
+ prepare_to_wait(&initcall_waitqueue, &wait, TASK_UNINTERRUPTIBLE);
+ printk("to be read initcall_count...\n");
+ if (atomic_read(&initcall_count)) {
+ printk("to be schedule()...\n");
+ schedule();
+ printk("schedule() complete...\n");
+ }
+ }
+ printk("to be finish wait...\n");
+ finish_wait(&initcall_waitqueue, &wait);
+ /* wait_event(initcall_waitqueue, (!atomic_read(&initcall_count))); */
+ printk("%s wait finished...\n", __FUNCTION__);
+ return 0;
+}
+
extern initcall_t __initcall_start[], __initcall_end[];
static void __init do_initcalls(void)
{
initcall_t *call;
- int count = preempt_count();
+ /* int count = preempt_count(); */
+ struct task_struct* initcall_task;
for (call = __initcall_start; call < __initcall_end; call++) {
- char *msg = NULL;
- char msgbuf[40];
- int result;
+
+ int result = 0;
if (initcall_debug) {
printk("Calling initcall 0x%p", *call);
@@ -669,25 +740,21 @@ static void __init do_initcalls(void)
printk("\n");
}
- result = (*call)();
-
- if (result && result != -ENODEV && initcall_debug) {
- sprintf(msgbuf, "error code %d", result);
- msg = msgbuf;
- }
- if (preempt_count() != count) {
- msg = "preemption imbalance";
- preempt_count() = count;
- }
- if (irqs_disabled()) {
- msg = "disabled interrupts";
- local_irq_enable();
- }
- if (msg) {
- printk(KERN_WARNING "initcall at 0x%p", *call);
- print_fn_descriptor_symbol(": %s()",
+ if (unlikely(*call == (wait_for_initcall))) {
+ printk("call blocked wait_for_initcall\n");
+ result = (*call)();
+ } else {
+ atomic_inc(&initcall_count);
+ initcall_task = kthread_run(initcall_run, call,
+ "initcall-%d", call);
+ if (IS_ERR(initcall_task)) {
+ printk("thread down...\n");
+ print_fn_descriptor_symbol(
+ "Fail to call %s() in thread, turn to block mode\n",
(unsigned long) *call);
- printk(": returned with %s\n", msg);
+ result = (*call)();
+ atomic_dec(&initcall_count);
+ }
}
}
@@ -695,6 +762,15 @@ static void __init do_initcalls(void)
flush_scheduled_work();
}
+core_initcall_sync(wait_for_initcall);
+postcore_initcall_sync(wait_for_initcall);
+arch_initcall_sync(wait_for_initcall);
+subsys_initcall_sync(wait_for_initcall);
+fs_initcall_sync(wait_for_initcall);
+device_initcall_sync(wait_for_initcall);
+late_initcall_sync(wait_for_initcall);
+
+
/*
* Ok, the machine is now initialized. None of the devices
* have been touched yet, but the CPU subsystem is up and
--
regards,
Yang Sheng
--- /opt/kernel/linux-2.6.21.orig/init/main.c 2007-05-01 13:19:27.000000000 +0800 +++ ./init/main.c 2007-05-03 12:19:31.000000000 +0800 @@ -54,6 +54,7 @@ #include <linux/lockdep.h> #include <linux/pid_namespace.h> #include <linux/device.h> +#include <linux/kthread.h> #include <asm/io.h> #include <asm/bugs.h> @@ -641,7 +642,7 @@ asmlinkage void __init start_kernel(void rest_init(); } -static int __initdata initcall_debug; +static int __initdata initcall_debug; static int __init initcall_debug_setup(char *str) { @@ -650,17 +651,87 @@ static int __init initcall_debug_setup(c } __setup("initcall_debug", initcall_debug_setup); +static atomic_t initcall_count = ATOMIC_INIT(0); +static DECLARE_WAIT_QUEUE_HEAD(initcall_waitqueue); + +static int __init initcall_run(void *void_call) +{ + int result; + initcall_t *call = void_call; + + result = (*call)(); + print_fn_descriptor_symbol("%s() ", + (unsigned long) *call); + printk("returns %d", result); + atomic_dec(&initcall_count); + + if (result && result != -ENODEV && initcall_debug) { + printk(KERN_WARNING "initcall at 0x%p", *call); + print_fn_descriptor_symbol(": %s()", + (unsigned long) *call); + printk(": returned with error code %d\n", result); + } + /* if (preempt_count() != count) { + printk(KERN_WARNING "initcall at 0x%p", *call); + print_fn_descriptor_symbol(": %s()", + (unsigned long) *call); + printk(": returned with error code preemption imbalance\n"); + preempt_count() = count; + } + if (irqs_disabled()) { + printk(KERN_WARNING "initcall at 0x%p", *call); + print_fn_descriptor_symbol(": %s()", + (unsigned long) *call); + printk(": returned with error code disabled interrupts\n"); + local_irq_enable(); + } */ + + wake_up(&initcall_waitqueue); + + printk(" and thread finished.\n"); + + return 0; +} + +static int __init wait_for_initcall(void) +{ + DEFINE_WAIT(wait); + + printk("%s wait for %d initcall threads...\n", + __FUNCTION__, atomic_read(&initcall_count)); + if (!atomic_read(&initcall_count)) { + printk("No need to wait...\n"); + return 0; + } + while (atomic_read(&initcall_count)) { + printk("to be prepare_to_wait() %d initcalls...\n", + atomic_read(&initcall_count)); + prepare_to_wait(&initcall_waitqueue, &wait, TASK_UNINTERRUPTIBLE); + printk("to be read initcall_count...\n"); + if (atomic_read(&initcall_count)) { + printk("to be schedule()...\n"); + schedule(); + printk("schedule() complete...\n"); + } + } + printk("to be finish wait...\n"); + finish_wait(&initcall_waitqueue, &wait); + /* wait_event(initcall_waitqueue, (!atomic_read(&initcall_count))); */ + printk("%s wait finished...\n", __FUNCTION__); + return 0; +} + extern initcall_t __initcall_start[], __initcall_end[]; static void __init do_initcalls(void) { initcall_t *call; - int count = preempt_count(); + /* int count = preempt_count(); */ + struct task_struct* initcall_task; for (call = __initcall_start; call < __initcall_end; call++) { - char *msg = NULL; - char msgbuf[40]; - int result; + + int result = 0; if (initcall_debug) { printk("Calling initcall 0x%p", *call); @@ -669,25 +740,21 @@ static void __init do_initcalls(void) printk("\n"); } - result = (*call)(); - - if (result && result != -ENODEV && initcall_debug) { - sprintf(msgbuf, "error code %d", result); - msg = msgbuf; - } - if (preempt_count() != count) { - msg = "preemption imbalance"; - preempt_count() = count; - } - if (irqs_disabled()) { - msg = "disabled interrupts"; - local_irq_enable(); - } - if (msg) { - printk(KERN_WARNING "initcall at 0x%p", *call); - print_fn_descriptor_symbol(": %s()", + if (unlikely(*call == (wait_for_initcall))) { + printk("call blocked wait_for_initcall\n"); + result = (*call)(); + } else { + atomic_inc(&initcall_count); + initcall_task = kthread_run(initcall_run, call, + "initcall-%d", call); + if (IS_ERR(initcall_task)) { + printk("thread down...\n"); + print_fn_descriptor_symbol( + "Fail to call %s() in thread, turn to block mode\n", (unsigned long) *call); - printk(": returned with %s\n", msg); + result = (*call)(); + atomic_dec(&initcall_count); + } } } @@ -695,6 +762,15 @@ static void __init do_initcalls(void) flush_scheduled_work(); } +core_initcall_sync(wait_for_initcall); +postcore_initcall_sync(wait_for_initcall); +arch_initcall_sync(wait_for_initcall); +subsys_initcall_sync(wait_for_initcall); +fs_initcall_sync(wait_for_initcall); +device_initcall_sync(wait_for_initcall); +late_initcall_sync(wait_for_initcall); + + /* * Ok, the machine is now initialized. None of the devices * have been touched yet, but the CPU subsystem is up and