Merge branch 'x86-bsp-hotplug-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 12 Dec 2012 03:56:33 +0000 (19:56 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 12 Dec 2012 03:56:33 +0000 (19:56 -0800)
Pull x86 BSP hotplug changes from Ingo Molnar:
 "This tree enables CPU#0 (the boot processor) to be onlined/offlined on
  x86, just like any other CPU.  Enabled on Intel CPUs for now.

  Allowing this required the identification and fixing of latent CPU#0
  assumptions (such as CPU#0 initializations, etc.) in the x86
  architecture code, plus the identification of barriers to
  BSP-offlining, such as active PIC interrupts which can only be
  serviced on the BSP.

  It's behind a default-off option, and there's a debug option that
  allows the automatic testing of this feature.

  The motivation of this feature is to allow and prepare for true
  CPU-hotplug hardware support: recent changes to MCE support enable us
  to detect a deteriorating but not yet hard-failing L1/L2 cache on a
  CPU that could be soft-unplugged - or a failing L3 cache on a
  multi-socket system.

  Note that true hardware hot-plug is not yet fully enabled by this,
  because that requires a special platform wakeup sequence to be sent to
  the freshly powered up CPU#0.  Future patches for this are planned,
  once such a platform exists.  Chicken and egg"

* 'x86-bsp-hotplug-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86, topology: Debug CPU0 hotplug
  x86/i387.c: Initialize thread xstate only on CPU0 only once
  x86, hotplug: Handle retrigger irq by the first available CPU
  x86, hotplug: The first online processor saves the MTRR state
  x86, hotplug: During CPU0 online, enable x2apic, set_numa_node.
  x86, hotplug: Wake up CPU0 via NMI instead of INIT, SIPI, SIPI
  x86-32, hotplug: Add start_cpu0() entry point to head_32.S
  x86-64, hotplug: Add start_cpu0() entry point to head_64.S
  kernel/cpu.c: Add comment for priority in cpu_hotplug_pm_callback
  x86, hotplug, suspend: Online CPU0 for suspend or hibernate
  x86, hotplug: Support functions for CPU0 online/offline
  x86, topology: Don't offline CPU0 if any PIC irq can not be migrated out of it
  x86, Kconfig: Add config switch for CPU0 hotplug
  doc: Add x86 CPU0 online/offline feature

1  2 
Documentation/kernel-parameters.txt
arch/x86/Kconfig
arch/x86/kernel/head_32.S
arch/x86/kernel/smpboot.c
kernel/cpu.c

@@@ -1984,6 -1984,20 +1984,20 @@@ bytes respectively. Such letter suffixe
  
        nox2apic        [X86-64,APIC] Do not enable x2APIC mode.
  
+       cpu0_hotplug    [X86] Turn on CPU0 hotplug feature when
+                       CONFIG_BOOTPARAM_HOTPLUG_CPU0 is off.
+                       Some features depend on CPU0. Known dependencies are:
+                       1. Resume from suspend/hibernate depends on CPU0.
+                       Suspend/hibernate will fail if CPU0 is offline and you
+                       need to online CPU0 before suspend/hibernate.
+                       2. PIC interrupts also depend on CPU0. CPU0 can't be
+                       removed if a PIC interrupt is detected.
+                       It's said poweroff/reboot may depend on CPU0 on some
+                       machines although I haven't seen such issues so far
+                       after CPU0 is offline on a few tested machines.
+                       If the dependencies are under your control, you can
+                       turn on cpu0_hotplug.
        nptcg=          [IA-64] Override max number of concurrent global TLB
                        purges which is reported from either PAL_VM_SUMMARY or
                        SAL PALO.
        ramdisk_size=   [RAM] Sizes of RAM disks in kilobytes
                        See Documentation/blockdev/ramdisk.txt.
  
 +      rcu_nocbs=      [KNL,BOOT]
 +                      In kernels built with CONFIG_RCU_NOCB_CPU=y, set
 +                      the specified list of CPUs to be no-callback CPUs.
 +                      Invocation of these CPUs' RCU callbacks will
 +                      be offloaded to "rcuoN" kthreads created for
 +                      that purpose.  This reduces OS jitter on the
 +                      offloaded CPUs, which can be useful for HPC and
 +                      real-time workloads.  It can also improve energy
 +                      efficiency for asymmetric multiprocessors.
 +
 +      rcu_nocbs_poll  [KNL,BOOT]
 +                      Rather than requiring that offloaded CPUs
 +                      (specified by rcu_nocbs= above) explicitly
 +                      awaken the corresponding "rcuoN" kthreads,
 +                      make these kthreads poll for callbacks.
 +                      This improves the real-time response for the
 +                      offloaded CPUs by relieving them of the need to
 +                      wake up the corresponding kthread, but degrades
 +                      energy efficiency by requiring that the kthreads
 +                      periodically wake up to do the polling.
 +
        rcutree.blimit= [KNL,BOOT]
                        Set maximum number of finished RCU callbacks to process
                        in one batch.
                        to facilitate early boot debugging.
                        See also Documentation/trace/events.txt
  
 +      trace_options=[option-list]
 +                      [FTRACE] Enable or disable tracer options at boot.
 +                      The option-list is a comma delimited list of options
 +                      that can be enabled or disabled just as if you were
 +                      to echo the option name into
 +
 +                          /sys/kernel/debug/tracing/trace_options
 +
 +                      For example, to enable stacktrace option (to dump the
 +                      stack trace of each event), add to the command line:
 +
 +                            trace_options=stacktrace
 +
 +                      See also Documentation/trace/ftrace.txt "trace options"
 +                      section.
 +
        transparent_hugepage=
                        [KNL]
                        Format: [always|madvise|never]
diff --combined arch/x86/Kconfig
@@@ -106,7 -106,7 +106,7 @@@ config X8
        select KTIME_SCALAR if X86_32
        select GENERIC_STRNCPY_FROM_USER
        select GENERIC_STRNLEN_USER
 -      select HAVE_RCU_USER_QS if X86_64
 +      select HAVE_CONTEXT_TRACKING if X86_64
        select HAVE_IRQ_TIME_ACCOUNTING
        select GENERIC_KERNEL_THREAD
        select GENERIC_KERNEL_EXECVE
@@@ -310,7 -310,7 +310,7 @@@ config X86_X2API
          If you don't know what to do here, say N.
  
  config X86_MPPARSE
 -      bool "Enable MPS table" if ACPI
 +      bool "Enable MPS table" if ACPI || SFI
        default y
        depends on X86_LOCAL_APIC
        ---help---
@@@ -1698,6 -1698,50 +1698,50 @@@ config HOTPLUG_CP
            automatically on SMP systems. )
          Say N if you want to disable CPU hotplug.
  
+ config BOOTPARAM_HOTPLUG_CPU0
+       bool "Set default setting of cpu0_hotpluggable"
+       default n
+       depends on HOTPLUG_CPU && EXPERIMENTAL
+       ---help---
+         Set whether default state of cpu0_hotpluggable is on or off.
+         Say Y here to enable CPU0 hotplug by default. If this switch
+         is turned on, there is no need to give cpu0_hotplug kernel
+         parameter and the CPU0 hotplug feature is enabled by default.
+         Please note: there are two known CPU0 dependencies if you want
+         to enable the CPU0 hotplug feature either by this switch or by
+         cpu0_hotplug kernel parameter.
+         First, resume from hibernate or suspend always starts from CPU0.
+         So hibernate and suspend are prevented if CPU0 is offline.
+         Second dependency is PIC interrupts always go to CPU0. CPU0 can not
+         offline if any interrupt can not migrate out of CPU0. There may
+         be other CPU0 dependencies.
+         Please make sure the dependencies are under your control before
+         you enable this feature.
+         Say N if you don't want to enable CPU0 hotplug feature by default.
+         You still can enable the CPU0 hotplug feature at boot by kernel
+         parameter cpu0_hotplug.
+ config DEBUG_HOTPLUG_CPU0
+       def_bool n
+       prompt "Debug CPU0 hotplug"
+       depends on HOTPLUG_CPU && EXPERIMENTAL
+       ---help---
+         Enabling this option offlines CPU0 (if CPU0 can be offlined) as
+         soon as possible and boots up userspace with CPU0 offlined. User
+         can online CPU0 back after boot time.
+         To debug CPU0 hotplug, you need to enable CPU0 offline/online
+         feature by either turning on CONFIG_BOOTPARAM_HOTPLUG_CPU0 during
+         compilation or giving cpu0_hotplug kernel parameter at boot.
+         If unsure, say N.
  config COMPAT_VDSO
        def_bool y
        prompt "Compat VDSO support"
@@@ -266,6 -266,19 +266,19 @@@ num_subarch_entries = (. - subarch_entr
        jmp default_entry
  #endif /* CONFIG_PARAVIRT */
  
+ #ifdef CONFIG_HOTPLUG_CPU
+ /*
+  * Boot CPU0 entry point. It's called from play_dead(). Everything has been set
+  * up already except stack. We just set up stack here. Then call
+  * start_secondary().
+  */
+ ENTRY(start_cpu0)
+       movl stack_start, %ecx
+       movl %ecx, %esp
+       jmp  *(initial_code)
+ ENDPROC(start_cpu0)
+ #endif
  /*
   * Non-boot CPU entry point; entered from trampoline.S
   * We can't lgdt here, because lgdt itself uses a data segment, but
@@@ -292,8 -305,8 +305,8 @@@ default_entry
   *    be using the global pages. 
   *
   *    NOTE! If we are on a 486 we may have no cr4 at all!
 - *    Specifically, cr4 exists if and only if CPUID exists,
 - *    which in turn exists if and only if EFLAGS.ID exists.
 + *    Specifically, cr4 exists if and only if CPUID exists
 + *    and has flags other than the FPU flag set.
   */
        movl $X86_EFLAGS_ID,%ecx
        pushl %ecx
        testl %ecx,%eax
        jz 6f                   # No ID flag = no CPUID = no CR4
  
 +      movl $1,%eax
 +      cpuid
 +      andl $~1,%edx           # Ignore CPUID.FPU
 +      jz 6f                   # No flags or only CPUID.FPU = no CR4
 +
        movl pa(mmu_cr4_features),%eax
        movl %eax,%cr4
  
@@@ -68,8 -68,6 +68,8 @@@
  #include <asm/mwait.h>
  #include <asm/apic.h>
  #include <asm/io_apic.h>
 +#include <asm/i387.h>
 +#include <asm/fpu-internal.h>
  #include <asm/setup.h>
  #include <asm/uv/uv.h>
  #include <linux/mc146818rtc.h>
@@@ -127,8 -125,8 +127,8 @@@ EXPORT_PER_CPU_SYMBOL(cpu_info)
  atomic_t init_deasserted;
  
  /*
-  * Report back to the Boot Processor.
-  * Running on AP.
+  * Report back to the Boot Processor during boot time or to the caller processor
+  * during CPU online.
   */
  static void __cpuinit smp_callin(void)
  {
         * we may get here before an INIT-deassert IPI reaches
         * our local APIC.  We have to wait for the IPI or we'll
         * lock up on an APIC access.
+        *
+        * Since CPU0 is not wakened up by INIT, it doesn't wait for the IPI.
         */
-       if (apic->wait_for_init_deassert)
+       cpuid = smp_processor_id();
+       if (apic->wait_for_init_deassert && cpuid != 0)
                apic->wait_for_init_deassert(&init_deasserted);
  
        /*
         * (This works even if the APIC is not enabled.)
         */
        phys_id = read_apic_id();
-       cpuid = smp_processor_id();
        if (cpumask_test_cpu(cpuid, cpu_callin_mask)) {
                panic("%s: phys CPU#%d, CPU#%d already present??\n", __func__,
                                        phys_id, cpuid);
        cpumask_set_cpu(cpuid, cpu_callin_mask);
  }
  
+ static int cpu0_logical_apicid;
+ static int enable_start_cpu0;
  /*
   * Activate a secondary processor.
   */
@@@ -245,6 -247,8 +249,8 @@@ notrace static void __cpuinit start_sec
        preempt_disable();
        smp_callin();
  
+       enable_start_cpu0 = 0;
  #ifdef CONFIG_X86_32
        /* switch away from the initial page table */
        load_cr3(swapper_pg_dir);
        cpu_idle();
  }
  
+ void __init smp_store_boot_cpu_info(void)
+ {
+       int id = 0; /* CPU 0 */
+       struct cpuinfo_x86 *c = &cpu_data(id);
+       *c = boot_cpu_data;
+       c->cpu_index = id;
+ }
  /*
   * The bootstrap kernel entry code has set these up. Save them for
   * a given CPU
   */
  void __cpuinit smp_store_cpu_info(int id)
  {
        struct cpuinfo_x86 *c = &cpu_data(id);
  
        *c = boot_cpu_data;
        c->cpu_index = id;
-       if (id != 0)
-               identify_secondary_cpu(c);
+       /*
+        * During boot time, CPU0 has this setup already. Save the info when
+        * bringing up AP or offlined CPU0.
+        */
+       identify_secondary_cpu(c);
  }
  
  static bool __cpuinit
@@@ -483,7 -498,7 +500,7 @@@ void __inquire_remote_apic(int apicid
   * won't ... remember to clear down the APIC, etc later.
   */
  int __cpuinit
- wakeup_secondary_cpu_via_nmi(int logical_apicid, unsigned long start_eip)
+ wakeup_secondary_cpu_via_nmi(int apicid, unsigned long start_eip)
  {
        unsigned long send_status, accept_status = 0;
        int maxlvt;
        /* Target chip */
        /* Boot on the stack */
        /* Kick the second */
-       apic_icr_write(APIC_DM_NMI | apic->dest_logical, logical_apicid);
+       apic_icr_write(APIC_DM_NMI | apic->dest_logical, apicid);
  
        pr_debug("Waiting for send to finish...\n");
        send_status = safe_apic_wait_icr_idle();
@@@ -651,6 -666,63 +668,63 @@@ static void __cpuinit announce_cpu(int 
                        node, cpu, apicid);
  }
  
+ static int wakeup_cpu0_nmi(unsigned int cmd, struct pt_regs *regs)
+ {
+       int cpu;
+       cpu = smp_processor_id();
+       if (cpu == 0 && !cpu_online(cpu) && enable_start_cpu0)
+               return NMI_HANDLED;
+       return NMI_DONE;
+ }
+ /*
+  * Wake up AP by INIT, INIT, STARTUP sequence.
+  *
+  * Instead of waiting for STARTUP after INITs, BSP will execute the BIOS
+  * boot-strap code which is not a desired behavior for waking up BSP. To
+  * void the boot-strap code, wake up CPU0 by NMI instead.
+  *
+  * This works to wake up soft offlined CPU0 only. If CPU0 is hard offlined
+  * (i.e. physically hot removed and then hot added), NMI won't wake it up.
+  * We'll change this code in the future to wake up hard offlined CPU0 if
+  * real platform and request are available.
+  */
+ static int __cpuinit
+ wakeup_cpu_via_init_nmi(int cpu, unsigned long start_ip, int apicid,
+              int *cpu0_nmi_registered)
+ {
+       int id;
+       int boot_error;
+       /*
+        * Wake up AP by INIT, INIT, STARTUP sequence.
+        */
+       if (cpu)
+               return wakeup_secondary_cpu_via_init(apicid, start_ip);
+       /*
+        * Wake up BSP by nmi.
+        *
+        * Register a NMI handler to help wake up CPU0.
+        */
+       boot_error = register_nmi_handler(NMI_LOCAL,
+                                         wakeup_cpu0_nmi, 0, "wake_cpu0");
+       if (!boot_error) {
+               enable_start_cpu0 = 1;
+               *cpu0_nmi_registered = 1;
+               if (apic->dest_logical == APIC_DEST_LOGICAL)
+                       id = cpu0_logical_apicid;
+               else
+                       id = apicid;
+               boot_error = wakeup_secondary_cpu_via_nmi(id, start_ip);
+       }
+       return boot_error;
+ }
  /*
   * NOTE - on most systems this is a PHYSICAL apic ID, but on multiquad
   * (ie clustered apic addressing mode), this is a LOGICAL apic ID.
@@@ -666,6 -738,7 +740,7 @@@ static int __cpuinit do_boot_cpu(int ap
  
        unsigned long boot_error = 0;
        int timeout;
+       int cpu0_nmi_registered = 0;
  
        /* Just in case we booted with a single CPU. */
        alternatives_enable_smp();
        }
  
        /*
-        * Kick the secondary CPU. Use the method in the APIC driver
-        * if it's defined - or use an INIT boot APIC message otherwise:
+        * Wake up a CPU in difference cases:
+        * - Use the method in the APIC driver if it's defined
+        * Otherwise,
+        * - Use an INIT boot APIC message for APs or NMI for BSP.
         */
        if (apic->wakeup_secondary_cpu)
                boot_error = apic->wakeup_secondary_cpu(apicid, start_ip);
        else
-               boot_error = wakeup_secondary_cpu_via_init(apicid, start_ip);
+               boot_error = wakeup_cpu_via_init_nmi(cpu, start_ip, apicid,
+                                                    &cpu0_nmi_registered);
  
        if (!boot_error) {
                /*
                 */
                smpboot_restore_warm_reset_vector();
        }
+       /*
+        * Clean up the nmi handler. Do this after the callin and callout sync
+        * to avoid impact of possible long unregister time.
+        */
+       if (cpu0_nmi_registered)
+               unregister_nmi_handler(NMI_LOCAL, "wake_cpu0");
        return boot_error;
  }
  
@@@ -797,7 -880,7 +882,7 @@@ int __cpuinit native_cpu_up(unsigned in
  
        pr_debug("++++++++++++++++++++=_---CPU UP  %u\n", cpu);
  
-       if (apicid == BAD_APICID || apicid == boot_cpu_physical_apicid ||
+       if (apicid == BAD_APICID ||
            !physid_isset(apicid, phys_cpu_present_map) ||
            !apic->apic_id_valid(apicid)) {
                pr_err("%s: bad cpu %d\n", __func__, cpu);
  
        per_cpu(cpu_state, cpu) = CPU_UP_PREPARE;
  
 +      /* the FPU context is blank, nobody can own it */
 +      __cpu_disable_lazy_restore(cpu);
 +
        err = do_boot_cpu(apicid, cpu, tidle);
        if (err) {
                pr_debug("do_boot_cpu failed %d\n", err);
@@@ -995,7 -1075,7 +1080,7 @@@ void __init native_smp_prepare_cpus(uns
        /*
         * Setup boot CPU information
         */
-       smp_store_cpu_info(0); /* Final full version of the data */
+       smp_store_boot_cpu_info(); /* Final full version of the data */
        cpumask_copy(cpu_callin_mask, cpumask_of(0));
        mb();
  
         */
        setup_local_APIC();
  
+       if (x2apic_mode)
+               cpu0_logical_apicid = apic_read(APIC_LDR);
+       else
+               cpu0_logical_apicid = GET_APIC_LOGICAL_ID(apic_read(APIC_LDR));
        /*
         * Enable IO APIC before setting up error vector
         */
@@@ -1219,19 -1304,6 +1309,6 @@@ void cpu_disable_common(void
  
  int native_cpu_disable(void)
  {
-       int cpu = smp_processor_id();
-       /*
-        * Perhaps use cpufreq to drop frequency, but that could go
-        * into generic code.
-        *
-        * We won't take down the boot processor on i386 due to some
-        * interrupts only being able to be serviced by the BSP.
-        * Especially so if we're not using an IOAPIC   -zwane
-        */
-       if (cpu == 0)
-               return -EBUSY;
        clear_local_APIC();
  
        cpu_disable_common();
@@@ -1271,6 -1343,14 +1348,14 @@@ void play_dead_common(void
        local_irq_disable();
  }
  
+ static bool wakeup_cpu0(void)
+ {
+       if (smp_processor_id() == 0 && enable_start_cpu0)
+               return true;
+       return false;
+ }
  /*
   * We need to flush the caches before going to sleep, lest we have
   * dirty data in our caches when we come back up.
@@@ -1334,6 -1414,11 +1419,11 @@@ static inline void mwait_play_dead(void
                __monitor(mwait_ptr, 0, 0);
                mb();
                __mwait(eax, 0);
+               /*
+                * If NMI wants to wake up CPU0, start CPU0.
+                */
+               if (wakeup_cpu0())
+                       start_cpu0();
        }
  }
  
@@@ -1344,6 -1429,11 +1434,11 @@@ static inline void hlt_play_dead(void
  
        while (1) {
                native_halt();
+               /*
+                * If NMI wants to wake up CPU0, start CPU0.
+                */
+               if (wakeup_cpu0())
+                       start_cpu0();
        }
  }
  
diff --combined kernel/cpu.c
@@@ -348,13 -348,11 +348,13 @@@ static int __cpuinit _cpu_up(unsigned i
        unsigned long mod = tasks_frozen ? CPU_TASKS_FROZEN : 0;
        struct task_struct *idle;
  
 -      if (cpu_online(cpu) || !cpu_present(cpu))
 -              return -EINVAL;
 -
        cpu_hotplug_begin();
  
 +      if (cpu_online(cpu) || !cpu_present(cpu)) {
 +              ret = -EINVAL;
 +              goto out;
 +      }
 +
        idle = idle_thread_get(cpu);
        if (IS_ERR(idle)) {
                ret = PTR_ERR(idle);
@@@ -603,6 -601,11 +603,11 @@@ cpu_hotplug_pm_callback(struct notifier
  
  static int __init cpu_hotplug_pm_sync_init(void)
  {
+       /*
+        * cpu_hotplug_pm_callback has higher priority than x86
+        * bsp_pm_callback which depends on cpu_hotplug_pm_callback
+        * to disable cpu hotplug to avoid cpu hotplug race.
+        */
        pm_notifier(cpu_hotplug_pm_callback, 0);
        return 0;
  }