diff options
Diffstat (limited to 'kernel/events/hw_breakpoint.c')
-rw-r--r-- | kernel/events/hw_breakpoint.c | 191 |
1 files changed, 83 insertions, 108 deletions
diff --git a/kernel/events/hw_breakpoint.c b/kernel/events/hw_breakpoint.c index 20185ea64aa6..1559fb0b9296 100644 --- a/kernel/events/hw_breakpoint.c +++ b/kernel/events/hw_breakpoint.c | |||
@@ -46,23 +46,26 @@ | |||
46 | #include <linux/smp.h> | 46 | #include <linux/smp.h> |
47 | 47 | ||
48 | #include <linux/hw_breakpoint.h> | 48 | #include <linux/hw_breakpoint.h> |
49 | |||
50 | |||
51 | /* | 49 | /* |
52 | * Constraints data | 50 | * Constraints data |
53 | */ | 51 | */ |
52 | struct bp_cpuinfo { | ||
53 | /* Number of pinned cpu breakpoints in a cpu */ | ||
54 | unsigned int cpu_pinned; | ||
55 | /* tsk_pinned[n] is the number of tasks having n+1 breakpoints */ | ||
56 | unsigned int *tsk_pinned; | ||
57 | /* Number of non-pinned cpu/task breakpoints in a cpu */ | ||
58 | unsigned int flexible; /* XXX: placeholder, see fetch_this_slot() */ | ||
59 | }; | ||
54 | 60 | ||
55 | /* Number of pinned cpu breakpoints in a cpu */ | 61 | static DEFINE_PER_CPU(struct bp_cpuinfo, bp_cpuinfo[TYPE_MAX]); |
56 | static DEFINE_PER_CPU(unsigned int, nr_cpu_bp_pinned[TYPE_MAX]); | ||
57 | |||
58 | /* Number of pinned task breakpoints in a cpu */ | ||
59 | static DEFINE_PER_CPU(unsigned int *, nr_task_bp_pinned[TYPE_MAX]); | ||
60 | |||
61 | /* Number of non-pinned cpu/task breakpoints in a cpu */ | ||
62 | static DEFINE_PER_CPU(unsigned int, nr_bp_flexible[TYPE_MAX]); | ||
63 | |||
64 | static int nr_slots[TYPE_MAX]; | 62 | static int nr_slots[TYPE_MAX]; |
65 | 63 | ||
64 | static 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 | |||
66 | /* Keep track of the breakpoints attached to tasks */ | 69 | /* Keep track of the breakpoints attached to tasks */ |
67 | static LIST_HEAD(bp_task_head); | 70 | static LIST_HEAD(bp_task_head); |
68 | 71 | ||
@@ -96,8 +99,8 @@ static inline enum bp_type_idx find_slot_idx(struct perf_event *bp) | |||
96 | */ | 99 | */ |
97 | static unsigned int max_task_bp_pinned(int cpu, enum bp_type_idx type) | 100 | static unsigned int max_task_bp_pinned(int cpu, enum bp_type_idx type) |
98 | { | 101 | { |
102 | unsigned int *tsk_pinned = get_bp_info(cpu, type)->tsk_pinned; | ||
99 | int i; | 103 | int i; |
100 | unsigned int *tsk_pinned = per_cpu(nr_task_bp_pinned[type], cpu); | ||
101 | 104 | ||
102 | for (i = nr_slots[type] - 1; i >= 0; i--) { | 105 | for (i = nr_slots[type] - 1; i >= 0; i--) { |
103 | if (tsk_pinned[i] > 0) | 106 | if (tsk_pinned[i] > 0) |
@@ -127,6 +130,13 @@ static int task_bp_pinned(int cpu, struct perf_event *bp, enum bp_type_idx type) | |||
127 | return count; | 130 | return count; |
128 | } | 131 | } |
129 | 132 | ||
133 | static 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 | |||
130 | /* | 140 | /* |
131 | * Report the number of pinned/un-pinned breakpoints we have in | 141 | * Report the number of pinned/un-pinned breakpoints we have in |
132 | * a given cpu (cpu > -1) or in all of them (cpu = -1). | 142 | * a given cpu (cpu > -1) or in all of them (cpu = -1). |
@@ -135,25 +145,15 @@ static void | |||
135 | fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp, | 145 | fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp, |
136 | enum bp_type_idx type) | 146 | enum bp_type_idx type) |
137 | { | 147 | { |
138 | int cpu = bp->cpu; | 148 | const struct cpumask *cpumask = cpumask_of_bp(bp); |
139 | struct task_struct *tsk = bp->hw.bp_target; | 149 | int cpu; |
140 | |||
141 | if (cpu >= 0) { | ||
142 | slots->pinned = per_cpu(nr_cpu_bp_pinned[type], cpu); | ||
143 | if (!tsk) | ||
144 | slots->pinned += max_task_bp_pinned(cpu, type); | ||
145 | else | ||
146 | slots->pinned += task_bp_pinned(cpu, bp, type); | ||
147 | slots->flexible = per_cpu(nr_bp_flexible[type], cpu); | ||
148 | |||
149 | return; | ||
150 | } | ||
151 | 150 | ||
152 | for_each_possible_cpu(cpu) { | 151 | for_each_cpu(cpu, cpumask) { |
153 | unsigned int nr; | 152 | struct bp_cpuinfo *info = get_bp_info(cpu, type); |
153 | int nr; | ||
154 | 154 | ||
155 | nr = per_cpu(nr_cpu_bp_pinned[type], cpu); | 155 | nr = info->cpu_pinned; |
156 | if (!tsk) | 156 | if (!bp->hw.bp_target) |
157 | nr += max_task_bp_pinned(cpu, type); | 157 | nr += max_task_bp_pinned(cpu, type); |
158 | else | 158 | else |
159 | nr += task_bp_pinned(cpu, bp, type); | 159 | nr += task_bp_pinned(cpu, bp, type); |
@@ -161,8 +161,7 @@ fetch_bp_busy_slots(struct bp_busy_slots *slots, struct perf_event *bp, | |||
161 | if (nr > slots->pinned) | 161 | if (nr > slots->pinned) |
162 | slots->pinned = nr; | 162 | slots->pinned = nr; |
163 | 163 | ||
164 | nr = per_cpu(nr_bp_flexible[type], cpu); | 164 | nr = info->flexible; |
165 | |||
166 | if (nr > slots->flexible) | 165 | if (nr > slots->flexible) |
167 | slots->flexible = nr; | 166 | slots->flexible = nr; |
168 | } | 167 | } |
@@ -182,29 +181,19 @@ fetch_this_slot(struct bp_busy_slots *slots, int weight) | |||
182 | /* | 181 | /* |
183 | * Add a pinned breakpoint for the given task in our constraint table | 182 | * Add a pinned breakpoint for the given task in our constraint table |
184 | */ | 183 | */ |
185 | static void toggle_bp_task_slot(struct perf_event *bp, int cpu, bool enable, | 184 | static void toggle_bp_task_slot(struct perf_event *bp, int cpu, |
186 | enum bp_type_idx type, int weight) | 185 | enum bp_type_idx type, int weight) |
187 | { | 186 | { |
188 | unsigned int *tsk_pinned; | 187 | unsigned int *tsk_pinned = get_bp_info(cpu, type)->tsk_pinned; |
189 | int old_count = 0; | 188 | int old_idx, new_idx; |
190 | int old_idx = 0; | 189 | |
191 | int idx = 0; | 190 | old_idx = task_bp_pinned(cpu, bp, type) - 1; |
192 | 191 | new_idx = old_idx + weight; | |
193 | old_count = task_bp_pinned(cpu, bp, type); | 192 | |
194 | old_idx = old_count - 1; | 193 | if (old_idx >= 0) |
195 | idx = old_idx + weight; | 194 | tsk_pinned[old_idx]--; |
196 | 195 | if (new_idx >= 0) | |
197 | /* tsk_pinned[n] is the number of tasks having n breakpoints */ | 196 | tsk_pinned[new_idx]++; |
198 | tsk_pinned = per_cpu(nr_task_bp_pinned[type], cpu); | ||
199 | if (enable) { | ||
200 | tsk_pinned[idx]++; | ||
201 | if (old_count > 0) | ||
202 | tsk_pinned[old_idx]--; | ||
203 | } else { | ||
204 | tsk_pinned[idx]--; | ||
205 | if (old_count > 0) | ||
206 | tsk_pinned[old_idx]++; | ||
207 | } | ||
208 | } | 197 | } |
209 | 198 | ||
210 | /* | 199 | /* |
@@ -214,33 +203,26 @@ static void | |||
214 | toggle_bp_slot(struct perf_event *bp, bool enable, enum bp_type_idx type, | 203 | toggle_bp_slot(struct perf_event *bp, bool enable, enum bp_type_idx type, |
215 | int weight) | 204 | int weight) |
216 | { | 205 | { |
217 | int cpu = bp->cpu; | 206 | const struct cpumask *cpumask = cpumask_of_bp(bp); |
218 | struct task_struct *tsk = bp->hw.bp_target; | 207 | int cpu; |
219 | 208 | ||
220 | /* Pinned counter cpu profiling */ | 209 | if (!enable) |
221 | if (!tsk) { | 210 | weight = -weight; |
222 | 211 | ||
223 | if (enable) | 212 | /* Pinned counter cpu profiling */ |
224 | per_cpu(nr_cpu_bp_pinned[type], bp->cpu) += weight; | 213 | if (!bp->hw.bp_target) { |
225 | else | 214 | get_bp_info(bp->cpu, type)->cpu_pinned += weight; |
226 | per_cpu(nr_cpu_bp_pinned[type], bp->cpu) -= weight; | ||
227 | return; | 215 | return; |
228 | } | 216 | } |
229 | 217 | ||
230 | /* Pinned counter task profiling */ | 218 | /* Pinned counter task profiling */ |
231 | 219 | for_each_cpu(cpu, cpumask) | |
232 | if (!enable) | 220 | toggle_bp_task_slot(bp, cpu, type, weight); |
233 | list_del(&bp->hw.bp_list); | ||
234 | |||
235 | if (cpu >= 0) { | ||
236 | toggle_bp_task_slot(bp, cpu, enable, type, weight); | ||
237 | } else { | ||
238 | for_each_possible_cpu(cpu) | ||
239 | toggle_bp_task_slot(bp, cpu, enable, type, weight); | ||
240 | } | ||
241 | 221 | ||
242 | if (enable) | 222 | if (enable) |
243 | list_add_tail(&bp->hw.bp_list, &bp_task_head); | 223 | list_add_tail(&bp->hw.bp_list, &bp_task_head); |
224 | else | ||
225 | list_del(&bp->hw.bp_list); | ||
244 | } | 226 | } |
245 | 227 | ||
246 | /* | 228 | /* |
@@ -261,8 +243,8 @@ __weak void arch_unregister_hw_breakpoint(struct perf_event *bp) | |||
261 | * | 243 | * |
262 | * - If attached to a single cpu, check: | 244 | * - If attached to a single cpu, check: |
263 | * | 245 | * |
264 | * (per_cpu(nr_bp_flexible, cpu) || (per_cpu(nr_cpu_bp_pinned, cpu) | 246 | * (per_cpu(info->flexible, cpu) || (per_cpu(info->cpu_pinned, cpu) |
265 | * + max(per_cpu(nr_task_bp_pinned, cpu)))) < HBP_NUM | 247 | * + max(per_cpu(info->tsk_pinned, cpu)))) < HBP_NUM |
266 | * | 248 | * |
267 | * -> If there are already non-pinned counters in this cpu, it means | 249 | * -> If there are already non-pinned counters in this cpu, it means |
268 | * there is already a free slot for them. | 250 | * there is already a free slot for them. |
@@ -272,8 +254,8 @@ __weak void arch_unregister_hw_breakpoint(struct perf_event *bp) | |||
272 | * | 254 | * |
273 | * - If attached to every cpus, check: | 255 | * - If attached to every cpus, check: |
274 | * | 256 | * |
275 | * (per_cpu(nr_bp_flexible, *) || (max(per_cpu(nr_cpu_bp_pinned, *)) | 257 | * (per_cpu(info->flexible, *) || (max(per_cpu(info->cpu_pinned, *)) |
276 | * + max(per_cpu(nr_task_bp_pinned, *)))) < HBP_NUM | 258 | * + max(per_cpu(info->tsk_pinned, *)))) < HBP_NUM |
277 | * | 259 | * |
278 | * -> This is roughly the same, except we check the number of per cpu | 260 | * -> This is roughly the same, except we check the number of per cpu |
279 | * bp for every cpu and we keep the max one. Same for the per tasks | 261 | * bp for every cpu and we keep the max one. Same for the per tasks |
@@ -284,16 +266,16 @@ __weak void arch_unregister_hw_breakpoint(struct perf_event *bp) | |||
284 | * | 266 | * |
285 | * - If attached to a single cpu, check: | 267 | * - If attached to a single cpu, check: |
286 | * | 268 | * |
287 | * ((per_cpu(nr_bp_flexible, cpu) > 1) + per_cpu(nr_cpu_bp_pinned, cpu) | 269 | * ((per_cpu(info->flexible, cpu) > 1) + per_cpu(info->cpu_pinned, cpu) |
288 | * + max(per_cpu(nr_task_bp_pinned, cpu))) < HBP_NUM | 270 | * + max(per_cpu(info->tsk_pinned, cpu))) < HBP_NUM |
289 | * | 271 | * |
290 | * -> Same checks as before. But now the nr_bp_flexible, if any, must keep | 272 | * -> Same checks as before. But now the info->flexible, if any, must keep |
291 | * one register at least (or they will never be fed). | 273 | * one register at least (or they will never be fed). |
292 | * | 274 | * |
293 | * - If attached to every cpus, check: | 275 | * - If attached to every cpus, check: |
294 | * | 276 | * |
295 | * ((per_cpu(nr_bp_flexible, *) > 1) + max(per_cpu(nr_cpu_bp_pinned, *)) | 277 | * ((per_cpu(info->flexible, *) > 1) + max(per_cpu(info->cpu_pinned, *)) |
296 | * + max(per_cpu(nr_task_bp_pinned, *))) < HBP_NUM | 278 | * + max(per_cpu(info->tsk_pinned, *))) < HBP_NUM |
297 | */ | 279 | */ |
298 | static int __reserve_bp_slot(struct perf_event *bp) | 280 | static int __reserve_bp_slot(struct perf_event *bp) |
299 | { | 281 | { |
@@ -518,8 +500,8 @@ register_wide_hw_breakpoint(struct perf_event_attr *attr, | |||
518 | perf_overflow_handler_t triggered, | 500 | perf_overflow_handler_t triggered, |
519 | void *context) | 501 | void *context) |
520 | { | 502 | { |
521 | struct perf_event * __percpu *cpu_events, **pevent, *bp; | 503 | struct perf_event * __percpu *cpu_events, *bp; |
522 | long err; | 504 | long err = 0; |
523 | int cpu; | 505 | int cpu; |
524 | 506 | ||
525 | cpu_events = alloc_percpu(typeof(*cpu_events)); | 507 | cpu_events = alloc_percpu(typeof(*cpu_events)); |
@@ -528,31 +510,21 @@ register_wide_hw_breakpoint(struct perf_event_attr *attr, | |||
528 | 510 | ||
529 | get_online_cpus(); | 511 | get_online_cpus(); |
530 | for_each_online_cpu(cpu) { | 512 | for_each_online_cpu(cpu) { |
531 | pevent = per_cpu_ptr(cpu_events, cpu); | ||
532 | bp = perf_event_create_kernel_counter(attr, cpu, NULL, | 513 | bp = perf_event_create_kernel_counter(attr, cpu, NULL, |
533 | triggered, context); | 514 | triggered, context); |
534 | |||
535 | *pevent = bp; | ||
536 | |||
537 | if (IS_ERR(bp)) { | 515 | if (IS_ERR(bp)) { |
538 | err = PTR_ERR(bp); | 516 | err = PTR_ERR(bp); |
539 | goto fail; | 517 | break; |
540 | } | 518 | } |
541 | } | ||
542 | put_online_cpus(); | ||
543 | 519 | ||
544 | return cpu_events; | 520 | per_cpu(*cpu_events, cpu) = bp; |
545 | |||
546 | fail: | ||
547 | for_each_online_cpu(cpu) { | ||
548 | pevent = per_cpu_ptr(cpu_events, cpu); | ||
549 | if (IS_ERR(*pevent)) | ||
550 | break; | ||
551 | unregister_hw_breakpoint(*pevent); | ||
552 | } | 521 | } |
553 | put_online_cpus(); | 522 | put_online_cpus(); |
554 | 523 | ||
555 | free_percpu(cpu_events); | 524 | if (likely(!err)) |
525 | return cpu_events; | ||
526 | |||
527 | unregister_wide_hw_breakpoint(cpu_events); | ||
556 | return (void __percpu __force *)ERR_PTR(err); | 528 | return (void __percpu __force *)ERR_PTR(err); |
557 | } | 529 | } |
558 | EXPORT_SYMBOL_GPL(register_wide_hw_breakpoint); | 530 | EXPORT_SYMBOL_GPL(register_wide_hw_breakpoint); |
@@ -564,12 +536,10 @@ EXPORT_SYMBOL_GPL(register_wide_hw_breakpoint); | |||
564 | void unregister_wide_hw_breakpoint(struct perf_event * __percpu *cpu_events) | 536 | void unregister_wide_hw_breakpoint(struct perf_event * __percpu *cpu_events) |
565 | { | 537 | { |
566 | int cpu; | 538 | int cpu; |
567 | struct perf_event **pevent; | ||
568 | 539 | ||
569 | for_each_possible_cpu(cpu) { | 540 | for_each_possible_cpu(cpu) |
570 | pevent = per_cpu_ptr(cpu_events, cpu); | 541 | unregister_hw_breakpoint(per_cpu(*cpu_events, cpu)); |
571 | unregister_hw_breakpoint(*pevent); | 542 | |
572 | } | ||
573 | free_percpu(cpu_events); | 543 | free_percpu(cpu_events); |
574 | } | 544 | } |
575 | EXPORT_SYMBOL_GPL(unregister_wide_hw_breakpoint); | 545 | EXPORT_SYMBOL_GPL(unregister_wide_hw_breakpoint); |
@@ -612,6 +582,11 @@ static int hw_breakpoint_add(struct perf_event *bp, int flags) | |||
612 | if (!(flags & PERF_EF_START)) | 582 | if (!(flags & PERF_EF_START)) |
613 | bp->hw.state = PERF_HES_STOPPED; | 583 | bp->hw.state = PERF_HES_STOPPED; |
614 | 584 | ||
585 | if (is_sampling_event(bp)) { | ||
586 | bp->hw.last_period = bp->hw.sample_period; | ||
587 | perf_swevent_set_period(bp); | ||
588 | } | ||
589 | |||
615 | return arch_install_hw_breakpoint(bp); | 590 | return arch_install_hw_breakpoint(bp); |
616 | } | 591 | } |
617 | 592 | ||
@@ -650,7 +625,6 @@ static struct pmu perf_breakpoint = { | |||
650 | 625 | ||
651 | int __init init_hw_breakpoint(void) | 626 | int __init init_hw_breakpoint(void) |
652 | { | 627 | { |
653 | unsigned int **task_bp_pinned; | ||
654 | int cpu, err_cpu; | 628 | int cpu, err_cpu; |
655 | int i; | 629 | int i; |
656 | 630 | ||
@@ -659,10 +633,11 @@ int __init init_hw_breakpoint(void) | |||
659 | 633 | ||
660 | for_each_possible_cpu(cpu) { | 634 | for_each_possible_cpu(cpu) { |
661 | for (i = 0; i < TYPE_MAX; i++) { | 635 | for (i = 0; i < TYPE_MAX; i++) { |
662 | task_bp_pinned = &per_cpu(nr_task_bp_pinned[i], cpu); | 636 | struct bp_cpuinfo *info = get_bp_info(cpu, i); |
663 | *task_bp_pinned = kzalloc(sizeof(int) * nr_slots[i], | 637 | |
664 | GFP_KERNEL); | 638 | info->tsk_pinned = kcalloc(nr_slots[i], sizeof(int), |
665 | if (!*task_bp_pinned) | 639 | GFP_KERNEL); |
640 | if (!info->tsk_pinned) | ||
666 | goto err_alloc; | 641 | goto err_alloc; |
667 | } | 642 | } |
668 | } | 643 | } |
@@ -676,7 +651,7 @@ int __init init_hw_breakpoint(void) | |||
676 | err_alloc: | 651 | err_alloc: |
677 | for_each_possible_cpu(err_cpu) { | 652 | for_each_possible_cpu(err_cpu) { |
678 | for (i = 0; i < TYPE_MAX; i++) | 653 | for (i = 0; i < TYPE_MAX; i++) |
679 | kfree(per_cpu(nr_task_bp_pinned[i], err_cpu)); | 654 | kfree(get_bp_info(err_cpu, i)->tsk_pinned); |
680 | if (err_cpu == cpu) | 655 | if (err_cpu == cpu) |
681 | break; | 656 | break; |
682 | } | 657 | } |