Commit b8d8b883 authored by Ashok Raj's avatar Ashok Raj Committed by Tony Luck

[IA64] cpu hotplug: return offlined cpus to SAL

This patch is required to support cpu removal for IPF systems. Existing code
just fakes the real offline by keeping it run the idle thread, and polling
for the bit to re-appear in the cpu_state to get out of the idle loop.

For the cpu-offline to work correctly, we need to pass control of this CPU 
back to SAL so it can continue in the boot-rendez mode. This gives the
SAL control to not pick this cpu as the monarch processor for global MCA
events, and addition does not wait for this cpu to checkin with SAL
for global MCA events as well. The handoff is implemented as documented in 
SAL specification section 3.2.5.1 "OS_BOOT_RENDEZ to SAL return State"
Signed-off-by: default avatarAshok Raj <ashok.raj@intel.com>
Signed-off-by: default avatarTony Luck <tony.luck@intel.com>
parent 71306671
This diff is collapsed.
...@@ -110,46 +110,19 @@ ...@@ -110,46 +110,19 @@
.global ia64_os_mca_dispatch_end .global ia64_os_mca_dispatch_end
.global ia64_sal_to_os_handoff_state .global ia64_sal_to_os_handoff_state
.global ia64_os_to_sal_handoff_state .global ia64_os_to_sal_handoff_state
.global ia64_do_tlb_purge
.text .text
.align 16 .align 16
ia64_os_mca_dispatch: /*
* Just the TLB purge part is moved to a separate function
// Serialize all MCA processing * so we can re-use the code for cpu hotplug code as well
mov r3=1;; * Caller should now setup b1, so we can branch once the
LOAD_PHYSICAL(p0,r2,ia64_mca_serialize);; * tlb flush is complete.
ia64_os_mca_spin: */
xchg8 r4=[r2],r3;;
cmp.ne p6,p0=r4,r0
(p6) br ia64_os_mca_spin
// Save the SAL to OS MCA handoff state as defined
// by SAL SPEC 3.0
// NOTE : The order in which the state gets saved
// is dependent on the way the C-structure
// for ia64_mca_sal_to_os_state_t has been
// defined in include/asm/mca.h
SAL_TO_OS_MCA_HANDOFF_STATE_SAVE(r2)
;;
// LOG PROCESSOR STATE INFO FROM HERE ON..
begin_os_mca_dump:
br ia64_os_mca_proc_state_dump;;
ia64_os_mca_done_dump:
LOAD_PHYSICAL(p0,r16,ia64_sal_to_os_handoff_state+56)
;;
ld8 r18=[r16] // Get processor state parameter on existing PALE_CHECK.
;;
tbit.nz p6,p7=r18,60
(p7) br.spnt done_tlb_purge_and_reload
// The following code purges TC and TR entries. Then reload all TC entries.
// Purge percpu data TC entries.
begin_tlb_purge_and_reload:
ia64_do_tlb_purge:
#define O(member) IA64_CPUINFO_##member##_OFFSET #define O(member) IA64_CPUINFO_##member##_OFFSET
GET_THIS_PADDR(r2, cpu_info) // load phys addr of cpu_info into r2 GET_THIS_PADDR(r2, cpu_info) // load phys addr of cpu_info into r2
...@@ -230,6 +203,51 @@ begin_tlb_purge_and_reload: ...@@ -230,6 +203,51 @@ begin_tlb_purge_and_reload:
;; ;;
srlz.i srlz.i
;; ;;
// Now branch away to caller.
br.sptk.many b1
;;
ia64_os_mca_dispatch:
// Serialize all MCA processing
mov r3=1;;
LOAD_PHYSICAL(p0,r2,ia64_mca_serialize);;
ia64_os_mca_spin:
xchg8 r4=[r2],r3;;
cmp.ne p6,p0=r4,r0
(p6) br ia64_os_mca_spin
// Save the SAL to OS MCA handoff state as defined
// by SAL SPEC 3.0
// NOTE : The order in which the state gets saved
// is dependent on the way the C-structure
// for ia64_mca_sal_to_os_state_t has been
// defined in include/asm/mca.h
SAL_TO_OS_MCA_HANDOFF_STATE_SAVE(r2)
;;
// LOG PROCESSOR STATE INFO FROM HERE ON..
begin_os_mca_dump:
br ia64_os_mca_proc_state_dump;;
ia64_os_mca_done_dump:
LOAD_PHYSICAL(p0,r16,ia64_sal_to_os_handoff_state+56)
;;
ld8 r18=[r16] // Get processor state parameter on existing PALE_CHECK.
;;
tbit.nz p6,p7=r18,60
(p7) br.spnt done_tlb_purge_and_reload
// The following code purges TC and TR entries. Then reload all TC entries.
// Purge percpu data TC entries.
begin_tlb_purge_and_reload:
movl r18=ia64_reload_tr;;
LOAD_PHYSICAL(p0,r18,ia64_reload_tr);;
mov b1=r18;;
br.sptk.many ia64_do_tlb_purge;;
ia64_reload_tr:
// Finally reload the TR registers. // Finally reload the TR registers.
// 1. Reload DTR/ITR registers for kernel. // 1. Reload DTR/ITR registers for kernel.
mov r18=KERNEL_TR_PAGE_SHIFT<<2 mov r18=KERNEL_TR_PAGE_SHIFT<<2
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* *
* Copyright (C) 1998-2003 Hewlett-Packard Co * Copyright (C) 1998-2003 Hewlett-Packard Co
* David Mosberger-Tang <davidm@hpl.hp.com> * David Mosberger-Tang <davidm@hpl.hp.com>
* 04/11/17 Ashok Raj <ashok.raj@intel.com> Added CPU Hotplug Support
*/ */
#define __KERNEL_SYSCALLS__ /* see <asm/unistd.h> */ #define __KERNEL_SYSCALLS__ /* see <asm/unistd.h> */
#include <linux/config.h> #include <linux/config.h>
...@@ -200,27 +201,20 @@ default_idle (void) ...@@ -200,27 +201,20 @@ default_idle (void)
static inline void play_dead(void) static inline void play_dead(void)
{ {
extern void ia64_cpu_local_tick (void); extern void ia64_cpu_local_tick (void);
unsigned int this_cpu = smp_processor_id();
/* Ack it */ /* Ack it */
__get_cpu_var(cpu_state) = CPU_DEAD; __get_cpu_var(cpu_state) = CPU_DEAD;
/* We shouldn't have to disable interrupts while dead, but
* some interrupts just don't seem to go away, and this makes
* it "work" for testing purposes. */
max_xtp(); max_xtp();
local_irq_disable(); local_irq_disable();
/* Death loop */ idle_task_exit();
while (__get_cpu_var(cpu_state) != CPU_UP_PREPARE) ia64_jump_to_sal(&sal_boot_rendez_state[this_cpu]);
cpu_relax();
/* /*
* Enable timer interrupts from now on * The above is a point of no-return, the processor is
* Not required if we put processor in SAL_BOOT_RENDEZ mode. * expected to be in SAL loop now.
*/ */
local_flush_tlb_all(); BUG();
cpu_set(smp_processor_id(), cpu_online_map);
wmb();
ia64_cpu_local_tick ();
local_irq_enable();
} }
#else #else
static inline void play_dead(void) static inline void play_dead(void)
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
* 02/07/31 David Mosberger <davidm@hpl.hp.com> Switch over to hotplug-CPU boot-sequence. * 02/07/31 David Mosberger <davidm@hpl.hp.com> Switch over to hotplug-CPU boot-sequence.
* smp_boot_cpus()/smp_commence() is replaced by * smp_boot_cpus()/smp_commence() is replaced by
* smp_prepare_cpus()/__cpu_up()/smp_cpus_done(). * smp_prepare_cpus()/__cpu_up()/smp_cpus_done().
* 04/06/21 Ashok Raj <ashok.raj@intel.com> Added CPU Hotplug Support
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -58,6 +59,37 @@ ...@@ -58,6 +59,37 @@
#define Dprintk(x...) #define Dprintk(x...)
#endif #endif
#ifdef CONFIG_HOTPLUG_CPU
/*
* Store all idle threads, this can be reused instead of creating
* a new thread. Also avoids complicated thread destroy functionality
* for idle threads.
*/
struct task_struct *idle_thread_array[NR_CPUS];
/*
* Global array allocated for NR_CPUS at boot time
*/
struct sal_to_os_boot sal_boot_rendez_state[NR_CPUS];
/*
* start_ap in head.S uses this to store current booting cpu
* info.
*/
struct sal_to_os_boot *sal_state_for_booting_cpu = &sal_boot_rendez_state[0];
#define set_brendez_area(x) (sal_state_for_booting_cpu = &sal_boot_rendez_state[(x)]);
#define get_idle_for_cpu(x) (idle_thread_array[(x)])
#define set_idle_for_cpu(x,p) (idle_thread_array[(x)] = (p))
#else
#define get_idle_for_cpu(x) (NULL)
#define set_idle_for_cpu(x,p)
#define set_brendez_area(x)
#endif
/* /*
* ITC synchronization related stuff: * ITC synchronization related stuff:
...@@ -345,7 +377,6 @@ start_secondary (void *unused) ...@@ -345,7 +377,6 @@ start_secondary (void *unused)
{ {
/* Early console may use I/O ports */ /* Early console may use I/O ports */
ia64_set_kr(IA64_KR_IO_BASE, __pa(ia64_iobase)); ia64_set_kr(IA64_KR_IO_BASE, __pa(ia64_iobase));
Dprintk("start_secondary: starting CPU 0x%x\n", hard_smp_processor_id()); Dprintk("start_secondary: starting CPU 0x%x\n", hard_smp_processor_id());
efi_map_pal_code(); efi_map_pal_code();
cpu_init(); cpu_init();
...@@ -384,6 +415,13 @@ do_boot_cpu (int sapicid, int cpu) ...@@ -384,6 +415,13 @@ do_boot_cpu (int sapicid, int cpu)
.done = COMPLETION_INITIALIZER(c_idle.done), .done = COMPLETION_INITIALIZER(c_idle.done),
}; };
DECLARE_WORK(work, do_fork_idle, &c_idle); DECLARE_WORK(work, do_fork_idle, &c_idle);
c_idle.idle = get_idle_for_cpu(cpu);
if (c_idle.idle) {
init_idle(c_idle.idle, cpu);
goto do_rest;
}
/* /*
* We can't use kernel_thread since we must avoid to reschedule the child. * We can't use kernel_thread since we must avoid to reschedule the child.
*/ */
...@@ -396,10 +434,15 @@ do_boot_cpu (int sapicid, int cpu) ...@@ -396,10 +434,15 @@ do_boot_cpu (int sapicid, int cpu)
if (IS_ERR(c_idle.idle)) if (IS_ERR(c_idle.idle))
panic("failed fork for CPU %d", cpu); panic("failed fork for CPU %d", cpu);
set_idle_for_cpu(cpu, c_idle.idle);
do_rest:
task_for_booting_cpu = c_idle.idle; task_for_booting_cpu = c_idle.idle;
Dprintk("Sending wakeup vector %lu to AP 0x%x/0x%x.\n", ap_wakeup_vector, cpu, sapicid); Dprintk("Sending wakeup vector %lu to AP 0x%x/0x%x.\n", ap_wakeup_vector, cpu, sapicid);
set_brendez_area(cpu);
platform_send_ipi(cpu, ap_wakeup_vector, IA64_IPI_DM_INT, 0); platform_send_ipi(cpu, ap_wakeup_vector, IA64_IPI_DM_INT, 0);
/* /*
...@@ -555,16 +598,6 @@ void __devinit smp_prepare_boot_cpu(void) ...@@ -555,16 +598,6 @@ void __devinit smp_prepare_boot_cpu(void)
#ifdef CONFIG_HOTPLUG_CPU #ifdef CONFIG_HOTPLUG_CPU
extern void fixup_irqs(void); extern void fixup_irqs(void);
/* must be called with cpucontrol mutex held */ /* must be called with cpucontrol mutex held */
static int __devinit cpu_enable(unsigned int cpu)
{
per_cpu(cpu_state,cpu) = CPU_UP_PREPARE;
wmb();
while (!cpu_online(cpu))
cpu_relax();
return 0;
}
int __cpu_disable(void) int __cpu_disable(void)
{ {
int cpu = smp_processor_id(); int cpu = smp_processor_id();
...@@ -577,7 +610,7 @@ int __cpu_disable(void) ...@@ -577,7 +610,7 @@ int __cpu_disable(void)
fixup_irqs(); fixup_irqs();
local_flush_tlb_all(); local_flush_tlb_all();
printk ("Disabled cpu %u\n", smp_processor_id()); cpu_clear(cpu, cpu_callin_map);
return 0; return 0;
} }
...@@ -589,12 +622,7 @@ void __cpu_die(unsigned int cpu) ...@@ -589,12 +622,7 @@ void __cpu_die(unsigned int cpu)
/* They ack this in play_dead by setting CPU_DEAD */ /* They ack this in play_dead by setting CPU_DEAD */
if (per_cpu(cpu_state, cpu) == CPU_DEAD) if (per_cpu(cpu_state, cpu) == CPU_DEAD)
{ {
/* printk ("CPU %d is now offline\n", cpu);
* TBD: Enable this when physical removal
* or when we put the processor is put in
* SAL_BOOT_RENDEZ mode
* cpu_clear(cpu, cpu_callin_map);
*/
return; return;
} }
msleep(100); msleep(100);
...@@ -602,11 +630,6 @@ void __cpu_die(unsigned int cpu) ...@@ -602,11 +630,6 @@ void __cpu_die(unsigned int cpu)
printk(KERN_ERR "CPU %u didn't die...\n", cpu); printk(KERN_ERR "CPU %u didn't die...\n", cpu);
} }
#else /* !CONFIG_HOTPLUG_CPU */ #else /* !CONFIG_HOTPLUG_CPU */
static int __devinit cpu_enable(unsigned int cpu)
{
return 0;
}
int __cpu_disable(void) int __cpu_disable(void)
{ {
return -ENOSYS; return -ENOSYS;
...@@ -648,16 +671,12 @@ __cpu_up (unsigned int cpu) ...@@ -648,16 +671,12 @@ __cpu_up (unsigned int cpu)
return -EINVAL; return -EINVAL;
/* /*
* Already booted.. just enable and get outa idle lool * Already booted cpu? not valid anymore since we dont
* do idle loop tightspin anymore.
*/ */
if (cpu_isset(cpu, cpu_callin_map)) if (cpu_isset(cpu, cpu_callin_map))
{ return -EINVAL;
cpu_enable(cpu);
local_irq_enable();
while (!cpu_isset(cpu, cpu_online_map))
mb();
return 0;
}
/* Processor goes to start_secondary(), sets online flag */ /* Processor goes to start_secondary(), sets online flag */
ret = do_boot_cpu(sapicid, cpu); ret = do_boot_cpu(sapicid, cpu);
if (ret < 0) if (ret < 0)
......
...@@ -832,6 +832,44 @@ extern int ia64_sal_oemcall_nolock(struct ia64_sal_retval *, u64, u64, u64, ...@@ -832,6 +832,44 @@ extern int ia64_sal_oemcall_nolock(struct ia64_sal_retval *, u64, u64, u64,
u64, u64, u64, u64, u64); u64, u64, u64, u64, u64);
extern int ia64_sal_oemcall_reentrant(struct ia64_sal_retval *, u64, u64, u64, extern int ia64_sal_oemcall_reentrant(struct ia64_sal_retval *, u64, u64, u64,
u64, u64, u64, u64, u64); u64, u64, u64, u64, u64);
#ifdef CONFIG_HOTPLUG_CPU
/*
* System Abstraction Layer Specification
* Section 3.2.5.1: OS_BOOT_RENDEZ to SAL return State.
* Note: region regs are stored first in head.S _start. Hence they must
* stay up front.
*/
struct sal_to_os_boot {
u64 rr[8]; /* Region Registers */
u64 br[6]; /* br0: return addr into SAL boot rendez routine */
u64 gr1; /* SAL:GP */
u64 gr12; /* SAL:SP */
u64 gr13; /* SAL: Task Pointer */
u64 fpsr;
u64 pfs;
u64 rnat;
u64 unat;
u64 bspstore;
u64 dcr; /* Default Control Register */
u64 iva;
u64 pta;
u64 itv;
u64 pmv;
u64 cmcv;
u64 lrr[2];
u64 gr[4];
u64 pr; /* Predicate registers */
u64 lc; /* Loop Count */
struct ia64_fpreg fp[20];
};
/*
* Global array allocated for NR_CPUS at boot time
*/
extern struct sal_to_os_boot sal_boot_rendez_state[NR_CPUS];
extern void ia64_jump_to_sal(struct sal_to_os_boot *);
#endif
extern void ia64_sal_handler_init(void *entry_point, void *gpval); extern void ia64_sal_handler_init(void *entry_point, void *gpval);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment