Commit 88a7c26a authored by Andy Lutomirski's avatar Andy Lutomirski Committed by Ingo Molnar

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: default avatarJiri Olsa <jolsa@kernel.org>
Signed-off-by: default avatarAndy Lutomirski <luto@amacapital.net>
Signed-off-by: default avatarPeter 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.netSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent 0f363b25
...@@ -28,3 +28,11 @@ u64 perf_reg_abi(struct task_struct *task) ...@@ -28,3 +28,11 @@ u64 perf_reg_abi(struct task_struct *task)
{ {
return PERF_SAMPLE_REGS_ABI_32; return PERF_SAMPLE_REGS_ABI_32;
} }
void perf_get_regs_user(struct perf_regs *regs_user,
struct pt_regs *regs,
struct pt_regs *regs_user_copy)
{
regs_user->regs = task_pt_regs(current);
regs_user->abi = perf_reg_abi(current);
}
...@@ -50,3 +50,11 @@ u64 perf_reg_abi(struct task_struct *task) ...@@ -50,3 +50,11 @@ u64 perf_reg_abi(struct task_struct *task)
else else
return PERF_SAMPLE_REGS_ABI_64; return PERF_SAMPLE_REGS_ABI_64;
} }
void perf_get_regs_user(struct perf_regs *regs_user,
struct pt_regs *regs,
struct pt_regs *regs_user_copy)
{
regs_user->regs = task_pt_regs(current);
regs_user->abi = perf_reg_abi(current);
}
...@@ -78,6 +78,14 @@ u64 perf_reg_abi(struct task_struct *task) ...@@ -78,6 +78,14 @@ u64 perf_reg_abi(struct task_struct *task)
{ {
return PERF_SAMPLE_REGS_ABI_32; return PERF_SAMPLE_REGS_ABI_32;
} }
void perf_get_regs_user(struct perf_regs *regs_user,
struct pt_regs *regs,
struct pt_regs *regs_user_copy)
{
regs_user->regs = task_pt_regs(current);
regs_user->abi = perf_reg_abi(current);
}
#else /* CONFIG_X86_64 */ #else /* CONFIG_X86_64 */
#define REG_NOSUPPORT ((1ULL << PERF_REG_X86_DS) | \ #define REG_NOSUPPORT ((1ULL << PERF_REG_X86_DS) | \
(1ULL << PERF_REG_X86_ES) | \ (1ULL << PERF_REG_X86_ES) | \
...@@ -102,4 +110,12 @@ u64 perf_reg_abi(struct task_struct *task) ...@@ -102,4 +110,12 @@ u64 perf_reg_abi(struct task_struct *task)
else else
return PERF_SAMPLE_REGS_ABI_64; return PERF_SAMPLE_REGS_ABI_64;
} }
void perf_get_regs_user(struct perf_regs *regs_user,
struct pt_regs *regs,
struct pt_regs *regs_user_copy)
{
regs_user->regs = task_pt_regs(current);
regs_user->abi = perf_reg_abi(current);
}
#endif /* CONFIG_X86_32 */ #endif /* CONFIG_X86_32 */
...@@ -79,11 +79,6 @@ struct perf_branch_stack { ...@@ -79,11 +79,6 @@ struct perf_branch_stack {
struct perf_branch_entry entries[0]; struct perf_branch_entry entries[0];
}; };
struct perf_regs {
__u64 abi;
struct pt_regs *regs;
};
struct task_struct; struct task_struct;
/* /*
...@@ -610,7 +605,14 @@ struct perf_sample_data { ...@@ -610,7 +605,14 @@ struct perf_sample_data {
u32 reserved; u32 reserved;
} cpu_entry; } cpu_entry;
struct perf_callchain_entry *callchain; struct perf_callchain_entry *callchain;
/*
* regs_user may point to task_pt_regs or to regs_user_copy, depending
* on arch details.
*/
struct perf_regs regs_user; struct perf_regs regs_user;
struct pt_regs regs_user_copy;
struct perf_regs regs_intr; struct perf_regs regs_intr;
u64 stack_user_size; u64 stack_user_size;
} ____cacheline_aligned; } ____cacheline_aligned;
......
#ifndef _LINUX_PERF_REGS_H #ifndef _LINUX_PERF_REGS_H
#define _LINUX_PERF_REGS_H #define _LINUX_PERF_REGS_H
struct perf_regs {
__u64 abi;
struct pt_regs *regs;
};
#ifdef CONFIG_HAVE_PERF_REGS #ifdef CONFIG_HAVE_PERF_REGS
#include <asm/perf_regs.h> #include <asm/perf_regs.h>
u64 perf_reg_value(struct pt_regs *regs, int idx); u64 perf_reg_value(struct pt_regs *regs, int idx);
int perf_reg_validate(u64 mask); int perf_reg_validate(u64 mask);
u64 perf_reg_abi(struct task_struct *task); u64 perf_reg_abi(struct task_struct *task);
void perf_get_regs_user(struct perf_regs *regs_user,
struct pt_regs *regs,
struct pt_regs *regs_user_copy);
#else #else
static inline u64 perf_reg_value(struct pt_regs *regs, int idx) static inline u64 perf_reg_value(struct pt_regs *regs, int idx)
{ {
...@@ -21,5 +29,13 @@ static inline u64 perf_reg_abi(struct task_struct *task) ...@@ -21,5 +29,13 @@ static inline u64 perf_reg_abi(struct task_struct *task)
{ {
return PERF_SAMPLE_REGS_ABI_NONE; return PERF_SAMPLE_REGS_ABI_NONE;
} }
static inline void perf_get_regs_user(struct perf_regs *regs_user,
struct pt_regs *regs,
struct pt_regs *regs_user_copy)
{
regs_user->regs = task_pt_regs(current);
regs_user->abi = perf_reg_abi(current);
}
#endif /* CONFIG_HAVE_PERF_REGS */ #endif /* CONFIG_HAVE_PERF_REGS */
#endif /* _LINUX_PERF_REGS_H */ #endif /* _LINUX_PERF_REGS_H */
...@@ -4461,18 +4461,14 @@ perf_output_sample_regs(struct perf_output_handle *handle, ...@@ -4461,18 +4461,14 @@ perf_output_sample_regs(struct perf_output_handle *handle,
} }
static void perf_sample_regs_user(struct perf_regs *regs_user, static void perf_sample_regs_user(struct perf_regs *regs_user,
struct pt_regs *regs) struct pt_regs *regs,
struct pt_regs *regs_user_copy)
{ {
if (!user_mode(regs)) { if (user_mode(regs)) {
if (current->mm)
regs = task_pt_regs(current);
else
regs = NULL;
}
if (regs) {
regs_user->abi = perf_reg_abi(current); regs_user->abi = perf_reg_abi(current);
regs_user->regs = regs; regs_user->regs = regs;
} else if (current->mm) {
perf_get_regs_user(regs_user, regs, regs_user_copy);
} else { } else {
regs_user->abi = PERF_SAMPLE_REGS_ABI_NONE; regs_user->abi = PERF_SAMPLE_REGS_ABI_NONE;
regs_user->regs = NULL; regs_user->regs = NULL;
...@@ -4951,7 +4947,8 @@ void perf_prepare_sample(struct perf_event_header *header, ...@@ -4951,7 +4947,8 @@ void perf_prepare_sample(struct perf_event_header *header,
} }
if (sample_type & (PERF_SAMPLE_REGS_USER | PERF_SAMPLE_STACK_USER)) if (sample_type & (PERF_SAMPLE_REGS_USER | PERF_SAMPLE_STACK_USER))
perf_sample_regs_user(&data->regs_user, regs); perf_sample_regs_user(&data->regs_user, regs,
&data->regs_user_copy);
if (sample_type & PERF_SAMPLE_REGS_USER) { if (sample_type & PERF_SAMPLE_REGS_USER) {
/* regs dump ABI info */ /* regs dump ABI info */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment