diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-22 21:13:26 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-22 21:13:26 -0800 |
commit | 5f32ed140dac726e880d292988ba20d16f545bda (patch) | |
tree | f08589f9489a05eb7a0b7f855ab96c57b0561cde /arch/parisc/kernel | |
parent | c68fea3464cbe4f3e1382f9f74a7c04cdbfb92ad (diff) | |
parent | 1dda59b4f3d03fa28d86f3ea235655f0f96aab3e (diff) |
Merge branch 'parisc-3.9' of git://git.kernel.org/pub/scm/linux/kernel/git/deller/parisc-linux
Pull parisc updates from Helge Deller.
The bulk of this is optimized page coping/clearing and cache flushing
(virtual caches are lovely) by John David Anglin.
* 'parisc-3.9' of git://git.kernel.org/pub/scm/linux/kernel/git/deller/parisc-linux: (31 commits)
arch/parisc/include/asm: use ARRAY_SIZE macro in mmzone.h
parisc: remove empty lines and unnecessary #ifdef coding in include/asm/signal.h
parisc: sendfile and sendfile64 syscall cleanups
parisc: switch to available compat_sched_rr_get_interval implementation
parisc: fix fallocate syscall
parisc: fix error return codes for rt_sigaction and rt_sigprocmask
parisc: convert msgrcv and msgsnd syscalls to use compat layer
parisc: correctly wire up mq_* functions for CONFIG_COMPAT case
parisc: fix personality on 32bit kernel
parisc: wire up process_vm_readv, process_vm_writev, kcmp and finit_module syscalls
parisc: led driver requires CONFIG_VM_EVENT_COUNTERS
parisc: remove unused compat_rt_sigframe.h header
parisc/mm/fault.c: Port OOM changes to do_page_fault
parisc: space register variables need to be in native length (unsigned long)
parisc: fix ptrace breakage
parisc: always detect multiple physical ranges
parisc: ensure that mmapped shared pages are aligned at SHMLBA addresses
parisc: disable preemption while flushing D- or I-caches through TMPALIAS region
parisc: remove IRQF_DISABLED
parisc: fixes and cleanups in page cache flushing (4/4)
...
Diffstat (limited to 'arch/parisc/kernel')
-rw-r--r-- | arch/parisc/kernel/cache.c | 221 | ||||
-rw-r--r-- | arch/parisc/kernel/entry.S | 4 | ||||
-rw-r--r-- | arch/parisc/kernel/inventory.c | 2 | ||||
-rw-r--r-- | arch/parisc/kernel/irq.c | 4 | ||||
-rw-r--r-- | arch/parisc/kernel/pacache.S | 335 | ||||
-rw-r--r-- | arch/parisc/kernel/parisc_ksyms.c | 5 | ||||
-rw-r--r-- | arch/parisc/kernel/signal.c | 2 | ||||
-rw-r--r-- | arch/parisc/kernel/signal32.c | 15 | ||||
-rw-r--r-- | arch/parisc/kernel/sys_parisc.c | 16 | ||||
-rw-r--r-- | arch/parisc/kernel/sys_parisc32.c | 122 | ||||
-rw-r--r-- | arch/parisc/kernel/syscall.S | 5 | ||||
-rw-r--r-- | arch/parisc/kernel/syscall_table.S | 27 |
12 files changed, 540 insertions, 218 deletions
diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c index 48e16dc20102..4b12890642eb 100644 --- a/arch/parisc/kernel/cache.c +++ b/arch/parisc/kernel/cache.c @@ -267,9 +267,11 @@ static inline void __flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr, unsigned long physaddr) { + preempt_disable(); flush_dcache_page_asm(physaddr, vmaddr); if (vma->vm_flags & VM_EXEC) flush_icache_page_asm(physaddr, vmaddr); + preempt_enable(); } void flush_dcache_page(struct page *page) @@ -329,17 +331,6 @@ EXPORT_SYMBOL(flush_kernel_dcache_page_asm); EXPORT_SYMBOL(flush_data_cache_local); EXPORT_SYMBOL(flush_kernel_icache_range_asm); -void clear_user_page_asm(void *page, unsigned long vaddr) -{ - unsigned long flags; - /* This function is implemented in assembly in pacache.S */ - extern void __clear_user_page_asm(void *page, unsigned long vaddr); - - purge_tlb_start(flags); - __clear_user_page_asm(page, vaddr); - purge_tlb_end(flags); -} - #define FLUSH_THRESHOLD 0x80000 /* 0.5MB */ int parisc_cache_flush_threshold __read_mostly = FLUSH_THRESHOLD; @@ -373,20 +364,9 @@ void __init parisc_setup_cache_timing(void) printk(KERN_INFO "Setting cache flush threshold to %x (%d CPUs online)\n", parisc_cache_flush_threshold, num_online_cpus()); } -extern void purge_kernel_dcache_page(unsigned long); -extern void clear_user_page_asm(void *page, unsigned long vaddr); - -void clear_user_page(void *page, unsigned long vaddr, struct page *pg) -{ - unsigned long flags; - - purge_kernel_dcache_page((unsigned long)page); - purge_tlb_start(flags); - pdtlb_kernel(page); - purge_tlb_end(flags); - clear_user_page_asm(page, vaddr); -} -EXPORT_SYMBOL(clear_user_page); +extern void purge_kernel_dcache_page_asm(unsigned long); +extern void clear_user_page_asm(void *, unsigned long); +extern void copy_user_page_asm(void *, void *, unsigned long); void flush_kernel_dcache_page_addr(void *addr) { @@ -399,11 +379,26 @@ void flush_kernel_dcache_page_addr(void *addr) } EXPORT_SYMBOL(flush_kernel_dcache_page_addr); +void clear_user_page(void *vto, unsigned long vaddr, struct page *page) +{ + clear_page_asm(vto); + if (!parisc_requires_coherency()) + flush_kernel_dcache_page_asm(vto); +} +EXPORT_SYMBOL(clear_user_page); + void copy_user_page(void *vto, void *vfrom, unsigned long vaddr, - struct page *pg) + struct page *pg) { - /* no coherency needed (all in kmap/kunmap) */ - copy_user_page_asm(vto, vfrom); + /* Copy using kernel mapping. No coherency is needed + (all in kmap/kunmap) on machines that don't support + non-equivalent aliasing. However, the `from' page + needs to be flushed before it can be accessed through + the kernel mapping. */ + preempt_disable(); + flush_dcache_page_asm(__pa(vfrom), vaddr); + preempt_enable(); + copy_page_asm(vto, vfrom); if (!parisc_requires_coherency()) flush_kernel_dcache_page_asm(vto); } @@ -419,6 +414,24 @@ void kunmap_parisc(void *addr) EXPORT_SYMBOL(kunmap_parisc); #endif +void purge_tlb_entries(struct mm_struct *mm, unsigned long addr) +{ + unsigned long flags; + + /* Note: purge_tlb_entries can be called at startup with + no context. */ + + /* Disable preemption while we play with %sr1. */ + preempt_disable(); + mtsp(mm->context, 1); + purge_tlb_start(flags); + pdtlb(addr); + pitlb(addr); + purge_tlb_end(flags); + preempt_enable(); +} +EXPORT_SYMBOL(purge_tlb_entries); + void __flush_tlb_range(unsigned long sid, unsigned long start, unsigned long end) { @@ -458,8 +471,66 @@ void flush_cache_all(void) on_each_cpu(cacheflush_h_tmp_function, NULL, 1); } +static inline unsigned long mm_total_size(struct mm_struct *mm) +{ + struct vm_area_struct *vma; + unsigned long usize = 0; + + for (vma = mm->mmap; vma; vma = vma->vm_next) + usize += vma->vm_end - vma->vm_start; + return usize; +} + +static inline pte_t *get_ptep(pgd_t *pgd, unsigned long addr) +{ + pte_t *ptep = NULL; + + if (!pgd_none(*pgd)) { + pud_t *pud = pud_offset(pgd, addr); + if (!pud_none(*pud)) { + pmd_t *pmd = pmd_offset(pud, addr); + if (!pmd_none(*pmd)) + ptep = pte_offset_map(pmd, addr); + } + } + return ptep; +} + void flush_cache_mm(struct mm_struct *mm) { + /* Flushing the whole cache on each cpu takes forever on + rp3440, etc. So, avoid it if the mm isn't too big. */ + if (mm_total_size(mm) < parisc_cache_flush_threshold) { + struct vm_area_struct *vma; + + if (mm->context == mfsp(3)) { + for (vma = mm->mmap; vma; vma = vma->vm_next) { + flush_user_dcache_range_asm(vma->vm_start, + vma->vm_end); + if (vma->vm_flags & VM_EXEC) + flush_user_icache_range_asm( + vma->vm_start, vma->vm_end); + } + } else { + pgd_t *pgd = mm->pgd; + + for (vma = mm->mmap; vma; vma = vma->vm_next) { + unsigned long addr; + + for (addr = vma->vm_start; addr < vma->vm_end; + addr += PAGE_SIZE) { + pte_t *ptep = get_ptep(pgd, addr); + if (ptep != NULL) { + pte_t pte = *ptep; + __flush_cache_page(vma, addr, + page_to_phys(pte_page(pte))); + } + } + } + } + return; + } + #ifdef CONFIG_SMP flush_cache_all(); #else @@ -485,20 +556,36 @@ flush_user_icache_range(unsigned long start, unsigned long end) flush_instruction_cache(); } - void flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) { - int sr3; - BUG_ON(!vma->vm_mm->context); - sr3 = mfsp(3); - if (vma->vm_mm->context == sr3) { - flush_user_dcache_range(start,end); - flush_user_icache_range(start,end); + if ((end - start) < parisc_cache_flush_threshold) { + if (vma->vm_mm->context == mfsp(3)) { + flush_user_dcache_range_asm(start, end); + if (vma->vm_flags & VM_EXEC) + flush_user_icache_range_asm(start, end); + } else { + unsigned long addr; + pgd_t *pgd = vma->vm_mm->pgd; + + for (addr = start & PAGE_MASK; addr < end; + addr += PAGE_SIZE) { + pte_t *ptep = get_ptep(pgd, addr); + if (ptep != NULL) { + pte_t pte = *ptep; + flush_cache_page(vma, + addr, pte_pfn(pte)); + } + } + } } else { +#ifdef CONFIG_SMP flush_cache_all(); +#else + flush_cache_all_local(); +#endif } } @@ -511,3 +598,67 @@ flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr, unsigned long __flush_cache_page(vma, vmaddr, page_to_phys(pfn_to_page(pfn))); } + +#ifdef CONFIG_PARISC_TMPALIAS + +void clear_user_highpage(struct page *page, unsigned long vaddr) +{ + void *vto; + unsigned long flags; + + /* Clear using TMPALIAS region. The page doesn't need to + be flushed but the kernel mapping needs to be purged. */ + + vto = kmap_atomic(page, KM_USER0); + + /* The PA-RISC 2.0 Architecture book states on page F-6: + "Before a write-capable translation is enabled, *all* + non-equivalently-aliased translations must be removed + from the page table and purged from the TLB. (Note + that the caches are not required to be flushed at this + time.) Before any non-equivalent aliased translation + is re-enabled, the virtual address range for the writeable + page (the entire page) must be flushed from the cache, + and the write-capable translation removed from the page + table and purged from the TLB." */ + + purge_kernel_dcache_page_asm((unsigned long)vto); + purge_tlb_start(flags); + pdtlb_kernel(vto); + purge_tlb_end(flags); + preempt_disable(); + clear_user_page_asm(vto, vaddr); + preempt_enable(); + + pagefault_enable(); /* kunmap_atomic(addr, KM_USER0); */ +} + +void copy_user_highpage(struct page *to, struct page *from, + unsigned long vaddr, struct vm_area_struct *vma) +{ + void *vfrom, *vto; + unsigned long flags; + + /* Copy using TMPALIAS region. This has the advantage + that the `from' page doesn't need to be flushed. However, + the `to' page must be flushed in copy_user_page_asm since + it can be used to bring in executable code. */ + + vfrom = kmap_atomic(from, KM_USER0); + vto = kmap_atomic(to, KM_USER1); + + purge_kernel_dcache_page_asm((unsigned long)vto); + purge_tlb_start(flags); + pdtlb_kernel(vto); + pdtlb_kernel(vfrom); + purge_tlb_end(flags); + preempt_disable(); + copy_user_page_asm(vto, vfrom, vaddr); + flush_dcache_page_asm(__pa(vto), vaddr); + preempt_enable(); + + pagefault_enable(); /* kunmap_atomic(addr, KM_USER1); */ + pagefault_enable(); /* kunmap_atomic(addr, KM_USER0); */ +} + +#endif /* CONFIG_PARISC_TMPALIAS */ diff --git a/arch/parisc/kernel/entry.S b/arch/parisc/kernel/entry.S index eb7850b46c25..7c9648919c91 100644 --- a/arch/parisc/kernel/entry.S +++ b/arch/parisc/kernel/entry.S @@ -483,7 +483,7 @@ * B <-> _PAGE_DMB (memory break) * * Then incredible subtlety: The access rights are - * _PAGE_GATEWAY _PAGE_EXEC _PAGE_READ + * _PAGE_GATEWAY, _PAGE_EXEC and _PAGE_WRITE * See 3-14 of the parisc 2.0 manual * * Finally, _PAGE_READ goes in the top bit of PL1 (so we @@ -493,7 +493,7 @@ /* PAGE_USER indicates the page can be read with user privileges, * so deposit X1|11 to PL1|PL2 (remember the upper bit of PL1 - * contains _PAGE_READ */ + * contains _PAGE_READ) */ extrd,u,*= \pte,_PAGE_USER_BIT+32,1,%r0 depdi 7,11,3,\prot /* If we're a gateway page, drop PL2 back to zero for promotion diff --git a/arch/parisc/kernel/inventory.c b/arch/parisc/kernel/inventory.c index 08324aac3544..3295ef4a185d 100644 --- a/arch/parisc/kernel/inventory.c +++ b/arch/parisc/kernel/inventory.c @@ -186,12 +186,14 @@ pat_query_module(ulong pcell_loc, ulong mod_index) if (status != PDC_OK) { /* no more cell modules or error */ + kfree(pa_pdc_cell); return status; } temp = pa_pdc_cell->cba; dev = alloc_pa_dev(PAT_GET_CBA(temp), &(pa_pdc_cell->mod_path)); if (!dev) { + kfree(pa_pdc_cell); return PDC_OK; } diff --git a/arch/parisc/kernel/irq.c b/arch/parisc/kernel/irq.c index 0299d63cd112..8094d3ed3b64 100644 --- a/arch/parisc/kernel/irq.c +++ b/arch/parisc/kernel/irq.c @@ -379,14 +379,14 @@ void do_cpu_irq_mask(struct pt_regs *regs) static struct irqaction timer_action = { .handler = timer_interrupt, .name = "timer", - .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_PERCPU | IRQF_IRQPOLL, + .flags = IRQF_TIMER | IRQF_PERCPU | IRQF_IRQPOLL, }; #ifdef CONFIG_SMP static struct irqaction ipi_action = { .handler = ipi_interrupt, .name = "IPI", - .flags = IRQF_DISABLED | IRQF_PERCPU, + .flags = IRQF_PERCPU, }; #endif diff --git a/arch/parisc/kernel/pacache.S b/arch/parisc/kernel/pacache.S index 5d7218ad885c..312b48422a56 100644 --- a/arch/parisc/kernel/pacache.S +++ b/arch/parisc/kernel/pacache.S @@ -199,7 +199,6 @@ ENTRY(flush_instruction_cache_local) .callinfo NO_CALLS .entry - mtsp %r0, %sr1 load32 cache_info, %r1 /* Flush Instruction Cache */ @@ -208,7 +207,8 @@ ENTRY(flush_instruction_cache_local) LDREG ICACHE_STRIDE(%r1), %arg1 LDREG ICACHE_COUNT(%r1), %arg2 LDREG ICACHE_LOOP(%r1), %arg3 - rsm PSW_SM_I, %r22 /* No mmgt ops during loop*/ + rsm PSW_SM_I, %r22 /* No mmgt ops during loop*/ + mtsp %r0, %sr1 addib,COND(=) -1, %arg3, fioneloop /* Preadjust and test */ movb,<,n %arg3, %r31, fisync /* If loop < 0, do sync */ @@ -220,7 +220,33 @@ fimanyloop: /* Loop if LOOP >= 2 */ addib,COND(<=),n -1, %arg2, fisync /* Outer loop decr */ fioneloop: /* Loop if LOOP = 1 */ - addib,COND(>) -1, %arg2, fioneloop /* Outer loop count decr */ + /* Some implementations may flush with a single fice instruction */ + cmpib,COND(>>=),n 15, %arg2, fioneloop2 + +fioneloop1: + fice,m %arg1(%sr1, %arg0) + fice,m %arg1(%sr1, %arg0) + fice,m %arg1(%sr1, %arg0) + fice,m %arg1(%sr1, %arg0) + fice,m %arg1(%sr1, %arg0) + fice,m %arg1(%sr1, %arg0) + fice,m %arg1(%sr1, %arg0) + fice,m %arg1(%sr1, %arg0) + fice,m %arg1(%sr1, %arg0) + fice,m %arg1(%sr1, %arg0) + fice,m %arg1(%sr1, %arg0) + fice,m %arg1(%sr1, %arg0) + fice,m %arg1(%sr1, %arg0) + fice,m %arg1(%sr1, %arg0) + fice,m %arg1(%sr1, %arg0) + addib,COND(>) -16, %arg2, fioneloop1 + fice,m %arg1(%sr1, %arg0) + + /* Check if done */ + cmpb,COND(=),n %arg2, %r0, fisync /* Predict branch taken */ + +fioneloop2: + addib,COND(>) -1, %arg2, fioneloop2 /* Outer loop count decr */ fice,m %arg1(%sr1, %arg0) /* Fice for one loop */ fisync: @@ -240,8 +266,7 @@ ENTRY(flush_data_cache_local) .callinfo NO_CALLS .entry - mtsp %r0, %sr1 - load32 cache_info, %r1 + load32 cache_info, %r1 /* Flush Data Cache */ @@ -249,7 +274,8 @@ ENTRY(flush_data_cache_local) LDREG DCACHE_STRIDE(%r1), %arg1 LDREG DCACHE_COUNT(%r1), %arg2 LDREG DCACHE_LOOP(%r1), %arg3 - rsm PSW_SM_I, %r22 + rsm PSW_SM_I, %r22 /* No mmgt ops during loop*/ + mtsp %r0, %sr1 addib,COND(=) -1, %arg3, fdoneloop /* Preadjust and test */ movb,<,n %arg3, %r31, fdsync /* If loop < 0, do sync */ @@ -261,7 +287,33 @@ fdmanyloop: /* Loop if LOOP >= 2 */ addib,COND(<=),n -1, %arg2, fdsync /* Outer loop decr */ fdoneloop: /* Loop if LOOP = 1 */ - addib,COND(>) -1, %arg2, fdoneloop /* Outer loop count decr */ + /* Some implementations may flush with a single fdce instruction */ + cmpib,COND(>>=),n 15, %arg2, fdoneloop2 + +fdoneloop1: + fdce,m %arg1(%sr1, %arg0) + fdce,m %arg1(%sr1, %arg0) + fdce,m %arg1(%sr1, %arg0) + fdce,m %arg1(%sr1, %arg0) + fdce,m %arg1(%sr1, %arg0) + fdce,m %arg1(%sr1, %arg0) + fdce,m %arg1(%sr1, %arg0) + fdce,m %arg1(%sr1, %arg0) + fdce,m %arg1(%sr1, %arg0) + fdce,m %arg1(%sr1, %arg0) + fdce,m %arg1(%sr1, %arg0) + fdce,m %arg1(%sr1, %arg0) + fdce,m %arg1(%sr1, %arg0) + fdce,m %arg1(%sr1, %arg0) + fdce,m %arg1(%sr1, %arg0) + addib,COND(>) -16, %arg2, fdoneloop1 + fdce,m %arg1(%sr1, %arg0) + + /* Check if done */ + cmpb,COND(=),n %arg2, %r0, fdsync /* Predict branch taken */ + +fdoneloop2: + addib,COND(>) -1, %arg2, fdoneloop2 /* Outer loop count decr */ fdce,m %arg1(%sr1, %arg0) /* Fdce for one loop */ fdsync: @@ -277,7 +329,104 @@ ENDPROC(flush_data_cache_local) .align 16 -ENTRY(copy_user_page_asm) +/* Macros to serialize TLB purge operations on SMP. */ + + .macro tlb_lock la,flags,tmp +#ifdef CONFIG_SMP + ldil L%pa_tlb_lock,%r1 + ldo R%pa_tlb_lock(%r1),\la + rsm PSW_SM_I,\flags +1: LDCW 0(\la),\tmp + cmpib,<>,n 0,\tmp,3f +2: ldw 0(\la),\tmp + cmpb,<> %r0,\tmp,1b + nop + b,n 2b +3: +#endif + .endm + + .macro tlb_unlock la,flags,tmp +#ifdef CONFIG_SMP + ldi 1,\tmp + stw \tmp,0(\la) + mtsm \flags +#endif + .endm + +/* Clear page using kernel mapping. */ + +ENTRY(clear_page_asm) + .proc + .callinfo NO_CALLS + .entry + +#ifdef CONFIG_64BIT + + /* Unroll the loop. */ + ldi (PAGE_SIZE / 128), %r1 + +1: + std %r0, 0(%r26) + std %r0, 8(%r26) + std %r0, 16(%r26) + std %r0, 24(%r26) + std %r0, 32(%r26) + std %r0, 40(%r26) + std %r0, 48(%r26) + std %r0, 56(%r26) + std %r0, 64(%r26) + std %r0, 72(%r26) + std %r0, 80(%r26) + std %r0, 88(%r26) + std %r0, 96(%r26) + std %r0, 104(%r26) + std %r0, 112(%r26) + std %r0, 120(%r26) + + /* Note reverse branch hint for addib is taken. */ + addib,COND(>),n -1, %r1, 1b + ldo 128(%r26), %r26 + +#else + + /* + * Note that until (if) we start saving the full 64-bit register + * values on interrupt, we can't use std on a 32 bit kernel. + */ + ldi (PAGE_SIZE / 64), %r1 + +1: + stw %r0, 0(%r26) + stw %r0, 4(%r26) + stw %r0, 8(%r26) + stw %r0, 12(%r26) + stw %r0, 16(%r26) + stw %r0, 20(%r26) + stw %r0, 24(%r26) + stw %r0, 28(%r26) + stw %r0, 32(%r26) + stw %r0, 36(%r26) + stw %r0, 40(%r26) + stw %r0, 44(%r26) + stw %r0, 48(%r26) + stw %r0, 52(%r26) + stw %r0, 56(%r26) + stw %r0, 60(%r26) + + addib,COND(>),n -1, %r1, 1b + ldo 64(%r26), %r26 +#endif + bv %r0(%r2) + nop + .exit + + .procend +ENDPROC(clear_page_asm) + +/* Copy page using kernel mapping. */ + +ENTRY(copy_page_asm) .proc .callinfo NO_CALLS .entry @@ -285,18 +434,14 @@ ENTRY(copy_user_page_asm) #ifdef CONFIG_64BIT /* PA8x00 CPUs can consume 2 loads or 1 store per cycle. * Unroll the loop by hand and arrange insn appropriately. - * GCC probably can do this just as well. + * Prefetch doesn't improve performance on rp3440. + * GCC probably can do this just as well... */ - ldd 0(%r25), %r19 ldi (PAGE_SIZE / 128), %r1 - ldw 64(%r25), %r0 /* prefetch 1 cacheline ahead */ - ldw 128(%r25), %r0 /* prefetch 2 */ - -1: ldd 8(%r25), %r20 - ldw 192(%r25), %r0 /* prefetch 3 */ - ldw 256(%r25), %r0 /* prefetch 4 */ +1: ldd 0(%r25), %r19 + ldd 8(%r25), %r20 ldd 16(%r25), %r21 ldd 24(%r25), %r22 @@ -330,20 +475,16 @@ ENTRY(copy_user_page_asm) ldd 112(%r25), %r21 ldd 120(%r25), %r22 + ldo 128(%r25), %r25 std %r19, 96(%r26) std %r20, 104(%r26) - ldo 128(%r25), %r25 std %r21, 112(%r26) std %r22, 120(%r26) - ldo 128(%r26), %r26 - /* conditional branches nullify on forward taken branch, and on - * non-taken backward branch. Note that .+4 is a backwards branch. - * The ldd should only get executed if the branch is taken. - */ - addib,COND(>),n -1, %r1, 1b /* bundle 10 */ - ldd 0(%r25), %r19 /* start next loads */ + /* Note reverse branch hint for addib is taken. */ + addib,COND(>),n -1, %r1, 1b + ldo 128(%r26), %r26 #else @@ -399,7 +540,7 @@ ENTRY(copy_user_page_asm) .exit .procend -ENDPROC(copy_user_page_asm) +ENDPROC(copy_page_asm) /* * NOTE: Code in clear_user_page has a hard coded dependency on the @@ -422,8 +563,6 @@ ENDPROC(copy_user_page_asm) * %r23 physical page (shifted for tlb insert) of "from" translation */ -#if 0 - /* * We can't do this since copy_user_page is used to bring in * file data that might have instructions. Since the data would @@ -435,6 +574,7 @@ ENDPROC(copy_user_page_asm) * use it if more information is passed into copy_user_page(). * Have to do some measurements to see if it is worthwhile to * lobby for such a change. + * */ ENTRY(copy_user_page_asm) @@ -442,16 +582,21 @@ ENTRY(copy_user_page_asm) .callinfo NO_CALLS .entry + /* Convert virtual `to' and `from' addresses to physical addresses. + Move `from' physical address to non shadowed register. */ ldil L%(__PAGE_OFFSET), %r1 sub %r26, %r1, %r26 - sub %r25, %r1, %r23 /* move physical addr into non shadowed reg */ + sub %r25, %r1, %r23 ldil L%(TMPALIAS_MAP_START), %r28 /* FIXME for different page sizes != 4k */ #ifdef CONFIG_64BIT - extrd,u %r26,56,32, %r26 /* convert phys addr to tlb insert format */ - extrd,u %r23,56,32, %r23 /* convert phys addr to tlb insert format */ - depd %r24,63,22, %r28 /* Form aliased virtual address 'to' */ +#if (TMPALIAS_MAP_START >= 0x80000000) + depdi 0, 31,32, %r28 /* clear any sign extension */ +#endif + extrd,u %r26,56,32, %r26 /* convert phys addr to tlb insert format */ + extrd,u %r23,56,32, %r23 /* convert phys addr to tlb insert format */ + depd %r24,63,22, %r28 /* Form aliased virtual address 'to' */ depdi 0, 63,12, %r28 /* Clear any offset bits */ copy %r28, %r29 depdi 1, 41,1, %r29 /* Form aliased virtual address 'from' */ @@ -466,10 +611,76 @@ ENTRY(copy_user_page_asm) /* Purge any old translations */ +#ifdef CONFIG_PA20 + pdtlb,l 0(%r28) + pdtlb,l 0(%r29) +#else + tlb_lock %r20,%r21,%r22 pdtlb 0(%r28) pdtlb 0(%r29) + tlb_unlock %r20,%r21,%r22 +#endif + +#ifdef CONFIG_64BIT + /* PA8x00 CPUs can consume 2 loads or 1 store per cycle. + * Unroll the loop by hand and arrange insn appropriately. + * GCC probably can do this just as well. + */ - ldi 64, %r1 + ldd 0(%r29), %r19 + ldi (PAGE_SIZE / 128), %r1 + +1: ldd 8(%r29), %r20 + + ldd 16(%r29), %r21 + ldd 24(%r29), %r22 + std %r19, 0(%r28) + std %r20, 8(%r28) + + ldd 32(%r29), %r19 + ldd 40(%r29), %r20 + std %r21, 16(%r28) + std %r22, 24(%r28) + + ldd 48(%r29), %r21 + ldd 56(%r29), %r22 + std %r19, 32(%r28) + std %r20, 40(%r28) + + ldd 64(%r29), %r19 + ldd 72(%r29), %r20 + std %r21, 48(%r28) + std %r22, 56(%r28) + + ldd 80(%r29), %r21 + ldd 88(%r29), %r22 + std %r19, 64(%r28) + std %r20, 72(%r28) + + ldd 96(%r29), %r19 + ldd 104(%r29), %r20 + std %r21, 80(%r28) + std %r22, 88(%r28) + + ldd 112(%r29), %r21 + ldd 120(%r29), %r22 + std %r19, 96(%r28) + std %r20, 104(%r28) + + ldo 128(%r29), %r29 + std %r21, 112(%r28) + std %r22, 120(%r28) + ldo 128(%r28), %r28 + + /* conditional branches nullify on forward taken branch, and on + * non-taken backward branch. Note that .+4 is a backwards branch. + * The ldd should only get executed if the branch is taken. + */ + addib,COND(>),n -1, %r1, 1b /* bundle 10 */ + ldd 0(%r29), %r19 /* start next loads */ + +#else + ldi (PAGE_SIZE / 64), %r1 /* * This loop is optimized for PCXL/PCXL2 ldw/ldw and stw/stw @@ -480,9 +691,7 @@ ENTRY(copy_user_page_asm) * use ldd/std on a 32 bit kernel. */ - -1: - ldw 0(%r29), %r19 +1: ldw 0(%r29), %r19 ldw 4(%r29), %r20 ldw 8(%r29), %r21 ldw 12(%r29), %r22 @@ -515,8 +724,10 @@ ENTRY(copy_user_page_asm) stw %r21, 56(%r28) stw %r22, 60(%r28) ldo 64(%r28), %r28 + addib,COND(>) -1, %r1,1b ldo 64(%r29), %r29 +#endif bv %r0(%r2) nop @@ -524,9 +735,8 @@ ENTRY(copy_user_page_asm) .procend ENDPROC(copy_user_page_asm) -#endif -ENTRY(__clear_user_page_asm) +ENTRY(clear_user_page_asm) .proc .callinfo NO_CALLS .entry @@ -550,7 +760,13 @@ ENTRY(__clear_user_page_asm) /* Purge any old translation */ +#ifdef CONFIG_PA20 + pdtlb,l 0(%r28) +#else + tlb_lock %r20,%r21,%r22 pdtlb 0(%r28) + tlb_unlock %r20,%r21,%r22 +#endif #ifdef CONFIG_64BIT ldi (PAGE_SIZE / 128), %r1 @@ -580,8 +796,7 @@ ENTRY(__clear_user_page_asm) #else /* ! CONFIG_64BIT */ ldi (PAGE_SIZE / 64), %r1 -1: - stw %r0, 0(%r28) +1: stw %r0, 0(%r28) stw %r0, 4(%r28) stw %r0, 8(%r28) stw %r0, 12(%r28) @@ -606,7 +821,7 @@ ENTRY(__clear_user_page_asm) .exit .procend -ENDPROC(__clear_user_page_asm) +ENDPROC(clear_user_page_asm) ENTRY(flush_dcache_page_asm) .proc @@ -630,7 +845,13 @@ ENTRY(flush_dcache_page_asm) /* Purge any old translation */ +#ifdef CONFIG_PA20 + pdtlb,l 0(%r28) +#else + tlb_lock %r20,%r21,%r22 pdtlb 0(%r28) + tlb_unlock %r20,%r21,%r22 +#endif ldil L%dcache_stride, %r1 ldw R%dcache_stride(%r1), %r1 @@ -663,8 +884,17 @@ ENTRY(flush_dcache_page_asm) fdc,m %r1(%r28) sync + +#ifdef CONFIG_PA20 + pdtlb,l 0(%r25) +#else + tlb_lock %r20,%r21,%r22 + pdtlb 0(%r25) + tlb_unlock %r20,%r21,%r22 +#endif + bv %r0(%r2) - pdtlb (%r25) + nop .exit .procend @@ -692,7 +922,13 @@ ENTRY(flush_icache_page_asm) /* Purge any old translation */ - pitlb (%sr4,%r28) +#ifdef CONFIG_PA20 + pitlb,l %r0(%sr4,%r28) +#else + tlb_lock %r20,%r21,%r22 + pitlb (%sr4,%r28) + tlb_unlock %r20,%r21,%r22 +#endif ldil L%icache_stride, %r1 ldw R%icache_stride(%r1), %r1 @@ -727,8 +963,17 @@ ENTRY(flush_icache_page_asm) fic,m %r1(%sr4,%r28) sync + +#ifdef CONFIG_PA20 + pitlb,l %r0(%sr4,%r25) +#else + tlb_lock %r20,%r21,%r22 + pitlb (%sr4,%r25) + tlb_unlock %r20,%r21,%r22 +#endif + bv %r0(%r2) - pitlb (%sr4,%r25) + nop .exit .procend @@ -777,7 +1022,7 @@ ENTRY(flush_kernel_dcache_page_asm) .procend ENDPROC(flush_kernel_dcache_page_asm) -ENTRY(purge_kernel_dcache_page) +ENTRY(purge_kernel_dcache_page_asm) .proc .callinfo NO_CALLS .entry @@ -817,7 +1062,7 @@ ENTRY(purge_kernel_dcache_page) .exit .procend -ENDPROC(purge_kernel_dcache_page) +ENDPROC(purge_kernel_dcache_page_asm) ENTRY(flush_user_dcache_range_asm) .proc diff --git a/arch/parisc/kernel/parisc_ksyms.c b/arch/parisc/kernel/parisc_ksyms.c index ceec85de6290..6795dc6c995f 100644 --- a/arch/parisc/kernel/parisc_ksyms.c +++ b/arch/parisc/kernel/parisc_ksyms.c @@ -157,5 +157,6 @@ extern void _mcount(void); EXPORT_SYMBOL(_mcount); #endif -/* from pacache.S -- needed for copy_page */ -EXPORT_SYMBOL(copy_user_page_asm); +/* from pacache.S -- needed for clear/copy_page */ +EXPORT_SYMBOL(clear_page_asm); +EXPORT_SYMBOL(copy_page_asm); diff --git a/arch/parisc/kernel/signal.c b/arch/parisc/kernel/signal.c index fd051705a407..52c85b2f502e 100644 --- a/arch/parisc/kernel/signal.c +++ b/arch/parisc/kernel/signal.c @@ -312,7 +312,7 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, #if DEBUG_SIG /* Assert that we're flushing in the correct space... */ { - int sid; + unsigned long sid; asm ("mfsp %%sr3,%0" : "=r" (sid)); DBG(1,"setup_rt_frame: Flushing 64 bytes at space %#x offset %p\n", sid, frame->tramp); diff --git a/arch/parisc/kernel/signal32.c b/arch/parisc/kernel/signal32.c index 5dede04f2f3e..2ddcabb616ce 100644 --- a/arch/parisc/kernel/signal32.c +++ b/arch/parisc/kernel/signal32.c @@ -65,7 +65,7 @@ put_sigset32(compat_sigset_t __user *up, sigset_t *set, size_t sz) { compat_sigset_t s; - if (sz != sizeof *set) + if (sz != sizeof(compat_sigset_t)) return -EINVAL; sigset_64to32(&s, set); @@ -78,7 +78,7 @@ get_sigset32(compat_sigset_t __user *up, sigset_t *set, size_t sz) compat_sigset_t s; int r; - if (sz != sizeof *set) + if (sz != sizeof(compat_sigset_t)) return -EINVAL; if ((r = copy_from_user(&s, up, sz)) == 0) { @@ -94,8 +94,11 @@ int sys32_rt_sigprocmask(int how, compat_sigset_t __user *set, compat_sigset_t _ sigset_t old_set, new_set; int ret; - if (set && get_sigset32(set, &new_set, sigsetsize)) - return -EFAULT; + if (set) { + ret = get_sigset32(set, &new_set, sigsetsize); + if (ret) + return ret; + } KERNEL_SYSCALL(ret, sys_rt_sigprocmask, how, set ? (sigset_t __user *)&new_set : NULL, oset ? (sigset_t __user *)&old_set : NULL, sigsetsize); @@ -128,6 +131,10 @@ sys32_rt_sigaction(int sig, const struct sigaction32 __user *act, struct sigacti struct k_sigaction new_sa, old_sa; int ret = -EINVAL; + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(compat_sigset_t)) + return -EINVAL; + if (act) { if (copy_from_user(&new_sa32.sa, act, sizeof new_sa32.sa)) return -EFAULT; diff --git a/arch/parisc/kernel/sys_parisc.c b/arch/parisc/kernel/sys_parisc.c index f76c10863c62..54d619d4cac6 100644 --- a/arch/parisc/kernel/sys_parisc.c +++ b/arch/parisc/kernel/sys_parisc.c @@ -94,11 +94,12 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, { if (len > TASK_SIZE) return -ENOMEM; - /* Might want to check for cache aliasing issues for MAP_FIXED case - * like ARM or MIPS ??? --BenH. - */ - if (flags & MAP_FIXED) + if (flags & MAP_FIXED) { + if ((flags & MAP_SHARED) && + (addr - (pgoff << PAGE_SHIFT)) & (SHMLBA - 1)) + return -EINVAL; return addr; + } if (!addr) addr = TASK_UNMAPPED_BASE; @@ -212,6 +213,13 @@ asmlinkage long parisc_sync_file_range(int fd, (loff_t)hi_nbytes << 32 | lo_nbytes, flags); } +asmlinkage long parisc_fallocate(int fd, int mode, u32 offhi, u32 offlo, + u32 lenhi, u32 lenlo) +{ + return sys_fallocate(fd, mode, ((u64)offhi << 32) | offlo, + ((u64)lenhi << 32) | lenlo); +} + asmlinkage unsigned long sys_alloc_hugepages(int key, unsigned long addr, unsigned long len, int prot, int flag) { return -ENOMEM; diff --git a/arch/parisc/kernel/sys_parisc32.c b/arch/parisc/kernel/sys_parisc32.c index 9cfdaa19ab63..eca69bb8ef5f 100644 --- a/arch/parisc/kernel/sys_parisc32.c +++ b/arch/parisc/kernel/sys_parisc32.c @@ -21,7 +21,6 @@ #include <linux/time.h> #include <linux/smp.h> #include <linux/sem.h> -#include <linux/msg.h> #include <linux/shm.h> #include <linux/slab.h> #include <linux/uio.h> @@ -61,111 +60,23 @@ asmlinkage long sys32_unimplemented(int r26, int r25, int r24, int r23, return -ENOSYS; } -asmlinkage long sys32_sched_rr_get_interval(pid_t pid, - struct compat_timespec __user *interval) -{ - struct timespec t; - int ret; - - KERNEL_SYSCALL(ret, sys_sched_rr_get_interval, pid, (struct timespec __user *)&t); - if (put_compat_timespec(&t, interval)) |