1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33#include <linux/irqflags.h>
34#include <linux/kallsyms.h>
35#include <linux/notifier.h>
36#include <linux/kprobes.h>
37#include <linux/kdebug.h>
38#include <linux/kernel.h>
39#include <linux/module.h>
40#include <linux/percpu.h>
41#include <linux/sched.h>
42#include <linux/init.h>
43#include <linux/slab.h>
44#include <linux/list.h>
45#include <linux/cpu.h>
46#include <linux/smp.h>
47
48#include <linux/hw_breakpoint.h>
49
50
51
52struct bp_cpuinfo {
53
54 unsigned int cpu_pinned;
55
56 unsigned int *tsk_pinned;
57
58 unsigned int flexible;
59};
60
61static DEFINE_PER_CPU(struct bp_cpuinfo, bp_cpuinfo[TYPE_MAX]);
62static int nr_slots[TYPE_MAX];
63
64static struct bp_cpuinfo *get_bp_info(int cpu, enum bp_type_idx type)
65{
66 return per_cpu_ptr(bp_cpuinfo + type, cpu);
67}
68
69
70static LIST_HEAD(bp_task_head);
71
72static int constraints_initialized;
73
74
75struct bp_busy_slots {
76 unsigned int pinned;
77 unsigned int flexible;
78};
79
80
81static DEFINE_MUTEX(nr_bp_mutex);
82
83__weak int hw_breakpoint_weight(struct perf_event *bp)
84{
85 return 1;
86}
87
88static inline enum bp_type_idx find_slot_idx(struct perf_event *bp)
89{
90 if (bp->attr.bp_type & HW_BREAKPOINT_RW)
91 return TYPE_DATA;
92
93 return TYPE_INST;
94}
95
96
97
98
99
100static unsigned int max_task_bp_pinned(int cpu, enum bp_type_idx type)
101{
102 unsigned int *tsk_pinned = get_bp_info(cpu, type)->tsk_pinned;
103 int i;
104
105 for (i = nr_slots[type] - 1; i >= 0; i--) {
106 if (tsk_pinned[i] > 0)
107 return i + 1;
108 }
109
110 return 0;
111}
112
113
114
115
116
117static int task_bp_pinned(int cpu, struct perf_event *bp, enum bp_type_idx type)
118{
119 struct task_struct *tsk = bp->hw.target;
120 struct perf_event *iter;
121 int count = 0;
122
123 list_for_each_entry(iter, &bp_task_head, hw.bp_list) {
124 if (iter->hw.target == tsk &&
125 find_slot_idx(iter) == type &&
126 (iter->cpu < 0 || cpu == iter->cpu))
127 count += hw_breakpoint_weight(iter);
128 }
129
130 return count;
131}
132
133static const struct cpumask *cpumask_of_bp(struct perf_event *bp)
134{
135 if (bp->cpu >= 0)
136 return cpumask_of(bp->cpu);
137 return cpu_possible_mask;
138}
139
140
141
142
143
144static void
145fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp,
146 enum bp_type_idx type)
147{
148 const struct cpumask *cpumask = cpumask_of_bp(bp);
149 int cpu;
150
151 for_each_cpu(cpu, cpumask) {
152 struct bp_cpuinfo *info = get_bp_info(cpu, type);
153 int nr;
154
155 nr = info->cpu_pinned;
156 if (!bp->hw.target)
157 nr += max_task_bp_pinned(cpu, type);
158 else
159 nr += task_bp_pinned(cpu, bp, type);
160
161 if (nr > slots->pinned)
162 slots->pinned = nr;
163
164 nr = info->flexible;
165 if (nr > slots->flexible)
166 slots->flexible = nr;
167 }
168}
169
170
171
172
173
174
175static void
176fetch_this_slot(struct bp_busy_slots *slots, int weight)
177{
178 slots->pinned += weight;
179}
180
181
182
183
184static void toggle_bp_task_slot(struct perf_event *bp, int cpu,
185 enum bp_type_idx type, int weight)
186{
187 unsigned int *tsk_pinned = get_bp_info(cpu, type)->tsk_pinned;
188 int old_idx, new_idx;
189
190 old_idx = task_bp_pinned(cpu, bp, type) - 1;
191 new_idx = old_idx + weight;
192
193 if (old_idx >= 0)
194 tsk_pinned[old_idx]--;
195 if (new_idx >= 0)
196 tsk_pinned[new_idx]++;
197}
198
199
200
201
202static void
203toggle_bp_slot(struct perf_event *bp, bool enable, enum bp_type_idx type,
204 int weight)
205{
206 const struct cpumask *cpumask = cpumask_of_bp(bp);
207 int cpu;
208
209 if (!enable)
210 weight = -weight;
211
212
213 if (!bp->hw.target) {
214 get_bp_info(bp->cpu, type)->cpu_pinned += weight;
215 return;
216 }
217
218
219 for_each_cpu(cpu, cpumask)
220 toggle_bp_task_slot(bp, cpu, type, weight);
221
222 if (enable)
223 list_add_tail(&bp->hw.bp_list, &bp_task_head);
224 else
225 list_del(&bp->hw.bp_list);
226}
227
228
229
230
231__weak void arch_unregister_hw_breakpoint(struct perf_event *bp)
232{
233
234
235
236
237}
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280static int __reserve_bp_slot(struct perf_event *bp)
281{
282 struct bp_busy_slots slots = {0};
283 enum bp_type_idx type;
284 int weight;
285
286
287 if (!constraints_initialized)
288 return -ENOMEM;
289
290
291 if (bp->attr.bp_type == HW_BREAKPOINT_EMPTY ||
292 bp->attr.bp_type == HW_BREAKPOINT_INVALID)
293 return -EINVAL;
294
295 type = find_slot_idx(bp);
296 weight = hw_breakpoint_weight(bp);
297
298 fetch_bp_busy_slots(&slots, bp, type);
299
300
301
302
303 fetch_this_slot(&slots, weight);
304
305
306 if (slots.pinned + (!!slots.flexible) > nr_slots[type])
307 return -ENOSPC;
308
309 toggle_bp_slot(bp, true, type, weight);
310
311 return 0;
312}
313
314int reserve_bp_slot(struct perf_event *bp)
315{
316 int ret;
317
318 mutex_lock(&nr_bp_mutex);
319
320 ret = __reserve_bp_slot(bp);
321
322 mutex_unlock(&nr_bp_mutex);
323
324 return ret;
325}
326
327static void __release_bp_slot(struct perf_event *bp)
328{
329 enum bp_type_idx type;
330 int weight;
331
332 type = find_slot_idx(bp);
333 weight = hw_breakpoint_weight(bp);
334 toggle_bp_slot(bp, false, type, weight);
335}
336
337void release_bp_slot(struct perf_event *bp)
338{
339 mutex_lock(&nr_bp_mutex);
340
341 arch_unregister_hw_breakpoint(bp);
342 __release_bp_slot(bp);
343
344 mutex_unlock(&nr_bp_mutex);
345}
346
347
348
349
350
351
352int dbg_reserve_bp_slot(struct perf_event *bp)
353{
354 if (mutex_is_locked(&nr_bp_mutex))
355 return -1;
356
357 return __reserve_bp_slot(bp);
358}
359
360int dbg_release_bp_slot(struct perf_event *bp)
361{
362 if (mutex_is_locked(&nr_bp_mutex))
363 return -1;
364
365 __release_bp_slot(bp);
366
367 return 0;
368}
369
370static int validate_hw_breakpoint(struct perf_event *bp)
371{
372 int ret;
373
374 ret = arch_validate_hwbkpt_settings(bp);
375 if (ret)
376 return ret;
377
378 if (arch_check_bp_in_kernelspace(bp)) {
379 if (bp->attr.exclude_kernel)
380 return -EINVAL;
381
382
383
384
385 if (!capable(CAP_SYS_ADMIN))
386 return -EPERM;
387 }
388
389 return 0;
390}
391
392int register_perf_hw_breakpoint(struct perf_event *bp)
393{
394 int ret;
395
396 ret = reserve_bp_slot(bp);
397 if (ret)
398 return ret;
399
400 ret = validate_hw_breakpoint(bp);
401
402
403 if (ret)
404 release_bp_slot(bp);
405
406 return ret;
407}
408
409
410
411
412
413
414
415struct perf_event *
416register_user_hw_breakpoint(struct perf_event_attr *attr,
417 perf_overflow_handler_t triggered,
418 void *context,
419 struct task_struct *tsk)
420{
421 return perf_event_create_kernel_counter(attr, -1, tsk, triggered,
422 context);
423}
424EXPORT_SYMBOL_GPL(register_user_hw_breakpoint);
425
426
427
428
429
430
431
432
433int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr)
434{
435 u64 old_addr = bp->attr.bp_addr;
436 u64 old_len = bp->attr.bp_len;
437 int old_type = bp->attr.bp_type;
438 int err = 0;
439
440
441
442
443
444
445
446 if (irqs_disabled() && bp->ctx && bp->ctx->task == current)
447 perf_event_disable_local(bp);
448 else
449 perf_event_disable(bp);
450
451 bp->attr.bp_addr = attr->bp_addr;
452 bp->attr.bp_type = attr->bp_type;
453 bp->attr.bp_len = attr->bp_len;
454
455 if (attr->disabled)
456 goto end;
457
458 err = validate_hw_breakpoint(bp);
459 if (!err)
460 perf_event_enable(bp);
461
462 if (err) {
463 bp->attr.bp_addr = old_addr;
464 bp->attr.bp_type = old_type;
465 bp->attr.bp_len = old_len;
466 if (!bp->attr.disabled)
467 perf_event_enable(bp);
468
469 return err;
470 }
471
472end:
473 bp->attr.disabled = attr->disabled;
474
475 return 0;
476}
477EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint);
478
479
480
481
482
483void unregister_hw_breakpoint(struct perf_event *bp)
484{
485 if (!bp)
486 return;
487 perf_event_release_kernel(bp);
488}
489EXPORT_SYMBOL_GPL(unregister_hw_breakpoint);
490
491
492
493
494
495
496
497
498struct perf_event * __percpu *
499register_wide_hw_breakpoint(struct perf_event_attr *attr,
500 perf_overflow_handler_t triggered,
501 void *context)
502{
503 struct perf_event * __percpu *cpu_events, *bp;
504 long err = 0;
505 int cpu;
506
507 cpu_events = alloc_percpu(typeof(*cpu_events));
508 if (!cpu_events)
509 return (void __percpu __force *)ERR_PTR(-ENOMEM);
510
511 get_online_cpus();
512 for_each_online_cpu(cpu) {
513 bp = perf_event_create_kernel_counter(attr, cpu, NULL,
514 triggered, context);
515 if (IS_ERR(bp)) {
516 err = PTR_ERR(bp);
517 break;
518 }
519
520 per_cpu(*cpu_events, cpu) = bp;
521 }
522 put_online_cpus();
523
524 if (likely(!err))
525 return cpu_events;
526
527 unregister_wide_hw_breakpoint(cpu_events);
528 return (void __percpu __force *)ERR_PTR(err);
529}
530EXPORT_SYMBOL_GPL(register_wide_hw_breakpoint);
531
532
533
534
535
536void unregister_wide_hw_breakpoint(struct perf_event * __percpu *cpu_events)
537{
538 int cpu;
539
540 for_each_possible_cpu(cpu)
541 unregister_hw_breakpoint(per_cpu(*cpu_events, cpu));
542
543 free_percpu(cpu_events);
544}
545EXPORT_SYMBOL_GPL(unregister_wide_hw_breakpoint);
546
547static struct notifier_block hw_breakpoint_exceptions_nb = {
548 .notifier_call = hw_breakpoint_exceptions_notify,
549
550 .priority = 0x7fffffff
551};
552
553static void bp_perf_event_destroy(struct perf_event *event)
554{
555 release_bp_slot(event);
556}
557
558static int hw_breakpoint_event_init(struct perf_event *bp)
559{
560 int err;
561
562 if (bp->attr.type != PERF_TYPE_BREAKPOINT)
563 return -ENOENT;
564
565
566
567
568 if (has_branch_stack(bp))
569 return -EOPNOTSUPP;
570
571 err = register_perf_hw_breakpoint(bp);
572 if (err)
573 return err;
574
575 bp->destroy = bp_perf_event_destroy;
576
577 return 0;
578}
579
580static int hw_breakpoint_add(struct perf_event *bp, int flags)
581{
582 if (!(flags & PERF_EF_START))
583 bp->hw.state = PERF_HES_STOPPED;
584
585 if (is_sampling_event(bp)) {
586 bp->hw.last_period = bp->hw.sample_period;
587 perf_swevent_set_period(bp);
588 }
589
590 return arch_install_hw_breakpoint(bp);
591}
592
593static void hw_breakpoint_del(struct perf_event *bp, int flags)
594{
595 arch_uninstall_hw_breakpoint(bp);
596}
597
598static void hw_breakpoint_start(struct perf_event *bp, int flags)
599{
600 bp->hw.state = 0;
601}
602
603static void hw_breakpoint_stop(struct perf_event *bp, int flags)
604{
605 bp->hw.state = PERF_HES_STOPPED;
606}
607
608static struct pmu perf_breakpoint = {
609 .task_ctx_nr = perf_sw_context,
610
611 .event_init = hw_breakpoint_event_init,
612 .add = hw_breakpoint_add,
613 .del = hw_breakpoint_del,
614 .start = hw_breakpoint_start,
615 .stop = hw_breakpoint_stop,
616 .read = hw_breakpoint_pmu_read,
617};
618
619int __init init_hw_breakpoint(void)
620{
621 int cpu, err_cpu;
622 int i;
623
624 for (i = 0; i < TYPE_MAX; i++)
625 nr_slots[i] = hw_breakpoint_slots(i);
626
627 for_each_possible_cpu(cpu) {
628 for (i = 0; i < TYPE_MAX; i++) {
629 struct bp_cpuinfo *info = get_bp_info(cpu, i);
630
631 info->tsk_pinned = kcalloc(nr_slots[i], sizeof(int),
632 GFP_KERNEL);
633 if (!info->tsk_pinned)
634 goto err_alloc;
635 }
636 }
637
638 constraints_initialized = 1;
639
640 perf_pmu_register(&perf_breakpoint, "breakpoint", PERF_TYPE_BREAKPOINT);
641
642 return register_die_notifier(&hw_breakpoint_exceptions_nb);
643
644 err_alloc:
645 for_each_possible_cpu(err_cpu) {
646 for (i = 0; i < TYPE_MAX; i++)
647 kfree(get_bp_info(err_cpu, i)->tsk_pinned);
648 if (err_cpu == cpu)
649 break;
650 }
651
652 return -ENOMEM;
653}
654
655
656