aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrederic Weisbecker <fweisbec@gmail.com>2009-11-26 22:55:53 -0500
committerIngo Molnar <mingo@elte.hu>2009-11-27 00:22:58 -0500
commit5fa10b28e57f94a90535cfeafe89dcee9f47d540 (patch)
tree951cf12a3b5b3a37546b4f6dd2994bebdac19dc0
parentb2e74a265ded1a185f762ebaab967e9e0d008dd8 (diff)
hw-breakpoints: Use struct perf_event_attr to define user breakpoints
In-kernel user breakpoints are created using functions in which we pass breakpoint parameters as individual variables: address, length and type. Although it fits well for x86, this just does not scale across archictectures that may support this api later as these may have more or different needs. Pass in a perf_event_attr structure instead because it is meant to evolve as much as possible into a generic hardware breakpoint parameter structure. Reported-by: K.Prasad <prasad@linux.vnet.ibm.com> Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com> LKML-Reference: <1259294154-5197-1-git-send-regression-fweisbec@gmail.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r--arch/x86/kernel/ptrace.c74
-rw-r--r--include/linux/hw_breakpoint.h36
-rw-r--r--kernel/hw_breakpoint.c87
3 files changed, 75 insertions, 122 deletions
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index 75e0cd847bd6..2941b32ea666 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -593,6 +593,34 @@ static unsigned long ptrace_get_dr7(struct perf_event *bp[])
593 return dr7; 593 return dr7;
594} 594}
595 595
596static struct perf_event *
597ptrace_modify_breakpoint(struct perf_event *bp, int len, int type,
598 struct task_struct *tsk)
599{
600 int err;
601 int gen_len, gen_type;
602 DEFINE_BREAKPOINT_ATTR(attr);
603
604 /*
605 * We shoud have at least an inactive breakpoint at this
606 * slot. It means the user is writing dr7 without having
607 * written the address register first
608 */
609 if (!bp)
610 return ERR_PTR(-EINVAL);
611
612 err = arch_bp_generic_fields(len, type, &gen_len, &gen_type);
613 if (err)
614 return ERR_PTR(err);
615
616 attr = bp->attr;
617 attr.bp_len = gen_len;
618 attr.bp_type = gen_type;
619 attr.disabled = 0;
620
621 return modify_user_hw_breakpoint(bp, &attr, bp->callback, tsk);
622}
623
596/* 624/*
597 * Handle ptrace writes to debug register 7. 625 * Handle ptrace writes to debug register 7.
598 */ 626 */
@@ -603,7 +631,6 @@ static int ptrace_write_dr7(struct task_struct *tsk, unsigned long data)
603 int i, orig_ret = 0, rc = 0; 631 int i, orig_ret = 0, rc = 0;
604 int enabled, second_pass = 0; 632 int enabled, second_pass = 0;
605 unsigned len, type; 633 unsigned len, type;
606 int gen_len, gen_type;
607 struct perf_event *bp; 634 struct perf_event *bp;
608 635
609 data &= ~DR_CONTROL_RESERVED; 636 data &= ~DR_CONTROL_RESERVED;
@@ -634,33 +661,12 @@ restore:
634 continue; 661 continue;
635 } 662 }
636 663
637 /* 664 bp = ptrace_modify_breakpoint(bp, len, type, tsk);
638 * We shoud have at least an inactive breakpoint at this
639 * slot. It means the user is writing dr7 without having
640 * written the address register first
641 */
642 if (!bp) {
643 rc = -EINVAL;
644 break;
645 }
646
647 rc = arch_bp_generic_fields(len, type, &gen_len, &gen_type);
648 if (rc)
649 break;
650
651 /*
652 * This is a temporary thing as bp is unregistered/registered
653 * to simulate modification
654 */
655 bp = modify_user_hw_breakpoint(bp, bp->attr.bp_addr, gen_len,
656 gen_type, bp->callback,
657 tsk, true);
658 thread->ptrace_bps[i] = NULL;
659 665
660 /* Incorrect bp, or we have a bug in bp API */ 666 /* Incorrect bp, or we have a bug in bp API */
661 if (IS_ERR(bp)) { 667 if (IS_ERR(bp)) {
662 rc = PTR_ERR(bp); 668 rc = PTR_ERR(bp);
663 bp = NULL; 669 thread->ptrace_bps[i] = NULL;
664 break; 670 break;
665 } 671 }
666 thread->ptrace_bps[i] = bp; 672 thread->ptrace_bps[i] = bp;
@@ -707,24 +713,26 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr,
707{ 713{
708 struct perf_event *bp; 714 struct perf_event *bp;
709 struct thread_struct *t = &tsk->thread; 715 struct thread_struct *t = &tsk->thread;
716 DEFINE_BREAKPOINT_ATTR(attr);
710 717
711 if (!t->ptrace_bps[nr]) { 718 if (!t->ptrace_bps[nr]) {
712 /* 719 /*
713 * Put stub len and type to register (reserve) an inactive but 720 * Put stub len and type to register (reserve) an inactive but
714 * correct bp 721 * correct bp
715 */ 722 */
716 bp = register_user_hw_breakpoint(addr, HW_BREAKPOINT_LEN_1, 723 attr.bp_addr = addr;
717 HW_BREAKPOINT_W, 724 attr.bp_len = HW_BREAKPOINT_LEN_1;
718 ptrace_triggered, tsk, 725 attr.bp_type = HW_BREAKPOINT_W;
719 false); 726 attr.disabled = 1;
727
728 bp = register_user_hw_breakpoint(&attr, ptrace_triggered, tsk);
720 } else { 729 } else {
721 bp = t->ptrace_bps[nr]; 730 bp = t->ptrace_bps[nr];
722 t->ptrace_bps[nr] = NULL; 731 t->ptrace_bps[nr] = NULL;
723 bp = modify_user_hw_breakpoint(bp, addr, bp->attr.bp_len, 732
724 bp->attr.bp_type, 733 attr = bp->attr;
725 bp->callback, 734 attr.bp_addr = addr;
726 tsk, 735 bp = modify_user_hw_breakpoint(bp, &attr, bp->callback, tsk);
727 bp->attr.disabled);
728 } 736 }
729 /* 737 /*
730 * CHECKME: the previous code returned -EIO if the addr wasn't a 738 * CHECKME: the previous code returned -EIO if the addr wasn't a
diff --git a/include/linux/hw_breakpoint.h b/include/linux/hw_breakpoint.h
index c9f7f7c7b0e0..5da472e434b7 100644
--- a/include/linux/hw_breakpoint.h
+++ b/include/linux/hw_breakpoint.h
@@ -20,6 +20,14 @@ enum {
20 20
21#ifdef CONFIG_HAVE_HW_BREAKPOINT 21#ifdef CONFIG_HAVE_HW_BREAKPOINT
22 22
23/* As it's for in-kernel or ptrace use, we want it to be pinned */
24#define DEFINE_BREAKPOINT_ATTR(name) \
25struct perf_event_attr name = { \
26 .type = PERF_TYPE_BREAKPOINT, \
27 .size = sizeof(name), \
28 .pinned = 1, \
29};
30
23static inline unsigned long hw_breakpoint_addr(struct perf_event *bp) 31static inline unsigned long hw_breakpoint_addr(struct perf_event *bp)
24{ 32{
25 return bp->attr.bp_addr; 33 return bp->attr.bp_addr;
@@ -36,22 +44,16 @@ static inline int hw_breakpoint_len(struct perf_event *bp)
36} 44}
37 45
38extern struct perf_event * 46extern struct perf_event *
39register_user_hw_breakpoint(unsigned long addr, 47register_user_hw_breakpoint(struct perf_event_attr *attr,
40 int len,
41 int type,
42 perf_callback_t triggered, 48 perf_callback_t triggered,
43 struct task_struct *tsk, 49 struct task_struct *tsk);
44 bool active);
45 50
46/* FIXME: only change from the attr, and don't unregister */ 51/* FIXME: only change from the attr, and don't unregister */
47extern struct perf_event * 52extern struct perf_event *
48modify_user_hw_breakpoint(struct perf_event *bp, 53modify_user_hw_breakpoint(struct perf_event *bp,
49 unsigned long addr, 54 struct perf_event_attr *attr,
50 int len,
51 int type,
52 perf_callback_t triggered, 55 perf_callback_t triggered,
53 struct task_struct *tsk, 56 struct task_struct *tsk);
54 bool active);
55 57
56/* 58/*
57 * Kernel breakpoints are not associated with any particular thread. 59 * Kernel breakpoints are not associated with any particular thread.
@@ -89,20 +91,14 @@ static inline struct arch_hw_breakpoint *counter_arch_bp(struct perf_event *bp)
89#else /* !CONFIG_HAVE_HW_BREAKPOINT */ 91#else /* !CONFIG_HAVE_HW_BREAKPOINT */
90 92
91static inline struct perf_event * 93static inline struct perf_event *
92register_user_hw_breakpoint(unsigned long addr, 94register_user_hw_breakpoint(struct perf_event_attr *attr,
93 int len,
94 int type,
95 perf_callback_t triggered, 95 perf_callback_t triggered,
96 struct task_struct *tsk, 96 struct task_struct *tsk) { return NULL; }
97 bool active) { return NULL; }
98static inline struct perf_event * 97static inline struct perf_event *
99modify_user_hw_breakpoint(struct perf_event *bp, 98modify_user_hw_breakpoint(struct perf_event *bp,
100 unsigned long addr, 99 struct perf_event_attr *attr,
101 int len,
102 int type,
103 perf_callback_t triggered, 100 perf_callback_t triggered,
104 struct task_struct *tsk, 101 struct task_struct *tsk) { return NULL; }
105 bool active) { return NULL; }
106static inline struct perf_event * 102static inline struct perf_event *
107register_wide_hw_breakpoint_cpu(unsigned long addr, 103register_wide_hw_breakpoint_cpu(unsigned long addr,
108 int len, 104 int len,
diff --git a/kernel/hw_breakpoint.c b/kernel/hw_breakpoint.c
index 32e1018191be..2a47514f12fd 100644
--- a/kernel/hw_breakpoint.c
+++ b/kernel/hw_breakpoint.c
@@ -289,90 +289,32 @@ int register_perf_hw_breakpoint(struct perf_event *bp)
289 return __register_perf_hw_breakpoint(bp); 289 return __register_perf_hw_breakpoint(bp);
290} 290}
291 291
292/*
293 * Register a breakpoint bound to a task and a given cpu.
294 * If cpu is -1, the breakpoint is active for the task in every cpu
295 * If the task is -1, the breakpoint is active for every tasks in the given
296 * cpu.
297 */
298static struct perf_event *
299register_user_hw_breakpoint_cpu(unsigned long addr,
300 int len,
301 int type,
302 perf_callback_t triggered,
303 pid_t pid,
304 int cpu,
305 bool active)
306{
307 struct perf_event_attr *attr;
308 struct perf_event *bp;
309
310 attr = kzalloc(sizeof(*attr), GFP_KERNEL);
311 if (!attr)
312 return ERR_PTR(-ENOMEM);
313
314 attr->type = PERF_TYPE_BREAKPOINT;
315 attr->size = sizeof(*attr);
316 attr->bp_addr = addr;
317 attr->bp_len = len;
318 attr->bp_type = type;
319 /*
320 * Such breakpoints are used by debuggers to trigger signals when
321 * we hit the excepted memory op. We can't miss such events, they
322 * must be pinned.
323 */
324 attr->pinned = 1;
325
326 if (!active)
327 attr->disabled = 1;
328
329 bp = perf_event_create_kernel_counter(attr, cpu, pid, triggered);
330 kfree(attr);
331
332 return bp;
333}
334
335/** 292/**
336 * register_user_hw_breakpoint - register a hardware breakpoint for user space 293 * register_user_hw_breakpoint - register a hardware breakpoint for user space
337 * @addr: is the memory address that triggers the breakpoint 294 * @attr: breakpoint attributes
338 * @len: the length of the access to the memory (1 byte, 2 bytes etc...)
339 * @type: the type of the access to the memory (read/write/exec)
340 * @triggered: callback to trigger when we hit the breakpoint 295 * @triggered: callback to trigger when we hit the breakpoint
341 * @tsk: pointer to 'task_struct' of the process to which the address belongs 296 * @tsk: pointer to 'task_struct' of the process to which the address belongs
342 * @active: should we activate it while registering it
343 *
344 */ 297 */
345struct perf_event * 298struct perf_event *
346register_user_hw_breakpoint(unsigned long addr, 299register_user_hw_breakpoint(struct perf_event_attr *attr,
347 int len,
348 int type,
349 perf_callback_t triggered, 300 perf_callback_t triggered,
350 struct task_struct *tsk, 301 struct task_struct *tsk)
351 bool active)
352{ 302{
353 return register_user_hw_breakpoint_cpu(addr, len, type, triggered, 303 return perf_event_create_kernel_counter(attr, -1, tsk->pid, triggered);
354 tsk->pid, -1, active);
355} 304}
356EXPORT_SYMBOL_GPL(register_user_hw_breakpoint); 305EXPORT_SYMBOL_GPL(register_user_hw_breakpoint);
357 306
358/** 307/**
359 * modify_user_hw_breakpoint - modify a user-space hardware breakpoint 308 * modify_user_hw_breakpoint - modify a user-space hardware breakpoint
360 * @bp: the breakpoint structure to modify 309 * @bp: the breakpoint structure to modify
361 * @addr: is the memory address that triggers the breakpoint 310 * @attr: new breakpoint attributes
362 * @len: the length of the access to the memory (1 byte, 2 bytes etc...)
363 * @type: the type of the access to the memory (read/write/exec)
364 * @triggered: callback to trigger when we hit the breakpoint 311 * @triggered: callback to trigger when we hit the breakpoint
365 * @tsk: pointer to 'task_struct' of the process to which the address belongs 312 * @tsk: pointer to 'task_struct' of the process to which the address belongs
366 * @active: should we activate it while registering it
367 */ 313 */
368struct perf_event * 314struct perf_event *
369modify_user_hw_breakpoint(struct perf_event *bp, 315modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr,
370 unsigned long addr,
371 int len,
372 int type,
373 perf_callback_t triggered, 316 perf_callback_t triggered,
374 struct task_struct *tsk, 317 struct task_struct *tsk)
375 bool active)
376{ 318{
377 /* 319 /*
378 * FIXME: do it without unregistering 320 * FIXME: do it without unregistering
@@ -381,8 +323,7 @@ modify_user_hw_breakpoint(struct perf_event *bp,
381 */ 323 */
382 unregister_hw_breakpoint(bp); 324 unregister_hw_breakpoint(bp);
383 325
384 return register_user_hw_breakpoint(addr, len, type, triggered, 326 return perf_event_create_kernel_counter(attr, -1, tsk->pid, triggered);
385 tsk, active);
386} 327}
387EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint); 328EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint);
388 329
@@ -406,8 +347,16 @@ register_kernel_hw_breakpoint_cpu(unsigned long addr,
406 int cpu, 347 int cpu,
407 bool active) 348 bool active)
408{ 349{
409 return register_user_hw_breakpoint_cpu(addr, len, type, triggered, 350 DEFINE_BREAKPOINT_ATTR(attr);
410 -1, cpu, active); 351
352 attr.bp_addr = addr;
353 attr.bp_len = len;
354 attr.bp_type = type;
355
356 if (!active)
357 attr.disabled = 1;
358
359 return perf_event_create_kernel_counter(&attr, cpu, -1, triggered);
411} 360}
412 361
413/** 362/**