diff options
-rw-r--r-- | include/linux/hw_breakpoint.h | 35 | ||||
-rw-r--r-- | kernel/hw_breakpoint.c | 35 | ||||
-rw-r--r-- | kernel/trace/trace_ksym.c | 42 | ||||
-rw-r--r-- | samples/hw_breakpoint/data_breakpoint.c | 10 |
4 files changed, 44 insertions, 78 deletions
diff --git a/include/linux/hw_breakpoint.h b/include/linux/hw_breakpoint.h index 5da472e434b7..a03daed08c59 100644 --- a/include/linux/hw_breakpoint.h +++ b/include/linux/hw_breakpoint.h | |||
@@ -28,6 +28,13 @@ struct perf_event_attr name = { \ | |||
28 | .pinned = 1, \ | 28 | .pinned = 1, \ |
29 | }; | 29 | }; |
30 | 30 | ||
31 | static inline void hw_breakpoint_init(struct perf_event_attr *attr) | ||
32 | { | ||
33 | attr->type = PERF_TYPE_BREAKPOINT; | ||
34 | attr->size = sizeof(*attr); | ||
35 | attr->pinned = 1; | ||
36 | } | ||
37 | |||
31 | static inline unsigned long hw_breakpoint_addr(struct perf_event *bp) | 38 | static inline unsigned long hw_breakpoint_addr(struct perf_event *bp) |
32 | { | 39 | { |
33 | return bp->attr.bp_addr; | 40 | return bp->attr.bp_addr; |
@@ -59,19 +66,13 @@ modify_user_hw_breakpoint(struct perf_event *bp, | |||
59 | * Kernel breakpoints are not associated with any particular thread. | 66 | * Kernel breakpoints are not associated with any particular thread. |
60 | */ | 67 | */ |
61 | extern struct perf_event * | 68 | extern struct perf_event * |
62 | register_wide_hw_breakpoint_cpu(unsigned long addr, | 69 | register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr, |
63 | int len, | ||
64 | int type, | ||
65 | perf_callback_t triggered, | 70 | perf_callback_t triggered, |
66 | int cpu, | 71 | int cpu); |
67 | bool active); | ||
68 | 72 | ||
69 | extern struct perf_event ** | 73 | extern struct perf_event ** |
70 | register_wide_hw_breakpoint(unsigned long addr, | 74 | register_wide_hw_breakpoint(struct perf_event_attr *attr, |
71 | int len, | 75 | perf_callback_t triggered); |
72 | int type, | ||
73 | perf_callback_t triggered, | ||
74 | bool active); | ||
75 | 76 | ||
76 | extern int register_perf_hw_breakpoint(struct perf_event *bp); | 77 | extern int register_perf_hw_breakpoint(struct perf_event *bp); |
77 | extern int __register_perf_hw_breakpoint(struct perf_event *bp); | 78 | extern int __register_perf_hw_breakpoint(struct perf_event *bp); |
@@ -100,18 +101,12 @@ modify_user_hw_breakpoint(struct perf_event *bp, | |||
100 | perf_callback_t triggered, | 101 | perf_callback_t triggered, |
101 | struct task_struct *tsk) { return NULL; } | 102 | struct task_struct *tsk) { return NULL; } |
102 | static inline struct perf_event * | 103 | static inline struct perf_event * |
103 | register_wide_hw_breakpoint_cpu(unsigned long addr, | 104 | register_wide_hw_breakpoint_cpu(struct perf_event_attr *attr, |
104 | int len, | ||
105 | int type, | ||
106 | perf_callback_t triggered, | 105 | perf_callback_t triggered, |
107 | int cpu, | 106 | int cpu) { return NULL; } |
108 | bool active) { return NULL; } | ||
109 | static inline struct perf_event ** | 107 | static inline struct perf_event ** |
110 | register_wide_hw_breakpoint(unsigned long addr, | 108 | register_wide_hw_breakpoint(struct perf_event_attr *attr, |
111 | int len, | 109 | perf_callback_t triggered) { return NULL; } |
112 | int type, | ||
113 | perf_callback_t triggered, | ||
114 | bool active) { return NULL; } | ||
115 | static inline int | 110 | static inline int |
116 | register_perf_hw_breakpoint(struct perf_event *bp) { return -ENOSYS; } | 111 | register_perf_hw_breakpoint(struct perf_event *bp) { return -ENOSYS; } |
117 | static inline int | 112 | static inline int |
diff --git a/kernel/hw_breakpoint.c b/kernel/hw_breakpoint.c index 2a47514f12fd..cf5ee1628411 100644 --- a/kernel/hw_breakpoint.c +++ b/kernel/hw_breakpoint.c | |||
@@ -339,42 +339,16 @@ void unregister_hw_breakpoint(struct perf_event *bp) | |||
339 | } | 339 | } |
340 | EXPORT_SYMBOL_GPL(unregister_hw_breakpoint); | 340 | EXPORT_SYMBOL_GPL(unregister_hw_breakpoint); |
341 | 341 | ||
342 | static struct perf_event * | ||
343 | register_kernel_hw_breakpoint_cpu(unsigned long addr, | ||
344 | int len, | ||
345 | int type, | ||
346 | perf_callback_t triggered, | ||
347 | int cpu, | ||
348 | bool active) | ||
349 | { | ||
350 | DEFINE_BREAKPOINT_ATTR(attr); | ||
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); | ||
360 | } | ||
361 | |||
362 | /** | 342 | /** |
363 | * register_wide_hw_breakpoint - register a wide breakpoint in the kernel | 343 | * register_wide_hw_breakpoint - register a wide breakpoint in the kernel |
364 | * @addr: is the memory address that triggers the breakpoint | 344 | * @attr: breakpoint attributes |
365 | * @len: the length of the access to the memory (1 byte, 2 bytes etc...) | ||
366 | * @type: the type of the access to the memory (read/write/exec) | ||
367 | * @triggered: callback to trigger when we hit the breakpoint | 345 | * @triggered: callback to trigger when we hit the breakpoint |
368 | * @active: should we activate it while registering it | ||
369 | * | 346 | * |
370 | * @return a set of per_cpu pointers to perf events | 347 | * @return a set of per_cpu pointers to perf events |
371 | */ | 348 | */ |
372 | struct perf_event ** | 349 | struct perf_event ** |
373 | register_wide_hw_breakpoint(unsigned long addr, | 350 | register_wide_hw_breakpoint(struct perf_event_attr *attr, |
374 | int len, | 351 | perf_callback_t triggered) |
375 | int type, | ||
376 | perf_callback_t triggered, | ||
377 | bool active) | ||
378 | { | 352 | { |
379 | struct perf_event **cpu_events, **pevent, *bp; | 353 | struct perf_event **cpu_events, **pevent, *bp; |
380 | long err; | 354 | long err; |
@@ -386,8 +360,7 @@ register_wide_hw_breakpoint(unsigned long addr, | |||
386 | 360 | ||
387 | for_each_possible_cpu(cpu) { | 361 | for_each_possible_cpu(cpu) { |
388 | pevent = per_cpu_ptr(cpu_events, cpu); | 362 | pevent = per_cpu_ptr(cpu_events, cpu); |
389 | bp = register_kernel_hw_breakpoint_cpu(addr, len, type, | 363 | bp = perf_event_create_kernel_counter(attr, cpu, -1, triggered); |
390 | triggered, cpu, active); | ||
391 | 364 | ||
392 | *pevent = bp; | 365 | *pevent = bp; |
393 | 366 | ||
diff --git a/kernel/trace/trace_ksym.c b/kernel/trace/trace_ksym.c index c538b15b95d6..ddfa0fd43bc0 100644 --- a/kernel/trace/trace_ksym.c +++ b/kernel/trace/trace_ksym.c | |||
@@ -42,9 +42,7 @@ | |||
42 | 42 | ||
43 | struct trace_ksym { | 43 | struct trace_ksym { |
44 | struct perf_event **ksym_hbp; | 44 | struct perf_event **ksym_hbp; |
45 | unsigned long ksym_addr; | 45 | struct perf_event_attr attr; |
46 | int type; | ||
47 | int len; | ||
48 | #ifdef CONFIG_PROFILE_KSYM_TRACER | 46 | #ifdef CONFIG_PROFILE_KSYM_TRACER |
49 | unsigned long counter; | 47 | unsigned long counter; |
50 | #endif | 48 | #endif |
@@ -71,7 +69,7 @@ void ksym_collect_stats(unsigned long hbp_hit_addr) | |||
71 | 69 | ||
72 | rcu_read_lock(); | 70 | rcu_read_lock(); |
73 | hlist_for_each_entry_rcu(entry, node, &ksym_filter_head, ksym_hlist) { | 71 | hlist_for_each_entry_rcu(entry, node, &ksym_filter_head, ksym_hlist) { |
74 | if ((entry->ksym_addr == hbp_hit_addr) && | 72 | if ((entry->attr.bp_addr == hbp_hit_addr) && |
75 | (entry->counter <= MAX_UL_INT)) { | 73 | (entry->counter <= MAX_UL_INT)) { |
76 | entry->counter++; | 74 | entry->counter++; |
77 | break; | 75 | break; |
@@ -192,14 +190,15 @@ int process_new_ksym_entry(char *ksymname, int op, unsigned long addr) | |||
192 | if (!entry) | 190 | if (!entry) |
193 | return -ENOMEM; | 191 | return -ENOMEM; |
194 | 192 | ||
195 | entry->type = op; | 193 | hw_breakpoint_init(&entry->attr); |
196 | entry->ksym_addr = addr; | 194 | |
197 | entry->len = HW_BREAKPOINT_LEN_4; | 195 | entry->attr.bp_type = op; |
196 | entry->attr.bp_addr = addr; | ||
197 | entry->attr.bp_len = HW_BREAKPOINT_LEN_4; | ||
198 | 198 | ||
199 | ret = -EAGAIN; | 199 | ret = -EAGAIN; |
200 | entry->ksym_hbp = register_wide_hw_breakpoint(entry->ksym_addr, | 200 | entry->ksym_hbp = register_wide_hw_breakpoint(&entry->attr, |
201 | entry->len, entry->type, | 201 | ksym_hbp_handler); |
202 | ksym_hbp_handler, true); | ||
203 | 202 | ||
204 | if (IS_ERR(entry->ksym_hbp)) { | 203 | if (IS_ERR(entry->ksym_hbp)) { |
205 | ret = PTR_ERR(entry->ksym_hbp); | 204 | ret = PTR_ERR(entry->ksym_hbp); |
@@ -236,12 +235,12 @@ static ssize_t ksym_trace_filter_read(struct file *filp, char __user *ubuf, | |||
236 | mutex_lock(&ksym_tracer_mutex); | 235 | mutex_lock(&ksym_tracer_mutex); |
237 | 236 | ||
238 | hlist_for_each_entry(entry, node, &ksym_filter_head, ksym_hlist) { | 237 | hlist_for_each_entry(entry, node, &ksym_filter_head, ksym_hlist) { |
239 | ret = trace_seq_printf(s, "%pS:", (void *)entry->ksym_addr); | 238 | ret = trace_seq_printf(s, "%pS:", (void *)entry->attr.bp_addr); |
240 | if (entry->type == HW_BREAKPOINT_R) | 239 | if (entry->attr.bp_type == HW_BREAKPOINT_R) |
241 | ret = trace_seq_puts(s, "r--\n"); | 240 | ret = trace_seq_puts(s, "r--\n"); |
242 | else if (entry->type == HW_BREAKPOINT_W) | 241 | else if (entry->attr.bp_type == HW_BREAKPOINT_W) |
243 | ret = trace_seq_puts(s, "-w-\n"); | 242 | ret = trace_seq_puts(s, "-w-\n"); |
244 | else if (entry->type == (HW_BREAKPOINT_W | HW_BREAKPOINT_R)) | 243 | else if (entry->attr.bp_type == (HW_BREAKPOINT_W | HW_BREAKPOINT_R)) |
245 | ret = trace_seq_puts(s, "rw-\n"); | 244 | ret = trace_seq_puts(s, "rw-\n"); |
246 | WARN_ON_ONCE(!ret); | 245 | WARN_ON_ONCE(!ret); |
247 | } | 246 | } |
@@ -317,9 +316,9 @@ static ssize_t ksym_trace_filter_write(struct file *file, | |||
317 | 316 | ||
318 | ret = -EINVAL; | 317 | ret = -EINVAL; |
319 | hlist_for_each_entry(entry, node, &ksym_filter_head, ksym_hlist) { | 318 | hlist_for_each_entry(entry, node, &ksym_filter_head, ksym_hlist) { |
320 | if (entry->ksym_addr == ksym_addr) { | 319 | if (entry->attr.bp_addr == ksym_addr) { |
321 | /* Check for malformed request: (6) */ | 320 | /* Check for malformed request: (6) */ |
322 | if (entry->type != op) | 321 | if (entry->attr.bp_type != op) |
323 | changed = 1; | 322 | changed = 1; |
324 | else | 323 | else |
325 | goto out; | 324 | goto out; |
@@ -328,13 +327,12 @@ static ssize_t ksym_trace_filter_write(struct file *file, | |||
328 | } | 327 | } |
329 | if (changed) { | 328 | if (changed) { |
330 | unregister_wide_hw_breakpoint(entry->ksym_hbp); | 329 | unregister_wide_hw_breakpoint(entry->ksym_hbp); |
331 | entry->type = op; | 330 | entry->attr.bp_type = op; |
332 | ret = 0; | 331 | ret = 0; |
333 | if (op > 0) { | 332 | if (op > 0) { |
334 | entry->ksym_hbp = | 333 | entry->ksym_hbp = |
335 | register_wide_hw_breakpoint(entry->ksym_addr, | 334 | register_wide_hw_breakpoint(&entry->attr, |
336 | entry->len, entry->type, | 335 | ksym_hbp_handler); |
337 | ksym_hbp_handler, true); | ||
338 | if (IS_ERR(entry->ksym_hbp)) | 336 | if (IS_ERR(entry->ksym_hbp)) |
339 | ret = PTR_ERR(entry->ksym_hbp); | 337 | ret = PTR_ERR(entry->ksym_hbp); |
340 | else | 338 | else |
@@ -489,7 +487,7 @@ static int ksym_tracer_stat_show(struct seq_file *m, void *v) | |||
489 | 487 | ||
490 | entry = hlist_entry(stat, struct trace_ksym, ksym_hlist); | 488 | entry = hlist_entry(stat, struct trace_ksym, ksym_hlist); |
491 | 489 | ||
492 | access_type = entry->type; | 490 | access_type = entry->attr.bp_type; |
493 | 491 | ||
494 | switch (access_type) { | 492 | switch (access_type) { |
495 | case HW_BREAKPOINT_R: | 493 | case HW_BREAKPOINT_R: |
@@ -505,7 +503,7 @@ static int ksym_tracer_stat_show(struct seq_file *m, void *v) | |||
505 | seq_puts(m, " NA "); | 503 | seq_puts(m, " NA "); |
506 | } | 504 | } |
507 | 505 | ||
508 | if (lookup_symbol_name(entry->ksym_addr, fn_name) >= 0) | 506 | if (lookup_symbol_name(entry->attr.bp_addr, fn_name) >= 0) |
509 | seq_printf(m, " %-36s", fn_name); | 507 | seq_printf(m, " %-36s", fn_name); |
510 | else | 508 | else |
511 | seq_printf(m, " %-36s", "<NA>"); | 509 | seq_printf(m, " %-36s", "<NA>"); |
diff --git a/samples/hw_breakpoint/data_breakpoint.c b/samples/hw_breakpoint/data_breakpoint.c index ee7f9fbaffbd..29525500df00 100644 --- a/samples/hw_breakpoint/data_breakpoint.c +++ b/samples/hw_breakpoint/data_breakpoint.c | |||
@@ -51,13 +51,13 @@ static void sample_hbp_handler(struct perf_event *temp, void *data) | |||
51 | static int __init hw_break_module_init(void) | 51 | static int __init hw_break_module_init(void) |
52 | { | 52 | { |
53 | int ret; | 53 | int ret; |
54 | unsigned long addr; | 54 | DEFINE_BREAKPOINT_ATTR(attr); |
55 | 55 | ||
56 | addr = kallsyms_lookup_name(ksym_name); | 56 | attr.bp_addr = kallsyms_lookup_name(ksym_name); |
57 | attr.bp_len = HW_BREAKPOINT_LEN_4; | ||
58 | attr.bp_type = HW_BREAKPOINT_W | HW_BREAKPOINT_R; | ||
57 | 59 | ||
58 | sample_hbp = register_wide_hw_breakpoint(addr, HW_BREAKPOINT_LEN_4, | 60 | sample_hbp = register_wide_hw_breakpoint(&attr, sample_hbp_handler); |
59 | HW_BREAKPOINT_W | HW_BREAKPOINT_R, | ||
60 | sample_hbp_handler, true); | ||
61 | if (IS_ERR(sample_hbp)) { | 61 | if (IS_ERR(sample_hbp)) { |
62 | ret = PTR_ERR(sample_hbp); | 62 | ret = PTR_ERR(sample_hbp); |
63 | goto fail; | 63 | goto fail; |