uprobes: Add uprobe_task->dup_xol_work/dup_xol_addr
[deliverable/linux.git] / kernel / events / uprobes.c
index 9f282e14925ddf0da6b750450f6f35c7c7fb10d9..df4ef0971266a3fd40cbd346dc9795286ad63471 100644 (file)
@@ -245,12 +245,12 @@ static int verify_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t
  * the architecture. If an arch has variable length instruction and the
  * breakpoint instruction is not of the smallest length instruction
  * supported by that architecture then we need to modify is_trap_at_addr and
- * write_opcode accordingly. This would never be a problem for archs that
- * have fixed length instructions.
+ * uprobe_write_opcode accordingly. This would never be a problem for archs
+ * that have fixed length instructions.
  */
 
 /*
- * write_opcode - write the opcode at a given virtual address.
+ * uprobe_write_opcode - write the opcode at a given virtual address.
  * @mm: the probed process address space.
  * @vaddr: the virtual address to store the opcode.
  * @opcode: opcode to be written at @vaddr.
@@ -261,7 +261,7 @@ static int verify_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t
  * For mm @mm, write the opcode at @vaddr.
  * Return 0 (success) or a negative errno.
  */
-static int write_opcode(struct mm_struct *mm, unsigned long vaddr,
+int uprobe_write_opcode(struct mm_struct *mm, unsigned long vaddr,
                        uprobe_opcode_t opcode)
 {
        struct page *old_page, *new_page;
@@ -315,7 +315,7 @@ put_old:
  */
 int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr)
 {
-       return write_opcode(mm, vaddr, UPROBE_SWBP_INSN);
+       return uprobe_write_opcode(mm, vaddr, UPROBE_SWBP_INSN);
 }
 
 /**
@@ -330,7 +330,7 @@ int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned
 int __weak
 set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr)
 {
-       return write_opcode(mm, vaddr, *(uprobe_opcode_t *)auprobe->insn);
+       return uprobe_write_opcode(mm, vaddr, *(uprobe_opcode_t *)auprobe->insn);
 }
 
 static int match_uprobe(struct uprobe *l, struct uprobe *r)
@@ -504,9 +504,8 @@ static bool consumer_del(struct uprobe *uprobe, struct uprobe_consumer *uc)
        return ret;
 }
 
-static int
-__copy_insn(struct address_space *mapping, struct file *filp, char *insn,
-                       unsigned long nbytes, loff_t offset)
+static int __copy_insn(struct address_space *mapping, struct file *filp,
+                       void *insn, int nbytes, loff_t offset)
 {
        struct page *page;
 
@@ -528,28 +527,28 @@ __copy_insn(struct address_space *mapping, struct file *filp, char *insn,
 
 static int copy_insn(struct uprobe *uprobe, struct file *filp)
 {
-       struct address_space *mapping;
-       unsigned long nbytes;
-       int bytes;
-
-       nbytes = PAGE_SIZE - (uprobe->offset & ~PAGE_MASK);
-       mapping = uprobe->inode->i_mapping;
+       struct address_space *mapping = uprobe->inode->i_mapping;
+       loff_t offs = uprobe->offset;
+       void *insn = uprobe->arch.insn;
+       int size = MAX_UINSN_BYTES;
+       int len, err = -EIO;
 
-       /* Instruction at end of binary; copy only available bytes */
-       if (uprobe->offset + MAX_UINSN_BYTES > uprobe->inode->i_size)
-               bytes = uprobe->inode->i_size - uprobe->offset;
-       else
-               bytes = MAX_UINSN_BYTES;
+       /* Copy only available bytes, -EIO if nothing was read */
+       do {
+               if (offs >= i_size_read(uprobe->inode))
+                       break;
 
-       /* Instruction at the page-boundary; copy bytes in second page */
-       if (nbytes < bytes) {
-               int err = __copy_insn(mapping, filp, uprobe->arch.insn + nbytes,
-                               bytes - nbytes, uprobe->offset + nbytes);
+               len = min_t(int, size, PAGE_SIZE - (offs & ~PAGE_MASK));
+               err = __copy_insn(mapping, filp, insn, len, offs);
                if (err)
-                       return err;
-               bytes = nbytes;
-       }
-       return __copy_insn(mapping, filp, uprobe->arch.insn, bytes, uprobe->offset);
+                       break;
+
+               insn += len;
+               offs += len;
+               size -= len;
+       } while (size);
+
+       return err;
 }
 
 static int prepare_uprobe(struct uprobe *uprobe, struct file *file,
@@ -577,7 +576,7 @@ static int prepare_uprobe(struct uprobe *uprobe, struct file *file,
        if (ret)
                goto out;
 
-       /* write_opcode() assumes we don't cross page boundary */
+       /* uprobe_write_opcode() assumes we don't cross page boundary */
        BUG_ON((uprobe->offset & ~PAGE_MASK) +
                        UPROBE_SWBP_INSN_SIZE > PAGE_SIZE);
 
@@ -1264,7 +1263,8 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe)
                return 0;
 
        /* Initialize the slot */
-       copy_to_page(area->page, xol_vaddr, uprobe->arch.insn, MAX_UINSN_BYTES);
+       copy_to_page(area->page, xol_vaddr,
+                       uprobe->arch.ixol, sizeof(uprobe->arch.ixol));
        /*
         * We probably need flush_icache_user_range() but it needs vma.
         * This should work on supported architectures too.
@@ -1403,28 +1403,28 @@ static void uprobe_warn(struct task_struct *t, const char *msg)
 
 static void dup_xol_work(struct callback_head *work)
 {
-       kfree(work);
-
        if (current->flags & PF_EXITING)
                return;
 
-       if (!__create_xol_area(current->utask->vaddr))
+       if (!__create_xol_area(current->utask->dup_xol_addr))
                uprobe_warn(current, "dup xol area");
 }
 
 /*
  * Called in context of a new clone/fork from copy_process.
  */
-void uprobe_copy_process(struct task_struct *t)
+void uprobe_copy_process(struct task_struct *t, unsigned long flags)
 {
        struct uprobe_task *utask = current->utask;
        struct mm_struct *mm = current->mm;
-       struct callback_head *work;
        struct xol_area *area;
 
        t->utask = NULL;
 
-       if (mm == t->mm || !utask || !utask->return_instances)
+       if (!utask || !utask->return_instances)
+               return;
+
+       if (mm == t->mm && !(flags & CLONE_VFORK))
                return;
 
        if (dup_utask(t, utask))
@@ -1435,14 +1435,12 @@ void uprobe_copy_process(struct task_struct *t)
        if (!area)
                return uprobe_warn(t, "dup xol area");
 
-       /* TODO: move it into the union in uprobe_task */
-       work = kmalloc(sizeof(*work), GFP_KERNEL);
-       if (!work)
-               return uprobe_warn(t, "dup xol area");
+       if (mm == t->mm)
+               return;
 
-       utask->vaddr = area->vaddr;
-       init_task_work(work, dup_xol_work);
-       task_work_add(t, work, true);
+       t->utask->dup_xol_addr = area->vaddr;
+       init_task_work(&t->utask->dup_xol_work, dup_xol_work);
+       task_work_add(t, &t->utask->dup_xol_work, true);
 }
 
 /*
@@ -1935,9 +1933,4 @@ static int __init init_uprobes(void)
 
        return register_die_notifier(&uprobe_exception_nb);
 }
-module_init(init_uprobes);
-
-static void __exit exit_uprobes(void)
-{
-}
-module_exit(exit_uprobes);
+__initcall(init_uprobes);
This page took 0.136947 seconds and 5 git commands to generate.