[PATCH 1/2] ptrace_multi: speedup for virtual machines (and debuggers) running on ptrace

From: Renzo Davoli
Date: Mon Jun 16 2008 - 04:20:56 EST


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@xxxxxxxxxxx>

---
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
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/