Merge branch 'uprobes/core' of git://git.kernel.org/pub/scm/linux/kernel/git/oleg...
authorIngo Molnar <mingo@kernel.org>
Sat, 8 Dec 2012 14:50:23 +0000 (15:50 +0100)
committerIngo Molnar <mingo@kernel.org>
Sat, 8 Dec 2012 14:51:10 +0000 (15:51 +0100)
Pull uprobes fixes, cleanups and preparation for the ARM port from Oleg Nesterov.

Signed-off-by: Ingo Molnar <mingo@kernel.org>
arch/powerpc/kernel/signal.c
arch/powerpc/kernel/uprobes.c
arch/x86/kernel/uprobes.c
include/linux/uprobes.h
kernel/events/uprobes.c
kernel/fork.c
kernel/trace/trace_uprobe.c

index a2dc757..3b99711 100644 (file)
@@ -158,10 +158,8 @@ static int do_signal(struct pt_regs *regs)
 
 void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags)
 {
-       if (thread_info_flags & _TIF_UPROBE) {
-               clear_thread_flag(TIF_UPROBE);
+       if (thread_info_flags & _TIF_UPROBE)
                uprobe_notify_resume(regs);
-       }
 
        if (thread_info_flags & _TIF_SIGPENDING)
                do_signal(regs);
index d2d46d1..bc77834 100644 (file)
@@ -64,6 +64,8 @@ int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
        autask->saved_trap_nr = current->thread.trap_nr;
        current->thread.trap_nr = UPROBE_TRAP_NR;
        regs->nip = current->utask->xol_vaddr;
+
+       user_enable_single_step(current);
        return 0;
 }
 
@@ -119,6 +121,8 @@ int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
         * to be executed.
         */
        regs->nip = utask->vaddr + MAX_UINSN_BYTES;
+
+       user_disable_single_step(current);
        return 0;
 }
 
@@ -162,6 +166,8 @@ void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 
        current->thread.trap_nr = utask->autask.saved_trap_nr;
        instruction_pointer_set(regs, utask->vaddr);
+
+       user_disable_single_step(current);
 }
 
 /*
index aafa555..c71025b 100644 (file)
@@ -478,6 +478,11 @@ int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
        regs->ip = current->utask->xol_vaddr;
        pre_xol_rip_insn(auprobe, regs, autask);
 
+       autask->saved_tf = !!(regs->flags & X86_EFLAGS_TF);
+       regs->flags |= X86_EFLAGS_TF;
+       if (test_tsk_thread_flag(current, TIF_BLOCKSTEP))
+               set_task_blockstep(current, false);
+
        return 0;
 }
 
@@ -603,6 +608,16 @@ int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
        if (auprobe->fixups & UPROBE_FIX_CALL)
                result = adjust_ret_addr(regs->sp, correction);
 
+       /*
+        * arch_uprobe_pre_xol() doesn't save the state of TIF_BLOCKSTEP
+        * so we can get an extra SIGTRAP if we do not clear TF. We need
+        * to examine the opcode to make it right.
+        */
+       if (utask->autask.saved_tf)
+               send_sig(SIGTRAP, current, 0);
+       else if (!(auprobe->fixups & UPROBE_FIX_SETF))
+               regs->flags &= ~X86_EFLAGS_TF;
+
        return result;
 }
 
@@ -647,6 +662,10 @@ void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
        current->thread.trap_nr = utask->autask.saved_trap_nr;
        handle_riprel_post_xol(auprobe, regs, NULL);
        instruction_pointer_set(regs, utask->vaddr);
+
+       /* clear TF if it was set by us in arch_uprobe_pre_xol() */
+       if (!utask->autask.saved_tf)
+               regs->flags &= ~X86_EFLAGS_TF;
 }
 
 /*
@@ -676,38 +695,3 @@ bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
                send_sig(SIGTRAP, current, 0);
        return ret;
 }
-
-void arch_uprobe_enable_step(struct arch_uprobe *auprobe)
-{
-       struct task_struct *task = current;
-       struct arch_uprobe_task *autask = &task->utask->autask;
-       struct pt_regs *regs = task_pt_regs(task);
-
-       autask->saved_tf = !!(regs->flags & X86_EFLAGS_TF);
-
-       regs->flags |= X86_EFLAGS_TF;
-       if (test_tsk_thread_flag(task, TIF_BLOCKSTEP))
-               set_task_blockstep(task, false);
-}
-
-void arch_uprobe_disable_step(struct arch_uprobe *auprobe)
-{
-       struct task_struct *task = current;
-       struct arch_uprobe_task *autask = &task->utask->autask;
-       bool trapped = (task->utask->state == UTASK_SSTEP_TRAPPED);
-       struct pt_regs *regs = task_pt_regs(task);
-       /*
-        * The state of TIF_BLOCKSTEP was not saved so we can get an extra
-        * SIGTRAP if we do not clear TF. We need to examine the opcode to
-        * make it right.
-        */
-       if (unlikely(trapped)) {
-               if (!autask->saved_tf)
-                       regs->flags &= ~X86_EFLAGS_TF;
-       } else {
-               if (autask->saved_tf)
-                       send_sig(SIGTRAP, task, 0);
-               else if (!(auprobe->fixups & UPROBE_FIX_SETF))
-                       regs->flags &= ~X86_EFLAGS_TF;
-       }
-}
index 2459457..4f628a6 100644 (file)
@@ -97,12 +97,12 @@ extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_con
 extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc);
 extern int uprobe_mmap(struct vm_area_struct *vma);
 extern void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end);
+extern void uprobe_start_dup_mmap(void);
+extern void uprobe_end_dup_mmap(void);
 extern void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm);
 extern void uprobe_free_utask(struct task_struct *t);
 extern void uprobe_copy_process(struct task_struct *t);
 extern unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs);
-extern void __weak arch_uprobe_enable_step(struct arch_uprobe *arch);
-extern void __weak arch_uprobe_disable_step(struct arch_uprobe *arch);
 extern int uprobe_post_sstep_notifier(struct pt_regs *regs);
 extern int uprobe_pre_sstep_notifier(struct pt_regs *regs);
 extern void uprobe_notify_resume(struct pt_regs *regs);
@@ -129,6 +129,12 @@ static inline void
 uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end)
 {
 }
+static inline void uprobe_start_dup_mmap(void)
+{
+}
+static inline void uprobe_end_dup_mmap(void)
+{
+}
 static inline void
 uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm)
 {
index 5cc4e7e..dea7acf 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/ptrace.h>      /* user_enable_single_step */
 #include <linux/kdebug.h>      /* notifier mechanism */
 #include "../../mm/internal.h" /* munlock_vma_page */
+#include <linux/percpu-rwsem.h>
 
 #include <linux/uprobes.h>
 
@@ -71,6 +72,8 @@ static struct mutex uprobes_mutex[UPROBES_HASH_SZ];
 static struct mutex uprobes_mmap_mutex[UPROBES_HASH_SZ];
 #define uprobes_mmap_hash(v)   (&uprobes_mmap_mutex[((unsigned long)(v)) % UPROBES_HASH_SZ])
 
+static struct percpu_rw_semaphore dup_mmap_sem;
+
 /*
  * uprobe_events allows us to skip the uprobe_mmap if there are no uprobe
  * events active at this time.  Probably a fine grained per inode count is
@@ -766,10 +769,13 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register)
        struct map_info *info;
        int err = 0;
 
+       percpu_down_write(&dup_mmap_sem);
        info = build_map_info(uprobe->inode->i_mapping,
                                        uprobe->offset, is_register);
-       if (IS_ERR(info))
-               return PTR_ERR(info);
+       if (IS_ERR(info)) {
+               err = PTR_ERR(info);
+               goto out;
+       }
 
        while (info) {
                struct mm_struct *mm = info->mm;
@@ -799,7 +805,8 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register)
                mmput(mm);
                info = free_map_info(info);
        }
-
+ out:
+       percpu_up_write(&dup_mmap_sem);
        return err;
 }
 
@@ -1131,6 +1138,16 @@ void uprobe_clear_state(struct mm_struct *mm)
        kfree(area);
 }
 
+void uprobe_start_dup_mmap(void)
+{
+       percpu_down_read(&dup_mmap_sem);
+}
+
+void uprobe_end_dup_mmap(void)
+{
+       percpu_up_read(&dup_mmap_sem);
+}
+
 void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm)
 {
        newmm->uprobes_state.xol_area = NULL;
@@ -1199,6 +1216,11 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe, unsigned long slot
        vaddr = kmap_atomic(area->page);
        memcpy(vaddr + offset, uprobe->arch.insn, MAX_UINSN_BYTES);
        kunmap_atomic(vaddr);
+       /*
+        * We probably need flush_icache_user_range() but it needs vma.
+        * This should work on supported architectures too.
+        */
+       flush_dcache_page(area->page);
 
        return current->utask->xol_vaddr;
 }
@@ -1430,16 +1452,6 @@ static struct uprobe *find_active_uprobe(unsigned long bp_vaddr, int *is_swbp)
        return uprobe;
 }
 
-void __weak arch_uprobe_enable_step(struct arch_uprobe *arch)
-{
-       user_enable_single_step(current);
-}
-
-void __weak arch_uprobe_disable_step(struct arch_uprobe *arch)
-{
-       user_disable_single_step(current);
-}
-
 /*
  * Run handler and ask thread to singlestep.
  * Ensure all non-fatal signals cannot interrupt thread while it singlesteps.
@@ -1493,7 +1505,6 @@ static void handle_swbp(struct pt_regs *regs)
                goto out;
 
        if (!pre_ssout(uprobe, regs, bp_vaddr)) {
-               arch_uprobe_enable_step(&uprobe->arch);
                utask->active_uprobe = uprobe;
                utask->state = UTASK_SSTEP;
                return;
@@ -1525,7 +1536,6 @@ static void handle_singlestep(struct uprobe_task *utask, struct pt_regs *regs)
        else
                WARN_ON_ONCE(1);
 
-       arch_uprobe_disable_step(&uprobe->arch);
        put_uprobe(uprobe);
        utask->active_uprobe = NULL;
        utask->state = UTASK_RUNNING;
@@ -1604,6 +1614,9 @@ static int __init init_uprobes(void)
                mutex_init(&uprobes_mmap_mutex[i]);
        }
 
+       if (percpu_init_rwsem(&dup_mmap_sem))
+               return -ENOMEM;
+
        return register_die_notifier(&uprobe_exception_nb);
 }
 module_init(init_uprobes);
index 8b20ab7..c497e57 100644 (file)
@@ -352,6 +352,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
        unsigned long charge;
        struct mempolicy *pol;
 
+       uprobe_start_dup_mmap();
        down_write(&oldmm->mmap_sem);
        flush_cache_dup_mm(oldmm);
        uprobe_dup_mmap(oldmm, mm);
@@ -469,6 +470,7 @@ out:
        up_write(&mm->mmap_sem);
        flush_tlb_mm(oldmm);
        up_write(&oldmm->mmap_sem);
+       uprobe_end_dup_mmap();
        return retval;
 fail_nomem_anon_vma_fork:
        mpol_put(pol);
index 4ff9ca4..9614db8 100644 (file)
@@ -189,7 +189,7 @@ static int create_trace_uprobe(int argc, char **argv)
        if (argv[0][0] == '-')
                is_delete = true;
        else if (argv[0][0] != 'p') {
-               pr_info("Probe definition must be started with 'p', 'r' or" " '-'.\n");
+               pr_info("Probe definition must be started with 'p' or '-'.\n");
                return -EINVAL;
        }