From dadeafdfc8da8c27e5a68e0706b9856eaac89391 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Sun, 17 Apr 2005 18:03:11 -0700 Subject: [PATCH] [PATCH] sparc64: Reduce ptrace cache flushing We were flushing the D-cache excessively for ptrace() processing and this makes debugging threads so slow as to be totally unusable. All process page accesses via ptrace() go via access_process_vm(). This routine, for each process page, uses get_user_pages(). That in turn does a flush_dcache_page() on the child pages before we copy in/out the ptrace request data. Therefore, all we need to do after the data movement is: 1) Flush the D-cache pages if the kernel maps the page to a different color than userspace does. 2) If we wrote to the page, we need to flush the I-cache on older cpus. Previously we just flushed the entire cache at the end of a ptrace() request, and that was beyond stupid. Signed-off-by: David S. Miller Signed-off-by: Linus Torvalds --- arch/sparc64/kernel/ptrace.c | 82 +++++++++++++++++++++----------- include/asm-sparc64/cacheflush.h | 22 +++++---- 2 files changed, 69 insertions(+), 35 deletions(-) diff --git a/arch/sparc64/kernel/ptrace.c b/arch/sparc64/kernel/ptrace.c index 08bac537262a..5f080cf04b33 100644 --- a/arch/sparc64/kernel/ptrace.c +++ b/arch/sparc64/kernel/ptrace.c @@ -103,6 +103,55 @@ void ptrace_disable(struct task_struct *child) /* nothing to do */ } +/* To get the necessary page struct, access_process_vm() first calls + * get_user_pages(). This has done a flush_dcache_page() on the + * accessed page. Then our caller (copy_{to,from}_user_page()) did + * to memcpy to read/write the data from that page. + * + * Now, the only thing we have to do is: + * 1) flush the D-cache if it's possible than an illegal alias + * has been created + * 2) flush the I-cache if this is pre-cheetah and we did a write + */ +void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, + unsigned long uaddr, void *kaddr, + unsigned long len, int write) +{ + BUG_ON(len > PAGE_SIZE); + +#ifdef DCACHE_ALIASING_POSSIBLE + /* If bit 13 of the kernel address we used to access the + * user page is the same as the virtual address that page + * is mapped to in the user's address space, we can skip the + * D-cache flush. + */ + if ((uaddr ^ kaddr) & (1UL << 13)) { + unsigned long start = __pa(kaddr); + unsigned long end = start + len; + + if (tlb_type == spitfire) { + for (; start < end; start += 32) + spitfire_put_dcache_tag(va & 0x3fe0, 0x0); + } else { + for (; start < end; start += 32) + __asm__ __volatile__( + "stxa %%g0, [%0] %1\n\t" + "membar #Sync" + : /* no outputs */ + : "r" (va), + "i" (ASI_DCACHE_INVALIDATE)); + } + } +#endif + if (write && tlb_type == spitfire) { + unsigned long start = (unsigned long) kaddr; + unsigned long end = start + len; + + for (; start < end; start += 32) + flushi(start); + } +} + asmlinkage void do_ptrace(struct pt_regs *regs) { int request = regs->u_regs[UREG_I0]; @@ -227,7 +276,7 @@ asmlinkage void do_ptrace(struct pt_regs *regs) pt_error_return(regs, -res); else pt_os_succ_return(regs, tmp64, (void __user *) data); - goto flush_and_out; + goto out_tsk; } case PTRACE_POKETEXT: /* write the word at location addr. */ @@ -253,7 +302,7 @@ asmlinkage void do_ptrace(struct pt_regs *regs) pt_error_return(regs, -res); else pt_succ_return(regs, res); - goto flush_and_out; + goto out_tsk; } case PTRACE_GETREGS: { @@ -485,12 +534,12 @@ asmlinkage void do_ptrace(struct pt_regs *regs) (char __user *)addr2, data); if (res == data) { pt_succ_return(regs, 0); - goto flush_and_out; + goto out_tsk; } if (res >= 0) res = -EIO; pt_error_return(regs, -res); - goto flush_and_out; + goto out_tsk; } case PTRACE_WRITETEXT: @@ -499,12 +548,12 @@ asmlinkage void do_ptrace(struct pt_regs *regs) addr, data); if (res == data) { pt_succ_return(regs, 0); - goto flush_and_out; + goto out_tsk; } if (res >= 0) res = -EIO; pt_error_return(regs, -res); - goto flush_and_out; + goto out_tsk; } case PTRACE_SYSCALL: /* continue and stop at (return from) syscall */ addr = 1; @@ -571,27 +620,6 @@ asmlinkage void do_ptrace(struct pt_regs *regs) goto out_tsk; } } -flush_and_out: - { - unsigned long va; - - if (tlb_type == cheetah || tlb_type == cheetah_plus) { - for (va = 0; va < (1 << 16); va += (1 << 5)) - spitfire_put_dcache_tag(va, 0x0); - /* No need to mess with I-cache on Cheetah. */ - } else { - for (va = 0; va < L1DCACHE_SIZE; va += 32) - spitfire_put_dcache_tag(va, 0x0); - if (request == PTRACE_PEEKTEXT || - request == PTRACE_POKETEXT || - request == PTRACE_READTEXT || - request == PTRACE_WRITETEXT) { - for (va = 0; va < (PAGE_SIZE << 1); va += 32) - spitfire_put_icache_tag(va, 0x0); - __asm__ __volatile__("flush %g6"); - } - } - } out_tsk: if (child) put_task_struct(child); diff --git a/include/asm-sparc64/cacheflush.h b/include/asm-sparc64/cacheflush.h index 86f02937ff1b..51b26e81d828 100644 --- a/include/asm-sparc64/cacheflush.h +++ b/include/asm-sparc64/cacheflush.h @@ -49,16 +49,22 @@ extern void flush_dcache_page(struct page *page); #define flush_icache_page(vma, pg) do { } while(0) #define flush_icache_user_range(vma,pg,adr,len) do { } while (0) -#define copy_to_user_page(vma, page, vaddr, dst, src, len) \ - do { \ - flush_cache_page(vma, vaddr, page_to_pfn(page));\ - memcpy(dst, src, len); \ +extern void flush_ptrace_access(struct vm_area_struct *, struct page *, + unsigned long uaddr, void *kaddr, + unsigned long len, int write); + +#define copy_to_user_page(vma, page, vaddr, dst, src, len) \ + do { \ + flush_cache_page(vma, vaddr, page_to_pfn(page)); \ + memcpy(dst, src, len); \ + flush_ptrace_access(vma, page, vaddr, src, len, 0); \ } while (0) -#define copy_from_user_page(vma, page, vaddr, dst, src, len) \ - do { \ - flush_cache_page(vma, vaddr, page_to_pfn(page));\ - memcpy(dst, src, len); \ +#define copy_from_user_page(vma, page, vaddr, dst, src, len) \ + do { \ + flush_cache_page(vma, vaddr, page_to_pfn(page)); \ + memcpy(dst, src, len); \ + flush_ptrace_access(vma, page, vaddr, dst, len, 1); \ } while (0) #define flush_dcache_mmap_lock(mapping) do { } while (0)