Commit a54123e2 authored by Bob Breuer's avatar Bob Breuer Committed by David S. Miller

[SPARC]: Try to start getting SMP back into shape.

Todo items:
 - IRQ_INPROGRESS flag - use sparc64 irq buckets, or generic irq_desc?
 - sun4d
 - re-indent large chunks of sun4m_smp.c
 - some places assume sequential cpu numbering (i.e. 0,1 instead of 0,2)

Last I checked (with 2.6.14), random programs segfault with dual
HyperSPARC.  And with SuperSPARC II's, it seems stable but will
eventually die from a write lock error (wrong lock owner or something).

I haven't tried the HyperSPARC + highmem combination recently, so that
may still be a problem.
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 674a396c
......@@ -23,7 +23,6 @@ menu "General machine setup"
config SMP
bool "Symmetric multi-processing support (does not work on sun4/sun4c)"
depends on BROKEN
---help---
This enables support for systems with more than one CPU. If you have
a system with only one CPU, say N. If you have a system with more
......
......@@ -154,9 +154,11 @@ void (*sparc_init_timers)(irqreturn_t (*)(int, void *,struct pt_regs *)) =
struct irqaction static_irqaction[MAX_STATIC_ALLOC];
int static_irq_count;
struct irqaction *irq_action[NR_IRQS] = {
[0 ... (NR_IRQS-1)] = NULL
};
struct {
struct irqaction *action;
int flags;
} sparc_irq[NR_IRQS];
#define SPARC_IRQ_INPROGRESS 1
/* Used to protect the IRQ action lists */
DEFINE_SPINLOCK(irq_action_lock);
......@@ -177,7 +179,7 @@ int show_interrupts(struct seq_file *p, void *v)
}
spin_lock_irqsave(&irq_action_lock, flags);
if (i < NR_IRQS) {
action = *(i + irq_action);
action = sparc_irq[i].action;
if (!action)
goto out_unlock;
seq_printf(p, "%3d: ", i);
......@@ -186,7 +188,7 @@ int show_interrupts(struct seq_file *p, void *v)
#else
for_each_online_cpu(j) {
seq_printf(p, "%10u ",
kstat_cpu(cpu_logical_map(j)).irqs[i]);
kstat_cpu(j).irqs[i]);
}
#endif
seq_printf(p, " %c %s",
......@@ -207,7 +209,7 @@ out_unlock:
void free_irq(unsigned int irq, void *dev_id)
{
struct irqaction * action;
struct irqaction * tmp = NULL;
struct irqaction **actionp;
unsigned long flags;
unsigned int cpu_irq;
......@@ -225,7 +227,8 @@ void free_irq(unsigned int irq, void *dev_id)
spin_lock_irqsave(&irq_action_lock, flags);
action = *(cpu_irq + irq_action);
actionp = &sparc_irq[cpu_irq].action;
action = *actionp;
if (!action->handler) {
printk("Trying to free free IRQ%d\n",irq);
......@@ -235,7 +238,7 @@ void free_irq(unsigned int irq, void *dev_id)
for (; action; action = action->next) {
if (action->dev_id == dev_id)
break;
tmp = action;
actionp = &action->next;
}
if (!action) {
printk("Trying to free free shared IRQ%d\n",irq);
......@@ -255,10 +258,7 @@ void free_irq(unsigned int irq, void *dev_id)
goto out_unlock;
}
if (action && tmp)
tmp->next = action->next;
else
*(cpu_irq + irq_action) = action->next;
*actionp = action->next;
spin_unlock_irqrestore(&irq_action_lock, flags);
......@@ -268,7 +268,7 @@ void free_irq(unsigned int irq, void *dev_id)
kfree(action);
if (!(*(cpu_irq + irq_action)))
if (!sparc_irq[cpu_irq].action)
disable_irq(irq);
out_unlock:
......@@ -287,8 +287,11 @@ EXPORT_SYMBOL(free_irq);
#ifdef CONFIG_SMP
void synchronize_irq(unsigned int irq)
{
printk("synchronize_irq says: implement me!\n");
BUG();
unsigned int cpu_irq;
cpu_irq = irq & (NR_IRQS - 1);
while (sparc_irq[cpu_irq].flags & SPARC_IRQ_INPROGRESS)
cpu_relax();
}
#endif /* SMP */
......@@ -299,7 +302,7 @@ void unexpected_irq(int irq, void *dev_id, struct pt_regs * regs)
unsigned int cpu_irq;
cpu_irq = irq & (NR_IRQS - 1);
action = *(cpu_irq + irq_action);
action = sparc_irq[cpu_irq].action;
printk("IO device interrupt, irq = %d\n", irq);
printk("PC = %08lx NPC = %08lx FP=%08lx\n", regs->pc,
......@@ -330,7 +333,8 @@ void handler_irq(int irq, struct pt_regs * regs)
if(irq < 10)
smp4m_irq_rotate(cpu);
#endif
action = *(irq + irq_action);
action = sparc_irq[irq].action;
sparc_irq[irq].flags |= SPARC_IRQ_INPROGRESS;
kstat_cpu(cpu).irqs[irq]++;
do {
if (!action || !action->handler)
......@@ -338,6 +342,7 @@ void handler_irq(int irq, struct pt_regs * regs)
action->handler(irq, action->dev_id, regs);
action = action->next;
} while (action);
sparc_irq[irq].flags &= ~SPARC_IRQ_INPROGRESS;
enable_pil_irq(irq);
irq_exit();
}
......@@ -389,7 +394,7 @@ int request_fast_irq(unsigned int irq,
spin_lock_irqsave(&irq_action_lock, flags);
action = *(cpu_irq + irq_action);
action = sparc_irq[cpu_irq].action;
if(action) {
if(action->flags & SA_SHIRQ)
panic("Trying to register fast irq when already shared.\n");
......@@ -452,7 +457,7 @@ int request_fast_irq(unsigned int irq,
action->dev_id = NULL;
action->next = NULL;
*(cpu_irq + irq_action) = action;
sparc_irq[cpu_irq].action = action;
enable_irq(irq);
......@@ -467,7 +472,7 @@ int request_irq(unsigned int irq,
irqreturn_t (*handler)(int, void *, struct pt_regs *),
unsigned long irqflags, const char * devname, void *dev_id)
{
struct irqaction * action, *tmp = NULL;
struct irqaction * action, **actionp;
unsigned long flags;
unsigned int cpu_irq;
int ret;
......@@ -490,20 +495,20 @@ int request_irq(unsigned int irq,
spin_lock_irqsave(&irq_action_lock, flags);
action = *(cpu_irq + irq_action);
actionp = &sparc_irq[cpu_irq].action;
action = *actionp;
if (action) {
if ((action->flags & SA_SHIRQ) && (irqflags & SA_SHIRQ)) {
for (tmp = action; tmp->next; tmp = tmp->next);
} else {
if (!(action->flags & SA_SHIRQ) || !(irqflags & SA_SHIRQ)) {
ret = -EBUSY;
goto out_unlock;
}
if ((action->flags & SA_INTERRUPT) ^ (irqflags & SA_INTERRUPT)) {
if ((action->flags & SA_INTERRUPT) != (irqflags & SA_INTERRUPT)) {
printk("Attempt to mix fast and slow interrupts on IRQ%d denied\n", irq);
ret = -EBUSY;
goto out_unlock;
}
action = NULL; /* Or else! */
for ( ; action; action = *actionp)
actionp = &action->next;
}
/* If this is flagged as statically allocated then we use our
......@@ -532,10 +537,7 @@ int request_irq(unsigned int irq,
action->next = NULL;
action->dev_id = dev_id;
if (tmp)
tmp->next = action;
else
*(cpu_irq + irq_action) = action;
*actionp = action;
enable_irq(irq);
......
......@@ -45,6 +45,7 @@ volatile int __cpu_logical_map[NR_CPUS];
cpumask_t cpu_online_map = CPU_MASK_NONE;
cpumask_t phys_cpu_present_map = CPU_MASK_NONE;
cpumask_t smp_commenced_mask = CPU_MASK_NONE;
/* The only guaranteed locking primitive available on all Sparc
* processors is 'ldstub [%reg + immediate], %dest_reg' which atomically
......@@ -57,11 +58,6 @@ cpumask_t phys_cpu_present_map = CPU_MASK_NONE;
/* Used to make bitops atomic */
unsigned char bitops_spinlock = 0;
volatile unsigned long ipi_count;
volatile int smp_process_available=0;
volatile int smp_commenced = 0;
void __init smp_store_cpu_info(int id)
{
int cpu_node;
......@@ -79,6 +75,22 @@ void __init smp_store_cpu_info(int id)
void __init smp_cpus_done(unsigned int max_cpus)
{
extern void smp4m_smp_done(void);
unsigned long bogosum = 0;
int cpu, num;
for (cpu = 0, num = 0; cpu < NR_CPUS; cpu++)
if (cpu_online(cpu)) {
num++;
bogosum += cpu_data(cpu).udelay_val;
}
printk("Total of %d processors activated (%lu.%02lu BogoMIPS).\n",
num, bogosum/(500000/HZ),
(bogosum/(5000/HZ))%100);
BUG_ON(sparc_cpu_model != sun4m);
smp4m_smp_done();
}
void cpu_panic(void)
......@@ -89,17 +101,6 @@ void cpu_panic(void)
struct linux_prom_registers smp_penguin_ctable __initdata = { 0 };
void __init smp_boot_cpus(void)
{
extern void smp4m_boot_cpus(void);
extern void smp4d_boot_cpus(void);
if (sparc_cpu_model == sun4m)
smp4m_boot_cpus();
else
smp4d_boot_cpus();
}
void smp_send_reschedule(int cpu)
{
/* See sparc64 */
......@@ -252,20 +253,61 @@ int setup_profiling_timer(unsigned int multiplier)
return 0;
}
void __init smp_prepare_cpus(unsigned int maxcpus)
void __init smp_prepare_cpus(unsigned int max_cpus)
{
extern void smp4m_boot_cpus(void);
int i, cpuid, ncpus, extra;
BUG_ON(sparc_cpu_model != sun4m);
printk("Entering SMP Mode...\n");
ncpus = 1;
extra = 0;
for (i = 0; !cpu_find_by_instance(i, NULL, &cpuid); i++) {
if (cpuid == boot_cpu_id)
continue;
if (cpuid < NR_CPUS && ncpus++ < max_cpus)
cpu_set(cpuid, phys_cpu_present_map);
else
extra++;
}
if (max_cpus >= NR_CPUS && extra)
printk("Warning: NR_CPUS is too low to start all cpus\n");
smp_store_cpu_info(boot_cpu_id);
smp4m_boot_cpus();
}
void __devinit smp_prepare_boot_cpu(void)
{
current_thread_info()->cpu = hard_smp_processor_id();
cpu_set(smp_processor_id(), cpu_online_map);
cpu_set(smp_processor_id(), phys_cpu_present_map);
int cpuid = hard_smp_processor_id();
if (cpuid >= NR_CPUS) {
prom_printf("Serious problem, boot cpu id >= NR_CPUS\n");
prom_halt();
}
if (cpuid != 0)
printk("boot cpu id != 0, this could work but is untested\n");
current_thread_info()->cpu = cpuid;
cpu_set(cpuid, cpu_online_map);
cpu_set(cpuid, phys_cpu_present_map);
}
int __devinit __cpu_up(unsigned int cpu)
{
panic("smp doesn't work\n");
extern int smp4m_boot_one_cpu(int);
int ret;
ret = smp4m_boot_one_cpu(cpu);
if (!ret) {
cpu_set(cpu, smp_commenced_mask);
while (!cpu_online(cpu))
mb();
}
return ret;
}
void smp_bogo(struct seq_file *m)
......
......@@ -136,10 +136,6 @@ EXPORT_PER_CPU_SYMBOL(__cpu_data);
/* IRQ implementation. */
EXPORT_SYMBOL(synchronize_irq);
/* Misc SMP information */
EXPORT_SYMBOL(__cpu_number_map);
EXPORT_SYMBOL(__cpu_logical_map);
/* CPU online map and active count. */
EXPORT_SYMBOL(cpu_online_map);
EXPORT_SYMBOL(phys_cpu_present_map);
......
......@@ -54,7 +54,7 @@ unsigned char cpu_leds[32];
unsigned char sbus_tid[32];
#endif
extern struct irqaction *irq_action[];
static struct irqaction *irq_action[NR_IRQS];
extern spinlock_t irq_action_lock;
struct sbus_action {
......
......@@ -46,14 +46,16 @@ extern volatile int smp_processors_ready;
extern int smp_num_cpus;
static int smp_highest_cpu;
extern volatile unsigned long cpu_callin_map[NR_CPUS];
extern struct cpuinfo_sparc cpu_data[NR_CPUS];
extern cpuinfo_sparc cpu_data[NR_CPUS];
extern unsigned char boot_cpu_id;
extern int smp_activated;
extern volatile int __cpu_number_map[NR_CPUS];
extern volatile int __cpu_logical_map[NR_CPUS];
extern volatile unsigned long ipi_count;
extern volatile int smp_process_available;
extern volatile int smp_commenced;
extern cpumask_t smp_commenced_mask;
extern int __smp4d_processor_id(void);
/* #define SMP_DEBUG */
......@@ -136,7 +138,7 @@ void __init smp4d_callin(void)
local_irq_enable(); /* We don't allow PIL 14 yet */
while(!smp_commenced)
while (!cpu_isset(cpuid, smp_commenced_mask))
barrier();
spin_lock_irqsave(&sun4d_imsk_lock, flags);
......
......@@ -40,15 +40,11 @@ extern ctxd_t *srmmu_ctx_table_phys;
extern void calibrate_delay(void);
extern volatile int smp_processors_ready;
extern int smp_num_cpus;
extern volatile unsigned long cpu_callin_map[NR_CPUS];
extern unsigned char boot_cpu_id;
extern int smp_activated;
extern volatile int __cpu_number_map[NR_CPUS];
extern volatile int __cpu_logical_map[NR_CPUS];
extern volatile unsigned long ipi_count;
extern volatile int smp_process_available;
extern volatile int smp_commenced;
extern cpumask_t smp_commenced_mask;
extern int __smp4m_processor_id(void);
/*#define SMP_DEBUG*/
......@@ -77,8 +73,6 @@ void __init smp4m_callin(void)
local_flush_cache_all();
local_flush_tlb_all();
set_irq_udt(boot_cpu_id);
/* Get our local ticker going. */
smp_setup_percpu_timer();
......@@ -95,8 +89,9 @@ void __init smp4m_callin(void)
* to call the scheduler code.
*/
/* Allow master to continue. */
swap((unsigned long *)&cpu_callin_map[cpuid], 1);
swap(&cpu_callin_map[cpuid], 1);
/* XXX: What's up with all the flushes? */
local_flush_cache_all();
local_flush_tlb_all();
......@@ -111,13 +106,14 @@ void __init smp4m_callin(void)
atomic_inc(&init_mm.mm_count);
current->active_mm = &init_mm;
while(!smp_commenced)
barrier();
local_flush_cache_all();
local_flush_tlb_all();
while (!cpu_isset(cpuid, smp_commenced_mask))
mb();
local_irq_enable();
cpu_set(cpuid, cpu_online_map);
/* last one in gets all the interrupts (for testing) */
set_irq_udt(boot_cpu_id);
}
extern void init_IRQ(void);
......@@ -134,45 +130,22 @@ extern unsigned long trapbase_cpu3[];
void __init smp4m_boot_cpus(void)
{
int cpucount = 0;
int i, mid;
printk("Entering SMP Mode...\n");
local_irq_enable();
cpus_clear(cpu_present_map);
for (i = 0; !cpu_find_by_instance(i, NULL, &mid); i++)
cpu_set(mid, cpu_present_map);
for(i=0; i < NR_CPUS; i++) {
__cpu_number_map[i] = -1;
__cpu_logical_map[i] = -1;
}
__cpu_number_map[boot_cpu_id] = 0;
__cpu_logical_map[0] = boot_cpu_id;
current_thread_info()->cpu = boot_cpu_id;
smp_store_cpu_info(boot_cpu_id);
set_irq_udt(boot_cpu_id);
smp_setup_percpu_timer();
local_flush_cache_all();
if(cpu_find_by_instance(1, NULL, NULL))
return; /* Not an MP box. */
for(i = 0; i < NR_CPUS; i++) {
if(i == boot_cpu_id)
continue;
}
if (cpu_isset(i, cpu_present_map)) {
int smp4m_boot_one_cpu(int i)
{
extern unsigned long sun4m_cpu_startup;
unsigned long *entry = &sun4m_cpu_startup;
struct task_struct *p;
int timeout;
int cpu_node;
cpu_find_by_mid(i, &cpu_node);
/* Cook up an idler for this guy. */
p = fork_idle(i);
cpucount++;
current_set[i] = task_thread_info(p);
/* See trampoline.S for details... */
entry += ((i-1) * 3);
......@@ -189,7 +162,7 @@ void __init smp4m_boot_cpus(void)
/* whirrr, whirrr, whirrrrrrrrr... */
printk("Starting CPU %d at %p\n", i, entry);
local_flush_cache_all();
prom_startcpu(cpu_data(i).prom_node,
prom_startcpu(cpu_node,
&smp_penguin_ctable, 0, (char *)entry);
/* wheee... it's going... */
......@@ -198,38 +171,35 @@ void __init smp4m_boot_cpus(void)
break;
udelay(200);
}
if(cpu_callin_map[i]) {
/* Another "Red Snapper". */
__cpu_number_map[i] = i;
__cpu_logical_map[i] = i;
} else {
cpucount--;
if (!(cpu_callin_map[i])) {
printk("Processor %d is stuck.\n", i);
return -ENODEV;
}
}
if(!(cpu_callin_map[i])) {
cpu_clear(i, cpu_present_map);
__cpu_number_map[i] = -1;
local_flush_cache_all();
return 0;
}
void __init smp4m_smp_done(void)
{
int i, first;
int *prev;
/* setup cpu list for irq rotation */
first = 0;
prev = &first;
for (i = 0; i < NR_CPUS; i++) {
if (cpu_online(i)) {
*prev = i;
prev = &cpu_data(i).next;
}
}
*prev = first;
local_flush_cache_all();
if(cpucount == 0) {
printk("Error: only one Processor found.\n");
cpu_present_map = cpumask_of_cpu(smp_processor_id());
} else {
unsigned long bogosum = 0;
for_each_present_cpu(i)
bogosum += cpu_data(i).udelay_val;
printk("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n",
cpucount + 1,
bogosum/(500000/HZ),
(bogosum/(5000/HZ))%100);
smp_activated = 1;
smp_num_cpus = cpucount + 1;
}
/* Free unneeded trap tables */
if (!cpu_isset(i, cpu_present_map)) {
if (!cpu_isset(1, cpu_present_map)) {
ClearPageReserved(virt_to_page(trapbase_cpu1));
init_page_count(virt_to_page(trapbase_cpu1));
free_page((unsigned long)trapbase_cpu1);
......@@ -263,6 +233,9 @@ void __init smp4m_boot_cpus(void)
*/
void smp4m_irq_rotate(int cpu)
{
int next = cpu_data(cpu).next;
if (next != cpu)
set_irq_udt(next);
}
/* Cross calls, in order to work efficiently and atomically do all
......@@ -289,7 +262,7 @@ void smp4m_message_pass(int target, int msg, unsigned long data, int wait)
smp_cpu_in_msg[me]++;
if(target == MSG_ALL_BUT_SELF || target == MSG_ALL) {
mask = cpu_present_map;
mask = cpu_online_map;
if(target == MSG_ALL_BUT_SELF)
cpu_clear(me, mask);
for(i = 0; i < 4; i++) {
......@@ -314,8 +287,8 @@ static struct smp_funcall {
unsigned long arg3;
unsigned long arg4;
unsigned long arg5;
unsigned long processors_in[NR_CPUS]; /* Set when ipi entered. */
unsigned long processors_out[NR_CPUS]; /* Set when ipi exited. */
unsigned long processors_in[SUN4M_NCPUS]; /* Set when ipi entered. */
unsigned long processors_out[SUN4M_NCPUS]; /* Set when ipi exited. */
} ccall_info;
static DEFINE_SPINLOCK(cross_call_lock);
......@@ -324,8 +297,7 @@ static DEFINE_SPINLOCK(cross_call_lock);
void smp4m_cross_call(smpfunc_t func, unsigned long arg1, unsigned long arg2,
unsigned long arg3, unsigned long arg4, unsigned long arg5)
{
if(smp_processors_ready) {
register int ncpus = smp_num_cpus;
register int ncpus = SUN4M_NCPUS;
unsigned long flags;
spin_lock_irqsave(&cross_call_lock, flags);
......@@ -340,7 +312,7 @@ void smp4m_cross_call(smpfunc_t func, unsigned long arg1, unsigned long arg2,
/* Init receive/complete mapping, plus fire the IPI's off. */
{
cpumask_t mask = cpu_present_map;
cpumask_t mask = cpu_online_map;
register int i;
cpu_clear(smp_processor_id(), mask);
......@@ -373,7 +345,6 @@ void smp4m_cross_call(smpfunc_t func, unsigned long arg1, unsigned long arg2,
}
spin_unlock_irqrestore(&cross_call_lock, flags);
}
}
/* Running cross calls. */
......
......@@ -1302,7 +1302,12 @@ void __init srmmu_paging_init(void)
flush_cache_all();
srmmu_set_ctable_ptr((unsigned long)srmmu_ctx_table_phys);
#ifdef CONFIG_SMP
/* Stop from hanging here... */
local_flush_tlb_all();
#else
flush_tlb_all();
#endif
poke_srmmu();
#ifdef CONFIG_SUN_IO
......@@ -1419,6 +1424,7 @@ static void __init init_vac_layout(void)
max_size = vac_cache_size;
if(vac_line_size < min_line_size)
min_line_size = vac_line_size;
//FIXME: cpus not contiguous!!
cpu++;
if (cpu >= NR_CPUS || !cpu_online(cpu))
break;
......
......@@ -18,6 +18,7 @@ typedef struct {
unsigned int counter;
int prom_node;
int mid;
int next;
} cpuinfo_sparc;
DECLARE_PER_CPU(cpuinfo_sparc, __cpu_data);
......
......@@ -81,16 +81,9 @@ static inline int smp_call_function(void (*func)(void *info), void *info, int no
return 0;
}
extern __volatile__ int __cpu_number_map[NR_CPUS];
extern __volatile__ int __cpu_logical_map[NR_CPUS];
static inline int cpu_logical_map(int cpu)
{
return __cpu_logical_map[cpu];
}
static inline int cpu_number_map(int cpu)
{
return __cpu_number_map[cpu];
return cpu;
}
static inline int hard_smp4m_processor_id(void)
......
......@@ -94,7 +94,7 @@ static inline void __read_lock(raw_rwlock_t *rw)
#define __raw_read_lock(lock) \
do { unsigned long flags; \
local_irq_save(flags); \
__raw_read_lock(lock); \
__read_lock(lock); \
local_irq_restore(flags); \
} while(0)
......@@ -114,11 +114,11 @@ static inline void __read_unlock(raw_rwlock_t *rw)
#define __raw_read_unlock(lock) \
do { unsigned long flags; \
local_irq_save(flags); \
__raw_read_unlock(lock); \
__read_unlock(lock); \
local_irq_restore(flags); \
} while(0)
extern __inline__ void __raw_write_lock(raw_rwlock_t *rw)
static inline void __raw_write_lock(raw_rwlock_t *rw)
{
register raw_rwlock_t *lp asm("g1");
lp = rw;
......@@ -131,9 +131,28 @@ extern __inline__ void __raw_write_lock(raw_rwlock_t *rw)
: "g2", "g4", "memory", "cc");
}
static inline int __raw_write_trylock(raw_rwlock_t *rw)
{
unsigned int val;
__asm__ __volatile__("ldstub [%1 + 3], %0"
: "=r" (val)
: "r" (&rw->lock)
: "memory");
if (val == 0) {
val = rw->lock & ~0xff;
if (val)
((volatile u8*)&rw->lock)[3] = 0;
}
return (val == 0);
}
#define __raw_write_unlock(rw) do { (rw)->lock = 0; } while(0)
#define __raw_spin_lock_flags(lock, flags) __raw_spin_lock(lock)
#define __raw_read_trylock(lock) generic__raw_read_trylock(lock)
#endif /* !(__ASSEMBLY__) */
......
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