Messages in this thread Patch in this message | | | From | Frederic Weisbecker <> | Subject | [PATCH 2/2] perf/hw_breakpoints: Pin perf contexts of breakpoints | Date | Tue, 9 Jul 2019 15:48:21 +0200 |
| |
The breakpoints core deals with limited architecture resources that need to be shared among all the breakpoint perf events. For example x86 has 4 breakpoint registers per-CPU. This means that a task can't open more than 4 breakpoints in this architecture.
To control that we must track the number of breakpoints held for each task. This is simply implemented with a global list of all breakpoints that we walk and count on top of their target task.
But this wrongly assume that the target task of a perf event remains stable after the event creation. Context tasks can get swapped on context switches and it's fairly common for a perf event's initial target (event->hw.target) to become irrelevantly out of date at struct perf_event::destroy() time.
Here is an interesting scenario where it matters: TASK A and B are clones that share 4 equivalent breakpoints. A and B contexts are swapped on context switch. So task A now carries the breakpoints that were initialized in B. Task A quits and release its breakpoints whose target (event->hw.target) still point to B. Therefore the breakpoints slots are spuriously released on B. Afterward, if task B were to create a 5th breakpoint then it would be accepted. But on architecture breakpoint installation time, that 5th breakpoint will be eventually rejected:
Can't find any breakpoint slot WARNING: CPU: 6 PID: 2433 at arch/x86/kernel/hw_breakpoint.c:109 arch_install_hw_breakpoint+0x176/0x180 Modules linked in: CPU: 6 PID: 2433 Comm: bp5 Not tainted 5.2.0-rc6+ #20 Hardware name: MSI MS-7850/Z87-G41 PC Mate(MS-7850), BIOS V1.3 08/18/2013 RIP: 0010:arch_install_hw_breakpoint+0x176/0x180 Code: 57 ff ff ff 0f 23 c8 e9 4f ff ff ff 0f 23 d0 e9 47 ff ff ff 48 c7 c7 d8 cd 2b 82 89 45 d0 c6 05 d4 bc 4f 01 01 e8 0a b8 09 00 <0f> 0b 8b 45 d0 e9 ed fe ff ff 0f 1f 44 00 00 55 48 89 e5 41 57 41 RSP: 0018:ffffc900006dfa48 EFLAGS: 00010082 RAX: 0000000000000000 RBX: ffff88821fb17f78 RCX: 0000000000000000 RDX: ffffffff81148b59 RSI: 0000000000000001 RDI: ffffffff81148bb5 RBP: ffffc900006dfa78 R08: 0000000000000000 R09: 0000000000000000 R10: ffffc900006dfaa0 R11: 0000000000000981 R12: 0000000000000003 R13: ffff88821206b800 R14: 0000000000017f80 R15: 0000000000000004 FS: 00007f46d1a54440(0000) GS:ffff88821fb00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f46d1590450 CR3: 000000020f1c8004 CR4: 00000000001706e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 00000000999906aa Call Trace: hw_breakpoint_add+0x44/0x50 event_sched_in.isra.119+0xab/0x240 group_sched_in+0x60/0x150 pinned_sched_in+0x85/0x170 ? group_sched_in+0x150/0x150 visit_groups_merge+0x10a/0x180 ctx_sched_in.isra.89+0x12b/0x150 perf_event_sched_in.isra.91+0x2f/0x70 ctx_resched+0x54/0x90 __perf_install_in_context+0x144/0x170 ? perf_duration_warn+0x40/0x40 remote_function+0x4a/0x60 generic_exec_single+0xad/0x110 ? perf_duration_warn+0x40/0x40 smp_call_function_single+0x9c/0x150 ? perf_duration_warn+0x40/0x40 task_function_call+0x4b/0x80 ? task_function_call+0x4b/0x80 ? __perf_event_enable+0x140/0x140 perf_install_in_context+0x99/0x160 __do_sys_perf_event_open+0xaf5/0xd80 __x64_sys_perf_event_open+0x20/0x30 do_syscall_64+0x4f/0x1b0 entry_SYSCALL_64_after_hwframe+0x49/0xbe RIP: 0033:0x7f46d1590469 Code: 00 f3 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d ff 49 2b 00 f7 d8 64 89 01 48 RSP: 002b:00007ffc86f0f488 EFLAGS: 00000246 ORIG_RAX: 000000000000012a RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f46d1590469 RDX: 00000000ffffffff RSI: 0000000000000000 RDI: 0000564f0c3a0080 RBP: 00007ffc86f0f4b0 R08: 0000000000000000 R09: 00007ffc86f0f598 R10: 00000000ffffffff R11: 0000000000000246 R12: 0000564f0c19f7e0 R13: 00007ffc86f0f590 R14: 0000000000000000 R15: 0000000000000000 irq event stamp: 12252 hardirqs last enabled at (12251): [<ffffffff81282909>] kmem_cache_alloc+0xb9/0x6e0 hardirqs last disabled at (12252): [<ffffffff81184637>] generic_exec_single+0xa7/0x110 softirqs last enabled at (12234): [<ffffffff81e003e7>] __do_softirq+0x3e7/0x497 softirqs last disabled at (12227): [<ffffffff810d4558>] irq_exit+0xc8/0xd0
We can't easily support dynamic event context swap when breakpoints are involved. The easiest solution to fix the issue is to pin a context as long as it contains a breakpoint PMU.
Reported-by: syzbot+370a6b0f11867bf13515@syzkaller.appspotmail.com Cc: Borislav Petkov <bp@suse.de> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Ingo Molnar <mingo@kernel.org> Cc: Dmitry Vyukov <dvyukov@google.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Arnaldo Carvalho de Melo <acme@kernel.org> Cc: Masami Hiramatsu <mhiramat@kernel.org> Signed-off-by: Frederic Weisbecker <frederic@kernel.org> --- kernel/events/hw_breakpoint.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/kernel/events/hw_breakpoint.c b/kernel/events/hw_breakpoint.c index c5cd852fe86b..514d704d60a2 100644 --- a/kernel/events/hw_breakpoint.c +++ b/kernel/events/hw_breakpoint.c @@ -656,6 +656,7 @@ static struct pmu perf_breakpoint = { .start = hw_breakpoint_start, .stop = hw_breakpoint_stop, .read = hw_breakpoint_pmu_read, + .pin_ctx = 1, }; int __init init_hw_breakpoint(void) -- 2.21.0
| |