diff options
-rw-r--r-- | arch/x86/kernel/perf_regs.c | 15 | ||||
-rw-r--r-- | include/linux/perf_event.h | 35 | ||||
-rw-r--r-- | include/linux/perf_regs.h | 6 | ||||
-rw-r--r-- | kernel/events/core.c | 66 |
4 files changed, 119 insertions, 3 deletions
diff --git a/arch/x86/kernel/perf_regs.c b/arch/x86/kernel/perf_regs.c index 3d6923528b1c..c5a3e5cfe07f 100644 --- a/arch/x86/kernel/perf_regs.c +++ b/arch/x86/kernel/perf_regs.c | |||
@@ -1,5 +1,7 @@ | |||
1 | #include <linux/errno.h> | 1 | #include <linux/errno.h> |
2 | #include <linux/kernel.h> | 2 | #include <linux/kernel.h> |
3 | #include <linux/sched.h> | ||
4 | #include <linux/perf_event.h> | ||
3 | #include <linux/bug.h> | 5 | #include <linux/bug.h> |
4 | #include <linux/stddef.h> | 6 | #include <linux/stddef.h> |
5 | #include <asm/perf_regs.h> | 7 | #include <asm/perf_regs.h> |
@@ -71,6 +73,11 @@ int perf_reg_validate(u64 mask) | |||
71 | 73 | ||
72 | return 0; | 74 | return 0; |
73 | } | 75 | } |
76 | |||
77 | u64 perf_reg_abi(struct task_struct *task) | ||
78 | { | ||
79 | return PERF_SAMPLE_REGS_ABI_32; | ||
80 | } | ||
74 | #else /* CONFIG_X86_64 */ | 81 | #else /* CONFIG_X86_64 */ |
75 | #define REG_NOSUPPORT ((1ULL << PERF_REG_X86_DS) | \ | 82 | #define REG_NOSUPPORT ((1ULL << PERF_REG_X86_DS) | \ |
76 | (1ULL << PERF_REG_X86_ES) | \ | 83 | (1ULL << PERF_REG_X86_ES) | \ |
@@ -87,4 +94,12 @@ int perf_reg_validate(u64 mask) | |||
87 | 94 | ||
88 | return 0; | 95 | return 0; |
89 | } | 96 | } |
97 | |||
98 | u64 perf_reg_abi(struct task_struct *task) | ||
99 | { | ||
100 | if (test_tsk_thread_flag(task, TIF_IA32)) | ||
101 | return PERF_SAMPLE_REGS_ABI_32; | ||
102 | else | ||
103 | return PERF_SAMPLE_REGS_ABI_64; | ||
104 | } | ||
90 | #endif /* CONFIG_X86_32 */ | 105 | #endif /* CONFIG_X86_32 */ |
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 7602ccb3f40e..3d4d84745f07 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h | |||
@@ -130,8 +130,9 @@ enum perf_event_sample_format { | |||
130 | PERF_SAMPLE_STREAM_ID = 1U << 9, | 130 | PERF_SAMPLE_STREAM_ID = 1U << 9, |
131 | PERF_SAMPLE_RAW = 1U << 10, | 131 | PERF_SAMPLE_RAW = 1U << 10, |
132 | PERF_SAMPLE_BRANCH_STACK = 1U << 11, | 132 | PERF_SAMPLE_BRANCH_STACK = 1U << 11, |
133 | PERF_SAMPLE_REGS_USER = 1U << 12, | ||
133 | 134 | ||
134 | PERF_SAMPLE_MAX = 1U << 12, /* non-ABI */ | 135 | PERF_SAMPLE_MAX = 1U << 13, /* non-ABI */ |
135 | }; | 136 | }; |
136 | 137 | ||
137 | /* | 138 | /* |
@@ -163,6 +164,15 @@ enum perf_branch_sample_type { | |||
163 | PERF_SAMPLE_BRANCH_HV) | 164 | PERF_SAMPLE_BRANCH_HV) |
164 | 165 | ||
165 | /* | 166 | /* |
167 | * Values to determine ABI of the registers dump. | ||
168 | */ | ||
169 | enum perf_sample_regs_abi { | ||
170 | PERF_SAMPLE_REGS_ABI_NONE = 0, | ||
171 | PERF_SAMPLE_REGS_ABI_32 = 1, | ||
172 | PERF_SAMPLE_REGS_ABI_64 = 2, | ||
173 | }; | ||
174 | |||
175 | /* | ||
166 | * The format of the data returned by read() on a perf event fd, | 176 | * The format of the data returned by read() on a perf event fd, |
167 | * as specified by attr.read_format: | 177 | * as specified by attr.read_format: |
168 | * | 178 | * |
@@ -194,6 +204,7 @@ enum perf_event_read_format { | |||
194 | #define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */ | 204 | #define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */ |
195 | #define PERF_ATTR_SIZE_VER1 72 /* add: config2 */ | 205 | #define PERF_ATTR_SIZE_VER1 72 /* add: config2 */ |
196 | #define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */ | 206 | #define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */ |
207 | #define PERF_ATTR_SIZE_VER3 88 /* add: sample_regs_user */ | ||
197 | 208 | ||
198 | /* | 209 | /* |
199 | * Hardware event_id to monitor via a performance monitoring event: | 210 | * Hardware event_id to monitor via a performance monitoring event: |
@@ -271,7 +282,13 @@ struct perf_event_attr { | |||
271 | __u64 bp_len; | 282 | __u64 bp_len; |
272 | __u64 config2; /* extension of config1 */ | 283 | __u64 config2; /* extension of config1 */ |
273 | }; | 284 | }; |
274 | __u64 branch_sample_type; /* enum branch_sample_type */ | 285 | __u64 branch_sample_type; /* enum perf_branch_sample_type */ |
286 | |||
287 | /* | ||
288 | * Defines set of user regs to dump on samples. | ||
289 | * See asm/perf_regs.h for details. | ||
290 | */ | ||
291 | __u64 sample_regs_user; | ||
275 | }; | 292 | }; |
276 | 293 | ||
277 | /* | 294 | /* |
@@ -548,6 +565,9 @@ enum perf_event_type { | |||
548 | * char data[size];}&& PERF_SAMPLE_RAW | 565 | * char data[size];}&& PERF_SAMPLE_RAW |
549 | * | 566 | * |
550 | * { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK | 567 | * { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK |
568 | * | ||
569 | * { u64 abi; # enum perf_sample_regs_abi | ||
570 | * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER | ||
551 | * }; | 571 | * }; |
552 | */ | 572 | */ |
553 | PERF_RECORD_SAMPLE = 9, | 573 | PERF_RECORD_SAMPLE = 9, |
@@ -609,6 +629,7 @@ struct perf_guest_info_callbacks { | |||
609 | #include <linux/static_key.h> | 629 | #include <linux/static_key.h> |
610 | #include <linux/atomic.h> | 630 | #include <linux/atomic.h> |
611 | #include <linux/sysfs.h> | 631 | #include <linux/sysfs.h> |
632 | #include <linux/perf_regs.h> | ||
612 | #include <asm/local.h> | 633 | #include <asm/local.h> |
613 | 634 | ||
614 | struct perf_callchain_entry { | 635 | struct perf_callchain_entry { |
@@ -654,6 +675,11 @@ struct perf_branch_stack { | |||
654 | struct perf_branch_entry entries[0]; | 675 | struct perf_branch_entry entries[0]; |
655 | }; | 676 | }; |
656 | 677 | ||
678 | struct perf_regs_user { | ||
679 | __u64 abi; | ||
680 | struct pt_regs *regs; | ||
681 | }; | ||
682 | |||
657 | struct task_struct; | 683 | struct task_struct; |
658 | 684 | ||
659 | /* | 685 | /* |
@@ -1133,6 +1159,7 @@ struct perf_sample_data { | |||
1133 | struct perf_callchain_entry *callchain; | 1159 | struct perf_callchain_entry *callchain; |
1134 | struct perf_raw_record *raw; | 1160 | struct perf_raw_record *raw; |
1135 | struct perf_branch_stack *br_stack; | 1161 | struct perf_branch_stack *br_stack; |
1162 | struct perf_regs_user regs_user; | ||
1136 | }; | 1163 | }; |
1137 | 1164 | ||
1138 | static inline void perf_sample_data_init(struct perf_sample_data *data, | 1165 | static inline void perf_sample_data_init(struct perf_sample_data *data, |
@@ -1142,7 +1169,9 @@ static inline void perf_sample_data_init(struct perf_sample_data *data, | |||
1142 | data->addr = addr; | 1169 | data->addr = addr; |
1143 | data->raw = NULL; | 1170 | data->raw = NULL; |
1144 | data->br_stack = NULL; | 1171 | data->br_stack = NULL; |
1145 | data->period = period; | 1172 | data->period = period; |
1173 | data->regs_user.abi = PERF_SAMPLE_REGS_ABI_NONE; | ||
1174 | data->regs_user.regs = NULL; | ||
1146 | } | 1175 | } |
1147 | 1176 | ||
1148 | extern void perf_output_sample(struct perf_output_handle *handle, | 1177 | extern void perf_output_sample(struct perf_output_handle *handle, |
diff --git a/include/linux/perf_regs.h b/include/linux/perf_regs.h index a2f1a98f7839..3c73d5fe18be 100644 --- a/include/linux/perf_regs.h +++ b/include/linux/perf_regs.h | |||
@@ -5,6 +5,7 @@ | |||
5 | #include <asm/perf_regs.h> | 5 | #include <asm/perf_regs.h> |
6 | u64 perf_reg_value(struct pt_regs *regs, int idx); | 6 | u64 perf_reg_value(struct pt_regs *regs, int idx); |
7 | int perf_reg_validate(u64 mask); | 7 | int perf_reg_validate(u64 mask); |
8 | u64 perf_reg_abi(struct task_struct *task); | ||
8 | #else | 9 | #else |
9 | static inline u64 perf_reg_value(struct pt_regs *regs, int idx) | 10 | static inline u64 perf_reg_value(struct pt_regs *regs, int idx) |
10 | { | 11 | { |
@@ -15,5 +16,10 @@ static inline int perf_reg_validate(u64 mask) | |||
15 | { | 16 | { |
16 | return mask ? -ENOSYS : 0; | 17 | return mask ? -ENOSYS : 0; |
17 | } | 18 | } |
19 | |||
20 | static inline u64 perf_reg_abi(struct task_struct *task) | ||
21 | { | ||
22 | return PERF_SAMPLE_REGS_ABI_NONE; | ||
23 | } | ||
18 | #endif /* CONFIG_HAVE_PERF_REGS */ | 24 | #endif /* CONFIG_HAVE_PERF_REGS */ |
19 | #endif /* _LINUX_PERF_REGS_H */ | 25 | #endif /* _LINUX_PERF_REGS_H */ |
diff --git a/kernel/events/core.c b/kernel/events/core.c index b7935fcec7d9..d3ce97525b9f 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c | |||
@@ -3756,6 +3756,37 @@ int perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *cbs) | |||
3756 | } | 3756 | } |
3757 | EXPORT_SYMBOL_GPL(perf_unregister_guest_info_callbacks); | 3757 | EXPORT_SYMBOL_GPL(perf_unregister_guest_info_callbacks); |
3758 | 3758 | ||
3759 | static void | ||
3760 | perf_output_sample_regs(struct perf_output_handle *handle, | ||
3761 | struct pt_regs *regs, u64 mask) | ||
3762 | { | ||
3763 | int bit; | ||
3764 | |||
3765 | for_each_set_bit(bit, (const unsigned long *) &mask, | ||
3766 | sizeof(mask) * BITS_PER_BYTE) { | ||
3767 | u64 val; | ||
3768 | |||
3769 | val = perf_reg_value(regs, bit); | ||
3770 | perf_output_put(handle, val); | ||
3771 | } | ||
3772 | } | ||
3773 | |||
3774 | static void perf_sample_regs_user(struct perf_regs_user *regs_user, | ||
3775 | struct pt_regs *regs) | ||
3776 | { | ||
3777 | if (!user_mode(regs)) { | ||
3778 | if (current->mm) | ||
3779 | regs = task_pt_regs(current); | ||
3780 | else | ||
3781 | regs = NULL; | ||
3782 | } | ||
3783 | |||
3784 | if (regs) { | ||
3785 | regs_user->regs = regs; | ||
3786 | regs_user->abi = perf_reg_abi(current); | ||
3787 | } | ||
3788 | } | ||
3789 | |||
3759 | static void __perf_event_header__init_id(struct perf_event_header *header, | 3790 | static void __perf_event_header__init_id(struct perf_event_header *header, |
3760 | struct perf_sample_data *data, | 3791 | struct perf_sample_data *data, |
3761 | struct perf_event *event) | 3792 | struct perf_event *event) |
@@ -4016,6 +4047,23 @@ void perf_output_sample(struct perf_output_handle *handle, | |||
4016 | perf_output_put(handle, nr); | 4047 | perf_output_put(handle, nr); |
4017 | } | 4048 | } |
4018 | } | 4049 | } |
4050 | |||
4051 | if (sample_type & PERF_SAMPLE_REGS_USER) { | ||
4052 | u64 abi = data->regs_user.abi; | ||
4053 | |||
4054 | /* | ||
4055 | * If there are no regs to dump, notice it through | ||
4056 | * first u64 being zero (PERF_SAMPLE_REGS_ABI_NONE). | ||
4057 | */ | ||
4058 | perf_output_put(handle, abi); | ||
4059 | |||
4060 | if (abi) { | ||
4061 | u64 mask = event->attr.sample_regs_user; | ||
4062 | perf_output_sample_regs(handle, | ||
4063 | data->regs_user.regs, | ||
4064 | mask); | ||
4065 | } | ||
4066 | } | ||
4019 | } | 4067 | } |
4020 | 4068 | ||
4021 | void perf_prepare_sample(struct perf_event_header *header, | 4069 | void perf_prepare_sample(struct perf_event_header *header, |
@@ -4067,6 +4115,20 @@ void perf_prepare_sample(struct perf_event_header *header, | |||
4067 | } | 4115 | } |
4068 | header->size += size; | 4116 | header->size += size; |
4069 | } | 4117 | } |
4118 | |||
4119 | if (sample_type & PERF_SAMPLE_REGS_USER) { | ||
4120 | /* regs dump ABI info */ | ||
4121 | int size = sizeof(u64); | ||
4122 | |||
4123 | perf_sample_regs_user(&data->regs_user, regs); | ||
4124 | |||
4125 | if (data->regs_user.regs) { | ||
4126 | u64 mask = event->attr.sample_regs_user; | ||
4127 | size += hweight64(mask) * sizeof(u64); | ||
4128 | } | ||
4129 | |||
4130 | header->size += size; | ||
4131 | } | ||
4070 | } | 4132 | } |
4071 | 4133 | ||
4072 | static void perf_event_output(struct perf_event *event, | 4134 | static void perf_event_output(struct perf_event *event, |
@@ -6142,6 +6204,10 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr, | |||
6142 | attr->branch_sample_type = mask; | 6204 | attr->branch_sample_type = mask; |
6143 | } | 6205 | } |
6144 | } | 6206 | } |
6207 | |||
6208 | if (attr->sample_type & PERF_SAMPLE_REGS_USER) | ||
6209 | ret = perf_reg_validate(attr->sample_regs_user); | ||
6210 | |||
6145 | out: | 6211 | out: |
6146 | return ret; | 6212 | return ret; |
6147 | 6213 | ||