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/cpu.h>
45#include <linux/smp.h>
46
47#include <linux/hw_breakpoint.h>
48
49
50
51
52
53
54
55static DEFINE_PER_CPU(unsigned int, nr_cpu_bp_pinned[TYPE_MAX]);
56
57
58static DEFINE_PER_CPU(unsigned int *, nr_task_bp_pinned[TYPE_MAX]);
59
60
61static DEFINE_PER_CPU(unsigned int, nr_bp_flexible[TYPE_MAX]);
62
63static int nr_slots[TYPE_MAX];
64
65static int constraints_initialized;
66
67
68struct bp_busy_slots {
69 unsigned int pinned;
70 unsigned int flexible;
71};
72
73
74static DEFINE_MUTEX(nr_bp_mutex);
75
76__weak int hw_breakpoint_weight(struct perf_event *bp)
77{
78 return 1;
79}
80
81static inline enum bp_type_idx find_slot_idx(struct perf_event *bp)
82{
83 if (bp->attr.bp_type & HW_BREAKPOINT_RW)
84 return TYPE_DATA;
85
86 return TYPE_INST;
87}
88
89
90
91
92
93static unsigned int max_task_bp_pinned(int cpu, enum bp_type_idx type)
94{
95 int i;
96 unsigned int *tsk_pinned = per_cpu(nr_task_bp_pinned[type], cpu);
97
98 for (i = nr_slots[type] - 1; i >= 0; i--) {
99 if (tsk_pinned[i] > 0)
100 return i + 1;
101 }
102
103 return 0;
104}
105
106static int task_bp_pinned(struct task_struct *tsk, enum bp_type_idx type)
107{
108 struct perf_event_context *ctx = tsk->perf_event_ctxp;
109 struct list_head *list;
110 struct perf_event *bp;
111 unsigned long flags;
112 int count = 0;
113
114 if (WARN_ONCE(!ctx, "No perf context for this task"))
115 return 0;
116
117 list = &ctx->event_list;
118
119 raw_spin_lock_irqsave(&ctx->lock, flags);
120
121
122
123
124
125 list_for_each_entry(bp, list, event_entry) {
126 if (bp->attr.type == PERF_TYPE_BREAKPOINT)
127 if (find_slot_idx(bp) == type)
128 count += hw_breakpoint_weight(bp);
129 }
130
131 raw_spin_unlock_irqrestore(&ctx->lock, flags);
132
133 return count;
134}
135
136
137
138
139
140static void
141fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp,
142 enum bp_type_idx type)
143{
144 int cpu = bp->cpu;
145 struct task_struct *tsk = bp->ctx->task;
146
147 if (cpu >= 0) {
148 slots->pinned = per_cpu(nr_cpu_bp_pinned[type], cpu);
149 if (!tsk)
150 slots->pinned += max_task_bp_pinned(cpu, type);
151 else
152 slots->pinned += task_bp_pinned(tsk, type);
153 slots->flexible = per_cpu(nr_bp_flexible[type], cpu);
154
155 return;
156 }
157
158 for_each_online_cpu(cpu) {
159 unsigned int nr;
160
161 nr = per_cpu(nr_cpu_bp_pinned[type], cpu);
162 if (!tsk)
163 nr += max_task_bp_pinned(cpu, type);
164 else
165 nr += task_bp_pinned(tsk, type);
166
167 if (nr > slots->pinned)
168 slots->pinned = nr;
169
170 nr = per_cpu(nr_bp_flexible[type], cpu);
171
172 if (nr > slots->flexible)
173 slots->flexible = nr;
174 }
175}
176
177
178
179
180
181
182static void
183fetch_this_slot(struct bp_busy_slots *slots, int weight)
184{
185 slots->pinned += weight;
186}
187
188
189
190
191static void toggle_bp_task_slot(struct task_struct *tsk, int cpu, bool enable,
192 enum bp_type_idx type, int weight)
193{
194 unsigned int *tsk_pinned;
195 int old_count = 0;
196 int old_idx = 0;
197 int idx = 0;
198
199 old_count = task_bp_pinned(tsk, type);
200 old_idx = old_count - 1;
201 idx = old_idx + weight;
202
203 tsk_pinned = per_cpu(nr_task_bp_pinned[type], cpu);
204 if (enable) {
205 tsk_pinned[idx]++;
206 if (old_count > 0)
207 tsk_pinned[old_idx]--;
208 } else {
209 tsk_pinned[idx]--;
210 if (old_count > 0)
211 tsk_pinned[old_idx]++;
212 }
213}
214
215
216
217
218static void
219toggle_bp_slot(struct perf_event *bp, bool enable, enum bp_type_idx type,
220 int weight)
221{
222 int cpu = bp->cpu;
223 struct task_struct *tsk = bp->ctx->task;
224
225
226 if (tsk) {
227 if (cpu >= 0) {
228 toggle_bp_task_slot(tsk, cpu, enable, type, weight);
229 return;
230 }
231
232 for_each_online_cpu(cpu)
233 toggle_bp_task_slot(tsk, cpu, enable, type, weight);
234 return;
235 }
236
237
238 if (enable)
239 per_cpu(nr_cpu_bp_pinned[type], bp->cpu) += weight;
240 else
241 per_cpu(nr_cpu_bp_pinned[type], bp->cpu) -= weight;
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
280
281
282
283
284
285static int __reserve_bp_slot(struct perf_event *bp)
286{
287 struct bp_busy_slots slots = {0};
288 enum bp_type_idx type;
289 int weight;
290
291
292 if (!constraints_initialized)
293 return -ENOMEM;
294
295
296 if (bp->attr.bp_type == HW_BREAKPOINT_EMPTY ||
297 bp->attr.bp_type == HW_BREAKPOINT_INVALID)
298 return -EINVAL;
299
300 type = find_slot_idx(bp);
301 weight = hw_breakpoint_weight(bp);
302
303 fetch_bp_busy_slots(&slots, bp, type);
304 fetch_this_slot(&slots, weight);
305
306
307 if (slots.pinned + (!!slots.flexible) > nr_slots[type])
308 return -ENOSPC;
309
310 toggle_bp_slot(bp, true, type, weight);
311
312 return 0;
313}
314
315int reserve_bp_slot(struct perf_event *bp)
316{
317 int ret;
318
319 mutex_lock(&nr_bp_mutex);
320
321 ret = __reserve_bp_slot(bp);
322
323 mutex_unlock(&nr_bp_mutex);
324
325 return ret;
326}
327
328static void __release_bp_slot(struct perf_event *bp)
329{
330 enum bp_type_idx type;
331 int weight;
332
333 type = find_slot_idx(bp);
334 weight = hw_breakpoint_weight(bp);
335 toggle_bp_slot(bp, false, type, weight);
336}
337
338void release_bp_slot(struct perf_event *bp)
339{
340 mutex_lock(&nr_bp_mutex);
341
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 struct task_struct *tsk)
419{
420 return perf_event_create_kernel_counter(attr, -1, tsk->pid, triggered);
421}
422EXPORT_SYMBOL_GPL(register_user_hw_breakpoint);
423
424
425
426
427
428
429
430
431int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr)
432{
433 u64 old_addr = bp->attr.bp_addr;
434 u64 old_len = bp->attr.bp_len;
435 int old_type = bp->attr.bp_type;
436 int err = 0;
437
438 perf_event_disable(bp);
439
440 bp->attr.bp_addr = attr->bp_addr;
441 bp->attr.bp_type = attr->bp_type;
442 bp->attr.bp_len = attr->bp_len;
443
444 if (attr->disabled)
445 goto end;
446
447 err = validate_hw_breakpoint(bp);
448 if (!err)
449 perf_event_enable(bp);
450
451 if (err) {
452 bp->attr.bp_addr = old_addr;
453 bp->attr.bp_type = old_type;
454 bp->attr.bp_len = old_len;
455 if (!bp->attr.disabled)
456 perf_event_enable(bp);
457
458 return err;
459 }
460
461end:
462 bp->attr.disabled = attr->disabled;
463
464 return 0;
465}
466EXPORT_SYMBOL_GPL(modify_user_hw_breakpoint);
467
468
469
470
471
472void unregister_hw_breakpoint(struct perf_event *bp)
473{
474 if (!bp)
475 return;
476 perf_event_release_kernel(bp);
477}
478EXPORT_SYMBOL_GPL(unregister_hw_breakpoint);
479
480
481
482
483
484
485
486
487struct perf_event * __percpu *
488register_wide_hw_breakpoint(struct perf_event_attr *attr,
489 perf_overflow_handler_t triggered)
490{
491 struct perf_event * __percpu *cpu_events, **pevent, *bp;
492 long err;
493 int cpu;
494
495 cpu_events = alloc_percpu(typeof(*cpu_events));
496 if (!cpu_events)
497 return (void __percpu __force *)ERR_PTR(-ENOMEM);
498
499 get_online_cpus();
500 for_each_online_cpu(cpu) {
501 pevent = per_cpu_ptr(cpu_events, cpu);
502 bp = perf_event_create_kernel_counter(attr, cpu, -1, triggered);
503
504 *pevent = bp;
505
506 if (IS_ERR(bp)) {
507 err = PTR_ERR(bp);
508 goto fail;
509 }
510 }
511 put_online_cpus();
512
513 return cpu_events;
514
515fail:
516 for_each_online_cpu(cpu) {
517 pevent = per_cpu_ptr(cpu_events, cpu);
518 if (IS_ERR(*pevent))
519 break;
520 unregister_hw_breakpoint(*pevent);
521 }
522 put_online_cpus();
523
524 free_percpu(cpu_events);
525 return (void __percpu __force *)ERR_PTR(err);
526}
527EXPORT_SYMBOL_GPL(register_wide_hw_breakpoint);
528
529
530
531
532
533void unregister_wide_hw_breakpoint(struct perf_event * __percpu *cpu_events)
534{
535 int cpu;
536 struct perf_event **pevent;
537
538 for_each_possible_cpu(cpu) {
539 pevent = per_cpu_ptr(cpu_events, cpu);
540 unregister_hw_breakpoint(*pevent);
541 }
542 free_percpu(cpu_events);
543}
544EXPORT_SYMBOL_GPL(unregister_wide_hw_breakpoint);
545
546static struct notifier_block hw_breakpoint_exceptions_nb = {
547 .notifier_call = hw_breakpoint_exceptions_notify,
548
549 .priority = 0x7fffffff
550};
551
552static int __init init_hw_breakpoint(void)
553{
554 unsigned int **task_bp_pinned;
555 int cpu, err_cpu;
556 int i;
557
558 for (i = 0; i < TYPE_MAX; i++)
559 nr_slots[i] = hw_breakpoint_slots(i);
560
561 for_each_possible_cpu(cpu) {
562 for (i = 0; i < TYPE_MAX; i++) {
563 task_bp_pinned = &per_cpu(nr_task_bp_pinned[i], cpu);
564 *task_bp_pinned = kzalloc(sizeof(int) * nr_slots[i],
565 GFP_KERNEL);
566 if (!*task_bp_pinned)
567 goto err_alloc;
568 }
569 }
570
571 constraints_initialized = 1;
572
573 return register_die_notifier(&hw_breakpoint_exceptions_nb);
574
575 err_alloc:
576 for_each_possible_cpu(err_cpu) {
577 if (err_cpu == cpu)
578 break;
579 for (i = 0; i < TYPE_MAX; i++)
580 kfree(per_cpu(nr_task_bp_pinned[i], cpu));
581 }
582
583 return -ENOMEM;
584}
585core_initcall(init_hw_breakpoint);
586
587
588struct pmu perf_ops_bp = {
589 .enable = arch_install_hw_breakpoint,
590 .disable = arch_uninstall_hw_breakpoint,
591 .read = hw_breakpoint_pmu_read,
592};
593