Le 01/04/2020 à 08:13, Ravi Bangoria a écrit :
With Book3s DAWR, ptrace and perf watchpoints on powerpc behaves
differently. Ptrace watchpoint works in one-shot mode and generates
signal before executing instruction. It's ptrace user's job to
single-step the instruction and re-enable the watchpoint. OTOH, in
case of perf watchpoint, kernel emulates/single-steps the instruction
and then generates event. If perf and ptrace creates two events with
same or overlapping address ranges, it's ambiguous to decide who
should single-step the instruction. Because of this issue, don't
allow perf and ptrace watchpoint at the same time if their address
range overlaps.

Signed-off-by: Ravi Bangoria <ravi.bango...@linux.ibm.com>
---
  arch/powerpc/include/asm/hw_breakpoint.h |   2 +
  arch/powerpc/kernel/hw_breakpoint.c      | 222 +++++++++++++++++++++++
  kernel/events/hw_breakpoint.c            |  16 ++
  3 files changed, 240 insertions(+)

diff --git a/arch/powerpc/include/asm/hw_breakpoint.h 
b/arch/powerpc/include/asm/hw_breakpoint.h
index abc4603c0efe..9d3bd1169591 100644
--- a/arch/powerpc/include/asm/hw_breakpoint.h
+++ b/arch/powerpc/include/asm/hw_breakpoint.h
@@ -70,6 +70,8 @@ extern int hw_breakpoint_exceptions_notify(struct 
notifier_block *unused,
                                                unsigned long val, void *data);
  int arch_install_hw_breakpoint(struct perf_event *bp);
  void arch_uninstall_hw_breakpoint(struct perf_event *bp);
+int arch_reserve_bp_slot(struct perf_event *bp);
+void arch_release_bp_slot(struct perf_event *bp);
  void arch_unregister_hw_breakpoint(struct perf_event *bp);
  void hw_breakpoint_pmu_read(struct perf_event *bp);
  extern void flush_ptrace_hw_breakpoint(struct task_struct *tsk);
diff --git a/arch/powerpc/kernel/hw_breakpoint.c 
b/arch/powerpc/kernel/hw_breakpoint.c
index 07a6cdea84ed..f813acb0d9f0 100644
--- a/arch/powerpc/kernel/hw_breakpoint.c
+++ b/arch/powerpc/kernel/hw_breakpoint.c
@@ -123,6 +123,228 @@ static bool is_ptrace_bp(struct perf_event *bp)
        return bp->overflow_handler == ptrace_triggered;
  }
+struct breakpoint {
+       struct list_head list;
+       struct perf_event *bp;
+       bool ptrace_bp;
+};
+
+static DEFINE_PER_CPU(struct breakpoint *, cpu_bps[HBP_NUM_MAX]);
+static LIST_HEAD(task_bps);
+
+static struct breakpoint *alloc_breakpoint(struct perf_event *bp)
+{
+       struct breakpoint *tmp;
+
+       tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
+       if (!tmp)
+               return ERR_PTR(-ENOMEM);
+       tmp->bp = bp;
+       tmp->ptrace_bp = is_ptrace_bp(bp);
+       return tmp;
+}
+
+static bool bp_addr_range_overlap(struct perf_event *bp1, struct perf_event 
*bp2)
+{
+       __u64 bp1_saddr, bp1_eaddr, bp2_saddr, bp2_eaddr;
+
+       bp1_saddr = ALIGN_DOWN(bp1->attr.bp_addr, HW_BREAKPOINT_SIZE);
+       bp1_eaddr = ALIGN(bp1->attr.bp_addr + bp1->attr.bp_len, 
HW_BREAKPOINT_SIZE) - 1;
+       bp2_saddr = ALIGN_DOWN(bp2->attr.bp_addr, HW_BREAKPOINT_SIZE);
+       bp2_eaddr = ALIGN(bp2->attr.bp_addr + bp2->attr.bp_len, 
HW_BREAKPOINT_SIZE) - 1;
+
+       return (bp1_saddr <= bp2_eaddr && bp1_eaddr >= bp2_saddr);

Could avoid the - 1 on bp1_eaddr and bp2_eaddr by doing:

        return (bp1_saddr < bp2_eaddr && bp1_eaddr > bp2_saddr);


Christophe

Reply via email to