linux/arch/x86_64/kernel
Ingo Molnar 0888f06ac9 [PATCH] sched: fix bad missed wakeups in the i386, x86_64, ia64, ACPI and APM idle code
Fernando Lopez-Lezcano reported frequent scheduling latencies and audio
xruns starting at the 2.6.18-rt kernel, and those problems persisted all
until current -rt kernels. The latencies were serious and unjustified by
system load, often in the milliseconds range.

After a patient and heroic multi-month effort of Fernando, where he
tested dozens of kernels, tried various configs, boot options,
test-patches of mine and provided latency traces of those incidents, the
following 'smoking gun' trace was captured by him:

                 _------=> CPU#
                / _-----=> irqs-off
               | / _----=> need-resched
               || / _---=> hardirq/softirq
               ||| / _--=> preempt-depth
               |||| /
               |||||     delay
   cmd     pid ||||| time  |   caller
      \   /    |||||   \   |   /
  IRQ_19-1479  1D..1    0us : __trace_start_sched_wakeup (try_to_wake_up)
  IRQ_19-1479  1D..1    0us : __trace_start_sched_wakeup <<...>-5856> (37 0)
  IRQ_19-1479  1D..1    0us : __trace_start_sched_wakeup (c01262ba 0 0)
  IRQ_19-1479  1D..1    0us : resched_task (try_to_wake_up)
  IRQ_19-1479  1D..1    0us : __spin_unlock_irqrestore (try_to_wake_up)
  ...
  <idle>-0     1...1   11us!: default_idle (cpu_idle)
  ...
  <idle>-0     0Dn.1  602us : smp_apic_timer_interrupt (c0103baf 1 0)
  ...
   <...>-5856  0D..2  618us : __switch_to (__schedule)
   <...>-5856  0D..2  618us : __schedule <<idle>-0> (20 162)
   <...>-5856  0D..2  619us : __spin_unlock_irq (__schedule)
   <...>-5856  0...1  619us : trace_stop_sched_switched (__schedule)
   <...>-5856  0D..1  619us : trace_stop_sched_switched <<...>-5856> (37 0)

what is visible in this trace is that CPU#1 ran try_to_wake_up() for
PID:5856, it placed PID:5856 on CPU#0's runqueue and ran resched_task()
for CPU#0. But it decided to not send an IPI that no CPU - due to
TS_POLLING. But CPU#0 never woke up after its NEED_RESCHED bit was set,
and only rescheduled to PID:5856 upon the next lapic timer IRQ. The
result was a 600+ usecs latency and a missed wakeup!

the bug turned out to be an idle-wakeup bug introduced into the mainline
kernel this summer via an optimization in the x86_64 tree:

    commit 495ab9c045
    Author: Andi Kleen <ak@suse.de>
    Date:   Mon Jun 26 13:59:11 2006 +0200

    [PATCH] i386/x86-64/ia64: Move polling flag into thread_info_status

    During some profiling I noticed that default_idle causes a lot of
    memory traffic. I think that is caused by the atomic operations
    to clear/set the polling flag in thread_info. There is actually
    no reason to make this atomic - only the idle thread does it
    to itself, other CPUs only read it. So I moved it into ti->status.

the problem is this type of change:

        if (!hlt_counter && boot_cpu_data.hlt_works_ok) {
-               clear_thread_flag(TIF_POLLING_NRFLAG);
+               current_thread_info()->status &= ~TS_POLLING;
                smp_mb__after_clear_bit();
                while (!need_resched()) {
                        local_irq_disable();

this changes clear_thread_flag() to an explicit clearing of TS_POLLING.
clear_thread_flag() is defined as:

        clear_bit(flag, &ti->flags);

and clear_bit() is a LOCK-ed atomic instruction on all x86 platforms:

  static inline void clear_bit(int nr, volatile unsigned long * addr)
  {
          __asm__ __volatile__( LOCK_PREFIX
                  "btrl %1,%0"

hence smp_mb__after_clear_bit() is defined as a simple compile barrier:

  #define smp_mb__after_clear_bit()       barrier()

but the explicit TS_POLLING clearing introduced by the patch:

+               current_thread_info()->status &= ~TS_POLLING;

is not an atomic op! So the clearing of the TS_POLLING bit is freely
reorderable with the reading of the NEED_RESCHED bit - and both now
reside in different memory addresses.

CPU idle wakeup very much depends on ordered memory ops, the clearing of
the TS_POLLING flag must always be done before we test need_resched()
and hit the idle instruction(s). [Symmetrically, the wakeup code needs
to set NEED_RESCHED before it tests the TS_POLLING flag, so memory
ordering is paramount.]

Fernando's dual-core Athlon64 system has a sufficiently advanced memory
ordering model so that it triggered this scenario very often.

( And it also turned out that the reason why these latencies never
  triggered on my testsystems is that i routinely use idle=poll, which
  was the only idle variant not affected by this bug. )

The fix is to change the smp_mb__after_clear_bit() to an smp_mb(), to
act as an absolute barrier between the TS_POLLING write and the
NEED_RESCHED read. This affects almost all idling methods (default,
ACPI, APM), on all 3 x86 architectures: i386, x86_64, ia64.

Signed-off-by: Ingo Molnar <mingo@elte.hu>
Tested-by: Fernando Lopez-Lezcano <nando@ccrma.Stanford.EDU>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-12-22 08:55:51 -08:00
..
acpi Remove obsolete #include <linux/config.h> 2006-06-30 19:25:36 +02:00
cpufreq [CPUFREQ][4/8] acpi-cpufreq: Mark speedstep-centrino ACPI as deprecated 2006-10-15 19:57:10 -04:00
Makefile [PATCH] x86: Refactor thermal throttle processing 2006-09-26 10:52:42 +02:00
aperture.c [PATCH] x86: Allow disabling early pci scans with pci=noearly or disallowing conf1 2006-09-26 10:52:41 +02:00
apic.c [PATCH] x86-64: Remove unused GET_APIC_VERSION call from clear_local_APIC 2006-12-07 02:14:11 +01:00
asm-offsets.c [CRYPTO] all: Pass tfm instead of ctx to algorithms 2006-06-26 17:34:39 +10:00
audit.c [PATCH] audit: AUDIT_PERM support 2006-09-11 13:32:30 -04:00
crash.c [PATCH] Kexec / Kdump: Unify elf note code 2006-12-07 08:39:46 -08:00
crash_dump.c [PATCH] kdump: read previous kernel's memory 2006-01-10 08:01:28 -08:00
e820.c [PATCH] x86-64: setup saved_max_pfn correctly (kdump) 2006-11-14 16:57:46 +01:00
early-quirks.c [PATCH] x86: fix the irqbalance quirk for E7320/E7520/E7525 2006-12-07 02:14:10 +01:00
early_printk.c [PATCH] x86_64: fix 'earlyprintk=...,keep' regression 2006-11-28 10:58:21 -08:00
entry.S Remove stack unwinder for now 2006-12-15 08:47:51 -08:00
functionlist [NET]: make skb_release_data() static 2006-06-29 16:58:30 -07:00
genapic.c [PATCH] x86-64: add genapic_force 2006-12-07 02:14:10 +01:00
genapic_cluster.c [PATCH] x86_64 irq: Allocate a vector across all cpus for genapic_flat. 2006-10-08 12:24:02 -07:00
genapic_flat.c [PATCH] x86-64: Put more than one cpu in TARGET_CPUS 2006-10-21 18:37:02 +02:00
head.S [PATCH] Reload CS when startup_64 is used. 2006-09-26 10:52:38 +02:00
head64.c [PATCH] x86_64: clear_bss before set_intr_gate with early_idt_handler 2006-12-07 02:14:12 +01:00
i387.c [PATCH] x86-64: use BUILD_BUG_ON in FPU code 2006-12-07 02:14:01 +01:00
i8259.c [PATCH] x86_64: interrupt array size should be aligned to NR_VECTORS 2006-12-07 02:14:12 +01:00
init_task.c [PATCH] nsproxy: move init_nsproxy into kernel/nsproxy.c 2006-10-02 07:57:20 -07:00
io_apic.c [PATCH] x86-64: check vector in setup_ioapic_dest to verify if need setup_IO_APIC_irq 2006-12-07 02:14:19 +01:00
ioport.c [PATCH] x86-64 TIF flags for debug regs and io bitmap in ctxsw 2006-09-26 10:52:28 +02:00
irq.c [PATCH] x86-64: Rate limit no irq handler messages 2006-12-07 02:14:09 +01:00
k8.c [PATCH] x86_64: Clean and enhance up K8 northbridge access code 2006-06-26 10:48:15 -07:00
kprobes.c [PATCH] kprobes: enable booster on the preemptible kernel 2006-12-07 08:39:38 -08:00
ldt.c
machine_kexec.c [PATCH] Avoid overwriting the current pgd (V4, x86_64) 2006-09-26 10:52:38 +02:00
mce.c Merge branch 'for-linus' of git://one.firstfloor.org/home/andi/git/linux-2.6 2006-12-07 08:59:11 -08:00
mce_amd.c [PATCH] hotplug CPU: clean up hotcpu_notifier() use 2006-12-07 08:39:39 -08:00
mce_intel.c [PATCH] x86: Add a cumulative thermal throttle event counter. 2006-09-26 10:52:42 +02:00
module.c [PATCH] Generic BUG for x86-64 2006-12-08 08:28:39 -08:00
mpparse.c [PATCH] x86-64: remove unused acpi_found_madt in mparse. 2006-12-07 02:14:12 +01:00
nmi.c [PATCH] x86: Fix boot hang due to nmi watchdog init code 2006-12-09 21:33:35 +01:00
pci-calgary.c [PATCH] x86_64: fix boot time hang in detect_calgary() 2006-12-21 00:08:28 -08:00
pci-dma.c [PATCH] Calgary: allow compiling Calgary in but not using it by default 2006-12-07 02:14:07 +01:00
pci-gart.c [PATCH] x86-64: Clarify error message in GART code 2006-12-07 02:14:13 +01:00
pci-nommu.c [PATCH] remove superflous BUG_ON's in nommu and gart 2006-09-26 10:52:32 +02:00
pci-swiotlb.c [PATCH] Move valid_dma_direction() from x86_64 to generic code 2006-09-29 09:18:10 -07:00
pmtimer.c [PATCH] make pmtmr_ioport __read_mostly 2006-06-26 09:58:21 -07:00
process.c [PATCH] sched: fix bad missed wakeups in the i386, x86_64, ia64, ACPI and APM idle code 2006-12-22 08:55:51 -08:00
ptrace.c [PATCH] i386/x86-64: rename is_at_popf(), add iret to tests and fix 2006-09-26 10:52:33 +02:00
reboot.c [PATCH] x86_64: Move export symbols to their C functions 2006-06-26 10:48:22 -07:00
relocate_kernel.S [PATCH] Avoid overwriting the current pgd (V4, x86_64) 2006-09-26 10:52:38 +02:00
setup.c Merge branch 'for-linus' of git://one.firstfloor.org/home/andi/git/linux-2.6 2006-12-07 08:59:11 -08:00
setup64.c [PATCH] Don't leak NT bit into next task 2006-09-26 10:52:41 +02:00
signal.c [PATCH] Remove all traces of signal number conversion 2006-09-26 10:52:41 +02:00
smp.c Merge branch 'for-linus' of git://one.firstfloor.org/home/andi/git/linux-2.6 2006-12-07 08:59:11 -08:00
smpboot.c Merge branch 'for-linus' of git://one.firstfloor.org/home/andi/git/linux-2.6 2006-12-07 08:59:11 -08:00
stacktrace.c [PATCH] Merge stacktrace and show_trace 2006-09-26 10:52:34 +02:00
suspend.c Remove obsolete #include <linux/config.h> 2006-06-30 19:25:36 +02:00
suspend_asm.S [PATCH] Change the name of pagedir_nosave 2006-09-26 08:49:01 -07:00
sys_x86_64.c [PATCH] namespaces: utsname: switch to using uts namespaces 2006-10-02 07:57:21 -07:00
syscall.c Remove obsolete #include <linux/config.h> 2006-06-30 19:25:36 +02:00
tce.c Remove all inclusions of <linux/config.h> 2006-10-04 03:38:54 -04:00
time.c WorkStruct: Pass the work_struct pointer instead of context data 2006-11-22 14:55:48 +00:00
trampoline.S [PATCH] Fix gdt table size in trampoline.S 2006-09-26 10:52:32 +02:00
traps.c Remove stack unwinder for now 2006-12-15 08:47:51 -08:00
vmlinux.lds.S Remove stack unwinder for now 2006-12-15 08:47:51 -08:00
vsmp.c [PATCH] Fix build breakage with CONFIG_X86_VSMP 2006-10-12 12:25:27 -07:00
vsyscall.c [PATCH] sysctl: remove unused "context" param 2006-12-10 09:55:41 -08:00
x8664_ksyms.c Remove all inclusions of <linux/config.h> 2006-10-04 03:38:54 -04:00