Messages in this thread Patch in this message |  | | Date | Mon, 16 Jun 2008 09:58:28 +0200 | Subject | [PATCH 1/2] ptrace_multi: speedup for virtual machines (and debuggers) running on ptrace | From | (Renzo Davoli) |
| |
This patch implements PTRACE_MULTI.
Several ptrace requests can be packed up and sent together to the kernel. This speeds up Virtual Machines and, if you like, debuggers.
Signed-off-by: Renzo Davoli <renzo@cs.unibo.it>
--- diff -Naur linux-2.6.26-rc6/include/linux/mm.h linux-2.6.26-rc6-ptrace_multi/include/linux/mm.h --- linux-2.6.26-rc6/include/linux/mm.h 2008-06-13 13:33:30.000000000 +0200 +++ linux-2.6.26-rc6-ptrace_multi/include/linux/mm.h 2008-06-14 16:56:11.000000000 +0200 @@ -804,6 +804,7 @@ extern int make_pages_present(unsigned long addr, unsigned long end); extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write); +extern int access_process_vm_user(struct task_struct *tsk, unsigned long addr, char __user *ubuf, int len, int write, int string); int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, unsigned long start, int len, int write, int force, struct page **pages, struct vm_area_struct **vmas); diff -Naur linux-2.6.26-rc6/include/linux/ptrace.h linux-2.6.26-rc6-ptrace_multi/include/linux/ptrace.h --- linux-2.6.26-rc6/include/linux/ptrace.h 2008-06-13 13:33:30.000000000 +0200 +++ linux-2.6.26-rc6-ptrace_multi/include/linux/ptrace.h 2008-06-14 16:56:11.000000000 +0200 @@ -27,6 +27,18 @@ #define PTRACE_GETSIGINFO 0x4202 #define PTRACE_SETSIGINFO 0x4203 +#define PTRACE_MULTI 0x4300 +#define PTRACE_PEEKCHARDATA 0x4301 +#define PTRACE_POKECHARDATA 0x4302 +#define PTRACE_PEEKSTRINGDATA 0x4303 + +struct ptrace_multi { + long request; + long addr; + void *localaddr; + long length; +}; + /* options set using PTRACE_SETOPTIONS */ #define PTRACE_O_TRACESYSGOOD 0x00000001 #define PTRACE_O_TRACEFORK 0x00000002 @@ -84,6 +96,7 @@ extern struct task_struct *ptrace_get_task_struct(pid_t pid); extern int ptrace_traceme(void); extern int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len); +extern int ptrace_readstringdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len); extern int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len); extern int ptrace_attach(struct task_struct *tsk); extern int ptrace_detach(struct task_struct *, unsigned int); diff -Naur linux-2.6.26-rc6/kernel/ptrace.c linux-2.6.26-rc6-ptrace_multi/kernel/ptrace.c --- linux-2.6.26-rc6/kernel/ptrace.c 2008-06-13 13:33:31.000000000 +0200 +++ linux-2.6.26-rc6-ptrace_multi/kernel/ptrace.c 2008-06-14 19:00:13.000000000 +0200 @@ -2,6 +2,7 @@ * linux/kernel/ptrace.c * * (C) Copyright 1999 Linus Torvalds + * PTRACE_MULTI support 2008 Renzo Davoli * * Common interfaces for "ptrace()" which we do not want * to continually duplicate across every architecture. @@ -244,52 +245,23 @@ int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len) { - int copied = 0; + if (!access_ok(VERIFY_WRITE, dst, len)) + return -EIO; + return access_process_vm_user(tsk, src, dst, len, 0, 0); +} - while (len > 0) { - char buf[128]; - int this_len, retval; - - this_len = (len > sizeof(buf)) ? sizeof(buf) : len; - retval = access_process_vm(tsk, src, buf, this_len, 0); - if (!retval) { - if (copied) - break; - return -EIO; - } - if (copy_to_user(dst, buf, retval)) - return -EFAULT; - copied += retval; - src += retval; - dst += retval; - len -= retval; - } - return copied; +int ptrace_readstringdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len) +{ + if (!access_ok(VERIFY_WRITE, dst, len)) + return -EIO; + return access_process_vm_user(tsk, src, dst, len, 0, 1); } int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len) { - int copied = 0; - - while (len > 0) { - char buf[128]; - int this_len, retval; - - this_len = (len > sizeof(buf)) ? sizeof(buf) : len; - if (copy_from_user(buf, src, this_len)) - return -EFAULT; - retval = access_process_vm(tsk, dst, buf, this_len, 1); - if (!retval) { - if (copied) - break; - return -EIO; - } - copied += retval; - src += retval; - dst += retval; - len -= retval; - } - return copied; + if (!access_ok(VERIFY_READ, dst, len)) + return -EIO; + return access_process_vm_user(tsk, dst, src, len, 1, 0); } static int ptrace_setoptions(struct task_struct *child, long data) @@ -534,6 +506,50 @@ #define arch_ptrace_attach(child) do { } while (0) #endif +static int multi_ptrace(struct task_struct *child, long request, long addr, long size) +{ + int i, ret; + long j; + ret=0; + if (!access_ok(VERIFY_READ, addr,size*sizeof(struct ptrace_multi))) { + ret = -EIO; + goto out_multi_ptrace; + } + for (i=0; i<size && ret==0; i++, addr+=sizeof(struct ptrace_multi)) { + unsigned long len; + struct ptrace_multi __user pm ; + if (__copy_from_user(&pm, (struct ptrace_multi __user *)addr, sizeof(struct ptrace_multi)) == 0) { + len = pm.length; + switch ( pm.request){ + case PTRACE_PEEKTEXT: + case PTRACE_PEEKDATA: + case PTRACE_PEEKUSR: + case PTRACE_POKETEXT: + case PTRACE_POKEDATA: + case PTRACE_POKEUSR: + if (len <= 0) len=1; + for (j=0; j<len && ret==0; j++) + ret=arch_ptrace(child, pm.request, (long) (pm.addr) + j*sizeof(long), (long) (pm.localaddr) + j*sizeof(long)); + break; + case PTRACE_PEEKCHARDATA: + ret = ptrace_readdata(child, pm.addr, pm.localaddr, len); + break; + case PTRACE_POKECHARDATA: + ret = ptrace_writedata(child, pm.localaddr, pm.addr, len); + break; + case PTRACE_PEEKSTRINGDATA: + ret = ptrace_readstringdata(child, pm.addr, pm.localaddr, len); + break; + default: + ret=arch_ptrace(child, pm.request, (long) (pm.addr), (long) (pm.localaddr)); + break; + } + } + } +out_multi_ptrace: + return ret; +} + asmlinkage long sys_ptrace(long request, long pid, long addr, long data) { struct task_struct *child; @@ -571,9 +587,10 @@ if (ret < 0) goto out_put_task_struct; - ret = arch_ptrace(child, request, addr, data); - if (ret < 0) - goto out_put_task_struct; + if (request == PTRACE_MULTI) + ret = multi_ptrace(child, request, addr, data); + else + ret = arch_ptrace(child, request, addr, data); out_put_task_struct: put_task_struct(child); @@ -656,6 +673,57 @@ return ret; } +struct compat_ptrace_multi { + compat_long_t request; + compat_ulong_t addr; + compat_ulong_t localaddr; + compat_ulong_t length; +}; + +static int compat_multi_ptrace(struct task_struct *child, compat_long_t request, compat_ulong_t addr, compat_ulong_t size) +{ + int i, ret; + compat_long_t j; + ret=0; + if (!access_ok(VERIFY_READ, addr,size*sizeof(struct compat_ptrace_multi))) { + ret = -EIO; + goto out_multi_ptrace; + } + for (i=0; i<size && ret==0; i++, addr+=sizeof(struct compat_ptrace_multi)) { + compat_ulong_t len; + struct compat_ptrace_multi __user pm ; + if (__copy_from_user(&pm, (struct compat_ptrace_multi __user *)(u_long)addr, sizeof(struct compat_ptrace_multi)) == 0) { + len = pm.length; + switch ( pm.request){ + case PTRACE_PEEKTEXT: + case PTRACE_PEEKDATA: + case PTRACE_PEEKUSR: + case PTRACE_POKETEXT: + case PTRACE_POKEDATA: + case PTRACE_POKEUSR: + if (len <= 0) len=1; + for (j=0; j<len && ret==0; j++) + ret=compat_arch_ptrace(child, pm.request, (compat_long_t) (pm.addr) + j*sizeof(compat_long_t), (compat_ulong_t) (pm.localaddr) + j*sizeof(compat_long_t)); + break; + case PTRACE_PEEKCHARDATA: + ret = ptrace_readdata(child, pm.addr, (char __user *)(long)pm.localaddr, len); + break; + case PTRACE_POKECHARDATA: + ret = ptrace_writedata(child, (char __user *)(long)pm.localaddr, pm.addr, len); + break; + case PTRACE_PEEKSTRINGDATA: + ret = ptrace_readstringdata(child, pm.addr, (char __user *)(long)pm.localaddr, len); + break; + default: + ret=compat_arch_ptrace(child, pm.request, (compat_ulong_t) (pm.addr), (compat_ulong_t) (pm.localaddr)); + break; + } + } + } +out_multi_ptrace: + return ret; +} + asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid, compat_long_t addr, compat_long_t data) { @@ -689,8 +757,12 @@ } ret = ptrace_check_attach(child, request == PTRACE_KILL); - if (!ret) - ret = compat_arch_ptrace(child, request, addr, data); + if (!ret) { + if (request == PTRACE_MULTI) + ret= compat_multi_ptrace(child, request, addr, data); + else + ret = compat_arch_ptrace(child, request, addr, data); + } out_put_task_struct: put_task_struct(child); diff -Naur linux-2.6.26-rc6/mm/memory.c linux-2.6.26-rc6-ptrace_multi/mm/memory.c --- linux-2.6.26-rc6/mm/memory.c 2008-06-13 13:33:31.000000000 +0200 +++ linux-2.6.26-rc6-ptrace_multi/mm/memory.c 2008-06-14 16:56:11.000000000 +0200 @@ -2793,6 +2793,74 @@ } /* + * Access another process' address space to/from user space + * Do not walk the page table directly, use get_user_pages + */ +int access_process_vm_user(struct task_struct *tsk, unsigned long addr, char __user *ubuf, int len, int write, int string) +{ + struct mm_struct *mm; + struct vm_area_struct *vma; + struct page *page; + char *buf; + unsigned long old_addr = addr; + + mm = get_task_mm(tsk); + if (!mm) + return 0; + + buf=kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return 0; + + down_read(&mm->mmap_sem); + /* ignore errors, just check how much was sucessfully transfered */ + while (len) { + int bytes, ret, offset; + void *maddr; + + ret = get_user_pages(tsk, mm, addr, 1, + write, 1, &page, &vma); + if (ret <= 0) + break; + + bytes = len; + offset = addr & (PAGE_SIZE-1); + if (bytes > PAGE_SIZE-offset) + bytes = PAGE_SIZE-offset; + + maddr = kmap(page); + if (write) { + __copy_from_user(buf,ubuf,bytes); + copy_to_user_page(vma, page, addr, + maddr + offset, buf, bytes); + if (!PageCompound(page)) + set_page_dirty_lock(page); + } else { + copy_from_user_page(vma, page, addr, + buf, maddr + offset, bytes); + if (string) { + for (offset=0;offset<bytes;offset++) + if (buf[offset]==0) + break; + if (offset < bytes) + bytes=len=offset+1; + } + ret=__copy_to_user(ubuf,buf,bytes); + } + kunmap(page); + page_cache_release(page); + len -= bytes; + ubuf += bytes; + addr += bytes; + } + up_read(&mm->mmap_sem); + mmput(mm); + + kfree(buf); + return addr - old_addr; +} + +/* * Print the name of a VMA. */ void print_vma_addr(char *prefix, unsigned long ip) diff -Naur linux-2.6.26-rc6/patch-linux-2.6.25.5-ptrace_vm linux-2.6.26-rc6-ptrace_multi/patch-linux-2.6.25.5-ptrace_vm --- linux-2.6.26-rc6/patch-linux-2.6.25.5-ptrace_vm 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.26-rc6-ptrace_multi/patch-linux-2.6.25.5-ptrace_vm 2008-06-14 16:56:11.000000000 +0200 @@ -0,0 +1 @@ +diff -Naur linux-2.6.25.5 linux-2.6.25.5.ptrace_vm
|  |