diff options
author | David S. Miller <davem@davemloft.net> | 2005-04-18 03:03:11 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-18 03:03:11 +0200 |
commit | dadeafdfc8da8c27e5a68e0706b9856eaac89391 (patch) | |
tree | 17993d26e93e598a2f449063fe213afad2a45814 /arch/sparc64 | |
parent | [PATCH] sparc: Fix PTRACE_CONT bogosity (diff) | |
download | linux-dadeafdfc8da8c27e5a68e0706b9856eaac89391.tar.xz linux-dadeafdfc8da8c27e5a68e0706b9856eaac89391.zip |
[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 <davem@davemloft.net>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/sparc64')
-rw-r--r-- | arch/sparc64/kernel/ptrace.c | 82 |
1 files changed, 55 insertions, 27 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); |