aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/hw_breakpoint.h35
-rw-r--r--kernel/hw_breakpoint.c35
-rw-r--r--kernel/trace/trace_ksym.c42
-rw-r--r--samples/hw_breakpoint/data_breakpoint.c10
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
31static 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
31static inline unsigned long hw_breakpoint_addr(struct perf_event *bp) 38static 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 */
61extern struct perf_event * 68extern struct perf_event *
62register_wide_hw_breakpoint_cpu(unsigned long addr, 69register_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
69extern struct perf_event ** 73extern struct perf_event **
70register_wide_hw_breakpoint(unsigned long addr, 74register_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
76extern int register_perf_hw_breakpoint(struct perf_event *bp); 77extern int register_perf_hw_breakpoint(struct perf_event *bp);
77extern int __register_perf_hw_breakpoint(struct perf_event *bp); 78extern 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; }
102static inline struct perf_event * 103static inline struct perf_event *
103register_wide_hw_breakpoint_cpu(unsigned long addr, 104register_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; }
109static inline struct perf_event ** 107static inline struct perf_event **
110register_wide_hw_breakpoint(unsigned long addr, 108register_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; }
115static inline int 110static inline int
116register_perf_hw_breakpoint(struct perf_event *bp) { return -ENOSYS; } 111register_perf_hw_breakpoint(struct perf_event *bp) { return -ENOSYS; }
117static inline int 112static 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}
340EXPORT_SYMBOL_GPL(unregister_hw_breakpoint); 340EXPORT_SYMBOL_GPL(unregister_hw_breakpoint);
341 341
342static struct perf_event *
343register_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 */
372struct perf_event ** 349struct perf_event **
373register_wide_hw_breakpoint(unsigned long addr, 350register_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
43struct trace_ksym { 43struct 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)
51static int __init hw_break_module_init(void) 51static 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;