hw-breakpoints: Keep track of user disabled breakpoints
authorFrederic Weisbecker <fweisbec@gmail.com>
Wed, 2 Dec 2009 06:32:16 +0000 (07:32 +0100)
committerIngo Molnar <mingo@elte.hu>
Wed, 2 Dec 2009 08:59:03 +0000 (09:59 +0100)
When we disable a breakpoint through dr7, we unregister it right
away, making us lose track of its corresponding address
register value.

It means that the following sequence would be unsupported:

 - set address in dr0
 - enable it through dr7
 - disable it through dr7
 - enable it through dr7

because we lost the address register value when we disabled the
breakpoint.

Don't unregister the disabled breakpoints but rather disable
them.

Reported-by: "K.Prasad" <prasad@linux.vnet.ibm.com>
Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
LKML-Reference: <1259735536-9236-1-git-send-regression-fweisbec@gmail.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
arch/x86/kernel/ptrace.c

index 2941b32ea666f09d584ef34cb06cde995aa7b89f..04d182a7cfdbd6e3c88c41658f6d0a91878e6366 100644 (file)
@@ -595,7 +595,7 @@ static unsigned long ptrace_get_dr7(struct perf_event *bp[])
 
 static struct perf_event *
 ptrace_modify_breakpoint(struct perf_event *bp, int len, int type,
-                        struct task_struct *tsk)
+                        struct task_struct *tsk, int disabled)
 {
        int err;
        int gen_len, gen_type;
@@ -616,7 +616,7 @@ ptrace_modify_breakpoint(struct perf_event *bp, int len, int type,
        attr = bp->attr;
        attr.bp_len = gen_len;
        attr.bp_type = gen_type;
-       attr.disabled = 0;
+       attr.disabled = disabled;
 
        return modify_user_hw_breakpoint(bp, &attr, bp->callback, tsk);
 }
@@ -655,13 +655,21 @@ restore:
                                 */
                                if (!second_pass)
                                        continue;
+
                                thread->ptrace_bps[i] = NULL;
-                               unregister_hw_breakpoint(bp);
+                               bp = ptrace_modify_breakpoint(bp, len, type,
+                                                             tsk, 1);
+                               if (IS_ERR(bp)) {
+                                       rc = PTR_ERR(bp);
+                                       thread->ptrace_bps[i] = NULL;
+                                       break;
+                               }
+                               thread->ptrace_bps[i] = bp;
                        }
                        continue;
                }
 
-               bp = ptrace_modify_breakpoint(bp, len, type, tsk);
+               bp = ptrace_modify_breakpoint(bp, len, type, tsk, 0);
 
                /* Incorrect bp, or we have a bug in bp API */
                if (IS_ERR(bp)) {