[PATCH] sched: disable preempt in idle tasks
authorNick Piggin <nickpiggin@yahoo.com.au>
Wed, 9 Nov 2005 05:39:01 +0000 (21:39 -0800)
committerLinus Torvalds <torvalds@g5.osdl.org>
Wed, 9 Nov 2005 15:56:33 +0000 (07:56 -0800)
Run idle threads with preempt disabled.

Also corrected a bugs in arm26's cpu_idle (make it actually call schedule()).
How did it ever work before?

Might fix the CPU hotplugging hang which Nigel Cunningham noted.

We think the bug hits if the idle thread is preempted after checking
need_resched() and before going to sleep, then the CPU offlined.

After calling stop_machine_run, the CPU eventually returns from preemption and
into the idle thread and goes to sleep.  The CPU will continue executing
previous idle and have no chance to call play_dead.

By disabling preemption until we are ready to explicitly schedule, this bug is
fixed and the idle threads generally become more robust.

From: alexs <ashepard@u.washington.edu>

  PPC build fix

From: Yoichi Yuasa <yuasa@hh.iij4u.or.jp>

  MIPS build fix

Signed-off-by: Nick Piggin <npiggin@suse.de>
Signed-off-by: Yoichi Yuasa <yuasa@hh.iij4u.or.jp>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
36 files changed:
arch/arm/kernel/process.c
arch/arm/kernel/smp.c
arch/arm26/kernel/process.c
arch/cris/arch-v32/kernel/smp.c
arch/cris/kernel/process.c
arch/frv/kernel/process.c
arch/h8300/kernel/process.c
arch/i386/kernel/process.c
arch/i386/kernel/smpboot.c
arch/ia64/kernel/process.c
arch/ia64/kernel/smpboot.c
arch/m32r/kernel/process.c
arch/m32r/kernel/smpboot.c
arch/m68k/kernel/process.c
arch/mips/kernel/process.c
arch/mips/kernel/smp.c
arch/parisc/kernel/process.c
arch/parisc/kernel/smp.c
arch/powerpc/platforms/iseries/setup.c
arch/powerpc/platforms/pseries/setup.c
arch/ppc/kernel/idle.c
arch/ppc/kernel/smp.c
arch/ppc64/kernel/idle.c
arch/s390/kernel/process.c
arch/s390/kernel/smp.c
arch/sh/kernel/process.c
arch/sh/kernel/smp.c
arch/sh64/kernel/process.c
arch/sparc/kernel/process.c
arch/sparc64/kernel/process.c
arch/sparc64/kernel/smp.c
arch/v850/kernel/process.c
arch/x86_64/kernel/process.c
arch/x86_64/kernel/smpboot.c
arch/xtensa/kernel/process.c
init/main.c

index ba298277becde78687365206e86cdfcbe4936545..93dd92cc12f8baf1c0ed808c45750910416c81d0 100644 (file)
@@ -116,13 +116,13 @@ void cpu_idle(void)
 
                if (!idle)
                        idle = default_idle;
-               preempt_disable();
                leds_event(led_idle_start);
                while (!need_resched())
                        idle();
                leds_event(led_idle_end);
-               preempt_enable();
+               preempt_enable_no_resched();
                schedule();
+               preempt_disable();
        }
 }
 
index 77e2e9ca89fab8477cdb13567d1c992c2e1c7d8b..e55ea952f7aa1d9f84ecce020d5caaa548f244fd 100644 (file)
@@ -256,7 +256,9 @@ void __cpuexit cpu_die(void)
 asmlinkage void __cpuinit secondary_start_kernel(void)
 {
        struct mm_struct *mm = &init_mm;
-       unsigned int cpu = smp_processor_id();
+       unsigned int cpu;
+
+       cpu = smp_processor_id();
 
        printk("CPU%u: Booted secondary processor\n", cpu);
 
@@ -273,6 +275,7 @@ asmlinkage void __cpuinit secondary_start_kernel(void)
        local_flush_tlb_all();
 
        cpu_init();
+       preempt_disable();
 
        /*
         * Give the platform a chance to do its own initialisation.
index 9eb9964d32a7368c0b71c53015c75a7e58626f48..15833a0057dd1c9ad7360275cd81b4e1716d053b 100644 (file)
@@ -74,15 +74,13 @@ __setup("hlt", hlt_setup);
 void cpu_idle(void)
 {
        /* endless idle loop with no priority at all */
-       preempt_disable();
        while (1) {
-               while (!need_resched()) {
-                       local_irq_disable();
-                       if (!need_resched() && !hlt_counter)
-                               local_irq_enable();
-               }
+               while (!need_resched())
+                       cpu_relax();
+               preempt_enable_no_resched();
+               schedule();
+               preempt_disable();
        }
-       schedule();
 }
 
 static char reboot_mode = 'h';
index 957f551ba5ce21602467506a4c7f6f5276a1523e..13867f4fad16a858a13d4305a3869998ef516b78 100644 (file)
@@ -161,6 +161,7 @@ void __init smp_callin(void)
        REG_WR(intr_vect, irq_regs[cpu], rw_mask, vect_mask);
        unmask_irq(IPI_INTR_VECT);
        unmask_irq(TIMER_INTR_VECT);
+       preempt_disable();
        local_irq_enable();
 
        cpu_set(cpu, cpu_online_map);
index 949a0e40e03cc63f6304c90e9909a254bbe909ce..7c80afb1046077d2fb219d2542c3b815c7d3916d 100644 (file)
@@ -218,7 +218,9 @@ void cpu_idle (void)
                                idle = default_idle;
                        idle();
                }
+               preempt_enable_no_resched();
                schedule();
+               preempt_disable();
        }
 }
 
index 3001b82b1514856efed7204f6b09e0345a8cb974..54a452136f00e8f93cabd0bcdf8e5a450d662b26 100644 (file)
@@ -77,16 +77,20 @@ void (*idle)(void) = core_sleep_idle;
  */
 void cpu_idle(void)
 {
+       int cpu = smp_processor_id();
+
        /* endless idle loop with no priority at all */
        while (1) {
                while (!need_resched()) {
-                       irq_stat[smp_processor_id()].idle_timestamp = jiffies;
+                       irq_stat[cpu].idle_timestamp = jiffies;
 
                        if (!frv_dma_inprogress && idle)
                                idle();
                }
 
+               preempt_enable_no_resched();
                schedule();
+               preempt_disable();
        }
 }
 
index 27f1fce64ce465026ee90c0cffd2e25717843075..fe21adf3e75e8c26e2619e71bbc5167edabc84a7 100644 (file)
@@ -53,22 +53,18 @@ asmlinkage void ret_from_fork(void);
 #if !defined(CONFIG_H8300H_SIM) && !defined(CONFIG_H8S_SIM)
 void default_idle(void)
 {
-       while(1) {
-               if (!need_resched()) {
-                       local_irq_enable();
-                       __asm__("sleep");
-                       local_irq_disable();
-               }
-               schedule();
-       }
+       local_irq_disable();
+       if (!need_resched()) {
+               local_irq_enable();
+               /* XXX: race here! What if need_resched() gets set now? */
+               __asm__("sleep");
+       } else
+               local_irq_enable();
 }
 #else
 void default_idle(void)
 {
-       while(1) {
-               if (need_resched())
-                       schedule();
-       }
+       cpu_relax();
 }
 #endif
 void (*idle)(void) = default_idle;
@@ -81,7 +77,13 @@ void (*idle)(void) = default_idle;
  */
 void cpu_idle(void)
 {
-       idle();
+       while (1) {
+               while (!need_resched())
+                       idle();
+               preempt_enable_no_resched();
+               schedule();
+               preempt_disable();
+       }
 }
 
 void machine_restart(char * __unused)
index 7a14fdfd3af95423275cab21ea56624bd4e1f4e7..5296e284ea363b631f1663204acfd581db5dd01d 100644 (file)
@@ -179,7 +179,7 @@ static inline void play_dead(void)
  */
 void cpu_idle(void)
 {
-       int cpu = raw_smp_processor_id();
+       int cpu = smp_processor_id();
 
        /* endless idle loop with no priority at all */
        while (1) {
@@ -201,7 +201,9 @@ void cpu_idle(void)
                        __get_cpu_var(irq_stat).idle_timestamp = jiffies;
                        idle();
                }
+               preempt_enable_no_resched();
                schedule();
+               preempt_disable();
        }
 }
 
index 47ec76794d02ac11bdcef2416bff7796a9099572..bc5a9d97466b572cf5e6755fc5e5de0eb828b11f 100644 (file)
@@ -485,6 +485,7 @@ static void __devinit start_secondary(void *unused)
         * things done here to the most necessary things.
         */
        cpu_init();
+       preempt_disable();
        smp_callin();
        while (!cpu_isset(smp_processor_id(), smp_commenced_mask))
                rep_nop();
index 051e050359e490ac4a5e44a90cb6c46849ea77f9..4c621fc3c3b9086da036d4a4390d8d9ac176e0ee 100644 (file)
@@ -292,7 +292,9 @@ cpu_idle (void)
 #ifdef CONFIG_SMP
                normal_xtp();
 #endif
+               preempt_enable_no_resched();
                schedule();
+               preempt_disable();
                check_pgt_cache();
                if (cpu_is_offline(smp_processor_id()))
                        play_dead();
index 400a4898712492da0bdbe28ea27aa525f7a842ef..8f44e7d2df66eb8e0d052132ab8fe7a390587212 100644 (file)
@@ -399,6 +399,7 @@ start_secondary (void *unused)
        Dprintk("start_secondary: starting CPU 0x%x\n", hard_smp_processor_id());
        efi_map_pal_code();
        cpu_init();
+       preempt_disable();
        smp_callin();
 
        cpu_idle();
index ea13a8f4d8b05aadc691f010435b6fa6ef751a91..cc4b571e5db715c1fba35eab7ce3ee0d1e6784ef 100644 (file)
@@ -104,7 +104,9 @@ void cpu_idle (void)
 
                        idle();
                }
+               preempt_enable_no_resched();
                schedule();
+               preempt_disable();
        }
 }
 
index 640d592ea07251207f59f4864cfd0ce9e1ad580d..b90c54169fa5ae22bd82f68988d44c2a14705873 100644 (file)
@@ -426,6 +426,7 @@ void __init smp_cpus_done(unsigned int max_cpus)
 int __init start_secondary(void *unused)
 {
        cpu_init();
+       preempt_disable();
        smp_callin();
        while (!cpu_isset(smp_processor_id(), smp_commenced_mask))
                cpu_relax();
index 11b1b90ba6ba49d4dc193517921fd88a5fe84a9f..13d109328a428166d4a11ba456595ff2d0170a91 100644 (file)
@@ -102,7 +102,9 @@ void cpu_idle(void)
        while (1) {
                while (!need_resched())
                        idle();
+               preempt_enable_no_resched();
                schedule();
+               preempt_disable();
        }
 }
 
index 4fe3d5715c416c6b2aa74152f974348f757b2c41..dd725779d91fa19857e667a9c4131d381a16f410 100644 (file)
@@ -52,7 +52,9 @@ ATTRIB_NORET void cpu_idle(void)
                while (!need_resched())
                        if (cpu_wait)
                                (*cpu_wait)();
+               preempt_enable_no_resched();
                schedule();
+               preempt_disable();
        }
 }
 
index fcacf1aae98aebd04e509107e56aeaf4d0c49f0f..25472fcaf7157c1cc7c888f5f759ec0404870c06 100644 (file)
@@ -82,7 +82,7 @@ extern ATTRIB_NORET void cpu_idle(void);
  */
 asmlinkage void start_secondary(void)
 {
-       unsigned int cpu = smp_processor_id();
+       unsigned int cpu;
 
        cpu_probe();
        cpu_report();
@@ -95,6 +95,8 @@ asmlinkage void start_secondary(void)
         */
 
        calibrate_delay();
+       preempt_disable();
+       cpu = smp_processor_id();
        cpu_data[cpu].udelay_val = loops_per_jiffy;
 
        prom_smp_finish();
index 7fdca87ef647369bc12a5c77330f35f15c7f7f53..f482f78de43537f0dc97866ab357d2b70823f278 100644 (file)
@@ -92,7 +92,9 @@ void cpu_idle(void)
        while (1) {
                while (!need_resched())
                        barrier();
+               preempt_enable_no_resched();
                schedule();
+               preempt_disable();
                check_pgt_cache();
        }
 }
index 5db3be4e2704ba8ed5e982b0e22ab841dff23c6c..a9ecf6465784eb0272518d648e39c2814d31dc02 100644 (file)
@@ -463,6 +463,7 @@ void __init smp_callin(void)
 #endif
 
        smp_cpu_init(slave_id);
+       preempt_disable();
 
 #if 0  /* NOT WORKING YET - see entry.S */
        istack = (void *)__get_free_pages(GFP_KERNEL,ISTACK_ORDER);
index d3e4bf756c8382f4a8b6434787c44018093eaa5f..0130f2619dacda2791dd7aaee5f097377dd520a9 100644 (file)
@@ -694,7 +694,9 @@ static void iseries_shared_idle(void)
                if (hvlpevent_is_pending())
                        process_iSeries_events();
 
+               preempt_enable_no_resched();
                schedule();
+               preempt_disable();
        }
 }
 
@@ -726,7 +728,9 @@ static void iseries_dedicated_idle(void)
                }
 
                ppc64_runlatch_on();
+               preempt_enable_no_resched();
                schedule();
+               preempt_disable();
        }
 }
 
index e78c39368841ba39251f3f6f219a987f2206a926..4854f5eb5c3da3954f26590d66e5efc9eb8d6d06 100644 (file)
@@ -539,7 +539,9 @@ static void pseries_dedicated_idle(void)
                lpaca->lppaca.idle = 0;
                ppc64_runlatch_on();
 
+               preempt_enable_no_resched();
                schedule();
+               preempt_disable();
 
                if (cpu_is_offline(cpu) && system_state == SYSTEM_RUNNING)
                        cpu_die();
@@ -583,7 +585,9 @@ static void pseries_shared_idle(void)
                lpaca->lppaca.idle = 0;
                ppc64_runlatch_on();
 
+               preempt_enable_no_resched();
                schedule();
+               preempt_disable();
 
                if (cpu_is_offline(cpu) && system_state == SYSTEM_RUNNING)
                        cpu_die();
index 11e5b44713f7237e3ec0ecf2b0fc331fff751619..a6141f05c91973348e4dfc7ec6f163bca2863fbd 100644 (file)
@@ -53,10 +53,6 @@ void default_idle(void)
                }
 #endif
        }
-       if (need_resched())
-               schedule();
-       if (cpu_is_offline(cpu) && system_state == SYSTEM_RUNNING)
-               cpu_die();
 }
 
 /*
@@ -64,11 +60,22 @@ void default_idle(void)
  */
 void cpu_idle(void)
 {
-       for (;;)
+       int cpu = smp_processor_id();
+
+       for (;;) {
                if (ppc_md.idle != NULL)
                        ppc_md.idle();
                else
                        default_idle();
+               if (cpu_is_offline(cpu) && system_state == SYSTEM_RUNNING)
+                       cpu_die();
+               if (need_resched()) {
+                       preempt_enable_no_resched();
+                       schedule();
+                       preempt_disable();
+               }
+
+       }
 }
 
 #if defined(CONFIG_SYSCTL) && defined(CONFIG_6xx)
index bc5bf1124836adb4013c8a09ceb28c9421feed26..43b8fc2ca591c86388427c4b822eababd6991e11 100644 (file)
@@ -341,6 +341,7 @@ int __devinit start_secondary(void *unused)
        cpu = smp_processor_id();
         smp_store_cpu_info(cpu);
        set_dec(tb_ticks_per_jiffy);
+       preempt_disable();
        cpu_callin_map[cpu] = 1;
 
        printk("CPU %d done callin...\n", cpu);
index 8fec27469802290251e91f6221e6c883dd31d891..909ea669af9159a3dd3341f69a623118e0693529 100644 (file)
@@ -61,7 +61,9 @@ void default_idle(void)
                }
 
                ppc64_runlatch_on();
+               preempt_enable_no_resched();
                schedule();
+               preempt_disable();
                if (cpu_is_offline(cpu) && system_state == SYSTEM_RUNNING)
                        cpu_die();
        }
@@ -77,7 +79,9 @@ void native_idle(void)
 
                if (need_resched()) {
                        ppc64_runlatch_on();
+                       preempt_enable_no_resched();
                        schedule();
+                       preempt_disable();
                }
 
                if (cpu_is_offline(smp_processor_id()) &&
index 9f3dff6c0b72286a7c769787dcb0d9046bc29d93..66ca5757e3686c7c837d7a415acaebe0f4266f06 100644 (file)
@@ -102,7 +102,6 @@ void default_idle(void)
        local_irq_disable();
         if (need_resched()) {
                local_irq_enable();
-                schedule();
                 return;
         }
 
@@ -139,8 +138,14 @@ void default_idle(void)
 
 void cpu_idle(void)
 {
-       for (;;)
-               default_idle();
+       for (;;) {
+               while (!need_resched())
+                       default_idle();
+
+               preempt_enable_no_resched();
+               schedule();
+               preempt_disable();
+       }
 }
 
 void show_regs(struct pt_regs *regs)
index e13c87b446b2e58051797dbe88b25d930fa5376a..5856b3fda6bfeabf598a94f42d17c1abb311691d 100644 (file)
@@ -533,6 +533,7 @@ int __devinit start_secondary(void *cpuvoid)
 {
         /* Setup the cpu */
         cpu_init();
+       preempt_disable();
         /* init per CPU timer */
         init_cpu_timer();
 #ifdef CONFIG_VIRT_TIMER
index 6dce9d0b81f8b8d53d4ac78378e4af0c5cbd82db..1cbc26b796ad1ac66f58d86ec972a35006c75ae3 100644 (file)
@@ -64,7 +64,9 @@ void default_idle(void)
                                cpu_sleep();
                }
 
+               preempt_enable_no_resched();
                schedule();
+               preempt_disable();
        }
 }
 
index 5ecefc02896a3a07e030a3013b0023da96c15e9e..59e49b18252c47ff49e3b4790c0c0a7ab8a9a883 100644 (file)
@@ -112,7 +112,9 @@ int __cpu_up(unsigned int cpu)
 
 int start_secondary(void *unused)
 {
-       unsigned int cpu = smp_processor_id();
+       unsigned int cpu;
+
+       cpu = smp_processor_id();
 
        atomic_inc(&init_mm.mm_count);
        current->active_mm = &init_mm;
@@ -120,6 +122,7 @@ int start_secondary(void *unused)
        smp_store_cpu_info(cpu);
 
        __smp_slave_init(cpu);
+       preempt_disable();
        per_cpu_trap_init();
        
        atomic_inc(&cpus_booted);
index efde41c0cd669388142dcb58dd7c87f5818e2d58..0c09537449b3a68cfdb8935e619c6c89213c4119 100644 (file)
@@ -334,7 +334,9 @@ void default_idle(void)
                        }
                        local_irq_enable();
                }
+               preempt_enable_no_resched();
                schedule();
+               preempt_disable();
        }
 }
 
index 29e72b57d4fd2cee97e6e9bc8b8ed63812c448f5..c39f4d01096d7cac71d22c39981152ede994ebb4 100644 (file)
@@ -120,7 +120,9 @@ void cpu_idle(void)
                        (*pm_idle)();
                }
 
+               preempt_enable_no_resched();
                schedule();
+               preempt_disable();
                check_pgt_cache();
        }
 }
@@ -133,7 +135,9 @@ void cpu_idle(void)
        /* endless idle loop with no priority at all */
        while(1) {
                if(need_resched()) {
+                       preempt_enable_no_resched();
                        schedule();
+                       preempt_disable();
                        check_pgt_cache();
                }
                barrier(); /* or else gcc optimizes... */
index 7d10b03970919c63b6d1c1dd138b203f6fa42568..2f89206e008f86e7f1b53f665d115e31cc194fd2 100644 (file)
@@ -74,7 +74,9 @@ void cpu_idle(void)
                while (!need_resched())
                        barrier();
 
+               preempt_enable_no_resched();
                schedule();
+               preempt_disable();
                check_pgt_cache();
        }
 }
@@ -93,7 +95,9 @@ void cpu_idle(void)
                if (need_resched()) {
                        unidle_me();
                        clear_thread_flag(TIF_POLLING_NRFLAG);
+                       preempt_enable_no_resched();
                        schedule();
+                       preempt_disable();
                        set_thread_flag(TIF_POLLING_NRFLAG);
                        check_pgt_cache();
                }
index 5d90ee9aebf1a29eb1d18e165b73551875b9c584..8aca4b1dc04e96964f398188e507de2cff68e3a6 100644 (file)
@@ -168,6 +168,9 @@ void __init smp_callin(void)
                rmb();
 
        cpu_set(cpuid, cpu_online_map);
+
+       /* idle thread is expected to have preempt disabled */
+       preempt_disable();
 }
 
 void cpu_panic(void)
index 9c708c32c1f077bd9f51aa99cd8a4b88d9fe3f22..39cf247cdae4ef4dcbe4d4fa3563432a520f1531 100644 (file)
@@ -36,11 +36,8 @@ extern void ret_from_fork (void);
 /* The idle loop.  */
 void default_idle (void)
 {
-       while (1) {
-               while (! need_resched ())
-                       asm ("halt; nop; nop; nop; nop; nop" ::: "cc");
-               schedule ();
-       }
+       while (! need_resched ())
+               asm ("halt; nop; nop; nop; nop; nop" ::: "cc");
 }
 
 void (*idle)(void) = default_idle;
@@ -54,7 +51,14 @@ void (*idle)(void) = default_idle;
 void cpu_idle (void)
 {
        /* endless idle loop with no priority at all */
-       (*idle) ();
+       while (1) {
+               while (!need_resched())
+                       (*idle) ();
+
+               preempt_enable_no_resched();
+               schedule();
+               preempt_disable();
+       }
 }
 
 /*
index b5a89c0bdf5914851fe32710b0b808587dfb6c32..571f9fe490ce19e93f741e1036f33a4b7e8bbb35 100644 (file)
@@ -204,7 +204,9 @@ void cpu_idle (void)
                        idle();
                }
 
+               preempt_enable_no_resched();
                schedule();
+               preempt_disable();
        }
 }
 
index 4b5b088ec1022a5d28c6e0e2dae76df9c4eed785..c4e59bbdc1872b4ce599f452b125183603cf8f8e 100644 (file)
@@ -472,6 +472,7 @@ void __cpuinit start_secondary(void)
         * things done here to the most necessary things.
         */
        cpu_init();
+       preempt_disable();
        smp_callin();
 
        /* otherwise gcc will move up the smp_processor_id before the cpu_init */
index 08ef6d82ee5144d1e30d98195a9d91de57af5806..6a44b54ae8173718b8e4a50518511e4777a43392 100644 (file)
@@ -96,8 +96,9 @@ void cpu_idle(void)
        while (1) {
                while (!need_resched())
                        platform_idle();
-               preempt_enable();
+               preempt_enable_no_resched();
                schedule();
+               preempt_disable();
        }
 }
 
index f142d403534190f4588b3d7eda585112077e0607..27f97f9b46362c4fa316619fdcc8e0d29751bbd3 100644 (file)
@@ -394,14 +394,16 @@ static void noinline rest_init(void)
        kernel_thread(init, NULL, CLONE_FS | CLONE_SIGHAND);
        numa_default_policy();
        unlock_kernel();
-       preempt_enable_no_resched();
 
        /*
         * The boot idle thread must execute schedule()
         * at least one to get things moving:
         */
+       preempt_enable_no_resched();
        schedule();
+       preempt_disable();
 
+       /* Call into cpu_idle with preempt disabled */
        cpu_idle();
 }