Commit 88a7c26af8dab2f2d69f5a6067eb670694ec38c0
Committed by
Ingo Molnar
1 parent
0f363b250b
Exists in
ti-lsk-linux-4.1.y
and in
10 other branches
perf: Move task_pt_regs sampling into arch code
On x86_64, at least, task_pt_regs may be only partially initialized in many contexts, so x86_64 should not use it without extra care from interrupt context, let alone NMI context. This will allow x86_64 to override the logic and will supply some scratch space to use to make a cleaner copy of user regs. Tested-by: Jiri Olsa <jolsa@kernel.org> Signed-off-by: Andy Lutomirski <luto@amacapital.net> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Cc: chenggang.qcg@taobao.com Cc: Wu Fengguang <fengguang.wu@intel.com> Cc: Namhyung Kim <namhyung@gmail.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Arjan van de Ven <arjan@linux.intel.com> Cc: David Ahern <dsahern@gmail.com> Cc: Arnaldo Carvalho de Melo <acme@kernel.org> Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: Jean Pihet <jean.pihet@linaro.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Mark Salter <msalter@redhat.com> Cc: Russell King <linux@arm.linux.org.uk> Cc: Will Deacon <will.deacon@arm.com> Cc: linux-arm-kernel@lists.infradead.org Link: http://lkml.kernel.org/r/e431cd4c18c2e1c44c774f10758527fb2d1025c4.1420396372.git.luto@amacapital.net Signed-off-by: Ingo Molnar <mingo@kernel.org>
Showing 6 changed files with 63 additions and 16 deletions Side-by-side Diff
arch/arm/kernel/perf_regs.c
... | ... | @@ -28,4 +28,12 @@ |
28 | 28 | { |
29 | 29 | return PERF_SAMPLE_REGS_ABI_32; |
30 | 30 | } |
31 | + | |
32 | +void perf_get_regs_user(struct perf_regs *regs_user, | |
33 | + struct pt_regs *regs, | |
34 | + struct pt_regs *regs_user_copy) | |
35 | +{ | |
36 | + regs_user->regs = task_pt_regs(current); | |
37 | + regs_user->abi = perf_reg_abi(current); | |
38 | +} |
arch/arm64/kernel/perf_regs.c
... | ... | @@ -50,4 +50,12 @@ |
50 | 50 | else |
51 | 51 | return PERF_SAMPLE_REGS_ABI_64; |
52 | 52 | } |
53 | + | |
54 | +void perf_get_regs_user(struct perf_regs *regs_user, | |
55 | + struct pt_regs *regs, | |
56 | + struct pt_regs *regs_user_copy) | |
57 | +{ | |
58 | + regs_user->regs = task_pt_regs(current); | |
59 | + regs_user->abi = perf_reg_abi(current); | |
60 | +} |
arch/x86/kernel/perf_regs.c
... | ... | @@ -78,6 +78,14 @@ |
78 | 78 | { |
79 | 79 | return PERF_SAMPLE_REGS_ABI_32; |
80 | 80 | } |
81 | + | |
82 | +void perf_get_regs_user(struct perf_regs *regs_user, | |
83 | + struct pt_regs *regs, | |
84 | + struct pt_regs *regs_user_copy) | |
85 | +{ | |
86 | + regs_user->regs = task_pt_regs(current); | |
87 | + regs_user->abi = perf_reg_abi(current); | |
88 | +} | |
81 | 89 | #else /* CONFIG_X86_64 */ |
82 | 90 | #define REG_NOSUPPORT ((1ULL << PERF_REG_X86_DS) | \ |
83 | 91 | (1ULL << PERF_REG_X86_ES) | \ |
... | ... | @@ -101,6 +109,14 @@ |
101 | 109 | return PERF_SAMPLE_REGS_ABI_32; |
102 | 110 | else |
103 | 111 | return PERF_SAMPLE_REGS_ABI_64; |
112 | +} | |
113 | + | |
114 | +void perf_get_regs_user(struct perf_regs *regs_user, | |
115 | + struct pt_regs *regs, | |
116 | + struct pt_regs *regs_user_copy) | |
117 | +{ | |
118 | + regs_user->regs = task_pt_regs(current); | |
119 | + regs_user->abi = perf_reg_abi(current); | |
104 | 120 | } |
105 | 121 | #endif /* CONFIG_X86_32 */ |
include/linux/perf_event.h
... | ... | @@ -79,11 +79,6 @@ |
79 | 79 | struct perf_branch_entry entries[0]; |
80 | 80 | }; |
81 | 81 | |
82 | -struct perf_regs { | |
83 | - __u64 abi; | |
84 | - struct pt_regs *regs; | |
85 | -}; | |
86 | - | |
87 | 82 | struct task_struct; |
88 | 83 | |
89 | 84 | /* |
90 | 85 | |
... | ... | @@ -610,7 +605,14 @@ |
610 | 605 | u32 reserved; |
611 | 606 | } cpu_entry; |
612 | 607 | struct perf_callchain_entry *callchain; |
608 | + | |
609 | + /* | |
610 | + * regs_user may point to task_pt_regs or to regs_user_copy, depending | |
611 | + * on arch details. | |
612 | + */ | |
613 | 613 | struct perf_regs regs_user; |
614 | + struct pt_regs regs_user_copy; | |
615 | + | |
614 | 616 | struct perf_regs regs_intr; |
615 | 617 | u64 stack_user_size; |
616 | 618 | } ____cacheline_aligned; |
include/linux/perf_regs.h
1 | 1 | #ifndef _LINUX_PERF_REGS_H |
2 | 2 | #define _LINUX_PERF_REGS_H |
3 | 3 | |
4 | +struct perf_regs { | |
5 | + __u64 abi; | |
6 | + struct pt_regs *regs; | |
7 | +}; | |
8 | + | |
4 | 9 | #ifdef CONFIG_HAVE_PERF_REGS |
5 | 10 | #include <asm/perf_regs.h> |
6 | 11 | u64 perf_reg_value(struct pt_regs *regs, int idx); |
7 | 12 | int perf_reg_validate(u64 mask); |
8 | 13 | u64 perf_reg_abi(struct task_struct *task); |
14 | +void perf_get_regs_user(struct perf_regs *regs_user, | |
15 | + struct pt_regs *regs, | |
16 | + struct pt_regs *regs_user_copy); | |
9 | 17 | #else |
10 | 18 | static inline u64 perf_reg_value(struct pt_regs *regs, int idx) |
11 | 19 | { |
... | ... | @@ -20,6 +28,14 @@ |
20 | 28 | static inline u64 perf_reg_abi(struct task_struct *task) |
21 | 29 | { |
22 | 30 | return PERF_SAMPLE_REGS_ABI_NONE; |
31 | +} | |
32 | + | |
33 | +static inline void perf_get_regs_user(struct perf_regs *regs_user, | |
34 | + struct pt_regs *regs, | |
35 | + struct pt_regs *regs_user_copy) | |
36 | +{ | |
37 | + regs_user->regs = task_pt_regs(current); | |
38 | + regs_user->abi = perf_reg_abi(current); | |
23 | 39 | } |
24 | 40 | #endif /* CONFIG_HAVE_PERF_REGS */ |
25 | 41 | #endif /* _LINUX_PERF_REGS_H */ |
kernel/events/core.c
... | ... | @@ -4461,18 +4461,14 @@ |
4461 | 4461 | } |
4462 | 4462 | |
4463 | 4463 | static void perf_sample_regs_user(struct perf_regs *regs_user, |
4464 | - struct pt_regs *regs) | |
4464 | + struct pt_regs *regs, | |
4465 | + struct pt_regs *regs_user_copy) | |
4465 | 4466 | { |
4466 | - if (!user_mode(regs)) { | |
4467 | - if (current->mm) | |
4468 | - regs = task_pt_regs(current); | |
4469 | - else | |
4470 | - regs = NULL; | |
4471 | - } | |
4472 | - | |
4473 | - if (regs) { | |
4474 | - regs_user->abi = perf_reg_abi(current); | |
4467 | + if (user_mode(regs)) { | |
4468 | + regs_user->abi = perf_reg_abi(current); | |
4475 | 4469 | regs_user->regs = regs; |
4470 | + } else if (current->mm) { | |
4471 | + perf_get_regs_user(regs_user, regs, regs_user_copy); | |
4476 | 4472 | } else { |
4477 | 4473 | regs_user->abi = PERF_SAMPLE_REGS_ABI_NONE; |
4478 | 4474 | regs_user->regs = NULL; |
... | ... | @@ -4951,7 +4947,8 @@ |
4951 | 4947 | } |
4952 | 4948 | |
4953 | 4949 | if (sample_type & (PERF_SAMPLE_REGS_USER | PERF_SAMPLE_STACK_USER)) |
4954 | - perf_sample_regs_user(&data->regs_user, regs); | |
4950 | + perf_sample_regs_user(&data->regs_user, regs, | |
4951 | + &data->regs_user_copy); | |
4955 | 4952 | |
4956 | 4953 | if (sample_type & PERF_SAMPLE_REGS_USER) { |
4957 | 4954 | /* regs dump ABI info */ |