Skip to contentSkip to conversation

CPU hot-plug support for x86_64

PATCH In review Ashok Raj 4 participants 22 messages May 20, 2005 – May 24, 2005

Subsystems (2)
lore
Share & tools
lore

Ashok Raj

author
#0 lore raw

cover CPU hot-plug support for x86_64

Hi Andi

Attached are patches that would apply over 2.6.12-rc4-mm2 to provide cpu hotplug support. It seems to hold up to stress (make -j's) on a 4way HT system)

FYI: Iam sending this via quilt mail, hopefully it makes its way ok, otherwise please pardon, and i will resend my usuall way again if this doesn't work.

- I tested with booting maxcpus=1, then you can kick each cpu online.
* I also manually migrate interrupts to the new CPU, and ensure they are off when that same cpu is being offlined.
* Seems to have trouble with CONFIG_SCHED_SMT, i need to look into this later.
- Offline works as well

Andi: You had mentioned that you would not prefer to replace the broadcast IPI
with the mask version for performance. Currently this seems to be the
most optimal way without putting a sledge hammer on the cpu_up process.

If there are any known performance issues that we are worried about we should look for alternative mechanism.

TBD:
0. Verify with CONFIG_NUMA (not done yet)
1. ACPI integration for physical cpu hotplug support.
2. replace fixup_irqs() with a correct implementation. This works fine for now but has potential to miss interrupts. (in works).

Cheers,
Ashok Raj

author
#1 lore raw

PATCH 1/4 CPU hot-plug support for x86_64

This patch removes all init sections that are required to exist past smpboot.

- Changed __cpuinit to __devinit since this is how the rest of the cpu hotplug infrastructure is written.

--------------------------------------------

 arch/x86_64/kernel/apic.c    |    8 ++++----
 arch/x86_64/kernel/i387.c    |    2 +-
 arch/x86_64/kernel/nmi.c     |    4 ++--
 arch/x86_64/kernel/process.c |    2 +-
 arch/x86_64/kernel/setup.c   |   18 +++++++++---------
 arch/x86_64/kernel/setup64.c |    8 ++++----
 arch/x86_64/kernel/smpboot.c |   37 ++++++++++++++++---------------------
 mm/page_alloc.c              |    2 +-
 8 files changed, 38 insertions(+), 43 deletions(-)
arch/x86_64/kernel/smpboot.c +16 -21
Index: linux-2.6.12-rc4-mm2/arch/x86_64/kernel/smpboot.c
===================================================================
@@ -58,11 +58,6 @@
 #include <asm/proto.h>
 #include <asm/nmi.h>
 
-/* Change for real CPU hotplug. Note other files need to be fixed
-   first too. */
-#define __cpuinit __init
-#define __cpuinitdata __initdata
-
 /* Number of siblings per CPU package */
 int smp_num_siblings = 1;
 /* Package ID of each logical CPU */
@@ -108,7 +103,7 @@
  * has made sure it's suitably aligned.
  */
 
-static unsigned long __cpuinit setup_trampoline(void)
+static unsigned long __devinit setup_trampoline(void)
 {
 	void *tramp = __va(SMP_TRAMPOLINE_BASE); 
 	memcpy(tramp, trampoline_data, trampoline_end - trampoline_data);
@@ -120,7 +115,7 @@
  * a given CPU
  */
 
-static void __cpuinit smp_store_cpu_info(int id)
+static void __devinit smp_store_cpu_info(int id)
 {
 	struct cpuinfo_x86 *c = cpu_data + id;
 
@@ -331,13 +326,13 @@
 }
 __setup("notscsync", notscsync_setup);
 
-static atomic_t init_deasserted __cpuinitdata;
+static atomic_t init_deasserted __devinitdata;
 
 /*
  * Report back to the Boot Processor.
  * Running on AP.
  */
-void __cpuinit smp_callin(void)
+void __devinit smp_callin(void)
 {
 	int cpuid, phys_id;
 	unsigned long timeout;
@@ -420,7 +415,7 @@
 /*
  * Setup code on secondary processor (after comming out of the trampoline)
  */
-void __cpuinit start_secondary(void)
+void __devinit start_secondary(void)
 {
 	/*
 	 * Dont put anything before smp_callin(), SMP
@@ -504,7 +499,7 @@
 /*
  * Kick the secondary to wake up.
  */
-static int __cpuinit wakeup_secondary_via_INIT(int phys_apicid, unsigned int start_rip)
+static int __devinit wakeup_secondary_via_INIT(int phys_apicid, unsigned int start_rip)
 {
 	unsigned long send_status = 0, accept_status = 0;
 	int maxlvt, timeout, num_starts, j;
@@ -630,7 +625,7 @@
 /*
  * Boot one CPU.
  */
-static int __cpuinit do_boot_cpu(int cpu, int apicid)
+static int __devinit do_boot_cpu(int cpu, int apicid)
 {
 	struct task_struct *idle;
 	unsigned long boot_error;
@@ -748,7 +743,7 @@
  * Construct cpu_sibling_map[], so that we can tell the sibling CPU
  * on SMT systems efficiently.
  */
-static __cpuinit void detect_siblings(void)
+static __devinit void detect_siblings(void)
 {
 	int cpu;
 
@@ -792,7 +787,7 @@
 /*
  * Cleanup possible dangling ends...
  */
-static __cpuinit void smp_cleanup_boot(void)
+static __devinit void smp_cleanup_boot(void)
 {
 	/*
 	 * Paranoid:  Set warm reset code and vector here back
@@ -822,7 +817,7 @@
  *
  * RED-PEN audit/test this more. I bet there is more state messed up here.
  */
-static __cpuinit void disable_smp(void)
+static __init void disable_smp(void)
 {
 	cpu_present_map = cpumask_of_cpu(0);
 	cpu_possible_map = cpumask_of_cpu(0);
@@ -837,7 +832,7 @@
 /*
  * Handle user cpus=... parameter.
  */
-static __cpuinit void enforce_max_cpus(unsigned max_cpus)
+static __init void enforce_max_cpus(unsigned max_cpus)
 {
 	int i, k;
 	k = 0;
@@ -854,7 +849,7 @@
 /*
  * Various sanity checks.
  */
-static int __cpuinit smp_sanity_check(unsigned max_cpus)
+static int __devinit smp_sanity_check(unsigned max_cpus)
 {
 	if (!physid_isset(hard_smp_processor_id(), phys_cpu_present_map)) {
 		printk("weird, boot CPU (#%d) not listed by the BIOS.\n",
@@ -912,7 +907,7 @@
  * Prepare for SMP bootup.  The MP table or ACPI has been read
  * earlier.  Just do some sanity checking here and enable APIC mode.
  */
-void __cpuinit smp_prepare_cpus(unsigned int max_cpus)
+void __init smp_prepare_cpus(unsigned int max_cpus)
 {
 	int i;
 
@@ -982,10 +977,10 @@
 /*
  * Entry point to boot a CPU.
  *
- * This is all __cpuinit, not __devinit for now because we don't support
+ * This is all __devinit, not __devinit for now because we don't support
  * CPU hotplug (yet).
  */
-int __cpuinit __cpu_up(unsigned int cpu)
+int __devinit __cpu_up(unsigned int cpu)
 {
 	int err;
 	int apicid = cpu_present_to_apicid(cpu);
@@ -1018,7 +1013,7 @@
 /*
  * Finish the SMP boot.
  */
-void __cpuinit smp_cpus_done(unsigned int max_cpus)
+void __init smp_cpus_done(unsigned int max_cpus)
 {
 	zap_low_mappings();
 	smp_cleanup_boot();
arch/x86_64/kernel/process.c +1 -1
Index: linux-2.6.12-rc4-mm2/arch/x86_64/kernel/process.c
===================================================================
@@ -204,7 +204,7 @@
 	}
 }
 
-void __init select_idle_routine(const struct cpuinfo_x86 *c)
+void __devinit select_idle_routine(const struct cpuinfo_x86 *c)
 {
 	static int printed;
 	if (cpu_has(c, X86_FEATURE_MWAIT)) {
arch/x86_64/kernel/setup.c +9 -9
Index: linux-2.6.12-rc4-mm2/arch/x86_64/kernel/setup.c
===================================================================
@@ -696,7 +696,7 @@
 #endif
 }
 
-static int __init get_model_name(struct cpuinfo_x86 *c)
+static int __devinit get_model_name(struct cpuinfo_x86 *c)
 {
 	unsigned int *v;
 
@@ -712,7 +712,7 @@
 }
 
 
-static void __init display_cacheinfo(struct cpuinfo_x86 *c)
+static void __devinit display_cacheinfo(struct cpuinfo_x86 *c)
 {
 	unsigned int n, dummy, eax, ebx, ecx, edx;
 
@@ -823,7 +823,7 @@
 	return r;
 }
 
-static void __init detect_ht(struct cpuinfo_x86 *c)
+static void __devinit detect_ht(struct cpuinfo_x86 *c)
 {
 #ifdef CONFIG_SMP
 	u32 	eax, ebx, ecx, edx;
@@ -884,7 +884,7 @@
 /*
  * find out the number of processor cores on the die
  */
-static int __init intel_num_cpu_cores(struct cpuinfo_x86 *c)
+static int __devinit intel_num_cpu_cores(struct cpuinfo_x86 *c)
 {
 	unsigned int eax;
 
@@ -902,7 +902,7 @@
 		return 1;
 }
 
-static void __init init_intel(struct cpuinfo_x86 *c)
+static void __devinit init_intel(struct cpuinfo_x86 *c)
 {
 	/* Cache sizes */
 	unsigned n;
@@ -922,7 +922,7 @@
  	c->x86_num_cores = intel_num_cpu_cores(c);
 }
 
-void __init get_cpu_vendor(struct cpuinfo_x86 *c)
+void __devinit get_cpu_vendor(struct cpuinfo_x86 *c)
 {
 	char *v = c->x86_vendor_id;
 
@@ -943,7 +943,7 @@
 /* Do some early cpuid on the boot CPU to get some parameter that are
    needed before check_bugs. Everything advanced is in identify_cpu
    below. */
-void __init early_identify_cpu(struct cpuinfo_x86 *c)
+void __devinit early_identify_cpu(struct cpuinfo_x86 *c)
 {
 	u32 tfms;
 
@@ -998,7 +998,7 @@
 /*
  * This does the hard work of actually picking apart the CPU stuff...
  */
-void __init identify_cpu(struct cpuinfo_x86 *c)
+void __devinit identify_cpu(struct cpuinfo_x86 *c)
 {
 	int i;
 	u32 xlvl;
@@ -1075,7 +1075,7 @@
 }
  
 
-void __init print_cpu_info(struct cpuinfo_x86 *c)
+void __devinit print_cpu_info(struct cpuinfo_x86 *c)
 {
 	if (c->x86_model_id[0])
 		printk("%s", c->x86_model_id);
arch/x86_64/kernel/apic.c +4 -4
Index: linux-2.6.12-rc4-mm2/arch/x86_64/kernel/apic.c
===================================================================
@@ -321,7 +321,7 @@
 	apic_write_around(APIC_LVT1, value);
 }
 
-void __init setup_local_APIC (void)
+void __devinit setup_local_APIC (void)
 {
 	unsigned int value, ver, maxlvt;
 
@@ -570,7 +570,7 @@
 	.cls		= &lapic_sysclass,
 };
 
-static void __init apic_pm_activate(void)
+static void __devinit apic_pm_activate(void)
 {
 	apic_pm_state.active = 1;
 }
@@ -810,14 +810,14 @@
 	local_irq_enable();
 }
 
-void __init setup_secondary_APIC_clock(void)
+void __devinit setup_secondary_APIC_clock(void)
 {
 	local_irq_disable(); /* FIXME: Do we need this? --RR */
 	setup_APIC_timer(calibration_result);
 	local_irq_enable();
 }
 
-void __init disable_APIC_timer(void)
+void __devinit disable_APIC_timer(void)
 {
 	if (using_apic_timer) {
 		unsigned long v;
arch/x86_64/kernel/setup64.c +4 -4
Index: linux-2.6.12-rc4-mm2/arch/x86_64/kernel/setup64.c
===================================================================
@@ -29,7 +29,7 @@
 
 char x86_boot_params[BOOT_PARAM_SIZE] __initdata = {0,};
 
-cpumask_t cpu_initialized __initdata = CPU_MASK_NONE;
+cpumask_t cpu_initialized __devinitdata = CPU_MASK_NONE;
 
 struct x8664_pda cpu_pda[NR_CPUS] __cacheline_aligned; 
 
@@ -171,7 +171,7 @@
 	wrmsrl(MSR_SYSCALL_MASK, EF_TF|EF_DF|EF_IE|0x3000); 
 }
 
-void __init check_efer(void)
+void __devinit check_efer(void)
 {
 	unsigned long efer;
 
@@ -188,7 +188,7 @@
  * 'CPU state barrier', nothing should get across.
  * A lot of state is already set up in PDA init.
  */
-void __init cpu_init (void)
+void __devinit cpu_init (void)
 {
 #ifdef CONFIG_SMP
 	int cpu = stack_smp_processor_id();
@@ -214,7 +214,7 @@
 
 	printk("Initializing CPU#%d\n", cpu);
 
-		clear_in_cr4(X86_CR4_VME|X86_CR4_PVI|X86_CR4_TSD|X86_CR4_DE);
+	clear_in_cr4(X86_CR4_VME|X86_CR4_PVI|X86_CR4_TSD|X86_CR4_DE);
 
 	/*
 	 * Initialize the per-CPU GDT with the boot GDT,
arch/x86_64/kernel/i387.c +1 -1
Index: linux-2.6.12-rc4-mm2/arch/x86_64/kernel/i387.c
===================================================================
@@ -42,7 +42,7 @@
  * Called at bootup to set up the initial FPU state that is later cloned
  * into all processes.
  */
-void __init fpu_init(void)
+void __devinit fpu_init(void)
 {
 	unsigned long oldcr0 = read_cr0();
 	extern void __bad_fxsave_alignment(void);
mm/page_alloc.c +1 -1
Index: linux-2.6.12-rc4-mm2/mm/page_alloc.c
===================================================================
@@ -72,7 +72,7 @@
 
 #ifdef CONFIG_NUMA
 static struct per_cpu_pageset
-	pageset_table[MAX_NR_ZONES*MAX_NUMNODES*NR_CPUS] __initdata;
+	pageset_table[MAX_NR_ZONES*MAX_NUMNODES*NR_CPUS] __devinitdata;
 #endif
 
 static char *zone_names[MAX_NR_ZONES] = { "DMA", "Normal", "HighMem" };
arch/x86_64/kernel/nmi.c +2 -2
Index: linux-2.6.12-rc4-mm2/arch/x86_64/kernel/nmi.c
===================================================================
@@ -98,7 +98,7 @@
 	(P4_CCCR_OVF_PMI0|P4_CCCR_THRESHOLD(15)|P4_CCCR_COMPLEMENT|	\
 	 P4_CCCR_COMPARE|P4_CCCR_REQUIRED|P4_CCCR_ESCR_SELECT(4)|P4_CCCR_ENABLE)
 
-static __init inline int nmi_known_cpu(void)
+static __devinit inline int nmi_known_cpu(void)
 {
 	switch (boot_cpu_data.x86_vendor) {
 	case X86_VENDOR_AMD:
@@ -110,7 +110,7 @@
 }
 
 /* Run after command line and cpu_init init, but before all other checks */
-void __init nmi_watchdog_default(void)
+void __devinit nmi_watchdog_default(void)
 {
 	if (nmi_watchdog != NMI_DEFAULT)
 		return;

--

author
#2 lore raw

PATCH 2/4 CPU hot-plug support for x86_64

In reply to Ashok Raj

Experimental CPU hotplug patch for x86_64
-----------------------------------------
- Most of init code that needs to be there for hotplug marked now as __devinit (Didn't use cpuinit, simply because the main framework code in kernel is not the same way, just trying to be consistent)
- Test with maxcpus=1, and then kick other cpu's off to test if init code is all cleaned up.
- idle threads are forked on demand from keventd threads for clean startup

TBD:

1. Currently not tested for CONFIG_NUMA
2. CONFIG_SCHED_SMT seems to have trouble with booting maxcpus=1 and then bringing up one cpu.
3. Handle ACPI pieces for physical hotplug support.

-----------

 arch/i386/mach-default/topology.c |    7 -
 arch/x86_64/Kconfig               |    9 +
 arch/x86_64/kernel/irq.c          |   30 ++++
 arch/x86_64/kernel/process.c      |   33 +++++
 arch/x86_64/kernel/smp.c          |   12 +
 arch/x86_64/kernel/smpboot.c      |  241 ++++++++++++++++++++++++++++++++++----
 arch/x86_64/kernel/traps.c        |    9 +
 include/asm-x86_64/irq.h          |    5 
 include/asm-x86_64/smp.h          |    4 
 9 files changed, 322 insertions(+), 28 deletions(-)
arch/x86_64/Kconfig +9 -0
Index: linux-2.6.12-rc4-mm2/arch/x86_64/Kconfig
===================================================================
@@ -294,6 +294,15 @@
 	  This is purely to save memory - each supported CPU requires
 	  memory in the static kernel configuration.
 
+config HOTPLUG_CPU
+	bool "Support for hot-pluggable CPUs (EXPERIMENTAL)"
+	depends on SMP && HOTPLUG && EXPERIMENTAL
+	help
+		Say Y here to experiment with turning CPUs off and on.  CPUs
+		can be controlled through /sys/devices/system/cpu/cpu#.
+		Say N if you want to disable CPU hotplug.
+
+
 config HPET_TIMER
 	bool
 	default y
arch/x86_64/kernel/smpboot.c +217 -24

368 lines not on this page · raw message

arch/x86_64/kernel/irq.c +30 -0
Index: linux-2.6.12-rc4-mm2/arch/x86_64/kernel/irq.c
===================================================================
@@ -14,6 +14,7 @@
 #include <linux/interrupt.h>
 #include <linux/seq_file.h>
 #include <linux/module.h>
+#include <linux/delay.h>
 #include <asm/uaccess.h>
 #include <asm/io_apic.h>
 
@@ -106,3 +107,32 @@
 
 	return 1;
 }
+
+#ifdef CONFIG_HOTPLUG_CPU
+void fixup_irqs(cpumask_t map)
+{
+	unsigned int irq;
+	static int warned;
+
+	for (irq = 0; irq < NR_IRQS; irq++) {
+		cpumask_t mask;
+		if (irq == 2)
+			continue;
+
+		cpus_and(mask, irq_affinity[irq], map);
+		if (any_online_cpu(mask) == NR_CPUS) {
+			printk("Breaking affinity for irq %i\n", irq);
+			mask = map;
+		}
+		if (irq_desc[irq].handler->set_affinity)
+			irq_desc[irq].handler->set_affinity(irq, mask);
+		else if (irq_desc[irq].action && !(warned++))
+			printk("Cannot set affinity for irq %i\n", irq);
+	}
+
+	/* That doesn't seem sufficient.  Give it 1ms. */
+	local_irq_enable();
+	mdelay(1);
+	local_irq_disable();
+}
+#endif
include/asm-x86_64/irq.h +5 -0
Index: linux-2.6.12-rc4-mm2/include/asm-x86_64/irq.h
===================================================================
@@ -52,4 +52,9 @@
 struct pt_regs;
 int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *);
 
+#ifdef CONFIG_HOTPLUG_CPU
+#include <linux/cpumask.h>
+extern void fixup_irqs(cpumask_t map);
+#endif
+
 #endif /* _ASM_IRQ_H */
arch/x86_64/kernel/process.c +32 -1
Index: linux-2.6.12-rc4-mm2/arch/x86_64/kernel/process.c
===================================================================
@@ -8,7 +8,8 @@
  * 
  *  X86-64 port
  *	Andi Kleen.
- * 
+ *
+ *	CPU hotplug support - [email protected]
  *  $Id: process.c,v 1.38 2002/01/15 10:08:03 ak Exp $
  */
 
@@ -18,6 +19,7 @@
 
 #include <stdarg.h>
 
+#include <linux/cpu.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
@@ -154,6 +156,33 @@
 }
 EXPORT_SYMBOL_GPL(cpu_idle_wait);
 
+#ifdef CONFIG_HOTPLUG_CPU
+DECLARE_PER_CPU(int, cpu_state);
+
+#include <asm/nmi.h>
+/* We don't actually take CPU down, just spin without interrupts. */
+static inline void play_dead(void)
+{
+	idle_task_exit();
+	mb();
+	/* Ack it */
+	__get_cpu_var(cpu_state) = CPU_DEAD;
+
+	local_irq_disable();
+	while (1)
+		safe_halt();
+#if 0
+	while (1)
+		__asm__ __volatile__("hlt":::"memory");
+#endif
+}
+#else
+static inline void play_dead(void)
+{
+	BUG();
+}
+#endif /* CONFIG_HOTPLUG_CPU */
+
 /*
  * The idle thread. There's no useful work to be
  * done, so just try to conserve power and have a
@@ -174,6 +203,8 @@
 			idle = pm_idle;
 			if (!idle)
 				idle = default_idle;
+			if (cpu_is_offline(smp_processor_id()))
+				play_dead();
 			idle();
 		}
 
include/asm-x86_64/smp.h +4 -0
Index: linux-2.6.12-rc4-mm2/include/asm-x86_64/smp.h
===================================================================
@@ -44,6 +44,8 @@
 extern volatile unsigned long smp_invalidate_needed;
 extern int pic_mode;
 extern int smp_num_siblings;
+extern void lock_ipi_calllock(void);
+extern void unlock_ipi_calllock(void);
 extern void smp_flush_tlb(void);
 extern void smp_message_irq(int cpl, void *dev_id, struct pt_regs *regs);
 extern void smp_send_reschedule(int cpu);
@@ -77,6 +79,8 @@
 }
 
 extern int safe_smp_processor_id(void);
+extern int __cpu_disable(void);
+extern void __cpu_die(unsigned int cpu);
 
 #endif /* !ASSEMBLY */
 
arch/x86_64/kernel/traps.c +8 -1
Index: linux-2.6.12-rc4-mm2/arch/x86_64/kernel/traps.c
===================================================================
@@ -587,11 +587,18 @@
 asmlinkage void default_do_nmi(struct pt_regs *regs)
 {
 	unsigned char reason = 0;
+	int cpu;
+
+	cpu = smp_processor_id();
 
 	/* Only the BSP gets external NMIs from the system.  */
-	if (!smp_processor_id())
+	if (!cpu)
 		reason = get_nmi_reason();
 
+#ifdef CONFIG_HOTPLUG_CPU
+	if (!cpu_online(cpu))
+		return;
+#endif
 	if (!(reason & 0xc0)) {
 		if (notify_die(DIE_NMI_IPI, "nmi_ipi", regs, reason, 0, SIGINT)
 								== NOTIFY_STOP)
arch/x86_64/kernel/smp.c +12 -0
Index: linux-2.6.12-rc4-mm2/arch/x86_64/kernel/smp.c
===================================================================
@@ -295,6 +295,18 @@
 
 static struct call_data_struct * call_data;
 
+void
+lock_ipi_calllock(void)
+{
+	spin_lock_irq(&call_lock);
+}
+
+void
+unlock_ipi_calllock(void)
+{
+	spin_unlock_irq(&call_lock);
+}
+
 /*
  * this function sends a 'generic call function' IPI to all other CPUs
  * in the system.
arch/i386/mach-default/topology.c +5 -2
Index: linux-2.6.12-rc4-mm2/arch/i386/mach-default/topology.c
===================================================================
@@ -88,8 +88,11 @@
 {
 	int i;
 
-	for (i = 0; i < NR_CPUS; i++)
-		if (cpu_possible(i)) arch_register_cpu(i);
+	for (i = 0; i < NR_CPUS; i++) {
+		if (cpu_possible(i))  {
+			arch_register_cpu(i);
+		}
+	}
 	return 0;
 }
 

--

#3 lore raw
Quoted 5 lines · Ashok Raj

Experimental CPU hotplug patch for x86_64
-----------------------------------------
- Most of init code that needs to be there for hotplug marked now as __devinit (Didn't use cpuinit, simply because the main framework code in kernel is not the same way, just trying to be consistent)

I dont like that. Can you keep it as __cpuinit please? e.g. if cpuhot plug turns out to be a lot of code we could later mark it free when we detect at boot the system does not support cpu hotplug. With devinit that is pretty much impossible these days.

Also it is better for documentation purposes.

Havent read the full patch yet sorry, but it seems to mostly consist of these changes anyways?

-Andi

author
#4 lore raw
Quoted 7 lines · Andi Kleen
Quoted 5 lines · Ashok Raj

Experimental CPU hotplug patch for x86_64
-----------------------------------------
- Most of init code that needs to be there for hotplug marked now as __devinit (Didn't use cpuinit, simply because the main framework code in kernel is not the same way, just trying to be consistent)

Quoted 6 lines

I dont like that. Can you keep it as __cpuinit please? e.g. if cpuhot plug turns out to be a lot of code we could later mark it free when we detect at boot the system does not support cpu hotplug. With devinit that is pretty much impossible these days.

Also it is better for documentation purposes.

If its for documentation, then its ok, the reason i thought it will be dead code/documentation soon is since 90% of the hotplug code is generic kernel code, which is not under __cpuinit, just some pieces of x86_64 would alone exist this way, and will not serve real purpose very soon.

If you still prefer to exist as __cpuinit, i dont have a problem leaving them around for the time being.

Signature · 3 lines
Cheers,
Ashok Raj
- Open Source Technology Center
#5 lore raw
Quoted 13 lines · Ashok Raj
Quoted 12 lines · Andi Kleen
Quoted 5 lines · Ashok Raj

Experimental CPU hotplug patch for x86_64
-----------------------------------------
- Most of init code that needs to be there for hotplug marked now as __devinit (Didn't use cpuinit, simply because the main framework code in kernel is not the same way, just trying to be consistent)

I dont like that. Can you keep it as __cpuinit please? e.g. if cpuhot plug turns out to be a lot of code we could later mark it free when we detect at boot the system does not support cpu hotplug. With devinit that is pretty much impossible these days.

Quoted 6 lines
Quoted 1 line

Also it is better for documentation purposes.

If its for documentation, then its ok, the reason i thought it will be dead code/documentation soon is since 90% of the hotplug code is generic kernel code, which is not under __cpuinit, just some pieces of x86_64 would alone exist this way, and will not serve real purpose very soon.

I would hope these other pieces get converted over. I will probably look into that soon if nobody beats me.

-Andi

author
#6 lore raw
Quoted 10 lines · Andi Kleen
Quoted 5 lines · Ashok Raj

If its for documentation, then its ok, the reason i thought it will be dead code/documentation soon is since 90% of the hotplug code is generic kernel code, which is not under __cpuinit, just some pieces of x86_64 would alone exist this way, and will not serve real purpose very soon.

I would hope these other pieces get converted over. I will probably look into that soon if nobody beats me.

If my IRC, this is how we got started early days. I remember rusty changed them during the end of the real development.

Rusty/Vatsa would know the exact scope on why we went away from __cpuinit.

[Both who's names i missed accidently in ccing, when i was trying the new quilt mail, i exited by mistake, and it sent to the partial list, sorry guys]

Signature · 3 lines
Cheers,
Ashok Raj
- Open Source Technology Center
#7 lore raw
In reply to Andi Kleen

Hi,

Quoted 11 lines · Andi Kleen
Quoted 5 lines · Ashok Raj

Experimental CPU hotplug patch for x86_64
-----------------------------------------
- Most of init code that needs to be there for hotplug marked now as __devinit (Didn't use cpuinit, simply because the main framework code in kernel is not the same way, just trying to be consistent)

I dont like that. Can you keep it as __cpuinit please? e.g. if cpuhot plug turns out to be a lot of code we could later mark it free when we detect at boot the system does not support cpu hotplug. With devinit that is pretty much impossible these days.

Please note that CPU hotplug will be necessary for swsusp on SMP systems (e.g. dual-core). It seems that currently __cpuinit <=> __init, so it's not quite suitable for this purpose.

Greets,
Rafael

Signature · 3 lines
- Would you tell me, please, which way I ought to go from here?
- That depends a good deal on where you want to get to.
		-- Lewis Carroll "Alice's Adventures in Wonderland"
#8 lore raw
Quoted 3 lines

Please note that CPU hotplug will be necessary for swsusp on SMP systems (e.g. dual-core). It seems that currently __cpuinit <=> __init, so it's not quite suitable for this purpose.

This was only temporary of course because real CPU hotplug was not yet implemented.

-Andi

author
#9 lore raw

PATCH 3/4 CPU hot-plug support for x86_64

In reply to Ashok Raj

This patch is a minor cleanup to the cpu sibling/core map. It is required that this setup happens on a per-cpu bringup time.

------------------------------

 smpboot.c |   89 ++++++++++++++++++++++++++++----------------------------------
 1 files changed, 41 insertions(+), 48 deletions(-)
arch/x86_64/kernel/smpboot.c +41 -48
Index: linux-2.6.12-rc4-mm2/arch/x86_64/kernel/smpboot.c
===================================================================
@@ -62,9 +62,12 @@
 /* Number of siblings per CPU package */
 int smp_num_siblings = 1;
 /* Package ID of each logical CPU */
-u8 phys_proc_id[NR_CPUS] = { [0 ... NR_CPUS-1] = BAD_APICID };
-u8 cpu_core_id[NR_CPUS] = { [0 ... NR_CPUS-1] = BAD_APICID };
+u8 phys_proc_id[NR_CPUS] __cacheline_aligned = 
+	{ [0 ... NR_CPUS-1] = BAD_APICID };
 EXPORT_SYMBOL(phys_proc_id);
+
+u8 cpu_core_id[NR_CPUS] __cacheline_aligned = 
+	{ [0 ... NR_CPUS-1] = BAD_APICID };
 EXPORT_SYMBOL(cpu_core_id);
 
 /* Bitmask of currently online CPUs */
@@ -434,6 +437,34 @@
 	local_flush_tlb();
 }
 
+static inline void
+set_cpu_sibling_map(int cpu)
+{
+	int i;
+
+	if (smp_num_siblings > 1) {
+		for_each_online_cpu(i) {
+			if (cpu_core_id[cpu] == cpu_core_id[i]) {
+				cpu_set(i, cpu_sibling_map[cpu]);
+				cpu_set(cpu, cpu_sibling_map[i]);
+			}
+		}
+	} else {
+		cpu_set(cpu, cpu_sibling_map[cpu]);
+	}
+
+	if (current_cpu_data.x86_num_cores > 1) {
+		for_each_online_cpu(i) {
+			if (phys_proc_id[cpu] == phys_proc_id[i]) {
+				cpu_set(i, cpu_core_map[cpu]);
+				cpu_set(cpu, cpu_core_map[i]);
+			}
+		}
+	} else {
+		cpu_core_map[cpu] = cpu_sibling_map[cpu];
+	}
+}
+
 /*
  * Setup code on secondary processor (after comming out of the trampoline)
  */
@@ -463,6 +494,12 @@
 
 	enable_APIC_timer();
 
+	/* 
+	 * The sibling maps must be set before turing the online map on for 
+	 * this cpu 
+	 */
+	set_cpu_sibling_map(smp_processor_id());
+
 	/*
 	 * Allow the master to continue.
 	 */
@@ -797,51 +834,6 @@
 unsigned long cache_decay_ticks;
 
 /*
- * Construct cpu_sibling_map[], so that we can tell the sibling CPU
- * on SMT systems efficiently.
- */
-static __devinit void detect_siblings(void)
-{
-	int cpu;
-
-	for (cpu = 0; cpu < NR_CPUS; cpu++) {
-		cpus_clear(cpu_sibling_map[cpu]);
-		cpus_clear(cpu_core_map[cpu]);
-	}
-
-	for_each_online_cpu (cpu) {
-		struct cpuinfo_x86 *c = cpu_data + cpu;
-		int siblings = 0;
-		int i;
-		if (smp_num_siblings > 1) {
-			for_each_online_cpu (i) {
-				if (cpu_core_id[cpu] == cpu_core_id[i]) {
-					siblings++;
-					cpu_set(i, cpu_sibling_map[cpu]);
-				}
-			}
-		} else {
-			siblings++;
-			cpu_set(cpu, cpu_sibling_map[cpu]);
-		}
-
-		if (siblings != smp_num_siblings) {
-			printk(KERN_WARNING
-	       "WARNING: %d siblings found for CPU%d, should be %d\n",
-			       siblings, cpu, smp_num_siblings);
-			smp_num_siblings = siblings;
-		}
-		if (c->x86_num_cores > 1) {
-			for_each_online_cpu(i) {
-				if (phys_proc_id[cpu] == phys_proc_id[i])
-					cpu_set(i, cpu_core_map[cpu]);
-			}
-		} else
-			cpu_core_map[cpu] = cpu_sibling_map[cpu];
-	}
-}
-
-/*
  * Cleanup possible dangling ends...
  */
 static __devinit void smp_cleanup_boot(void)
@@ -1035,6 +1027,8 @@
 	int me = smp_processor_id();
 	cpu_set(me, cpu_online_map);
 	cpu_set(me, cpu_callout_map);
+	cpu_set(0, cpu_sibling_map[0]);
+	cpu_set(0, cpu_core_map[0]);
 }
 
 #ifdef CONFIG_HOTPLUG_CPU
@@ -1122,7 +1116,6 @@
 	setup_ioapic_dest();
 #endif
 
-	detect_siblings();
 	time_init_gtod();
 
 	check_nmi_watchdog();

--

author
#10 lore raw

PATCH 4/4 CPU hot-plug support for x86_64

In reply to Ashok Raj

This patch removes the usage of shortcut to send smp_call_function using ALL_BUT_SELF shortcut. This is a problem for an upcomming CPU, and is still not ready to take this interrupt. Since cpu_online_map is updated with the call_lock held, this mechanism should avoid sending IPI to cpu's not yet prepared to participate in this party.

----------------------------------------

 smp.c |   10 ++++++++--
 1 files changed, 8 insertions(+), 2 deletions(-)
arch/x86_64/kernel/smp.c +8 -2
Index: linux-2.6.12-rc4-mm2/arch/x86_64/kernel/smp.c
===================================================================
@@ -315,8 +315,10 @@
 				int nonatomic, int wait)
 {
 	struct call_data_struct data;
-	int cpus = num_online_cpus()-1;
+	cpumask_t cpu_mask;
+	int cpus;
 
+	cpus = num_online_cpus() - 1;
 	if (!cpus)
 		return;
 
@@ -327,10 +329,14 @@
 	if (wait)
 		atomic_set(&data.finished, 0);
 
+	cpu_mask = cpu_online_map;
+	cpu_clear(smp_processor_id(), cpu_mask);
+
 	call_data = &data;
 	wmb();
+
 	/* Send a message to all other CPUs and wait for them to respond */
-	send_IPI_allbutself(CALL_FUNCTION_VECTOR);
+	send_IPI_mask(cpu_mask, CALL_FUNCTION_VECTOR);
 
 	/* Wait for response */
 	while (atomic_read(&data.started) != cpus)

--

#11 lore raw
In reply to Ashok Raj
Quoted 3 lines · Ashok Raj

Andi: You had mentioned that you would not prefer to replace the broadcast IPI
with the mask version for performance. Currently this seems to be the
most optimal way without putting a sledge hammer on the cpu_up process.

I already put a sledgehammer to __cpu_up with that last patch. Some more hammering surely wouldnt be a big issue. Unlike i386 we actually still have a chance to test all relevant platforms, so I dont think it is a big issue.

What changes did you plan?

P.S.: An alternative would be to define a new genapic subarch that you only enable when you detect cpuhotplug support at boot.

Again just commenting on the text, not patch sorry.

-Andi

author
#12 lore raw
Quoted 6 lines · Andi Kleen
Quoted 3 lines · Ashok Raj

Andi: You had mentioned that you would not prefer to replace the broadcast IPI
with the mask version for performance. Currently this seems to be the
most optimal way without putting a sledge hammer on the cpu_up process.

I already put a sledgehammer to __cpu_up with that last

Yours was a good sledge hammer :-) the way it should have been done but carried legacy boot from i386 that wasnt pretty. The one iam referring to is pretty darn slow, and think it wont be liked my many to slow down the system just to add a new cpu.

Quoted 5 lines

patch. Some more hammering surely wouldnt be a big issue. Unlike i386 we actually still have a chance to test all relevant platforms, so I dont think it is a big issue.

What changes did you plan?

The only other workable alternate would be to use the stop_machine() like thing which we use to automically update cpu_online_map. This means we execute a high priority thread on all cpus, bringing the system to knees before just adding a new cpu. On very large systems this will definitly be visible.

Just curious, what performance impact did you allude to that would be lost if we dont use the shortcut IPI version?

Quoted 4 lines

P.S.: An alternative would be to define a new genapic subarch that you only enable when you detect cpuhotplug support at boot.

There is nothing currently there to find out if something is hotplug capable in a generic way at platform level, other than adding cmdline options etc.

Also FYI: ACPI folks are experimenting using CPU hotplug to suspend/resume support. So hotplug support may be required not just on platforms that support it but also for other related uses.

Cheers,
Ashok Raj
- Open Source Technology Center

#13 lore raw
Quoted 17 lines · Ashok Raj
Quoted 6 lines · Andi Kleen
Quoted 3 lines · Ashok Raj

Andi: You had mentioned that you would not prefer to replace the broadcast IPI
with the mask version for performance. Currently this seems to be the
most optimal way without putting a sledge hammer on the cpu_up process.

I already put a sledgehammer to __cpu_up with that last

Yours was a good sledge hammer :-) the way it should have been done but carried legacy boot from i386 that wasnt pretty. The one iam referring to is pretty darn slow, and think it wont be liked my many to slow down the system just to add a new cpu.

Quoted 4 lines

patch. Some more hammering surely wouldnt be a big issue. Unlike i386 we actually still have a chance to test all relevant platforms, so I dont think it is a big issue.

Quoted 5 lines
Quoted 1 line

What changes did you plan?

The only other workable alternate would be to use the stop_machine() like thing which we use to automically update cpu_online_map. This means we execute a high priority thread on all cpus, bringing the system to knees before

That is not nice agreed.

Quoted 2 lines

just adding a new cpu. On very large systems this will definitly be visible.

I still dont quite get it why it is not enough to keep interrupts off until the CPU enters idle. Currently we enable them shortly in the middle of the initialization (whcih is already dangerous because interrupts can see half initialized state like out of date TSC), but I hope to get rid of that soon too. With the full startup in CLI would you problems be gone?

Quoted 3 lines

Just curious, what performance impact did you allude to that would be lost if we dont use the shortcut IPI version?

I am worried about the TLB flush interrupt. I used to have some workloads in 2.4 that stresses it very badly (e.g. process with working set just above the physical memory. It would always fault in new pages while on another CPU kswapd would unmap and age pages. Leads to a constant flood of flush IPIs). Another case is COW in a multithreaded process. You always have to flush all the other CPUs there.

Even smp_call_function is a bit of an issue in slab intensive loads because the per CPU slab cache relies on them. I dont think it is that big an issue as the flush above, but still would be better to keep it fast.

Quoted 7 lines
Quoted 3 lines

P.S.: An alternative would be to define a new genapic subarch that you only enable when you detect cpuhotplug support at boot.

There is nothing currently there to find out if something is hotplug capable in a generic way at platform level, other than adding cmdline options etc.

When you have the command line option you can do it. Later I guess you will have a way to get it from ACPI (e.g. CPUs in tables but marked inactive etc.).

Quoted 3 lines

Also FYI: ACPI folks are experimenting using CPU hotplug to suspend/resume support. So hotplug support may be required not just on platforms that support it but also for other related uses.

I am aware of that. Also the virtualization people will likely use it.

-Andi

author
#14 lore raw
Quoted 9 lines · Andi Kleen
Quoted 3 lines

The only other workable alternate would be to use the stop_machine() like thing which we use to automically update cpu_online_map. This means we execute a high priority thread on all cpus, bringing the system to knees before

That is not nice agreed.

Quoted 2 lines

just adding a new cpu. On very large systems this will definitly be visible.

Quoted 7 lines

I still dont quite get it why it is not enough to keep interrupts off until the CPU enters idle. Currently we enable them shortly in the middle of the initialization (whcih is already dangerous because interrupts can see half initialized state like out of date TSC), but I hope to get rid of that soon too. With the full startup in CLI would you problems be gone?

I think so, if we can ensure none is delivered to the partially up cpu we probably are covered.

Iam not a 100% sure about above either, if the smp_call_function is started with 3 cpus initially, and 1 just came up, the counts in the smp_call data struct could be set to 3 as a result of the new cpu received this broadcast as well, and we might quit earlier in the wait.

sending to only relevant cpus removes that ambiquity.

[Vatsa would know this better, since was the corner case man then :-)]

#15 lore raw
Quoted 4 lines · Ashok Raj

Iam not a 100% sure about above either, if the smp_call_function is started with 3 cpus initially, and 1 just came up, the counts in the smp_call data struct could be set to 3 as a result of the new cpu received this broadcast as well, and we might quit earlier in the wait.

True.

Quoted 1 line

sending to only relevant cpus removes that ambiquity.

Or grab the 'call_lock' before setting the upcoming cpu in the online_map. This should also avoid the race when a CPU is coming online.

Signature · 5 lines
Thanks and Regards,
Srivatsa Vaddagiri,
Linux Technology Center,
IBM Software Labs,
Bangalore, INDIA - 560017
author
#16 lore raw
Quoted 7 lines · Srivatsa Vaddagiri
Quoted 4 lines · Ashok Raj

Iam not a 100% sure about above either, if the smp_call_function is started with 3 cpus initially, and 1 just came up, the counts in the smp_call data struct could be set to 3 as a result of the new cpu received this broadcast as well, and we might quit earlier in the wait.

True.

Thanks for confirming.

I think the window would be a little bit more narrow, but not completely closed even if we bring cpu in cli state completely.

Quoted 6 lines
Quoted 2 lines

sending to only relevant cpus removes that ambiquity.

Or grab the 'call_lock' before setting the upcoming cpu in the online_map. This should also avoid the race when a CPU is coming online.

We do this today in x86_64 case when we setup this upcomming cpu in cpu_online_map. But the issue is when we use ipi broadcast, its an ugly case when we dont know if the new cpu is receiving this as well, and we dont have real control there.

i converted to use send_IPI_mask(cpu_online_map, CALL_FUNCTION_VECTOR) instead of the send_IPI_allbutself(CALL_FUNCTION_VECTOR) in __smp_call_function(), apart from taking the call_lock before setting the bit in online_map.

Since Andi is concerned about tlb flush intr performance in the 8cpu and less case, iam planning temporarily use a startup cmd or choose this option automatically if CONFIG_HOTPLUG_CPU is set for the time being, until we can find a clean solution that satisfies everyone.

Cheers,
Ashok Raj

#17 lore raw
Quoted 2 lines · Ashok Raj

We do this today in x86_64 case when we setup this upcomming cpu in cpu_online_map. But the issue is when we use ipi broadcast, its an ugly

I don't know of x86-64, but atleast on x86 ipi broadcast will send to _all_ CPUs present, right? I mean the h/w does not know of the offline CPUs and will send to them also. This could lead to a problem for the offline CPUs when they come online and can take a spurious IPI (unless there is support in h/w to clear pending IPI before doing STI).

Quoted 8 lines

case when we dont know if the new cpu is receiving this as well, and we dont have real control there.

i converted to use send_IPI_mask(cpu_online_map, CALL_FUNCTION_VECTOR) instead of the send_IPI_allbutself(CALL_FUNCTION_VECTOR) in __smp_call_function(), apart from taking the call_lock before setting the bit in online_map.

Quoted 4 lines

Since Andi is concerned about tlb flush intr performance in the 8cpu and less case, iam planning temporarily use a startup cmd or choose this option automatically if CONFIG_HOTPLUG_CPU is set for the time being, until we can find a clean solution that satisfies everyone.

I think this should be the recommended approach (using send_IPI_mask if CONFIG_HOTPLUG_CPU is defined) unless h/w is intelligent enough that it avoids sending IPIs to offline CPUs or it supports clearing pending interrupts.

Signature · 5 lines
Thanks and Regards,
Srivatsa Vaddagiri,
Linux Technology Center,
IBM Software Labs,
Bangalore, INDIA - 560017
#18 lore raw
Quoted 9 lines · Srivatsa Vaddagiri
Quoted 2 lines · Ashok Raj

We do this today in x86_64 case when we setup this upcomming cpu in cpu_online_map. But the issue is when we use ipi broadcast, its an ugly

I don't know of x86-64, but atleast on x86 ipi broadcast will send to _all_ CPUs present, right? I mean the h/w does not know of the offline CPUs and will send to them also. This could lead to a problem for the offline CPUs when they come online and can take a spurious IPI (unless there is support in h/w to clear pending IPI before doing STI).

x86-64 works the same here.

The hardware does not clear pending IPIs AFAIK, but software could do that manually during cpu bootup. Races can be avoided by taking call_lock and the tlb flush lock while doing that.

-Andi

#19 lore raw
In reply to Srivatsa Vaddagiri
Quoted 4 lines
Quoted 1 line

sending to only relevant cpus removes that ambiquity.

Or grab the 'call_lock' before setting the upcoming cpu in the online_map. This should also avoid the race when a CPU is coming online.

Such a simple solution would be great.

-Andi

#20 lore raw
In reply to Ashok Raj
Quoted 18 lines · Ashok Raj
Quoted 9 lines · Andi Kleen
Quoted 3 lines

The only other workable alternate would be to use the stop_machine() like thing which we use to automically update cpu_online_map. This means we execute a high priority thread on all cpus, bringing the system to knees before

That is not nice agreed.

Quoted 2 lines

just adding a new cpu. On very large systems this will definitly be visible.

Quoted 7 lines

I still dont quite get it why it is not enough to keep interrupts off until the CPU enters idle. Currently we enable them shortly in the middle of the initialization (whcih is already dangerous because interrupts can see half initialized state like out of date TSC), but I hope to get rid of that soon too. With the full startup in CLI would you problems be gone?

Quoted 2 lines

I think so, if we can ensure none is delivered to the partially up cpu we probably are covered.

You mean not delivered to its APIC or not delivered as an visible interrupt in the instruction stream?

The later can be ensured, the first not. I guess if the first is a problem you could add a function to ack all pending interrupts after initial sti.

e.g. we can assume the CPU will deliver everything pending after two instruction after the sti and when there are interrupts left in the APIC you can ack them. But why would they not be raised as real interruptions at this point anyways?

Quoted 4 lines

Iam not a 100% sure about above either, if the smp_call_function is started with 3 cpus initially, and 1 just came up, the counts in the smp_call data struct could be set to 3 as a result of the new cpu received this broadcast as well, and we might quit earlier in the wait.

In the worst case a smp_call_function would be delayed for the whole boot up time of a new CPU which should be quite bounded. The longest delay in there is probably the bogomips calibrate, but I believe Venkatesh recently sped that up greatly anyways so it should not be an issue anymore. If the delay is < 1s that is probably tolerable.

Or do I miss some shade of the problem you are worried about?

-Andi

#21 lore raw
Quoted 6 lines · Andi Kleen
Quoted 2 lines · Ashok Raj

I think so, if we can ensure none is delivered to the partially up cpu we probably are covered.

You mean not delivered to its APIC or not delivered as an visible interrupt in the instruction stream?

I think Ashok is referring to first.

Quoted 3 lines

The later can be ensured, the first not. I guess if the first is a problem you could add a function to ack all pending interrupts after initial sti.

I dont think it is that simple.

First, do we handle the pending interrupt as normal interrupts? For ex: in case of call function interrupt, do we read the call_data_struct, which may be stale if we are handling a stale IPI (IPI that was sent to us but caller didnt wait for us to read the call_data_struct)?

Ideally we have to drop (not handle) such stale interrupts. Which means add a flag in the interrupt handler. If flag is set, interrupt is dropped otherwise it is handled. This flag may be set by the upcoming CPU before doing STI. It may be cleared by it after a brief time after STI. And this may affect the fast path.

Alternately we may register dummy handlers for various IRQs, sti, wait for pending IRQs to be flushed and then register the real handlers (and _then_ set the CPU in the online map?)

Secondly, I think there is another subtle race if we broadcast IPIs. Consider this:

CPU0 CPU1

Puts itself Offline

smp_call_function

broadcasts IPI
IPI queued on CPU1

Wants to come online now

spin_lock(call_lock)

spin_lock(call_lock) cpu_set(cpu1, online_map);

spin_unlock(call_lock);

broadcast IPI
wait_for_all_cpus
(including CPU1)
sti();

gets IPI, but decides to not handle it since it may be a stale IPI

This causes CPU0 to wait forever? Not sure if I have missed some h/w subtelity here.

This problem could probably be prevented if cpu1 is set in the online_map after the sti.

Quoted 12 lines

e.g. we can assume the CPU will deliver everything pending after two instruction after the sti and when there are interrupts left in the APIC you can ack them. But why would they not be raised as real interruptions at this point anyways?

Quoted 4 lines

Iam not a 100% sure about above either, if the smp_call_function is started with 3 cpus initially, and 1 just came up, the counts in the smp_call data struct could be set to 3 as a result of the new cpu received this broadcast as well, and we might quit earlier in the wait.

Quoted 7 lines

In the worst case a smp_call_function would be delayed for the whole boot up time of a new CPU which should be quite bounded. The longest delay in there is probably the bogomips calibrate, but I believe Venkatesh recently sped that up greatly anyways so it should not be an issue anymore. If the delay is < 1s that is probably tolerable.

Or do I miss some shade of the problem you are worried about?

The problem is at the time smp_call_function was running on CPU0 say, only CPU1 and CPU2 were online. So it decides that it has to wait for 2 CPUs to ack the IPI. Next it broadcasts the IPI. At this particular point, CPU3 is coming up and is yet to do a STI. Meanwhile, CPU1 and CPU2 ACK CPU0's call_function IPI. Hence smp_call_function on CPU0 decides to return, which makes call_data_struct stale (since it was on the stack). After this CPU3 does a STI and gets now the call_function IPI. If it were to handle this IPI normally, it will read the stale call_data_struct and may crash. Ideally, CPU3 should have dropped that stale IPI.

I think doing a send_IPI_mask is a safe solution for this problem, unless h/w avoids broadcasting IPIs to offline CPUs.

Signature · 5 lines
Thanks and Regards,
Srivatsa Vaddagiri,
Linux Technology Center,
IBM Software Labs,
Bangalore, INDIA - 560017