2005-04-16 16:20:36 -06:00
|
|
|
/* smp.c: Sparc64 SMP support.
|
|
|
|
*
|
2008-03-26 02:11:55 -06:00
|
|
|
* Copyright (C) 1997, 2007, 2008 David S. Miller (davem@davemloft.net)
|
2005-04-16 16:20:36 -06:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/pagemap.h>
|
|
|
|
#include <linux/threads.h>
|
|
|
|
#include <linux/smp.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/kernel_stat.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/spinlock.h>
|
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/seq_file.h>
|
|
|
|
#include <linux/cache.h>
|
|
|
|
#include <linux/jiffies.h>
|
|
|
|
#include <linux/profile.h>
|
2009-04-01 17:15:20 -06:00
|
|
|
#include <linux/bootmem.h>
|
2009-04-08 21:32:02 -06:00
|
|
|
#include <linux/vmalloc.h>
|
2008-10-12 21:55:24 -06:00
|
|
|
#include <linux/cpu.h>
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
#include <asm/head.h>
|
|
|
|
#include <asm/ptrace.h>
|
|
|
|
#include <asm/atomic.h>
|
|
|
|
#include <asm/tlbflush.h>
|
|
|
|
#include <asm/mmu_context.h>
|
|
|
|
#include <asm/cpudata.h>
|
2007-07-14 01:58:53 -06:00
|
|
|
#include <asm/hvtramp.h>
|
|
|
|
#include <asm/io.h>
|
2008-03-26 02:11:55 -06:00
|
|
|
#include <asm/timer.h>
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
#include <asm/irq.h>
|
2006-10-08 06:23:28 -06:00
|
|
|
#include <asm/irq_regs.h>
|
2005-04-16 16:20:36 -06:00
|
|
|
#include <asm/page.h>
|
|
|
|
#include <asm/pgtable.h>
|
|
|
|
#include <asm/oplib.h>
|
|
|
|
#include <asm/uaccess.h>
|
|
|
|
#include <asm/starfire.h>
|
|
|
|
#include <asm/tlb.h>
|
2006-02-27 00:24:22 -07:00
|
|
|
#include <asm/sections.h>
|
2006-06-22 00:34:02 -06:00
|
|
|
#include <asm/prom.h>
|
2007-05-25 16:49:59 -06:00
|
|
|
#include <asm/mdesc.h>
|
[SPARC64]: Initial LDOM cpu hotplug support.
Only adding cpus is supports at the moment, removal
will come next.
When new cpus are configured, the machine description is
updated. When we get the configure request we pass in a
cpu mask of to-be-added cpus to the mdesc CPU node parser
so it only fetches information for those cpus. That code
also proceeds to update the SMT/multi-core scheduling bitmaps.
cpu_up() does all the work and we return the status back
over the DS channel.
CPUs via dr-cpu need to be booted straight out of the
hypervisor, and this requires:
1) A new trampoline mechanism. CPUs are booted straight
out of the hypervisor with MMU disabled and running in
physical addresses with no mappings installed in the TLB.
The new hvtramp.S code sets up the critical cpu state,
installs the locked TLB mappings for the kernel, and
turns the MMU on. It then proceeds to follow the logic
of the existing trampoline.S SMP cpu bringup code.
2) All calls into OBP have to be disallowed when domaining
is enabled. Since cpus boot straight into the kernel from
the hypervisor, OBP has no state about that cpu and therefore
cannot handle being invoked on that cpu.
Luckily it's only a handful of interfaces which can be called
after the OBP device tree is obtained. For example, rebooting,
halting, powering-off, and setting options node variables.
CPU removal support will require some infrastructure changes
here. Namely we'll have to process the requests via a true
kernel thread instead of in a workqueue. workqueues run on
a per-cpu thread, but when unconfiguring we might need to
force the thread to execute on another cpu if the current cpu
is the one being removed. Removal of a cpu also causes the kernel
to destroy that cpu's workqueue running thread.
Another issue on removal is that we may have interrupts still
pointing to the cpu-to-be-removed. So new code will be needed
to walk the active INO list and retarget those cpus as-needed.
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-07-13 17:03:42 -06:00
|
|
|
#include <asm/ldc.h>
|
2007-07-16 04:49:40 -06:00
|
|
|
#include <asm/hypervisor.h>
|
2005-04-16 16:20:36 -06:00
|
|
|
|
2009-06-04 03:10:11 -06:00
|
|
|
#include "cpumap.h"
|
|
|
|
|
2007-06-04 22:48:33 -06:00
|
|
|
int sparc64_multi_core __read_mostly;
|
|
|
|
|
2007-10-16 02:24:05 -06:00
|
|
|
DEFINE_PER_CPU(cpumask_t, cpu_sibling_map) = CPU_MASK_NONE;
|
2007-06-04 18:01:39 -06:00
|
|
|
cpumask_t cpu_core_map[NR_CPUS] __read_mostly =
|
|
|
|
{ [0 ... NR_CPUS-1] = CPU_MASK_NONE };
|
[SPARC64]: Initial LDOM cpu hotplug support.
Only adding cpus is supports at the moment, removal
will come next.
When new cpus are configured, the machine description is
updated. When we get the configure request we pass in a
cpu mask of to-be-added cpus to the mdesc CPU node parser
so it only fetches information for those cpus. That code
also proceeds to update the SMT/multi-core scheduling bitmaps.
cpu_up() does all the work and we return the status back
over the DS channel.
CPUs via dr-cpu need to be booted straight out of the
hypervisor, and this requires:
1) A new trampoline mechanism. CPUs are booted straight
out of the hypervisor with MMU disabled and running in
physical addresses with no mappings installed in the TLB.
The new hvtramp.S code sets up the critical cpu state,
installs the locked TLB mappings for the kernel, and
turns the MMU on. It then proceeds to follow the logic
of the existing trampoline.S SMP cpu bringup code.
2) All calls into OBP have to be disallowed when domaining
is enabled. Since cpus boot straight into the kernel from
the hypervisor, OBP has no state about that cpu and therefore
cannot handle being invoked on that cpu.
Luckily it's only a handful of interfaces which can be called
after the OBP device tree is obtained. For example, rebooting,
halting, powering-off, and setting options node variables.
CPU removal support will require some infrastructure changes
here. Namely we'll have to process the requests via a true
kernel thread instead of in a workqueue. workqueues run on
a per-cpu thread, but when unconfiguring we might need to
force the thread to execute on another cpu if the current cpu
is the one being removed. Removal of a cpu also causes the kernel
to destroy that cpu's workqueue running thread.
Another issue on removal is that we may have interrupts still
pointing to the cpu-to-be-removed. So new code will be needed
to walk the active INO list and retarget those cpus as-needed.
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-07-13 17:03:42 -06:00
|
|
|
|
2007-10-16 02:24:05 -06:00
|
|
|
EXPORT_PER_CPU_SYMBOL(cpu_sibling_map);
|
[SPARC64]: Initial LDOM cpu hotplug support.
Only adding cpus is supports at the moment, removal
will come next.
When new cpus are configured, the machine description is
updated. When we get the configure request we pass in a
cpu mask of to-be-added cpus to the mdesc CPU node parser
so it only fetches information for those cpus. That code
also proceeds to update the SMT/multi-core scheduling bitmaps.
cpu_up() does all the work and we return the status back
over the DS channel.
CPUs via dr-cpu need to be booted straight out of the
hypervisor, and this requires:
1) A new trampoline mechanism. CPUs are booted straight
out of the hypervisor with MMU disabled and running in
physical addresses with no mappings installed in the TLB.
The new hvtramp.S code sets up the critical cpu state,
installs the locked TLB mappings for the kernel, and
turns the MMU on. It then proceeds to follow the logic
of the existing trampoline.S SMP cpu bringup code.
2) All calls into OBP have to be disallowed when domaining
is enabled. Since cpus boot straight into the kernel from
the hypervisor, OBP has no state about that cpu and therefore
cannot handle being invoked on that cpu.
Luckily it's only a handful of interfaces which can be called
after the OBP device tree is obtained. For example, rebooting,
halting, powering-off, and setting options node variables.
CPU removal support will require some infrastructure changes
here. Namely we'll have to process the requests via a true
kernel thread instead of in a workqueue. workqueues run on
a per-cpu thread, but when unconfiguring we might need to
force the thread to execute on another cpu if the current cpu
is the one being removed. Removal of a cpu also causes the kernel
to destroy that cpu's workqueue running thread.
Another issue on removal is that we may have interrupts still
pointing to the cpu-to-be-removed. So new code will be needed
to walk the active INO list and retarget those cpus as-needed.
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-07-13 17:03:42 -06:00
|
|
|
EXPORT_SYMBOL(cpu_core_map);
|
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
static cpumask_t smp_commenced_mask;
|
|
|
|
|
|
|
|
void smp_info(struct seq_file *m)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
seq_printf(m, "State:\n");
|
2006-03-23 04:01:05 -07:00
|
|
|
for_each_online_cpu(i)
|
|
|
|
seq_printf(m, "CPU%d:\t\tonline\n", i);
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
void smp_bogo(struct seq_file *m)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2006-03-23 04:01:05 -07:00
|
|
|
for_each_online_cpu(i)
|
|
|
|
seq_printf(m,
|
|
|
|
"Cpu%dClkTck\t: %016lx\n",
|
|
|
|
i, cpu_data(i).clock_tick);
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
|
2007-03-05 16:28:37 -07:00
|
|
|
extern void setup_sparc64_timer(void);
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
static volatile unsigned long callin_flag = 0;
|
|
|
|
|
2008-02-20 23:22:16 -07:00
|
|
|
void __cpuinit smp_callin(void)
|
2005-04-16 16:20:36 -06:00
|
|
|
{
|
|
|
|
int cpuid = hard_smp_processor_id();
|
|
|
|
|
2006-02-27 00:24:22 -07:00
|
|
|
__local_per_cpu_offset = __per_cpu_offset(cpuid);
|
2005-04-16 16:20:36 -06:00
|
|
|
|
2006-02-14 14:49:32 -07:00
|
|
|
if (tlb_type == hypervisor)
|
2006-02-11 15:41:18 -07:00
|
|
|
sun4v_ktsb_register();
|
2006-02-07 22:51:08 -07:00
|
|
|
|
2006-02-27 00:24:22 -07:00
|
|
|
__flush_tlb_all();
|
2005-04-16 16:20:36 -06:00
|
|
|
|
2007-03-05 16:28:37 -07:00
|
|
|
setup_sparc64_timer();
|
2005-04-16 16:20:36 -06:00
|
|
|
|
2005-05-23 16:52:08 -06:00
|
|
|
if (cheetah_pcache_forced_on)
|
|
|
|
cheetah_enable_pcache();
|
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
local_irq_enable();
|
|
|
|
|
|
|
|
callin_flag = 1;
|
|
|
|
__asm__ __volatile__("membar #Sync\n\t"
|
|
|
|
"flush %%g6" : : : "memory");
|
|
|
|
|
|
|
|
/* Clear this or we will die instantly when we
|
|
|
|
* schedule back to this idler...
|
|
|
|
*/
|
2005-07-24 20:36:26 -06:00
|
|
|
current_thread_info()->new_child = 0;
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
/* Attach to the address space of init_task. */
|
|
|
|
atomic_inc(&init_mm.mm_count);
|
|
|
|
current->active_mm = &init_mm;
|
|
|
|
|
2008-10-12 21:55:24 -06:00
|
|
|
/* inform the notifiers about the new cpu */
|
|
|
|
notify_cpu_starting(cpuid);
|
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
while (!cpu_isset(cpuid, smp_commenced_mask))
|
2005-08-29 13:46:22 -06:00
|
|
|
rmb();
|
2005-04-16 16:20:36 -06:00
|
|
|
|
2009-04-08 22:06:35 -06:00
|
|
|
ipi_call_lock_irq();
|
2005-04-16 16:20:36 -06:00
|
|
|
cpu_set(cpuid, cpu_online_map);
|
2009-04-08 22:06:35 -06:00
|
|
|
ipi_call_unlock_irq();
|
2005-11-08 22:39:01 -07:00
|
|
|
|
|
|
|
/* idle thread is expected to have preempt disabled */
|
|
|
|
preempt_disable();
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
void cpu_panic(void)
|
|
|
|
{
|
|
|
|
printk("CPU[%d]: Returns from cpu_idle!\n", smp_processor_id());
|
|
|
|
panic("SMP bolixed\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This tick register synchronization scheme is taken entirely from
|
|
|
|
* the ia64 port, see arch/ia64/kernel/smpboot.c for details and credit.
|
|
|
|
*
|
|
|
|
* The only change I've made is to rework it so that the master
|
|
|
|
* initiates the synchonization instead of the slave. -DaveM
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define MASTER 0
|
|
|
|
#define SLAVE (SMP_CACHE_BYTES/sizeof(unsigned long))
|
|
|
|
|
|
|
|
#define NUM_ROUNDS 64 /* magic value */
|
|
|
|
#define NUM_ITERS 5 /* likewise */
|
|
|
|
|
|
|
|
static DEFINE_SPINLOCK(itc_sync_lock);
|
|
|
|
static unsigned long go[SLAVE + 1];
|
|
|
|
|
|
|
|
#define DEBUG_TICK_SYNC 0
|
|
|
|
|
|
|
|
static inline long get_delta (long *rt, long *master)
|
|
|
|
{
|
|
|
|
unsigned long best_t0 = 0, best_t1 = ~0UL, best_tm = 0;
|
|
|
|
unsigned long tcenter, t0, t1, tm;
|
|
|
|
unsigned long i;
|
|
|
|
|
|
|
|
for (i = 0; i < NUM_ITERS; i++) {
|
|
|
|
t0 = tick_ops->get_tick();
|
|
|
|
go[MASTER] = 1;
|
2008-11-15 14:33:25 -07:00
|
|
|
membar_safe("#StoreLoad");
|
2005-04-16 16:20:36 -06:00
|
|
|
while (!(tm = go[SLAVE]))
|
2005-08-29 13:46:22 -06:00
|
|
|
rmb();
|
2005-04-16 16:20:36 -06:00
|
|
|
go[SLAVE] = 0;
|
2005-08-29 13:46:22 -06:00
|
|
|
wmb();
|
2005-04-16 16:20:36 -06:00
|
|
|
t1 = tick_ops->get_tick();
|
|
|
|
|
|
|
|
if (t1 - t0 < best_t1 - best_t0)
|
|
|
|
best_t0 = t0, best_t1 = t1, best_tm = tm;
|
|
|
|
}
|
|
|
|
|
|
|
|
*rt = best_t1 - best_t0;
|
|
|
|
*master = best_tm - best_t0;
|
|
|
|
|
|
|
|
/* average best_t0 and best_t1 without overflow: */
|
|
|
|
tcenter = (best_t0/2 + best_t1/2);
|
|
|
|
if (best_t0 % 2 + best_t1 % 2 == 2)
|
|
|
|
tcenter++;
|
|
|
|
return tcenter - best_tm;
|
|
|
|
}
|
|
|
|
|
|
|
|
void smp_synchronize_tick_client(void)
|
|
|
|
{
|
|
|
|
long i, delta, adj, adjust_latency = 0, done = 0;
|
|
|
|
unsigned long flags, rt, master_time_stamp, bound;
|
|
|
|
#if DEBUG_TICK_SYNC
|
|
|
|
struct {
|
|
|
|
long rt; /* roundtrip time */
|
|
|
|
long master; /* master's timestamp */
|
|
|
|
long diff; /* difference between midpoint and master's timestamp */
|
|
|
|
long lat; /* estimate of itc adjustment latency */
|
|
|
|
} t[NUM_ROUNDS];
|
|
|
|
#endif
|
|
|
|
|
|
|
|
go[MASTER] = 1;
|
|
|
|
|
|
|
|
while (go[MASTER])
|
2005-08-29 13:46:22 -06:00
|
|
|
rmb();
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
local_irq_save(flags);
|
|
|
|
{
|
|
|
|
for (i = 0; i < NUM_ROUNDS; i++) {
|
|
|
|
delta = get_delta(&rt, &master_time_stamp);
|
|
|
|
if (delta == 0) {
|
|
|
|
done = 1; /* let's lock on to this... */
|
|
|
|
bound = rt;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!done) {
|
|
|
|
if (i > 0) {
|
|
|
|
adjust_latency += -delta;
|
|
|
|
adj = -delta + adjust_latency/4;
|
|
|
|
} else
|
|
|
|
adj = -delta;
|
|
|
|
|
2007-03-05 16:28:37 -07:00
|
|
|
tick_ops->add_tick(adj);
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
#if DEBUG_TICK_SYNC
|
|
|
|
t[i].rt = rt;
|
|
|
|
t[i].master = master_time_stamp;
|
|
|
|
t[i].diff = delta;
|
|
|
|
t[i].lat = adjust_latency/4;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
local_irq_restore(flags);
|
|
|
|
|
|
|
|
#if DEBUG_TICK_SYNC
|
|
|
|
for (i = 0; i < NUM_ROUNDS; i++)
|
|
|
|
printk("rt=%5ld master=%5ld diff=%5ld adjlat=%5ld\n",
|
|
|
|
t[i].rt, t[i].master, t[i].diff, t[i].lat);
|
|
|
|
#endif
|
|
|
|
|
2007-11-20 00:43:00 -07:00
|
|
|
printk(KERN_INFO "CPU %d: synchronized TICK with master CPU "
|
|
|
|
"(last diff %ld cycles, maxerr %lu cycles)\n",
|
|
|
|
smp_processor_id(), delta, rt);
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static void smp_start_sync_tick_client(int cpu);
|
|
|
|
|
|
|
|
static void smp_synchronize_one_tick(int cpu)
|
|
|
|
{
|
|
|
|
unsigned long flags, i;
|
|
|
|
|
|
|
|
go[MASTER] = 0;
|
|
|
|
|
|
|
|
smp_start_sync_tick_client(cpu);
|
|
|
|
|
|
|
|
/* wait for client to be ready */
|
|
|
|
while (!go[MASTER])
|
2005-08-29 13:46:22 -06:00
|
|
|
rmb();
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
/* now let the client proceed into his loop */
|
|
|
|
go[MASTER] = 0;
|
2008-11-15 14:33:25 -07:00
|
|
|
membar_safe("#StoreLoad");
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
spin_lock_irqsave(&itc_sync_lock, flags);
|
|
|
|
{
|
|
|
|
for (i = 0; i < NUM_ROUNDS*NUM_ITERS; i++) {
|
|
|
|
while (!go[MASTER])
|
2005-08-29 13:46:22 -06:00
|
|
|
rmb();
|
2005-04-16 16:20:36 -06:00
|
|
|
go[MASTER] = 0;
|
2005-08-29 13:46:22 -06:00
|
|
|
wmb();
|
2005-04-16 16:20:36 -06:00
|
|
|
go[SLAVE] = tick_ops->get_tick();
|
2008-11-15 14:33:25 -07:00
|
|
|
membar_safe("#StoreLoad");
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&itc_sync_lock, flags);
|
|
|
|
}
|
|
|
|
|
2007-07-14 01:45:16 -06:00
|
|
|
#if defined(CONFIG_SUN_LDOMS) && defined(CONFIG_HOTPLUG_CPU)
|
2007-07-14 01:58:53 -06:00
|
|
|
/* XXX Put this in some common place. XXX */
|
|
|
|
static unsigned long kimage_addr_to_ra(void *p)
|
|
|
|
{
|
|
|
|
unsigned long val = (unsigned long) p;
|
|
|
|
|
|
|
|
return kern_base + (val - KERNBASE);
|
|
|
|
}
|
|
|
|
|
2009-03-31 18:15:40 -06:00
|
|
|
static void __cpuinit ldom_startcpu_cpuid(unsigned int cpu, unsigned long thread_reg, void **descrp)
|
2007-07-14 01:45:16 -06:00
|
|
|
{
|
|
|
|
extern unsigned long sparc64_ttable_tl0;
|
|
|
|
extern unsigned long kern_locked_tte_data;
|
|
|
|
struct hvtramp_descr *hdesc;
|
|
|
|
unsigned long trampoline_ra;
|
|
|
|
struct trap_per_cpu *tb;
|
|
|
|
u64 tte_vaddr, tte_data;
|
|
|
|
unsigned long hv_err;
|
2008-03-21 18:01:38 -06:00
|
|
|
int i;
|
2007-07-14 01:45:16 -06:00
|
|
|
|
2008-03-21 18:01:38 -06:00
|
|
|
hdesc = kzalloc(sizeof(*hdesc) +
|
|
|
|
(sizeof(struct hvtramp_mapping) *
|
|
|
|
num_kernel_image_mappings - 1),
|
|
|
|
GFP_KERNEL);
|
2007-07-14 01:45:16 -06:00
|
|
|
if (!hdesc) {
|
2007-07-14 01:58:53 -06:00
|
|
|
printk(KERN_ERR "ldom_startcpu_cpuid: Cannot allocate "
|
2007-07-14 01:45:16 -06:00
|
|
|
"hvtramp_descr.\n");
|
|
|
|
return;
|
|
|
|
}
|
2009-03-31 18:15:40 -06:00
|
|
|
*descrp = hdesc;
|
2007-07-14 01:45:16 -06:00
|
|
|
|
|
|
|
hdesc->cpu = cpu;
|
2008-03-21 18:01:38 -06:00
|
|
|
hdesc->num_mappings = num_kernel_image_mappings;
|
2007-07-14 01:45:16 -06:00
|
|
|
|
|
|
|
tb = &trap_block[cpu];
|
|
|
|
|
|
|
|
hdesc->fault_info_va = (unsigned long) &tb->fault_info;
|
|
|
|
hdesc->fault_info_pa = kimage_addr_to_ra(&tb->fault_info);
|
|
|
|
|
|
|
|
hdesc->thread_reg = thread_reg;
|
|
|
|
|
|
|
|
tte_vaddr = (unsigned long) KERNBASE;
|
|
|
|
tte_data = kern_locked_tte_data;
|
|
|
|
|
2008-03-21 18:01:38 -06:00
|
|
|
for (i = 0; i < hdesc->num_mappings; i++) {
|
|
|
|
hdesc->maps[i].vaddr = tte_vaddr;
|
|
|
|
hdesc->maps[i].tte = tte_data;
|
2007-07-14 01:45:16 -06:00
|
|
|
tte_vaddr += 0x400000;
|
|
|
|
tte_data += 0x400000;
|
|
|
|
}
|
|
|
|
|
|
|
|
trampoline_ra = kimage_addr_to_ra(hv_cpu_startup);
|
|
|
|
|
|
|
|
hv_err = sun4v_cpu_start(cpu, trampoline_ra,
|
|
|
|
kimage_addr_to_ra(&sparc64_ttable_tl0),
|
|
|
|
__pa(hdesc));
|
2007-07-16 04:49:40 -06:00
|
|
|
if (hv_err)
|
|
|
|
printk(KERN_ERR "ldom_startcpu_cpuid: sun4v_cpu_start() "
|
|
|
|
"gives error %lu\n", hv_err);
|
2007-07-14 01:45:16 -06:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
extern unsigned long sparc64_cpu_startup;
|
|
|
|
|
|
|
|
/* The OBP cpu startup callback truncates the 3rd arg cookie to
|
|
|
|
* 32-bits (I think) so to be safe we have it read the pointer
|
|
|
|
* contained here so we work on >4GB machines. -DaveM
|
|
|
|
*/
|
|
|
|
static struct thread_info *cpu_new_thread = NULL;
|
|
|
|
|
2008-11-22 10:34:04 -07:00
|
|
|
static int __cpuinit smp_boot_one_cpu(unsigned int cpu)
|
2005-04-16 16:20:36 -06:00
|
|
|
{
|
|
|
|
unsigned long entry =
|
|
|
|
(unsigned long)(&sparc64_cpu_startup);
|
|
|
|
unsigned long cookie =
|
|
|
|
(unsigned long)(&cpu_new_thread);
|
|
|
|
struct task_struct *p;
|
2009-03-31 18:15:40 -06:00
|
|
|
void *descr = NULL;
|
2006-02-15 03:26:54 -07:00
|
|
|
int timeout, ret;
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
p = fork_idle(cpu);
|
2007-10-04 15:55:59 -06:00
|
|
|
if (IS_ERR(p))
|
|
|
|
return PTR_ERR(p);
|
2005-04-16 16:20:36 -06:00
|
|
|
callin_flag = 0;
|
2006-01-12 02:05:42 -07:00
|
|
|
cpu_new_thread = task_thread_info(p);
|
2005-04-16 16:20:36 -06:00
|
|
|
|
2006-02-15 03:26:54 -07:00
|
|
|
if (tlb_type == hypervisor) {
|
2007-07-14 01:45:16 -06:00
|
|
|
#if defined(CONFIG_SUN_LDOMS) && defined(CONFIG_HOTPLUG_CPU)
|
[SPARC64]: Initial LDOM cpu hotplug support.
Only adding cpus is supports at the moment, removal
will come next.
When new cpus are configured, the machine description is
updated. When we get the configure request we pass in a
cpu mask of to-be-added cpus to the mdesc CPU node parser
so it only fetches information for those cpus. That code
also proceeds to update the SMT/multi-core scheduling bitmaps.
cpu_up() does all the work and we return the status back
over the DS channel.
CPUs via dr-cpu need to be booted straight out of the
hypervisor, and this requires:
1) A new trampoline mechanism. CPUs are booted straight
out of the hypervisor with MMU disabled and running in
physical addresses with no mappings installed in the TLB.
The new hvtramp.S code sets up the critical cpu state,
installs the locked TLB mappings for the kernel, and
turns the MMU on. It then proceeds to follow the logic
of the existing trampoline.S SMP cpu bringup code.
2) All calls into OBP have to be disallowed when domaining
is enabled. Since cpus boot straight into the kernel from
the hypervisor, OBP has no state about that cpu and therefore
cannot handle being invoked on that cpu.
Luckily it's only a handful of interfaces which can be called
after the OBP device tree is obtained. For example, rebooting,
halting, powering-off, and setting options node variables.
CPU removal support will require some infrastructure changes
here. Namely we'll have to process the requests via a true
kernel thread instead of in a workqueue. workqueues run on
a per-cpu thread, but when unconfiguring we might need to
force the thread to execute on another cpu if the current cpu
is the one being removed. Removal of a cpu also causes the kernel
to destroy that cpu's workqueue running thread.
Another issue on removal is that we may have interrupts still
pointing to the cpu-to-be-removed. So new code will be needed
to walk the active INO list and retarget those cpus as-needed.
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-07-13 17:03:42 -06:00
|
|
|
if (ldom_domaining_enabled)
|
|
|
|
ldom_startcpu_cpuid(cpu,
|
2009-03-31 18:15:40 -06:00
|
|
|
(unsigned long) cpu_new_thread,
|
|
|
|
&descr);
|
[SPARC64]: Initial LDOM cpu hotplug support.
Only adding cpus is supports at the moment, removal
will come next.
When new cpus are configured, the machine description is
updated. When we get the configure request we pass in a
cpu mask of to-be-added cpus to the mdesc CPU node parser
so it only fetches information for those cpus. That code
also proceeds to update the SMT/multi-core scheduling bitmaps.
cpu_up() does all the work and we return the status back
over the DS channel.
CPUs via dr-cpu need to be booted straight out of the
hypervisor, and this requires:
1) A new trampoline mechanism. CPUs are booted straight
out of the hypervisor with MMU disabled and running in
physical addresses with no mappings installed in the TLB.
The new hvtramp.S code sets up the critical cpu state,
installs the locked TLB mappings for the kernel, and
turns the MMU on. It then proceeds to follow the logic
of the existing trampoline.S SMP cpu bringup code.
2) All calls into OBP have to be disallowed when domaining
is enabled. Since cpus boot straight into the kernel from
the hypervisor, OBP has no state about that cpu and therefore
cannot handle being invoked on that cpu.
Luckily it's only a handful of interfaces which can be called
after the OBP device tree is obtained. For example, rebooting,
halting, powering-off, and setting options node variables.
CPU removal support will require some infrastructure changes
here. Namely we'll have to process the requests via a true
kernel thread instead of in a workqueue. workqueues run on
a per-cpu thread, but when unconfiguring we might need to
force the thread to execute on another cpu if the current cpu
is the one being removed. Removal of a cpu also causes the kernel
to destroy that cpu's workqueue running thread.
Another issue on removal is that we may have interrupts still
pointing to the cpu-to-be-removed. So new code will be needed
to walk the active INO list and retarget those cpus as-needed.
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-07-13 17:03:42 -06:00
|
|
|
else
|
|
|
|
#endif
|
|
|
|
prom_startcpu_cpuid(cpu, entry, cookie);
|
2006-02-15 03:26:54 -07:00
|
|
|
} else {
|
2007-05-25 16:49:59 -06:00
|
|
|
struct device_node *dp = of_find_node_by_cpuid(cpu);
|
2006-02-15 03:26:54 -07:00
|
|
|
|
2006-06-22 00:34:02 -06:00
|
|
|
prom_startcpu(dp->node, entry, cookie);
|
2006-02-15 03:26:54 -07:00
|
|
|
}
|
2005-04-16 16:20:36 -06:00
|
|
|
|
[SPARC64]: Initial LDOM cpu hotplug support.
Only adding cpus is supports at the moment, removal
will come next.
When new cpus are configured, the machine description is
updated. When we get the configure request we pass in a
cpu mask of to-be-added cpus to the mdesc CPU node parser
so it only fetches information for those cpus. That code
also proceeds to update the SMT/multi-core scheduling bitmaps.
cpu_up() does all the work and we return the status back
over the DS channel.
CPUs via dr-cpu need to be booted straight out of the
hypervisor, and this requires:
1) A new trampoline mechanism. CPUs are booted straight
out of the hypervisor with MMU disabled and running in
physical addresses with no mappings installed in the TLB.
The new hvtramp.S code sets up the critical cpu state,
installs the locked TLB mappings for the kernel, and
turns the MMU on. It then proceeds to follow the logic
of the existing trampoline.S SMP cpu bringup code.
2) All calls into OBP have to be disallowed when domaining
is enabled. Since cpus boot straight into the kernel from
the hypervisor, OBP has no state about that cpu and therefore
cannot handle being invoked on that cpu.
Luckily it's only a handful of interfaces which can be called
after the OBP device tree is obtained. For example, rebooting,
halting, powering-off, and setting options node variables.
CPU removal support will require some infrastructure changes
here. Namely we'll have to process the requests via a true
kernel thread instead of in a workqueue. workqueues run on
a per-cpu thread, but when unconfiguring we might need to
force the thread to execute on another cpu if the current cpu
is the one being removed. Removal of a cpu also causes the kernel
to destroy that cpu's workqueue running thread.
Another issue on removal is that we may have interrupts still
pointing to the cpu-to-be-removed. So new code will be needed
to walk the active INO list and retarget those cpus as-needed.
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-07-13 17:03:42 -06:00
|
|
|
for (timeout = 0; timeout < 50000; timeout++) {
|
2005-04-16 16:20:36 -06:00
|
|
|
if (callin_flag)
|
|
|
|
break;
|
|
|
|
udelay(100);
|
|
|
|
}
|
[SPARC64]: Get SUN4V SMP working.
The sibling cpu bringup is extremely fragile. We can only
perform the most basic calls until we take over the trap
table from the firmware/hypervisor on the new cpu.
This means no accesses to %g4, %g5, %g6 since those can't be
TLB translated without our trap handlers.
In order to achieve this:
1) Change sun4v_init_mondo_queues() so that it can operate in
several modes.
It can allocate the queues, or install them in the current
processor, or both.
The boot cpu does both in it's call early on.
Later, the boot cpu allocates the sibling cpu queue, starts
the sibling cpu, then the sibling cpu loads them in.
2) init_cur_cpu_trap() is changed to take the current_thread_info()
as an argument instead of reading %g6 directly on the current
cpu.
3) Create a trampoline stack for the sibling cpus. We do our basic
kernel calls using this stack, which is locked into the kernel
image, then go to our proper thread stack after taking over the
trap table.
4) While we are in this delicate startup state, we put 0xdeadbeef
into %g4/%g5/%g6 in order to catch accidental accesses.
5) On the final prom_set_trap_table*() call, we put &init_thread_union
into %g6. This is a hack to make prom_world(0) work. All that
wants to do is restore the %asi register using
get_thread_current_ds().
Longer term we should just do the OBP calls to set the trap table by
hand just like we do for everything else. This would avoid that silly
prom_world(0) issue, then we can remove the init_thread_union hack.
Signed-off-by: David S. Miller <davem@davemloft.net>
2006-02-17 02:29:17 -07:00
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
if (callin_flag) {
|
|
|
|
ret = 0;
|
|
|
|
} else {
|
|
|
|
printk("Processor %d is stuck.\n", cpu);
|
|
|
|
ret = -ENODEV;
|
|
|
|
}
|
|
|
|
cpu_new_thread = NULL;
|
|
|
|
|
2009-03-31 18:15:40 -06:00
|
|
|
kfree(descr);
|
2007-07-15 02:08:03 -06:00
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void spitfire_xcall_helper(u64 data0, u64 data1, u64 data2, u64 pstate, unsigned long cpu)
|
|
|
|
{
|
|
|
|
u64 result, target;
|
|
|
|
int stuck, tmp;
|
|
|
|
|
|
|
|
if (this_is_starfire) {
|
|
|
|
/* map to real upaid */
|
|
|
|
cpu = (((cpu & 0x3c) << 1) |
|
|
|
|
((cpu & 0x40) >> 4) |
|
|
|
|
(cpu & 0x3));
|
|
|
|
}
|
|
|
|
|
|
|
|
target = (cpu << 14) | 0x70;
|
|
|
|
again:
|
|
|
|
/* Ok, this is the real Spitfire Errata #54.
|
|
|
|
* One must read back from a UDB internal register
|
|
|
|
* after writes to the UDB interrupt dispatch, but
|
|
|
|
* before the membar Sync for that write.
|
|
|
|
* So we use the high UDB control register (ASI 0x7f,
|
|
|
|
* ADDR 0x20) for the dummy read. -DaveM
|
|
|
|
*/
|
|
|
|
tmp = 0x40;
|
|
|
|
__asm__ __volatile__(
|
|
|
|
"wrpr %1, %2, %%pstate\n\t"
|
|
|
|
"stxa %4, [%0] %3\n\t"
|
|
|
|
"stxa %5, [%0+%8] %3\n\t"
|
|
|
|
"add %0, %8, %0\n\t"
|
|
|
|
"stxa %6, [%0+%8] %3\n\t"
|
|
|
|
"membar #Sync\n\t"
|
|
|
|
"stxa %%g0, [%7] %3\n\t"
|
|
|
|
"membar #Sync\n\t"
|
|
|
|
"mov 0x20, %%g1\n\t"
|
|
|
|
"ldxa [%%g1] 0x7f, %%g0\n\t"
|
|
|
|
"membar #Sync"
|
|
|
|
: "=r" (tmp)
|
|
|
|
: "r" (pstate), "i" (PSTATE_IE), "i" (ASI_INTR_W),
|
|
|
|
"r" (data0), "r" (data1), "r" (data2), "r" (target),
|
|
|
|
"r" (0x10), "0" (tmp)
|
|
|
|
: "g1");
|
|
|
|
|
|
|
|
/* NOTE: PSTATE_IE is still clear. */
|
|
|
|
stuck = 100000;
|
|
|
|
do {
|
|
|
|
__asm__ __volatile__("ldxa [%%g0] %1, %0"
|
|
|
|
: "=r" (result)
|
|
|
|
: "i" (ASI_INTR_DISPATCH_STAT));
|
|
|
|
if (result == 0) {
|
|
|
|
__asm__ __volatile__("wrpr %0, 0x0, %%pstate"
|
|
|
|
: : "r" (pstate));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
stuck -= 1;
|
|
|
|
if (stuck == 0)
|
|
|
|
break;
|
|
|
|
} while (result & 0x1);
|
|
|
|
__asm__ __volatile__("wrpr %0, 0x0, %%pstate"
|
|
|
|
: : "r" (pstate));
|
|
|
|
if (stuck == 0) {
|
2009-01-06 14:19:28 -07:00
|
|
|
printk("CPU[%d]: mondo stuckage result[%016llx]\n",
|
2005-04-16 16:20:36 -06:00
|
|
|
smp_processor_id(), result);
|
|
|
|
} else {
|
|
|
|
udelay(2);
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-04 17:42:58 -06:00
|
|
|
static void spitfire_xcall_deliver(struct trap_per_cpu *tb, int cnt)
|
2005-04-16 16:20:36 -06:00
|
|
|
{
|
2008-08-04 17:42:58 -06:00
|
|
|
u64 *mondo, data0, data1, data2;
|
|
|
|
u16 *cpu_list;
|
2005-04-16 16:20:36 -06:00
|
|
|
u64 pstate;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
__asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate));
|
2008-08-04 17:42:58 -06:00
|
|
|
cpu_list = __va(tb->cpu_list_pa);
|
|
|
|
mondo = __va(tb->cpu_mondo_block_pa);
|
|
|
|
data0 = mondo[0];
|
|
|
|
data1 = mondo[1];
|
|
|
|
data2 = mondo[2];
|
|
|
|
for (i = 0; i < cnt; i++)
|
|
|
|
spitfire_xcall_helper(data0, data1, data2, pstate, cpu_list[i]);
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Cheetah now allows to send the whole 64-bytes of data in the interrupt
|
|
|
|
* packet, but we have no use for that. However we do take advantage of
|
|
|
|
* the new pipelining feature (ie. dispatch to multiple cpus simultaneously).
|
|
|
|
*/
|
2008-08-04 17:42:58 -06:00
|
|
|
static void cheetah_xcall_deliver(struct trap_per_cpu *tb, int cnt)
|
2005-04-16 16:20:36 -06:00
|
|
|
{
|
2007-05-26 02:14:43 -06:00
|
|
|
int nack_busy_id, is_jbus, need_more;
|
2008-08-04 17:42:58 -06:00
|
|
|
u64 *mondo, pstate, ver, busy_mask;
|
|
|
|
u16 *cpu_list;
|
2005-04-16 16:20:36 -06:00
|
|
|
|
2008-08-04 17:42:58 -06:00
|
|
|
cpu_list = __va(tb->cpu_list_pa);
|
|
|
|
mondo = __va(tb->cpu_mondo_block_pa);
|
2008-08-04 00:24:26 -06:00
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
/* Unfortunately, someone at Sun had the brilliant idea to make the
|
|
|
|
* busy/nack fields hard-coded by ITID number for this Ultra-III
|
|
|
|
* derivative processor.
|
|
|
|
*/
|
|
|
|
__asm__ ("rdpr %%ver, %0" : "=r" (ver));
|
2006-02-27 00:27:19 -07:00
|
|
|
is_jbus = ((ver >> 32) == __JALAPENO_ID ||
|
|
|
|
(ver >> 32) == __SERRANO_ID);
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
__asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate));
|
|
|
|
|
|
|
|
retry:
|
2007-05-26 02:14:43 -06:00
|
|
|
need_more = 0;
|
2005-04-16 16:20:36 -06:00
|
|
|
__asm__ __volatile__("wrpr %0, %1, %%pstate\n\t"
|
|
|
|
: : "r" (pstate), "i" (PSTATE_IE));
|
|
|
|
|
|
|
|
/* Setup the dispatch data registers. */
|
|
|
|
__asm__ __volatile__("stxa %0, [%3] %6\n\t"
|
|
|
|
"stxa %1, [%4] %6\n\t"
|
|
|
|
"stxa %2, [%5] %6\n\t"
|
|
|
|
"membar #Sync\n\t"
|
|
|
|
: /* no outputs */
|
2008-08-04 17:42:58 -06:00
|
|
|
: "r" (mondo[0]), "r" (mondo[1]), "r" (mondo[2]),
|
2005-04-16 16:20:36 -06:00
|
|
|
"r" (0x40), "r" (0x50), "r" (0x60),
|
|
|
|
"i" (ASI_INTR_W));
|
|
|
|
|
|
|
|
nack_busy_id = 0;
|
2007-12-12 08:31:46 -07:00
|
|
|
busy_mask = 0;
|
2005-04-16 16:20:36 -06:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2008-08-04 17:42:58 -06:00
|
|
|
for (i = 0; i < cnt; i++) {
|
|
|
|
u64 target, nr;
|
|
|
|
|
|
|
|
nr = cpu_list[i];
|
|
|
|
if (nr == 0xffff)
|
|
|
|
continue;
|
2005-04-16 16:20:36 -06:00
|
|
|
|
2008-08-04 17:42:58 -06:00
|
|
|
target = (nr << 14) | 0x70;
|
2007-12-12 08:31:46 -07:00
|
|
|
if (is_jbus) {
|
2008-08-04 17:42:58 -06:00
|
|
|
busy_mask |= (0x1UL << (nr * 2));
|
2007-12-12 08:31:46 -07:00
|
|
|
} else {
|
2005-04-16 16:20:36 -06:00
|
|
|
target |= (nack_busy_id << 24);
|
2007-12-12 08:31:46 -07:00
|
|
|
busy_mask |= (0x1UL <<
|
|
|
|
(nack_busy_id * 2));
|
|
|
|
}
|
2005-04-16 16:20:36 -06:00
|
|
|
__asm__ __volatile__(
|
|
|
|
"stxa %%g0, [%0] %1\n\t"
|
|
|
|
"membar #Sync\n\t"
|
|
|
|
: /* no outputs */
|
|
|
|
: "r" (target), "i" (ASI_INTR_W));
|
|
|
|
nack_busy_id++;
|
2007-05-26 02:14:43 -06:00
|
|
|
if (nack_busy_id == 32) {
|
|
|
|
need_more = 1;
|
|
|
|
break;
|
|
|
|
}
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now, poll for completion. */
|
|
|
|
{
|
2007-12-12 08:31:46 -07:00
|
|
|
u64 dispatch_stat, nack_mask;
|
2005-04-16 16:20:36 -06:00
|
|
|
long stuck;
|
|
|
|
|
|
|
|
stuck = 100000 * nack_busy_id;
|
2007-12-12 08:31:46 -07:00
|
|
|
nack_mask = busy_mask << 1;
|
2005-04-16 16:20:36 -06:00
|
|
|
do {
|
|
|
|
__asm__ __volatile__("ldxa [%%g0] %1, %0"
|
|
|
|
: "=r" (dispatch_stat)
|
|
|
|
: "i" (ASI_INTR_DISPATCH_STAT));
|
2007-12-12 08:31:46 -07:00
|
|
|
if (!(dispatch_stat & (busy_mask | nack_mask))) {
|
2005-04-16 16:20:36 -06:00
|
|
|
__asm__ __volatile__("wrpr %0, 0x0, %%pstate"
|
|
|
|
: : "r" (pstate));
|
2007-05-26 02:14:43 -06:00
|
|
|
if (unlikely(need_more)) {
|
2008-08-04 17:42:58 -06:00
|
|
|
int i, this_cnt = 0;
|
|
|
|
for (i = 0; i < cnt; i++) {
|
|
|
|
if (cpu_list[i] == 0xffff)
|
|
|
|
continue;
|
|
|
|
cpu_list[i] = 0xffff;
|
|
|
|
this_cnt++;
|
|
|
|
if (this_cnt == 32)
|
2007-05-26 02:14:43 -06:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
goto retry;
|
|
|
|
}
|
2005-04-16 16:20:36 -06:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!--stuck)
|
|
|
|
break;
|
2007-12-12 08:31:46 -07:00
|
|
|
} while (dispatch_stat & busy_mask);
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
__asm__ __volatile__("wrpr %0, 0x0, %%pstate"
|
|
|
|
: : "r" (pstate));
|
|
|
|
|
2007-12-12 08:31:46 -07:00
|
|
|
if (dispatch_stat & busy_mask) {
|
2005-04-16 16:20:36 -06:00
|
|
|
/* Busy bits will not clear, continue instead
|
|
|
|
* of freezing up on this cpu.
|
|
|
|
*/
|
2009-01-06 14:19:28 -07:00
|
|
|
printk("CPU[%d]: mondo stuckage result[%016llx]\n",
|
2005-04-16 16:20:36 -06:00
|
|
|
smp_processor_id(), dispatch_stat);
|
|
|
|
} else {
|
|
|
|
int i, this_busy_nack = 0;
|
|
|
|
|
|
|
|
/* Delay some random time with interrupts enabled
|
|
|
|
* to prevent deadlock.
|
|
|
|
*/
|
|
|
|
udelay(2 * nack_busy_id);
|
|
|
|
|
|
|
|
/* Clear out the mask bits for cpus which did not
|
|
|
|
* NACK us.
|
|
|
|
*/
|
2008-08-04 17:42:58 -06:00
|
|
|
for (i = 0; i < cnt; i++) {
|
|
|
|
u64 check_mask, nr;
|
|
|
|
|
|
|
|
nr = cpu_list[i];
|
|
|
|
if (nr == 0xffff)
|
|
|
|
continue;
|
2005-04-16 16:20:36 -06:00
|
|
|
|
2006-02-27 00:27:19 -07:00
|
|
|
if (is_jbus)
|
2008-08-04 17:42:58 -06:00
|
|
|
check_mask = (0x2UL << (2*nr));
|
2005-04-16 16:20:36 -06:00
|
|
|
else
|
|
|
|
check_mask = (0x2UL <<
|
|
|
|
this_busy_nack);
|
|
|
|
if ((dispatch_stat & check_mask) == 0)
|
2008-08-04 17:42:58 -06:00
|
|
|
cpu_list[i] = 0xffff;
|
2005-04-16 16:20:36 -06:00
|
|
|
this_busy_nack += 2;
|
2007-05-26 02:14:43 -06:00
|
|
|
if (this_busy_nack == 64)
|
|
|
|
break;
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
goto retry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-02-08 17:41:20 -07:00
|
|
|
/* Multi-cpu list version. */
|
2008-08-04 17:42:58 -06:00
|
|
|
static void hypervisor_xcall_deliver(struct trap_per_cpu *tb, int cnt)
|
2006-02-04 04:10:53 -07:00
|
|
|
{
|
2008-08-04 17:47:57 -06:00
|
|
|
int retries, this_cpu, prev_sent, i, saw_cpu_error;
|
2008-08-04 17:18:40 -06:00
|
|
|
unsigned long status;
|
2006-02-28 16:10:26 -07:00
|
|
|
u16 *cpu_list;
|
2007-05-14 03:01:52 -06:00
|
|
|
|
2006-02-28 16:10:26 -07:00
|
|
|
this_cpu = smp_processor_id();
|
2006-02-08 17:41:20 -07:00
|
|
|
|
2006-02-28 16:10:26 -07:00
|
|
|
cpu_list = __va(tb->cpu_list_pa);
|
|
|
|
|
2008-08-04 17:47:57 -06:00
|
|
|
saw_cpu_error = 0;
|
2006-02-08 17:41:20 -07:00
|
|
|
retries = 0;
|
2006-03-02 22:50:47 -07:00
|
|
|
prev_sent = 0;
|
2006-02-08 17:41:20 -07:00
|
|
|
do {
|
2006-03-02 22:50:47 -07:00
|
|
|
int forward_progress, n_sent;
|
2006-02-08 17:41:20 -07:00
|
|
|
|
2006-02-28 16:10:26 -07:00
|
|
|
status = sun4v_cpu_mondo_send(cnt,
|
|
|
|
tb->cpu_list_pa,
|
|
|
|
tb->cpu_mondo_block_pa);
|
|
|
|
|
|
|
|
/* HV_EOK means all cpus received the xcall, we're done. */
|
|
|
|
if (likely(status == HV_EOK))
|
2006-02-08 17:41:20 -07:00
|
|
|
break;
|
2006-02-28 16:10:26 -07:00
|
|
|
|
2006-03-02 22:50:47 -07:00
|
|
|
/* First, see if we made any forward progress.
|
|
|
|
*
|
|
|
|
* The hypervisor indicates successful sends by setting
|
|
|
|
* cpu list entries to the value 0xffff.
|
2006-02-28 16:10:26 -07:00
|
|
|
*/
|
2006-03-02 22:50:47 -07:00
|
|
|
n_sent = 0;
|
2006-02-28 16:10:26 -07:00
|
|
|
for (i = 0; i < cnt; i++) {
|
2006-03-02 22:50:47 -07:00
|
|
|
if (likely(cpu_list[i] == 0xffff))
|
|
|
|
n_sent++;
|
2006-02-08 17:41:20 -07:00
|
|
|
}
|
|
|
|
|
2006-03-02 22:50:47 -07:00
|
|
|
forward_progress = 0;
|
|
|
|
if (n_sent > prev_sent)
|
|
|
|
forward_progress = 1;
|
|
|
|
|
|
|
|
prev_sent = n_sent;
|
|
|
|
|
2006-02-28 16:10:26 -07:00
|
|
|
/* If we get a HV_ECPUERROR, then one or more of the cpus
|
|
|
|
* in the list are in error state. Use the cpu_state()
|
|
|
|
* hypervisor call to find out which cpus are in error state.
|
|
|
|
*/
|
|
|
|
if (unlikely(status == HV_ECPUERROR)) {
|
|
|
|
for (i = 0; i < cnt; i++) {
|
|
|
|
long err;
|
|
|
|
u16 cpu;
|
|
|
|
|
|
|
|
cpu = cpu_list[i];
|
|
|
|
if (cpu == 0xffff)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
err = sun4v_cpu_state(cpu);
|
2008-08-04 17:47:57 -06:00
|
|
|
if (err == HV_CPU_STATE_ERROR) {
|
|
|
|
saw_cpu_error = (cpu + 1);
|
2006-03-02 22:50:47 -07:00
|
|
|
cpu_list[i] = 0xffff;
|
2006-02-28 16:10:26 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (unlikely(status != HV_EWOULDBLOCK))
|
|
|
|
goto fatal_mondo_error;
|
|
|
|
|
2006-03-02 22:50:47 -07:00
|
|
|
/* Don't bother rewriting the CPU list, just leave the
|
|
|
|
* 0xffff and non-0xffff entries in there and the
|
|
|
|
* hypervisor will do the right thing.
|
|
|
|
*
|
|
|
|
* Only advance timeout state if we didn't make any
|
|
|
|
* forward progress.
|
|
|
|
*/
|
2006-02-28 16:10:26 -07:00
|
|
|
if (unlikely(!forward_progress)) {
|
|
|
|
if (unlikely(++retries > 10000))
|
|
|
|
goto fatal_mondo_timeout;
|
|
|
|
|
|
|
|
/* Delay a little bit to let other cpus catch up
|
|
|
|
* on their cpu mondo queue work.
|
|
|
|
*/
|
|
|
|
udelay(2 * cnt);
|
|
|
|
}
|
2006-02-08 17:41:20 -07:00
|
|
|
} while (1);
|
|
|
|
|
2008-08-04 17:47:57 -06:00
|
|
|
if (unlikely(saw_cpu_error))
|
2006-02-28 16:10:26 -07:00
|
|
|
goto fatal_mondo_cpu_error;
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
fatal_mondo_cpu_error:
|
|
|
|
printk(KERN_CRIT "CPU[%d]: SUN4V mondo cpu error, some target cpus "
|
2008-08-04 17:47:57 -06:00
|
|
|
"(including %d) were in error state\n",
|
|
|
|
this_cpu, saw_cpu_error - 1);
|
2006-02-28 16:10:26 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
fatal_mondo_timeout:
|
|
|
|
printk(KERN_CRIT "CPU[%d]: SUN4V mondo timeout, no forward "
|
|
|
|
" progress after %d retries.\n",
|
|
|
|
this_cpu, retries);
|
|
|
|
goto dump_cpu_list_and_out;
|
|
|
|
|
|
|
|
fatal_mondo_error:
|
|
|
|
printk(KERN_CRIT "CPU[%d]: Unexpected SUN4V mondo error %lu\n",
|
|
|
|
this_cpu, status);
|
|
|
|
printk(KERN_CRIT "CPU[%d]: Args were cnt(%d) cpulist_pa(%lx) "
|
|
|
|
"mondo_block_pa(%lx)\n",
|
|
|
|
this_cpu, cnt, tb->cpu_list_pa, tb->cpu_mondo_block_pa);
|
|
|
|
|
|
|
|
dump_cpu_list_and_out:
|
|
|
|
printk(KERN_CRIT "CPU[%d]: CPU list [ ", this_cpu);
|
|
|
|
for (i = 0; i < cnt; i++)
|
|
|
|
printk("%u ", cpu_list[i]);
|
|
|
|
printk("]\n");
|
2006-02-08 17:41:20 -07:00
|
|
|
}
|
2006-02-04 04:10:53 -07:00
|
|
|
|
2008-08-04 17:42:58 -06:00
|
|
|
static void (*xcall_deliver_impl)(struct trap_per_cpu *, int);
|
2008-08-04 17:16:20 -06:00
|
|
|
|
|
|
|
static void xcall_deliver(u64 data0, u64 data1, u64 data2, const cpumask_t *mask)
|
|
|
|
{
|
2008-08-04 17:42:58 -06:00
|
|
|
struct trap_per_cpu *tb;
|
|
|
|
int this_cpu, i, cnt;
|
2008-08-04 17:18:40 -06:00
|
|
|
unsigned long flags;
|
2008-08-04 17:42:58 -06:00
|
|
|
u16 *cpu_list;
|
|
|
|
u64 *mondo;
|
2008-08-04 17:18:40 -06:00
|
|
|
|
|
|
|
/* We have to do this whole thing with interrupts fully disabled.
|
|
|
|
* Otherwise if we send an xcall from interrupt context it will
|
|
|
|
* corrupt both our mondo block and cpu list state.
|
|
|
|
*
|
|
|
|
* One consequence of this is that we cannot use timeout mechanisms
|
|
|
|
* that depend upon interrupts being delivered locally. So, for
|
|
|
|
* example, we cannot sample jiffies and expect it to advance.
|
|
|
|
*
|
|
|
|
* Fortunately, udelay() uses %stick/%tick so we can use that.
|
|
|
|
*/
|
|
|
|
local_irq_save(flags);
|
2008-08-04 17:42:58 -06:00
|
|
|
|
|
|
|
this_cpu = smp_processor_id();
|
|
|
|
tb = &trap_block[this_cpu];
|
|
|
|
|
|
|
|
mondo = __va(tb->cpu_mondo_block_pa);
|
|
|
|
mondo[0] = data0;
|
|
|
|
mondo[1] = data1;
|
|
|
|
mondo[2] = data2;
|
|
|
|
wmb();
|
|
|
|
|
|
|
|
cpu_list = __va(tb->cpu_list_pa);
|
|
|
|
|
|
|
|
/* Setup the initial cpu list. */
|
|
|
|
cnt = 0;
|
2008-12-08 02:10:08 -07:00
|
|
|
for_each_cpu(i, mask) {
|
2008-08-04 17:42:58 -06:00
|
|
|
if (i == this_cpu || !cpu_online(i))
|
|
|
|
continue;
|
|
|
|
cpu_list[cnt++] = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cnt)
|
|
|
|
xcall_deliver_impl(tb, cnt);
|
|
|
|
|
2008-08-04 17:18:40 -06:00
|
|
|
local_irq_restore(flags);
|
2008-08-04 17:16:20 -06:00
|
|
|
}
|
2008-08-03 23:52:41 -06:00
|
|
|
|
2008-08-04 01:51:18 -06:00
|
|
|
/* Send cross call to all processors mentioned in MASK_P
|
|
|
|
* except self. Really, there are only two cases currently,
|
|
|
|
* "&cpu_online_map" and "&mm->cpu_vm_mask".
|
2005-04-16 16:20:36 -06:00
|
|
|
*/
|
2008-08-04 17:56:15 -06:00
|
|
|
static void smp_cross_call_masked(unsigned long *func, u32 ctx, u64 data1, u64 data2, const cpumask_t *mask)
|
2005-04-16 16:20:36 -06:00
|
|
|
{
|
|
|
|
u64 data0 = (((u64)ctx)<<32 | (((u64)func) & 0xffffffff));
|
|
|
|
|
2008-08-04 17:56:15 -06:00
|
|
|
xcall_deliver(data0, data1, data2, mask);
|
|
|
|
}
|
2005-04-16 16:20:36 -06:00
|
|
|
|
2008-08-04 17:56:15 -06:00
|
|
|
/* Send cross call to all processors except self. */
|
|
|
|
static void smp_cross_call(unsigned long *func, u32 ctx, u64 data1, u64 data2)
|
|
|
|
{
|
|
|
|
smp_cross_call_masked(func, ctx, data1, data2, &cpu_online_map);
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
extern unsigned long xcall_sync_tick;
|
|
|
|
|
|
|
|
static void smp_start_sync_tick_client(int cpu)
|
|
|
|
{
|
2008-08-04 01:02:31 -06:00
|
|
|
xcall_deliver((u64) &xcall_sync_tick, 0, 0,
|
|
|
|
&cpumask_of_cpu(cpu));
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
extern unsigned long xcall_call_function;
|
|
|
|
|
2009-03-15 22:10:22 -06:00
|
|
|
void arch_send_call_function_ipi_mask(const struct cpumask *mask)
|
2005-04-16 16:20:36 -06:00
|
|
|
{
|
2009-03-15 22:10:22 -06:00
|
|
|
xcall_deliver((u64) &xcall_call_function, 0, 0, mask);
|
2008-07-18 00:44:50 -06:00
|
|
|
}
|
2005-04-16 16:20:36 -06:00
|
|
|
|
2008-07-18 00:44:50 -06:00
|
|
|
extern unsigned long xcall_call_function_single;
|
2005-04-16 16:20:36 -06:00
|
|
|
|
2008-07-18 00:44:50 -06:00
|
|
|
void arch_send_call_function_single_ipi(int cpu)
|
|
|
|
{
|
2008-08-04 00:56:28 -06:00
|
|
|
xcall_deliver((u64) &xcall_call_function_single, 0, 0,
|
|
|
|
&cpumask_of_cpu(cpu));
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
void smp_call_function_client(int irq, struct pt_regs *regs)
|
|
|
|
{
|
2008-07-18 00:44:50 -06:00
|
|
|
clear_softint(1 << irq);
|
|
|
|
generic_smp_call_function_interrupt();
|
|
|
|
}
|
2005-04-16 16:20:36 -06:00
|
|
|
|
2008-07-18 00:44:50 -06:00
|
|
|
void smp_call_function_single_client(int irq, struct pt_regs *regs)
|
|
|
|
{
|
2005-04-16 16:20:36 -06:00
|
|
|
clear_softint(1 << irq);
|
2008-07-18 00:44:50 -06:00
|
|
|
generic_smp_call_function_single_interrupt();
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
|
2006-01-31 19:31:38 -07:00
|
|
|
static void tsb_sync(void *info)
|
|
|
|
{
|
2006-03-28 14:29:26 -07:00
|
|
|
struct trap_per_cpu *tp = &trap_block[raw_smp_processor_id()];
|
2006-01-31 19:31:38 -07:00
|
|
|
struct mm_struct *mm = info;
|
|
|
|
|
2006-03-28 14:29:26 -07:00
|
|
|
/* It is not valid to test "currrent->active_mm == mm" here.
|
|
|
|
*
|
|
|
|
* The value of "current" is not changed atomically with
|
|
|
|
* switch_mm(). But that's OK, we just need to check the
|
|
|
|
* current cpu's trap block PGD physical address.
|
|
|
|
*/
|
|
|
|
if (tp->pgd_paddr == __pa(mm->pgd))
|
2006-01-31 19:31:38 -07:00
|
|
|
tsb_context_switch(mm);
|
|
|
|
}
|
|
|
|
|
|
|
|
void smp_tsb_sync(struct mm_struct *mm)
|
|
|
|
{
|
2009-03-15 22:10:39 -06:00
|
|
|
smp_call_function_many(mm_cpumask(mm), tsb_sync, mm, 1);
|
2006-01-31 19:31:38 -07:00
|
|
|
}
|
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
extern unsigned long xcall_flush_tlb_mm;
|
|
|
|
extern unsigned long xcall_flush_tlb_pending;
|
|
|
|
extern unsigned long xcall_flush_tlb_kernel_range;
|
2008-05-20 00:46:00 -06:00
|
|
|
extern unsigned long xcall_fetch_glob_regs;
|
2005-04-16 16:20:36 -06:00
|
|
|
extern unsigned long xcall_receive_signal;
|
2006-03-06 23:50:44 -07:00
|
|
|
extern unsigned long xcall_new_mmu_context_version;
|
2008-04-29 03:38:50 -06:00
|
|
|
#ifdef CONFIG_KGDB
|
|
|
|
extern unsigned long xcall_kgdb_capture;
|
|
|
|
#endif
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
#ifdef DCACHE_ALIASING_POSSIBLE
|
|
|
|
extern unsigned long xcall_flush_dcache_page_cheetah;
|
|
|
|
#endif
|
|
|
|
extern unsigned long xcall_flush_dcache_page_spitfire;
|
|
|
|
|
|
|
|
#ifdef CONFIG_DEBUG_DCFLUSH
|
|
|
|
extern atomic_t dcpage_flushes;
|
|
|
|
extern atomic_t dcpage_flushes_xcall;
|
|
|
|
#endif
|
|
|
|
|
2007-10-27 01:13:04 -06:00
|
|
|
static inline void __local_flush_dcache_page(struct page *page)
|
2005-04-16 16:20:36 -06:00
|
|
|
{
|
|
|
|
#ifdef DCACHE_ALIASING_POSSIBLE
|
|
|
|
__flush_dcache_page(page_address(page),
|
|
|
|
((tlb_type == spitfire) &&
|
|
|
|
page_mapping(page) != NULL));
|
|
|
|
#else
|
|
|
|
if (page_mapping(page) != NULL &&
|
|
|
|
tlb_type == spitfire)
|
|
|
|
__flush_icache_page(__pa(page_address(page)));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void smp_flush_dcache_page_impl(struct page *page, int cpu)
|
|
|
|
{
|
2006-02-04 04:10:53 -07:00
|
|
|
int this_cpu;
|
|
|
|
|
|
|
|
if (tlb_type == hypervisor)
|
|
|
|
return;
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
#ifdef CONFIG_DEBUG_DCFLUSH
|
|
|
|
atomic_inc(&dcpage_flushes);
|
|
|
|
#endif
|
2006-02-04 04:10:53 -07:00
|
|
|
|
|
|
|
this_cpu = get_cpu();
|
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
if (cpu == this_cpu) {
|
|
|
|
__local_flush_dcache_page(page);
|
|
|
|
} else if (cpu_online(cpu)) {
|
|
|
|
void *pg_addr = page_address(page);
|
2008-08-04 00:07:18 -06:00
|
|
|
u64 data0 = 0;
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
if (tlb_type == spitfire) {
|
2008-08-04 00:07:18 -06:00
|
|
|
data0 = ((u64)&xcall_flush_dcache_page_spitfire);
|
2005-04-16 16:20:36 -06:00
|
|
|
if (page_mapping(page) != NULL)
|
|
|
|
data0 |= ((u64)1 << 32);
|
2006-02-04 04:10:53 -07:00
|
|
|
} else if (tlb_type == cheetah || tlb_type == cheetah_plus) {
|
2005-04-16 16:20:36 -06:00
|
|
|
#ifdef DCACHE_ALIASING_POSSIBLE
|
2008-08-04 00:07:18 -06:00
|
|
|
data0 = ((u64)&xcall_flush_dcache_page_cheetah);
|
2005-04-16 16:20:36 -06:00
|
|
|
#endif
|
|
|
|
}
|
2008-08-04 00:07:18 -06:00
|
|
|
if (data0) {
|
|
|
|
xcall_deliver(data0, __pa(pg_addr),
|
2008-08-04 17:56:15 -06:00
|
|
|
(u64) pg_addr, &cpumask_of_cpu(cpu));
|
2005-04-16 16:20:36 -06:00
|
|
|
#ifdef CONFIG_DEBUG_DCFLUSH
|
2008-08-04 00:07:18 -06:00
|
|
|
atomic_inc(&dcpage_flushes_xcall);
|
2005-04-16 16:20:36 -06:00
|
|
|
#endif
|
2008-08-04 00:07:18 -06:00
|
|
|
}
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
put_cpu();
|
|
|
|
}
|
|
|
|
|
|
|
|
void flush_dcache_page_all(struct mm_struct *mm, struct page *page)
|
|
|
|
{
|
2008-08-04 00:07:18 -06:00
|
|
|
void *pg_addr;
|
2006-02-04 04:10:53 -07:00
|
|
|
int this_cpu;
|
2008-08-04 00:07:18 -06:00
|
|
|
u64 data0;
|
2006-02-04 04:10:53 -07:00
|
|
|
|
|
|
|
if (tlb_type == hypervisor)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this_cpu = get_cpu();
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
#ifdef CONFIG_DEBUG_DCFLUSH
|
|
|
|
atomic_inc(&dcpage_flushes);
|
|
|
|
#endif
|
2008-08-04 00:07:18 -06:00
|
|
|
data0 = 0;
|
|
|
|
pg_addr = page_address(page);
|
2005-04-16 16:20:36 -06:00
|
|
|
if (tlb_type == spitfire) {
|
|
|
|
data0 = ((u64)&xcall_flush_dcache_page_spitfire);
|
|
|
|
if (page_mapping(page) != NULL)
|
|
|
|
data0 |= ((u64)1 << 32);
|
2006-02-04 04:10:53 -07:00
|
|
|
} else if (tlb_type == cheetah || tlb_type == cheetah_plus) {
|
2005-04-16 16:20:36 -06:00
|
|
|
#ifdef DCACHE_ALIASING_POSSIBLE
|
|
|
|
data0 = ((u64)&xcall_flush_dcache_page_cheetah);
|
|
|
|
#endif
|
|
|
|
}
|
2008-08-04 00:07:18 -06:00
|
|
|
if (data0) {
|
|
|
|
xcall_deliver(data0, __pa(pg_addr),
|
2008-08-04 17:56:15 -06:00
|
|
|
(u64) pg_addr, &cpu_online_map);
|
2005-04-16 16:20:36 -06:00
|
|
|
#ifdef CONFIG_DEBUG_DCFLUSH
|
2008-08-04 00:07:18 -06:00
|
|
|
atomic_inc(&dcpage_flushes_xcall);
|
2005-04-16 16:20:36 -06:00
|
|
|
#endif
|
2008-08-04 00:07:18 -06:00
|
|
|
}
|
2005-04-16 16:20:36 -06:00
|
|
|
__local_flush_dcache_page(page);
|
|
|
|
|
|
|
|
put_cpu();
|
|
|
|
}
|
|
|
|
|
2006-03-06 23:50:44 -07:00
|
|
|
void smp_new_mmu_context_version_client(int irq, struct pt_regs *regs)
|
2005-04-16 16:20:36 -06:00
|
|
|
{
|
2006-02-23 15:19:28 -07:00
|
|
|
struct mm_struct *mm;
|
2006-03-06 23:50:44 -07:00
|
|
|
unsigned long flags;
|
2006-02-23 15:19:28 -07:00
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
clear_softint(1 << irq);
|
2006-02-23 15:19:28 -07:00
|
|
|
|
|
|
|
/* See if we need to allocate a new TLB context because
|
|
|
|
* the version of the one we are using is now out of date.
|
|
|
|
*/
|
|
|
|
mm = current->active_mm;
|
2006-03-06 23:50:44 -07:00
|
|
|
if (unlikely(!mm || (mm == &init_mm)))
|
|
|
|
return;
|
2006-02-23 15:19:28 -07:00
|
|
|
|
2006-03-06 23:50:44 -07:00
|
|
|
spin_lock_irqsave(&mm->context.lock, flags);
|
2006-02-27 18:56:51 -07:00
|
|
|
|
2006-03-06 23:50:44 -07:00
|
|
|
if (unlikely(!CTX_VALID(mm->context)))
|
|
|
|
get_new_mmu_context(mm);
|
2006-02-27 18:56:51 -07:00
|
|
|
|
2006-03-06 23:50:44 -07:00
|
|
|
spin_unlock_irqrestore(&mm->context.lock, flags);
|
2006-02-27 18:56:51 -07:00
|
|
|
|
2006-03-06 23:50:44 -07:00
|
|
|
load_secondary_context(mm);
|
|
|
|
__flush_tlb_mm(CTX_HWBITS(mm->context),
|
|
|
|
SECONDARY_CONTEXT);
|
2006-02-23 15:19:28 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void smp_new_mmu_context_version(void)
|
|
|
|
{
|
2006-03-06 23:50:44 -07:00
|
|
|
smp_cross_call(&xcall_new_mmu_context_version, 0, 0, 0);
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
|
2008-04-29 03:38:50 -06:00
|
|
|
#ifdef CONFIG_KGDB
|
|
|
|
void kgdb_roundup_cpus(unsigned long flags)
|
|
|
|
{
|
|
|
|
smp_cross_call(&xcall_kgdb_capture, 0, 0, 0);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-05-20 00:46:00 -06:00
|
|
|
void smp_fetch_global_regs(void)
|
|
|
|
{
|
|
|
|
smp_cross_call(&xcall_fetch_glob_regs, 0, 0, 0);
|
|
|
|
}
|
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
/* We know that the window frames of the user have been flushed
|
|
|
|
* to the stack before we get here because all callers of us
|
|
|
|
* are flush_tlb_*() routines, and these run after flush_cache_*()
|
|
|
|
* which performs the flushw.
|
|
|
|
*
|
|
|
|
* The SMP TLB coherency scheme we use works as follows:
|
|
|
|
*
|
|
|
|
* 1) mm->cpu_vm_mask is a bit mask of which cpus an address
|
|
|
|
* space has (potentially) executed on, this is the heuristic
|
|
|
|
* we use to avoid doing cross calls.
|
|
|
|
*
|
|
|
|
* Also, for flushing from kswapd and also for clones, we
|
|
|
|
* use cpu_vm_mask as the list of cpus to make run the TLB.
|
|
|
|
*
|
|
|
|
* 2) TLB context numbers are shared globally across all processors
|
|
|
|
* in the system, this allows us to play several games to avoid
|
|
|
|
* cross calls.
|
|
|
|
*
|
|
|
|
* One invariant is that when a cpu switches to a process, and
|
|
|
|
* that processes tsk->active_mm->cpu_vm_mask does not have the
|
|
|
|
* current cpu's bit set, that tlb context is flushed locally.
|
|
|
|
*
|
|
|
|
* If the address space is non-shared (ie. mm->count == 1) we avoid
|
|
|
|
* cross calls when we want to flush the currently running process's
|
|
|
|
* tlb state. This is done by clearing all cpu bits except the current
|
sparc64: Fix MM refcount check in smp_flush_tlb_pending().
As explained by Benjamin Herrenschmidt:
> CPU 0 is running the context, task->mm == task->active_mm == your
> context. The CPU is in userspace happily churning things.
>
> CPU 1 used to run it, not anymore, it's now running fancyfsd which
> is a kernel thread, but current->active_mm still points to that
> same context.
>
> Because there's only one "real" user, mm_users is 1 (but mm_count is
> elevated, it's just that the presence on CPU 1 as active_mm has no
> effect on mm_count().
>
> At this point, fancyfsd decides to invalidate a mapping currently mapped
> by that context, for example because a networked file has changed
> remotely or something like that, using unmap_mapping_ranges().
>
> So CPU 1 goes into the zapping code, which eventually ends up calling
> flush_tlb_pending(). Your test will succeed, as current->active_mm is
> indeed the target mm for the flush, and mm_users is indeed 1. So you
> will -not- send an IPI to the other CPU, and CPU 0 will continue happily
> accessing the pages that should have been unmapped.
To fix this problem, check ->mm instead of ->active_mm, and this
means:
> So if you test current->mm, you effectively account for mm_users == 1,
> so the only way the mm can be active on another processor is as a lazy
> mm for a kernel thread. So your test should work properly as long
> as you don't have a HW that will do speculative TLB reloads into the
> TLB on that other CPU (and even if you do, you flush-on-switch-in should
> get rid of any crap here).
And therefore we should be OK.
Signed-off-by: David S. Miller <davem@davemloft.net>
2009-03-27 02:09:17 -06:00
|
|
|
* processor's in current->mm->cpu_vm_mask and performing the
|
2005-04-16 16:20:36 -06:00
|
|
|
* flush locally only. This will force any subsequent cpus which run
|
|
|
|
* this task to flush the context from the local tlb if the process
|
|
|
|
* migrates to another cpu (again).
|
|
|
|
*
|
|
|
|
* 3) For shared address spaces (threads) and swapping we bite the
|
|
|
|
* bullet for most cases and perform the cross call (but only to
|
|
|
|
* the cpus listed in cpu_vm_mask).
|
|
|
|
*
|
|
|
|
* The performance gain from "optimizing" away the cross call for threads is
|
|
|
|
* questionable (in theory the big win for threads is the massive sharing of
|
|
|
|
* address space state across processors).
|
|
|
|
*/
|
2005-11-07 15:09:58 -07:00
|
|
|
|
|
|
|
/* This currently is only used by the hugetlb arch pre-fault
|
|
|
|
* hook on UltraSPARC-III+ and later when changing the pagesize
|
|
|
|
* bits of the context register for an address space.
|
|
|
|
*/
|
2005-04-16 16:20:36 -06:00
|
|
|
void smp_flush_tlb_mm(struct mm_struct *mm)
|
|
|
|
{
|
2005-11-07 15:09:58 -07:00
|
|
|
u32 ctx = CTX_HWBITS(mm->context);
|
|
|
|
int cpu = get_cpu();
|
2005-04-16 16:20:36 -06:00
|
|
|
|
2005-11-07 15:09:58 -07:00
|
|
|
if (atomic_read(&mm->mm_users) == 1) {
|
2009-03-15 22:10:39 -06:00
|
|
|
cpumask_copy(mm_cpumask(mm), cpumask_of(cpu));
|
2005-11-07 15:09:58 -07:00
|
|
|
goto local_flush_and_out;
|
|
|
|
}
|
2005-04-16 16:20:36 -06:00
|
|
|
|
2005-11-07 15:09:58 -07:00
|
|
|
smp_cross_call_masked(&xcall_flush_tlb_mm,
|
|
|
|
ctx, 0, 0,
|
2009-03-15 22:10:39 -06:00
|
|
|
mm_cpumask(mm));
|
2005-04-16 16:20:36 -06:00
|
|
|
|
2005-11-07 15:09:58 -07:00
|
|
|
local_flush_and_out:
|
|
|
|
__flush_tlb_mm(ctx, SECONDARY_CONTEXT);
|
2005-04-16 16:20:36 -06:00
|
|
|
|
2005-11-07 15:09:58 -07:00
|
|
|
put_cpu();
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
void smp_flush_tlb_pending(struct mm_struct *mm, unsigned long nr, unsigned long *vaddrs)
|
|
|
|
{
|
|
|
|
u32 ctx = CTX_HWBITS(mm->context);
|
|
|
|
int cpu = get_cpu();
|
|
|
|
|
sparc64: Fix MM refcount check in smp_flush_tlb_pending().
As explained by Benjamin Herrenschmidt:
> CPU 0 is running the context, task->mm == task->active_mm == your
> context. The CPU is in userspace happily churning things.
>
> CPU 1 used to run it, not anymore, it's now running fancyfsd which
> is a kernel thread, but current->active_mm still points to that
> same context.
>
> Because there's only one "real" user, mm_users is 1 (but mm_count is
> elevated, it's just that the presence on CPU 1 as active_mm has no
> effect on mm_count().
>
> At this point, fancyfsd decides to invalidate a mapping currently mapped
> by that context, for example because a networked file has changed
> remotely or something like that, using unmap_mapping_ranges().
>
> So CPU 1 goes into the zapping code, which eventually ends up calling
> flush_tlb_pending(). Your test will succeed, as current->active_mm is
> indeed the target mm for the flush, and mm_users is indeed 1. So you
> will -not- send an IPI to the other CPU, and CPU 0 will continue happily
> accessing the pages that should have been unmapped.
To fix this problem, check ->mm instead of ->active_mm, and this
means:
> So if you test current->mm, you effectively account for mm_users == 1,
> so the only way the mm can be active on another processor is as a lazy
> mm for a kernel thread. So your test should work properly as long
> as you don't have a HW that will do speculative TLB reloads into the
> TLB on that other CPU (and even if you do, you flush-on-switch-in should
> get rid of any crap here).
And therefore we should be OK.
Signed-off-by: David S. Miller <davem@davemloft.net>
2009-03-27 02:09:17 -06:00
|
|
|
if (mm == current->mm && atomic_read(&mm->mm_users) == 1)
|
2009-03-15 22:10:39 -06:00
|
|
|
cpumask_copy(mm_cpumask(mm), cpumask_of(cpu));
|
[SPARC64] mm: context switch ptlock
sparc64 is unique among architectures in taking the page_table_lock in
its context switch (well, cris does too, but erroneously, and it's not
yet SMP anyway).
This seems to be a private affair between switch_mm and activate_mm,
using page_table_lock as a per-mm lock, without any relation to its uses
elsewhere. That's fine, but comment it as such; and unlock sooner in
switch_mm, more like in activate_mm (preemption is disabled here).
There is a block of "if (0)"ed code in smp_flush_tlb_pending which would
have liked to rely on the page_table_lock, in switch_mm and elsewhere;
but its comment explains how dup_mmap's flush_tlb_mm defeated it. And
though that could have been changed at any time over the past few years,
now the chance vanishes as we push the page_table_lock downwards, and
perhaps split it per page table page. Just delete that block of code.
Which leaves the mysterious spin_unlock_wait(&oldmm->page_table_lock)
in kernel/fork.c copy_mm. Textual analysis (supported by Nick Piggin)
suggests that the comment was written by DaveM, and that it relates to
the defeated approach in the sparc64 smp_flush_tlb_pending. Just delete
this block too.
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-11-07 15:09:01 -07:00
|
|
|
else
|
|
|
|
smp_cross_call_masked(&xcall_flush_tlb_pending,
|
|
|
|
ctx, nr, (unsigned long) vaddrs,
|
2009-03-15 22:10:39 -06:00
|
|
|
mm_cpumask(mm));
|
2005-04-16 16:20:36 -06:00
|
|
|
|
|
|
|
__flush_tlb_pending(ctx, nr, vaddrs);
|
|
|
|
|
|
|
|
put_cpu();
|
|
|
|
}
|
|
|
|
|
|
|
|
void smp_flush_tlb_kernel_range(unsigned long start, unsigned long end)
|
|
|
|
{
|
|
|
|
start &= PAGE_MASK;
|
|
|
|
end = PAGE_ALIGN(end);
|
|
|
|
if (start != end) {
|
|
|
|
smp_cross_call(&xcall_flush_tlb_kernel_range,
|
|
|
|
0, start, end);
|
|
|
|
|
|
|
|
__flush_tlb_kernel_range(start, end);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* CPU capture. */
|
|
|
|
/* #define CAPTURE_DEBUG */
|
|
|
|
extern unsigned long xcall_capture;
|
|
|
|
|
|
|
|
static atomic_t smp_capture_depth = ATOMIC_INIT(0);
|
|
|
|
static atomic_t smp_capture_registry = ATOMIC_INIT(0);
|
|
|
|
static unsigned long penguins_are_doing_time;
|
|
|
|
|
|
|
|
void smp_capture(void)
|
|
|
|
{
|
|
|
|
int result = atomic_add_ret(1, &smp_capture_depth);
|
|
|
|
|
|
|
|
if (result == 1) {
|
|
|
|
int ncpus = num_online_cpus();
|
|
|
|
|
|
|
|
#ifdef CAPTURE_DEBUG
|
|
|
|
printk("CPU[%d]: Sending penguins to jail...",
|
|
|
|
smp_processor_id());
|
|
|
|
#endif
|
|
|
|
penguins_are_doing_time = 1;
|
|
|
|
atomic_inc(&smp_capture_registry);
|
|
|
|
smp_cross_call(&xcall_capture, 0, 0, 0);
|
|
|
|
while (atomic_read(&smp_capture_registry) != ncpus)
|
2005-08-29 13:46:22 -06:00
|
|
|
rmb();
|
2005-04-16 16:20:36 -06:00
|
|
|
#ifdef CAPTURE_DEBUG
|
|
|
|
printk("done\n");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void smp_release(void)
|
|
|
|
{
|
|
|
|
if (atomic_dec_and_test(&smp_capture_depth)) {
|
|
|
|
#ifdef CAPTURE_DEBUG
|
|
|
|
printk("CPU[%d]: Giving pardon to "
|
|
|
|
"imprisoned penguins\n",
|
|
|
|
smp_processor_id());
|
|
|
|
#endif
|
|
|
|
penguins_are_doing_time = 0;
|
2008-11-15 14:33:25 -07:00
|
|
|
membar_safe("#StoreLoad");
|
2005-04-16 16:20:36 -06:00
|
|
|
atomic_dec(&smp_capture_registry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-23 22:55:29 -07:00
|
|
|
/* Imprisoned penguins run with %pil == PIL_NORMAL_MAX, but PSTATE_IE
|
|
|
|
* set, so they can service tlb flush xcalls...
|
2005-04-16 16:20:36 -06:00
|
|
|
*/
|
|
|
|
extern void prom_world(int);
|
2006-01-31 19:32:29 -07:00
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
void smp_penguin_jailcell(int irq, struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
clear_softint(1 << irq);
|
|
|
|
|
|
|
|
preempt_disable();
|
|
|
|
|
|
|
|
__asm__ __volatile__("flushw");
|
|
|
|
prom_world(1);
|
|
|
|
atomic_inc(&smp_capture_registry);
|
2008-11-15 14:33:25 -07:00
|
|
|
membar_safe("#StoreLoad");
|
2005-04-16 16:20:36 -06:00
|
|
|
while (penguins_are_doing_time)
|
2005-08-29 13:46:22 -06:00
|
|
|
rmb();
|
2005-04-16 16:20:36 -06:00
|
|
|
atomic_dec(&smp_capture_registry);
|
|
|
|
prom_world(0);
|
|
|
|
|
|
|
|
preempt_enable();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* /proc/profile writes can call this, don't __init it please. */
|
|
|
|
int setup_profiling_timer(unsigned int multiplier)
|
|
|
|
{
|
2007-02-22 07:24:10 -07:00
|
|
|
return -EINVAL;
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
void __init smp_prepare_cpus(unsigned int max_cpus)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2007-05-25 16:49:59 -06:00
|
|
|
void __devinit smp_prepare_boot_cpu(void)
|
2006-02-25 14:39:56 -07:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2008-08-03 23:52:41 -06:00
|
|
|
void __init smp_setup_processor_id(void)
|
|
|
|
{
|
|
|
|
if (tlb_type == spitfire)
|
2008-08-04 17:16:20 -06:00
|
|
|
xcall_deliver_impl = spitfire_xcall_deliver;
|
2008-08-03 23:52:41 -06:00
|
|
|
else if (tlb_type == cheetah || tlb_type == cheetah_plus)
|
2008-08-04 17:16:20 -06:00
|
|
|
xcall_deliver_impl = cheetah_xcall_deliver;
|
2008-08-03 23:52:41 -06:00
|
|
|
else
|
2008-08-04 17:16:20 -06:00
|
|
|
xcall_deliver_impl = hypervisor_xcall_deliver;
|
2008-08-03 23:52:41 -06:00
|
|
|
}
|
|
|
|
|
2007-05-25 16:49:59 -06:00
|
|
|
void __devinit smp_fill_in_sib_core_maps(void)
|
2005-04-16 16:20:36 -06:00
|
|
|
{
|
2007-05-25 16:49:59 -06:00
|
|
|
unsigned int i;
|
|
|
|
|
2007-07-16 04:49:40 -06:00
|
|
|
for_each_present_cpu(i) {
|
2007-05-25 16:49:59 -06:00
|
|
|
unsigned int j;
|
|
|
|
|
2007-07-15 02:29:24 -06:00
|
|
|
cpus_clear(cpu_core_map[i]);
|
2007-05-25 16:49:59 -06:00
|
|
|
if (cpu_data(i).core_id == 0) {
|
2007-06-04 18:01:39 -06:00
|
|
|
cpu_set(i, cpu_core_map[i]);
|
2007-05-25 16:49:59 -06:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2007-07-16 04:49:40 -06:00
|
|
|
for_each_present_cpu(j) {
|
2007-05-25 16:49:59 -06:00
|
|
|
if (cpu_data(i).core_id ==
|
|
|
|
cpu_data(j).core_id)
|
2007-06-04 18:01:39 -06:00
|
|
|
cpu_set(j, cpu_core_map[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-16 04:49:40 -06:00
|
|
|
for_each_present_cpu(i) {
|
2007-06-04 18:01:39 -06:00
|
|
|
unsigned int j;
|
|
|
|
|
2007-10-16 02:24:05 -06:00
|
|
|
cpus_clear(per_cpu(cpu_sibling_map, i));
|
2007-06-04 18:01:39 -06:00
|
|
|
if (cpu_data(i).proc_id == -1) {
|
2007-10-16 02:24:05 -06:00
|
|
|
cpu_set(i, per_cpu(cpu_sibling_map, i));
|
2007-06-04 18:01:39 -06:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2007-07-16 04:49:40 -06:00
|
|
|
for_each_present_cpu(j) {
|
2007-06-04 18:01:39 -06:00
|
|
|
if (cpu_data(i).proc_id ==
|
|
|
|
cpu_data(j).proc_id)
|
2007-10-16 02:24:05 -06:00
|
|
|
cpu_set(j, per_cpu(cpu_sibling_map, i));
|
2007-05-25 16:49:59 -06:00
|
|
|
}
|
|
|
|
}
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
|
2007-01-11 00:15:34 -07:00
|
|
|
int __cpuinit __cpu_up(unsigned int cpu)
|
2005-04-16 16:20:36 -06:00
|
|
|
{
|
|
|
|
int ret = smp_boot_one_cpu(cpu);
|
|
|
|
|
|
|
|
if (!ret) {
|
|
|
|
cpu_set(cpu, smp_commenced_mask);
|
|
|
|
while (!cpu_isset(cpu, cpu_online_map))
|
|
|
|
mb();
|
|
|
|
if (!cpu_isset(cpu, cpu_online_map)) {
|
|
|
|
ret = -ENODEV;
|
|
|
|
} else {
|
2006-02-12 00:22:47 -07:00
|
|
|
/* On SUN4V, writes to %tick and %stick are
|
|
|
|
* not allowed.
|
|
|
|
*/
|
|
|
|
if (tlb_type != hypervisor)
|
|
|
|
smp_synchronize_one_tick(cpu);
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
[SPARC64]: Initial LDOM cpu hotplug support.
Only adding cpus is supports at the moment, removal
will come next.
When new cpus are configured, the machine description is
updated. When we get the configure request we pass in a
cpu mask of to-be-added cpus to the mdesc CPU node parser
so it only fetches information for those cpus. That code
also proceeds to update the SMT/multi-core scheduling bitmaps.
cpu_up() does all the work and we return the status back
over the DS channel.
CPUs via dr-cpu need to be booted straight out of the
hypervisor, and this requires:
1) A new trampoline mechanism. CPUs are booted straight
out of the hypervisor with MMU disabled and running in
physical addresses with no mappings installed in the TLB.
The new hvtramp.S code sets up the critical cpu state,
installs the locked TLB mappings for the kernel, and
turns the MMU on. It then proceeds to follow the logic
of the existing trampoline.S SMP cpu bringup code.
2) All calls into OBP have to be disallowed when domaining
is enabled. Since cpus boot straight into the kernel from
the hypervisor, OBP has no state about that cpu and therefore
cannot handle being invoked on that cpu.
Luckily it's only a handful of interfaces which can be called
after the OBP device tree is obtained. For example, rebooting,
halting, powering-off, and setting options node variables.
CPU removal support will require some infrastructure changes
here. Namely we'll have to process the requests via a true
kernel thread instead of in a workqueue. workqueues run on
a per-cpu thread, but when unconfiguring we might need to
force the thread to execute on another cpu if the current cpu
is the one being removed. Removal of a cpu also causes the kernel
to destroy that cpu's workqueue running thread.
Another issue on removal is that we may have interrupts still
pointing to the cpu-to-be-removed. So new code will be needed
to walk the active INO list and retarget those cpus as-needed.
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-07-13 17:03:42 -06:00
|
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
2007-07-16 04:49:40 -06:00
|
|
|
void cpu_play_dead(void)
|
|
|
|
{
|
|
|
|
int cpu = smp_processor_id();
|
|
|
|
unsigned long pstate;
|
|
|
|
|
|
|
|
idle_task_exit();
|
|
|
|
|
|
|
|
if (tlb_type == hypervisor) {
|
|
|
|
struct trap_per_cpu *tb = &trap_block[cpu];
|
|
|
|
|
|
|
|
sun4v_cpu_qconf(HV_CPU_QUEUE_CPU_MONDO,
|
|
|
|
tb->cpu_mondo_pa, 0);
|
|
|
|
sun4v_cpu_qconf(HV_CPU_QUEUE_DEVICE_MONDO,
|
|
|
|
tb->dev_mondo_pa, 0);
|
|
|
|
sun4v_cpu_qconf(HV_CPU_QUEUE_RES_ERROR,
|
|
|
|
tb->resum_mondo_pa, 0);
|
|
|
|
sun4v_cpu_qconf(HV_CPU_QUEUE_NONRES_ERROR,
|
|
|
|
tb->nonresum_mondo_pa, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
cpu_clear(cpu, smp_commenced_mask);
|
|
|
|
membar_safe("#Sync");
|
|
|
|
|
|
|
|
local_irq_disable();
|
|
|
|
|
|
|
|
__asm__ __volatile__(
|
|
|
|
"rdpr %%pstate, %0\n\t"
|
|
|
|
"wrpr %0, %1, %%pstate"
|
|
|
|
: "=r" (pstate)
|
|
|
|
: "i" (PSTATE_IE));
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
barrier();
|
|
|
|
}
|
|
|
|
|
[SPARC64]: Initial LDOM cpu hotplug support.
Only adding cpus is supports at the moment, removal
will come next.
When new cpus are configured, the machine description is
updated. When we get the configure request we pass in a
cpu mask of to-be-added cpus to the mdesc CPU node parser
so it only fetches information for those cpus. That code
also proceeds to update the SMT/multi-core scheduling bitmaps.
cpu_up() does all the work and we return the status back
over the DS channel.
CPUs via dr-cpu need to be booted straight out of the
hypervisor, and this requires:
1) A new trampoline mechanism. CPUs are booted straight
out of the hypervisor with MMU disabled and running in
physical addresses with no mappings installed in the TLB.
The new hvtramp.S code sets up the critical cpu state,
installs the locked TLB mappings for the kernel, and
turns the MMU on. It then proceeds to follow the logic
of the existing trampoline.S SMP cpu bringup code.
2) All calls into OBP have to be disallowed when domaining
is enabled. Since cpus boot straight into the kernel from
the hypervisor, OBP has no state about that cpu and therefore
cannot handle being invoked on that cpu.
Luckily it's only a handful of interfaces which can be called
after the OBP device tree is obtained. For example, rebooting,
halting, powering-off, and setting options node variables.
CPU removal support will require some infrastructure changes
here. Namely we'll have to process the requests via a true
kernel thread instead of in a workqueue. workqueues run on
a per-cpu thread, but when unconfiguring we might need to
force the thread to execute on another cpu if the current cpu
is the one being removed. Removal of a cpu also causes the kernel
to destroy that cpu's workqueue running thread.
Another issue on removal is that we may have interrupts still
pointing to the cpu-to-be-removed. So new code will be needed
to walk the active INO list and retarget those cpus as-needed.
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-07-13 17:03:42 -06:00
|
|
|
int __cpu_disable(void)
|
|
|
|
{
|
2007-07-16 04:49:40 -06:00
|
|
|
int cpu = smp_processor_id();
|
|
|
|
cpuinfo_sparc *c;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for_each_cpu_mask(i, cpu_core_map[cpu])
|
|
|
|
cpu_clear(cpu, cpu_core_map[i]);
|
|
|
|
cpus_clear(cpu_core_map[cpu]);
|
|
|
|
|
2007-10-16 02:24:05 -06:00
|
|
|
for_each_cpu_mask(i, per_cpu(cpu_sibling_map, cpu))
|
|
|
|
cpu_clear(cpu, per_cpu(cpu_sibling_map, i));
|
|
|
|
cpus_clear(per_cpu(cpu_sibling_map, cpu));
|
2007-07-16 04:49:40 -06:00
|
|
|
|
|
|
|
c = &cpu_data(cpu);
|
|
|
|
|
|
|
|
c->core_id = 0;
|
|
|
|
c->proc_id = -1;
|
|
|
|
|
|
|
|
smp_wmb();
|
|
|
|
|
|
|
|
/* Make sure no interrupts point to this cpu. */
|
|
|
|
fixup_irqs();
|
|
|
|
|
|
|
|
local_irq_enable();
|
|
|
|
mdelay(1);
|
|
|
|
local_irq_disable();
|
|
|
|
|
2008-09-03 03:15:30 -06:00
|
|
|
ipi_call_lock();
|
|
|
|
cpu_clear(cpu, cpu_online_map);
|
|
|
|
ipi_call_unlock();
|
|
|
|
|
2009-06-04 03:10:11 -06:00
|
|
|
cpu_map_rebuild();
|
|
|
|
|
2007-07-16 04:49:40 -06:00
|
|
|
return 0;
|
[SPARC64]: Initial LDOM cpu hotplug support.
Only adding cpus is supports at the moment, removal
will come next.
When new cpus are configured, the machine description is
updated. When we get the configure request we pass in a
cpu mask of to-be-added cpus to the mdesc CPU node parser
so it only fetches information for those cpus. That code
also proceeds to update the SMT/multi-core scheduling bitmaps.
cpu_up() does all the work and we return the status back
over the DS channel.
CPUs via dr-cpu need to be booted straight out of the
hypervisor, and this requires:
1) A new trampoline mechanism. CPUs are booted straight
out of the hypervisor with MMU disabled and running in
physical addresses with no mappings installed in the TLB.
The new hvtramp.S code sets up the critical cpu state,
installs the locked TLB mappings for the kernel, and
turns the MMU on. It then proceeds to follow the logic
of the existing trampoline.S SMP cpu bringup code.
2) All calls into OBP have to be disallowed when domaining
is enabled. Since cpus boot straight into the kernel from
the hypervisor, OBP has no state about that cpu and therefore
cannot handle being invoked on that cpu.
Luckily it's only a handful of interfaces which can be called
after the OBP device tree is obtained. For example, rebooting,
halting, powering-off, and setting options node variables.
CPU removal support will require some infrastructure changes
here. Namely we'll have to process the requests via a true
kernel thread instead of in a workqueue. workqueues run on
a per-cpu thread, but when unconfiguring we might need to
force the thread to execute on another cpu if the current cpu
is the one being removed. Removal of a cpu also causes the kernel
to destroy that cpu's workqueue running thread.
Another issue on removal is that we may have interrupts still
pointing to the cpu-to-be-removed. So new code will be needed
to walk the active INO list and retarget those cpus as-needed.
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-07-13 17:03:42 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
void __cpu_die(unsigned int cpu)
|
|
|
|
{
|
2007-07-16 04:49:40 -06:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < 100; i++) {
|
|
|
|
smp_rmb();
|
|
|
|
if (!cpu_isset(cpu, smp_commenced_mask))
|
|
|
|
break;
|
|
|
|
msleep(100);
|
|
|
|
}
|
|
|
|
if (cpu_isset(cpu, smp_commenced_mask)) {
|
|
|
|
printk(KERN_ERR "CPU %u didn't die...\n", cpu);
|
|
|
|
} else {
|
|
|
|
#if defined(CONFIG_SUN_LDOMS)
|
|
|
|
unsigned long hv_err;
|
|
|
|
int limit = 100;
|
|
|
|
|
|
|
|
do {
|
|
|
|
hv_err = sun4v_cpu_stop(cpu);
|
|
|
|
if (hv_err == HV_EOK) {
|
|
|
|
cpu_clear(cpu, cpu_present_map);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (--limit > 0);
|
|
|
|
if (limit <= 0) {
|
|
|
|
printk(KERN_ERR "sun4v_cpu_stop() fails err=%lu\n",
|
|
|
|
hv_err);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
[SPARC64]: Initial LDOM cpu hotplug support.
Only adding cpus is supports at the moment, removal
will come next.
When new cpus are configured, the machine description is
updated. When we get the configure request we pass in a
cpu mask of to-be-added cpus to the mdesc CPU node parser
so it only fetches information for those cpus. That code
also proceeds to update the SMT/multi-core scheduling bitmaps.
cpu_up() does all the work and we return the status back
over the DS channel.
CPUs via dr-cpu need to be booted straight out of the
hypervisor, and this requires:
1) A new trampoline mechanism. CPUs are booted straight
out of the hypervisor with MMU disabled and running in
physical addresses with no mappings installed in the TLB.
The new hvtramp.S code sets up the critical cpu state,
installs the locked TLB mappings for the kernel, and
turns the MMU on. It then proceeds to follow the logic
of the existing trampoline.S SMP cpu bringup code.
2) All calls into OBP have to be disallowed when domaining
is enabled. Since cpus boot straight into the kernel from
the hypervisor, OBP has no state about that cpu and therefore
cannot handle being invoked on that cpu.
Luckily it's only a handful of interfaces which can be called
after the OBP device tree is obtained. For example, rebooting,
halting, powering-off, and setting options node variables.
CPU removal support will require some infrastructure changes
here. Namely we'll have to process the requests via a true
kernel thread instead of in a workqueue. workqueues run on
a per-cpu thread, but when unconfiguring we might need to
force the thread to execute on another cpu if the current cpu
is the one being removed. Removal of a cpu also causes the kernel
to destroy that cpu's workqueue running thread.
Another issue on removal is that we may have interrupts still
pointing to the cpu-to-be-removed. So new code will be needed
to walk the active INO list and retarget those cpus as-needed.
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-07-13 17:03:42 -06:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
void __init smp_cpus_done(unsigned int max_cpus)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void smp_send_reschedule(int cpu)
|
|
|
|
{
|
2008-08-04 00:56:28 -06:00
|
|
|
xcall_deliver((u64) &xcall_receive_signal, 0, 0,
|
|
|
|
&cpumask_of_cpu(cpu));
|
|
|
|
}
|
|
|
|
|
|
|
|
void smp_receive_signal_client(int irq, struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
clear_softint(1 << irq);
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/* This is a nop because we capture all other cpus
|
|
|
|
* anyways when making the PROM active.
|
|
|
|
*/
|
|
|
|
void smp_send_stop(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2009-04-08 21:32:02 -06:00
|
|
|
/**
|
|
|
|
* pcpu_alloc_bootmem - NUMA friendly alloc_bootmem wrapper for percpu
|
|
|
|
* @cpu: cpu to allocate for
|
|
|
|
* @size: size allocation in bytes
|
|
|
|
* @align: alignment
|
|
|
|
*
|
|
|
|
* Allocate @size bytes aligned at @align for cpu @cpu. This wrapper
|
|
|
|
* does the right thing for NUMA regardless of the current
|
|
|
|
* configuration.
|
|
|
|
*
|
|
|
|
* RETURNS:
|
|
|
|
* Pointer to the allocated area on success, NULL on failure.
|
|
|
|
*/
|
|
|
|
static void * __init pcpu_alloc_bootmem(unsigned int cpu, unsigned long size,
|
|
|
|
unsigned long align)
|
|
|
|
{
|
|
|
|
const unsigned long goal = __pa(MAX_DMA_ADDRESS);
|
|
|
|
#ifdef CONFIG_NEED_MULTIPLE_NODES
|
|
|
|
int node = cpu_to_node(cpu);
|
|
|
|
void *ptr;
|
|
|
|
|
|
|
|
if (!node_online(node) || !NODE_DATA(node)) {
|
|
|
|
ptr = __alloc_bootmem(size, align, goal);
|
|
|
|
pr_info("cpu %d has no node %d or node-local memory\n",
|
|
|
|
cpu, node);
|
|
|
|
pr_debug("per cpu data for cpu%d %lu bytes at %016lx\n",
|
|
|
|
cpu, size, __pa(ptr));
|
|
|
|
} else {
|
|
|
|
ptr = __alloc_bootmem_node(NODE_DATA(node),
|
|
|
|
size, align, goal);
|
|
|
|
pr_debug("per cpu data for cpu%d %lu bytes on node%d at "
|
|
|
|
"%016lx\n", cpu, size, node, __pa(ptr));
|
|
|
|
}
|
|
|
|
return ptr;
|
|
|
|
#else
|
|
|
|
return __alloc_bootmem(size, align, goal);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t pcpur_size __initdata;
|
|
|
|
static void **pcpur_ptrs __initdata;
|
|
|
|
|
|
|
|
static struct page * __init pcpur_get_page(unsigned int cpu, int pageno)
|
|
|
|
{
|
|
|
|
size_t off = (size_t)pageno << PAGE_SHIFT;
|
|
|
|
|
|
|
|
if (off >= pcpur_size)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return virt_to_page(pcpur_ptrs[cpu] + off);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define PCPU_CHUNK_SIZE (4UL * 1024UL * 1024UL)
|
|
|
|
|
|
|
|
static void __init pcpu_map_range(unsigned long start, unsigned long end,
|
|
|
|
struct page *page)
|
|
|
|
{
|
|
|
|
unsigned long pfn = page_to_pfn(page);
|
|
|
|
unsigned long pte_base;
|
|
|
|
|
|
|
|
BUG_ON((pfn<<PAGE_SHIFT)&(PCPU_CHUNK_SIZE - 1UL));
|
|
|
|
|
|
|
|
pte_base = (_PAGE_VALID | _PAGE_SZ4MB_4U |
|
|
|
|
_PAGE_CP_4U | _PAGE_CV_4U |
|
|
|
|
_PAGE_P_4U | _PAGE_W_4U);
|
|
|
|
if (tlb_type == hypervisor)
|
|
|
|
pte_base = (_PAGE_VALID | _PAGE_SZ4MB_4V |
|
|
|
|
_PAGE_CP_4V | _PAGE_CV_4V |
|
|
|
|
_PAGE_P_4V | _PAGE_W_4V);
|
|
|
|
|
|
|
|
while (start < end) {
|
|
|
|
pgd_t *pgd = pgd_offset_k(start);
|
|
|
|
unsigned long this_end;
|
|
|
|
pud_t *pud;
|
|
|
|
pmd_t *pmd;
|
|
|
|
pte_t *pte;
|
|
|
|
|
|
|
|
pud = pud_offset(pgd, start);
|
|
|
|
if (pud_none(*pud)) {
|
|
|
|
pmd_t *new;
|
|
|
|
|
|
|
|
new = __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, PAGE_SIZE);
|
|
|
|
pud_populate(&init_mm, pud, new);
|
|
|
|
}
|
|
|
|
|
|
|
|
pmd = pmd_offset(pud, start);
|
|
|
|
if (!pmd_present(*pmd)) {
|
|
|
|
pte_t *new;
|
|
|
|
|
|
|
|
new = __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, PAGE_SIZE);
|
|
|
|
pmd_populate_kernel(&init_mm, pmd, new);
|
|
|
|
}
|
|
|
|
|
|
|
|
pte = pte_offset_kernel(pmd, start);
|
|
|
|
this_end = (start + PMD_SIZE) & PMD_MASK;
|
|
|
|
if (this_end > end)
|
|
|
|
this_end = end;
|
|
|
|
|
|
|
|
while (start < this_end) {
|
|
|
|
unsigned long paddr = pfn << PAGE_SHIFT;
|
|
|
|
|
|
|
|
pte_val(*pte) = (paddr | pte_base);
|
|
|
|
|
|
|
|
start += PAGE_SIZE;
|
|
|
|
pte++;
|
|
|
|
pfn++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-01 17:15:20 -06:00
|
|
|
void __init setup_per_cpu_areas(void)
|
2005-04-16 16:20:36 -06:00
|
|
|
{
|
2009-04-08 21:32:02 -06:00
|
|
|
size_t dyn_size, static_size = __per_cpu_end - __per_cpu_start;
|
|
|
|
static struct vm_struct vm;
|
|
|
|
unsigned long delta, cpu;
|
|
|
|
size_t pcpu_unit_size;
|
|
|
|
size_t ptrs_size;
|
|
|
|
|
|
|
|
pcpur_size = PFN_ALIGN(static_size + PERCPU_MODULE_RESERVE +
|
|
|
|
PERCPU_DYNAMIC_RESERVE);
|
|
|
|
dyn_size = pcpur_size - static_size - PERCPU_MODULE_RESERVE;
|
|
|
|
|
|
|
|
|
2009-07-21 02:11:50 -06:00
|
|
|
ptrs_size = PFN_ALIGN(nr_cpu_ids * sizeof(pcpur_ptrs[0]));
|
2009-04-08 21:32:02 -06:00
|
|
|
pcpur_ptrs = alloc_bootmem(ptrs_size);
|
|
|
|
|
|
|
|
for_each_possible_cpu(cpu) {
|
|
|
|
pcpur_ptrs[cpu] = pcpu_alloc_bootmem(cpu, PCPU_CHUNK_SIZE,
|
|
|
|
PCPU_CHUNK_SIZE);
|
|
|
|
|
|
|
|
free_bootmem(__pa(pcpur_ptrs[cpu] + pcpur_size),
|
|
|
|
PCPU_CHUNK_SIZE - pcpur_size);
|
|
|
|
|
|
|
|
memcpy(pcpur_ptrs[cpu], __per_cpu_load, static_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* allocate address and map */
|
|
|
|
vm.flags = VM_ALLOC;
|
2009-07-21 02:11:50 -06:00
|
|
|
vm.size = nr_cpu_ids * PCPU_CHUNK_SIZE;
|
2009-04-08 21:32:02 -06:00
|
|
|
vm_area_register_early(&vm, PCPU_CHUNK_SIZE);
|
|
|
|
|
|
|
|
for_each_possible_cpu(cpu) {
|
|
|
|
unsigned long start = (unsigned long) vm.addr;
|
|
|
|
unsigned long end;
|
|
|
|
|
|
|
|
start += cpu * PCPU_CHUNK_SIZE;
|
|
|
|
end = start + PCPU_CHUNK_SIZE;
|
|
|
|
pcpu_map_range(start, end, virt_to_page(pcpur_ptrs[cpu]));
|
|
|
|
}
|
|
|
|
|
|
|
|
pcpu_unit_size = pcpu_setup_first_chunk(pcpur_get_page, static_size,
|
|
|
|
PERCPU_MODULE_RESERVE, dyn_size,
|
|
|
|
PCPU_CHUNK_SIZE, vm.addr, NULL);
|
2005-04-16 16:20:36 -06:00
|
|
|
|
2009-04-08 21:32:02 -06:00
|
|
|
free_bootmem(__pa(pcpur_ptrs), ptrs_size);
|
2006-12-15 00:40:57 -07:00
|
|
|
|
2009-04-08 21:32:02 -06:00
|
|
|
delta = (unsigned long)pcpu_base_addr - (unsigned long)__per_cpu_start;
|
|
|
|
for_each_possible_cpu(cpu) {
|
|
|
|
__per_cpu_offset(cpu) = delta + cpu * pcpu_unit_size;
|
2009-04-01 02:47:10 -06:00
|
|
|
}
|
2006-05-31 02:24:02 -06:00
|
|
|
|
|
|
|
/* Setup %g5 for the boot cpu. */
|
|
|
|
__local_per_cpu_offset = __per_cpu_offset(smp_processor_id());
|
2009-05-26 23:37:25 -06:00
|
|
|
|
|
|
|
of_fill_in_cpu_data();
|
|
|
|
if (tlb_type == hypervisor)
|
2009-06-15 04:06:18 -06:00
|
|
|
mdesc_fill_in_cpu_data(cpu_all_mask);
|
2005-04-16 16:20:36 -06:00
|
|
|
}
|