diff options
| -rw-r--r-- | include/linux/perf_event.h | 7 | ||||
| -rw-r--r-- | include/uapi/linux/perf_event.h | 15 | ||||
| -rw-r--r-- | kernel/events/core.c | 46 |
3 files changed, 63 insertions, 5 deletions
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 893a0d07986f..68d46d536e24 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h | |||
| @@ -79,7 +79,7 @@ struct perf_branch_stack { | |||
| 79 | struct perf_branch_entry entries[0]; | 79 | struct perf_branch_entry entries[0]; |
| 80 | }; | 80 | }; |
| 81 | 81 | ||
| 82 | struct perf_regs_user { | 82 | struct perf_regs { |
| 83 | __u64 abi; | 83 | __u64 abi; |
| 84 | struct pt_regs *regs; | 84 | struct pt_regs *regs; |
| 85 | }; | 85 | }; |
| @@ -600,7 +600,8 @@ struct perf_sample_data { | |||
| 600 | struct perf_callchain_entry *callchain; | 600 | struct perf_callchain_entry *callchain; |
| 601 | struct perf_raw_record *raw; | 601 | struct perf_raw_record *raw; |
| 602 | struct perf_branch_stack *br_stack; | 602 | struct perf_branch_stack *br_stack; |
| 603 | struct perf_regs_user regs_user; | 603 | struct perf_regs regs_user; |
| 604 | struct perf_regs regs_intr; | ||
| 604 | u64 stack_user_size; | 605 | u64 stack_user_size; |
| 605 | u64 weight; | 606 | u64 weight; |
| 606 | /* | 607 | /* |
| @@ -630,6 +631,8 @@ static inline void perf_sample_data_init(struct perf_sample_data *data, | |||
| 630 | data->weight = 0; | 631 | data->weight = 0; |
| 631 | data->data_src.val = PERF_MEM_NA; | 632 | data->data_src.val = PERF_MEM_NA; |
| 632 | data->txn = 0; | 633 | data->txn = 0; |
| 634 | data->regs_intr.abi = PERF_SAMPLE_REGS_ABI_NONE; | ||
| 635 | data->regs_intr.regs = NULL; | ||
| 633 | } | 636 | } |
| 634 | 637 | ||
| 635 | extern void perf_output_sample(struct perf_output_handle *handle, | 638 | extern void perf_output_sample(struct perf_output_handle *handle, |
diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 9d845404d875..9b79abbd1ab8 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h | |||
| @@ -137,8 +137,9 @@ enum perf_event_sample_format { | |||
| 137 | PERF_SAMPLE_DATA_SRC = 1U << 15, | 137 | PERF_SAMPLE_DATA_SRC = 1U << 15, |
| 138 | PERF_SAMPLE_IDENTIFIER = 1U << 16, | 138 | PERF_SAMPLE_IDENTIFIER = 1U << 16, |
| 139 | PERF_SAMPLE_TRANSACTION = 1U << 17, | 139 | PERF_SAMPLE_TRANSACTION = 1U << 17, |
| 140 | PERF_SAMPLE_REGS_INTR = 1U << 18, | ||
| 140 | 141 | ||
| 141 | PERF_SAMPLE_MAX = 1U << 18, /* non-ABI */ | 142 | PERF_SAMPLE_MAX = 1U << 19, /* non-ABI */ |
| 142 | }; | 143 | }; |
| 143 | 144 | ||
| 144 | /* | 145 | /* |
| @@ -238,6 +239,7 @@ enum perf_event_read_format { | |||
| 238 | #define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */ | 239 | #define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */ |
| 239 | #define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */ | 240 | #define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */ |
| 240 | /* add: sample_stack_user */ | 241 | /* add: sample_stack_user */ |
| 242 | #define PERF_ATTR_SIZE_VER4 104 /* add: sample_regs_intr */ | ||
| 241 | 243 | ||
| 242 | /* | 244 | /* |
| 243 | * Hardware event_id to monitor via a performance monitoring event: | 245 | * Hardware event_id to monitor via a performance monitoring event: |
| @@ -334,6 +336,15 @@ struct perf_event_attr { | |||
| 334 | 336 | ||
| 335 | /* Align to u64. */ | 337 | /* Align to u64. */ |
| 336 | __u32 __reserved_2; | 338 | __u32 __reserved_2; |
| 339 | /* | ||
| 340 | * Defines set of regs to dump for each sample | ||
| 341 | * state captured on: | ||
| 342 | * - precise = 0: PMU interrupt | ||
| 343 | * - precise > 0: sampled instruction | ||
| 344 | * | ||
| 345 | * See asm/perf_regs.h for details. | ||
| 346 | */ | ||
| 347 | __u64 sample_regs_intr; | ||
| 337 | }; | 348 | }; |
| 338 | 349 | ||
| 339 | #define perf_flags(attr) (*(&(attr)->read_format + 1)) | 350 | #define perf_flags(attr) (*(&(attr)->read_format + 1)) |
| @@ -686,6 +697,8 @@ enum perf_event_type { | |||
| 686 | * { u64 weight; } && PERF_SAMPLE_WEIGHT | 697 | * { u64 weight; } && PERF_SAMPLE_WEIGHT |
| 687 | * { u64 data_src; } && PERF_SAMPLE_DATA_SRC | 698 | * { u64 data_src; } && PERF_SAMPLE_DATA_SRC |
| 688 | * { u64 transaction; } && PERF_SAMPLE_TRANSACTION | 699 | * { u64 transaction; } && PERF_SAMPLE_TRANSACTION |
| 700 | * { u64 abi; # enum perf_sample_regs_abi | ||
| 701 | * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_INTR | ||
| 689 | * }; | 702 | * }; |
| 690 | */ | 703 | */ |
| 691 | PERF_RECORD_SAMPLE = 9, | 704 | PERF_RECORD_SAMPLE = 9, |
diff --git a/kernel/events/core.c b/kernel/events/core.c index 1cd5eef1fcdd..c2be1597ece7 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c | |||
| @@ -4460,7 +4460,7 @@ perf_output_sample_regs(struct perf_output_handle *handle, | |||
| 4460 | } | 4460 | } |
| 4461 | } | 4461 | } |
| 4462 | 4462 | ||
| 4463 | static void perf_sample_regs_user(struct perf_regs_user *regs_user, | 4463 | static void perf_sample_regs_user(struct perf_regs *regs_user, |
| 4464 | struct pt_regs *regs) | 4464 | struct pt_regs *regs) |
| 4465 | { | 4465 | { |
| 4466 | if (!user_mode(regs)) { | 4466 | if (!user_mode(regs)) { |
| @@ -4476,6 +4476,14 @@ static void perf_sample_regs_user(struct perf_regs_user *regs_user, | |||
| 4476 | } | 4476 | } |
| 4477 | } | 4477 | } |
| 4478 | 4478 | ||
| 4479 | static void perf_sample_regs_intr(struct perf_regs *regs_intr, | ||
| 4480 | struct pt_regs *regs) | ||
| 4481 | { | ||
| 4482 | regs_intr->regs = regs; | ||
| 4483 | regs_intr->abi = perf_reg_abi(current); | ||
| 4484 | } | ||
| 4485 | |||
| 4486 | |||
| 4479 | /* | 4487 | /* |
| 4480 | * Get remaining task size from user stack pointer. | 4488 | * Get remaining task size from user stack pointer. |
| 4481 | * | 4489 | * |
| @@ -4857,6 +4865,23 @@ void perf_output_sample(struct perf_output_handle *handle, | |||
| 4857 | if (sample_type & PERF_SAMPLE_TRANSACTION) | 4865 | if (sample_type & PERF_SAMPLE_TRANSACTION) |
| 4858 | perf_output_put(handle, data->txn); | 4866 | perf_output_put(handle, data->txn); |
| 4859 | 4867 | ||
| 4868 | if (sample_type & PERF_SAMPLE_REGS_INTR) { | ||
| 4869 | u64 abi = data->regs_intr.abi; | ||
| 4870 | /* | ||
| 4871 | * If there are no regs to dump, notice it through | ||
| 4872 | * first u64 being zero (PERF_SAMPLE_REGS_ABI_NONE). | ||
| 4873 | */ | ||
| 4874 | perf_output_put(handle, abi); | ||
| 4875 | |||
| 4876 | if (abi) { | ||
| 4877 | u64 mask = event->attr.sample_regs_intr; | ||
| 4878 | |||
| 4879 | perf_output_sample_regs(handle, | ||
| 4880 | data->regs_intr.regs, | ||
| 4881 | mask); | ||
| 4882 | } | ||
| 4883 | } | ||
| 4884 | |||
| 4860 | if (!event->attr.watermark) { | 4885 | if (!event->attr.watermark) { |
| 4861 | int wakeup_events = event->attr.wakeup_events; | 4886 | int wakeup_events = event->attr.wakeup_events; |
| 4862 | 4887 | ||
| @@ -4943,7 +4968,7 @@ void perf_prepare_sample(struct perf_event_header *header, | |||
| 4943 | * in case new sample type is added, because we could eat | 4968 | * in case new sample type is added, because we could eat |
| 4944 | * up the rest of the sample size. | 4969 | * up the rest of the sample size. |
| 4945 | */ | 4970 | */ |
| 4946 | struct perf_regs_user *uregs = &data->regs_user; | 4971 | struct perf_regs *uregs = &data->regs_user; |
| 4947 | u16 stack_size = event->attr.sample_stack_user; | 4972 | u16 stack_size = event->attr.sample_stack_user; |
| 4948 | u16 size = sizeof(u64); | 4973 | u16 size = sizeof(u64); |
| 4949 | 4974 | ||
| @@ -4964,6 +4989,21 @@ void perf_prepare_sample(struct perf_event_header *header, | |||
| 4964 | data->stack_user_size = stack_size; | 4989 | data->stack_user_size = stack_size; |
| 4965 | header->size += size; | 4990 | header->size += size; |
| 4966 | } | 4991 | } |
| 4992 | |||
| 4993 | if (sample_type & PERF_SAMPLE_REGS_INTR) { | ||
| 4994 | /* regs dump ABI info */ | ||
| 4995 | int size = sizeof(u64); | ||
| 4996 | |||
| 4997 | perf_sample_regs_intr(&data->regs_intr, regs); | ||
| 4998 | |||
| 4999 | if (data->regs_intr.regs) { | ||
| 5000 | u64 mask = event->attr.sample_regs_intr; | ||
| 5001 | |||
| 5002 | size += hweight64(mask) * sizeof(u64); | ||
| 5003 | } | ||
| 5004 | |||
| 5005 | header->size += size; | ||
| 5006 | } | ||
| 4967 | } | 5007 | } |
| 4968 | 5008 | ||
| 4969 | static void perf_event_output(struct perf_event *event, | 5009 | static void perf_event_output(struct perf_event *event, |
| @@ -7151,6 +7191,8 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr, | |||
| 7151 | ret = -EINVAL; | 7191 | ret = -EINVAL; |
| 7152 | } | 7192 | } |
| 7153 | 7193 | ||
| 7194 | if (attr->sample_type & PERF_SAMPLE_REGS_INTR) | ||
| 7195 | ret = perf_reg_validate(attr->sample_regs_intr); | ||
| 7154 | out: | 7196 | out: |
| 7155 | return ret; | 7197 | return ret; |
| 7156 | 7198 | ||
