Commit b95a5c4e authored by Matthew Wilcox's avatar Matthew Wilcox Committed by Linus Torvalds

[PATCH] parisc: Fix N-class SMP

Fix N class SMP

The main fixes are:

 - memory barriers to our space and control register moves
 - fix for the N class merced bus problem which can't have more than
   one PxTLB broadcast outstanding at once
 - make smp_call_function() wait until the function completes

This now boots and runs on a 32MB N4000 in Fort Collins with 2 cpus
and discontig mem support.
Committed-by: default avatarJames Bottomley <jejb@parisc-linux.org>
Committed-by: default avatarRandolph Chung <tausq@parisc-linux.org>
parent 865e59dd
......@@ -33,6 +33,17 @@ int dcache_stride;
int icache_stride;
EXPORT_SYMBOL(dcache_stride);
#if defined(CONFIG_SMP)
/* On some machines (e.g. ones with the Merced bus), there can be
* only a single PxTLB broadcast at a time; this must be guaranteed
* by software. We put a spinlock around all TLB flushes to
* ensure this.
*/
spinlock_t pa_tlb_lock = SPIN_LOCK_UNLOCKED;
EXPORT_SYMBOL(pa_tlb_lock);
#endif
struct pdc_cache_info cache_info;
#ifndef CONFIG_PA20
static struct pdc_btlb_info btlb_info;
......@@ -306,3 +317,13 @@ EXPORT_SYMBOL(flush_kernel_dcache_range_asm);
EXPORT_SYMBOL(flush_kernel_dcache_page);
EXPORT_SYMBOL(flush_data_cache_local);
EXPORT_SYMBOL(flush_kernel_icache_range_asm);
void clear_user_page_asm(void *page, unsigned long vaddr)
{
/* This function is implemented in assembly in pacache.S */
extern void __clear_user_page_asm(void *page, unsigned long vaddr);
purge_tlb_start();
__clear_user_page_asm(page, vaddr);
purge_tlb_end();
}
......@@ -475,9 +475,9 @@ copy_user_page_asm:
.procend
#endif
.export clear_user_page_asm,code
.export __clear_user_page_asm,code
clear_user_page_asm:
__clear_user_page_asm:
.proc
.callinfo NO_CALLS
.entry
......
......@@ -104,7 +104,9 @@ static inline int map_pte_uncached(pte_t * pte,
if (!pte_none(*pte))
printk(KERN_ERR "map_pte_uncached: page already exists\n");
set_pte(pte, __mk_pte(*paddr_ptr, PAGE_KERNEL_UNC));
purge_tlb_start();
pdtlb_kernel(orig_vaddr);
purge_tlb_end();
vaddr += PAGE_SIZE;
orig_vaddr += PAGE_SIZE;
(*paddr_ptr) += PAGE_SIZE;
......@@ -179,7 +181,9 @@ static inline void unmap_uncached_pte(pmd_t * pmd, unsigned long vaddr,
do {
pte_t page = *pte;
pte_clear(pte);
purge_tlb_start();
pdtlb_kernel(orig_vaddr);
purge_tlb_end();
vaddr += PAGE_SIZE;
orig_vaddr += PAGE_SIZE;
pte++;
......
......@@ -333,6 +333,7 @@ smp_call_function (void (*func) (void *info), void *info, int retry, int wait)
struct smp_call_struct data;
unsigned long timeout;
static spinlock_t lock = SPIN_LOCK_UNLOCKED;
int retries = 0;
if (num_online_cpus() < 2)
return 0;
......@@ -365,21 +366,22 @@ smp_call_function (void (*func) (void *info), void *info, int retry, int wait)
/* Send a message to all other CPUs and wait for them to respond */
send_IPI_allbutself(IPI_CALL_FUNC);
retry:
/* Wait for response */
timeout = jiffies + HZ;
while ( (atomic_read (&data.unstarted_count) > 0) &&
time_before (jiffies, timeout) )
barrier ();
if (atomic_read (&data.unstarted_count) > 0) {
printk(KERN_CRIT "SMP CALL FUNCTION TIMED OUT! (cpu=%d), try %d\n",
smp_processor_id(), ++retries);
goto retry;
}
/* We either got one or timed out. Release the lock */
mb();
smp_call_function_data = NULL;
if (atomic_read (&data.unstarted_count) > 0) {
printk(KERN_CRIT "SMP CALL FUNCTION TIMED OUT! (cpu=%d)\n",
smp_processor_id());
return -ETIMEDOUT;
}
while (wait && atomic_read (&data.unfinished_count) > 0)
barrier ();
......
......@@ -184,4 +184,22 @@ typedef struct {
#define KERNEL_START (0x10100000 - 0x1000)
/* This is for the serialisation of PxTLB broadcasts. At least on the
* N class systems, only one PxTLB inter processor broadcast can be
* active at any one time on the Merced bus. This tlb purge
* synchronisation is fairly lightweight and harmless so we activate
* it on all SMP systems not just the N class. */
#ifdef CONFIG_SMP
extern spinlock_t pa_tlb_lock;
#define purge_tlb_start(x) spin_lock(&pa_tlb_lock)
#define purge_tlb_end(x) spin_unlock(&pa_tlb_lock)
#else
#define purge_tlb_start(x) do { } while(0)
#define purge_tlb_end(x) do { } while (0)
#endif
#endif
......@@ -51,9 +51,12 @@ static inline void flush_tlb_page(struct vm_area_struct *vma,
{
/* For one page, it's not worth testing the split_tlb variable */
mb();
mtsp(vma->vm_mm->context,1);
purge_tlb_start();
pdtlb(addr);
pitlb(addr);
purge_tlb_end();
}
static inline void flush_tlb_range(struct vm_area_struct *vma,
......@@ -61,6 +64,7 @@ static inline void flush_tlb_range(struct vm_area_struct *vma,
{
unsigned long npages;
npages = ((end - (start & PAGE_MASK)) + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
if (npages >= 512) /* XXX arbitrary, should be tuned */
flush_tlb_all();
......@@ -68,16 +72,20 @@ static inline void flush_tlb_range(struct vm_area_struct *vma,
mtsp(vma->vm_mm->context,1);
if (split_tlb) {
purge_tlb_start();
while (npages--) {
pdtlb(start);
pitlb(start);
start += PAGE_SIZE;
}
purge_tlb_end();
} else {
purge_tlb_start();
while (npages--) {
pdtlb(start);
start += PAGE_SIZE;
}
purge_tlb_end();
}
}
}
......
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