Commit 88a7c26af8dab2f2d69f5a6067eb670694ec38c0

Authored by Andy Lutomirski
Committed by Ingo Molnar
1 parent 0f363b250b

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 */