[PATCH]The idea about multi-threaded initcall

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

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

[Index of Archives]     [Newbies FAQ]     [Linux Kernel Mentors]     [Linux Kernel Development]     [IETF Annouce]     [Git]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux SCSI]     [Linux ACPI]
  Powered by Linux