aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/events
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/events')
-rw-r--r--kernel/events/core.c68
1 files changed, 68 insertions, 0 deletions
diff --git a/kernel/events/core.c b/kernel/events/core.c
index e8b32ac75ce3..5820efdf47cd 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -118,6 +118,13 @@ static int cpu_function_call(int cpu, int (*func) (void *info), void *info)
118 PERF_FLAG_FD_OUTPUT |\ 118 PERF_FLAG_FD_OUTPUT |\
119 PERF_FLAG_PID_CGROUP) 119 PERF_FLAG_PID_CGROUP)
120 120
121/*
122 * branch priv levels that need permission checks
123 */
124#define PERF_SAMPLE_BRANCH_PERM_PLM \
125 (PERF_SAMPLE_BRANCH_KERNEL |\
126 PERF_SAMPLE_BRANCH_HV)
127
121enum event_type_t { 128enum event_type_t {
122 EVENT_FLEXIBLE = 0x1, 129 EVENT_FLEXIBLE = 0x1,
123 EVENT_PINNED = 0x2, 130 EVENT_PINNED = 0x2,
@@ -3907,6 +3914,24 @@ void perf_output_sample(struct perf_output_handle *handle,
3907 } 3914 }
3908 } 3915 }
3909 } 3916 }
3917
3918 if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
3919 if (data->br_stack) {
3920 size_t size;
3921
3922 size = data->br_stack->nr
3923 * sizeof(struct perf_branch_entry);
3924
3925 perf_output_put(handle, data->br_stack->nr);
3926 perf_output_copy(handle, data->br_stack->entries, size);
3927 } else {
3928 /*
3929 * we always store at least the value of nr
3930 */
3931 u64 nr = 0;
3932 perf_output_put(handle, nr);
3933 }
3934 }
3910} 3935}
3911 3936
3912void perf_prepare_sample(struct perf_event_header *header, 3937void perf_prepare_sample(struct perf_event_header *header,
@@ -3949,6 +3974,15 @@ void perf_prepare_sample(struct perf_event_header *header,
3949 WARN_ON_ONCE(size & (sizeof(u64)-1)); 3974 WARN_ON_ONCE(size & (sizeof(u64)-1));
3950 header->size += size; 3975 header->size += size;
3951 } 3976 }
3977
3978 if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
3979 int size = sizeof(u64); /* nr */
3980 if (data->br_stack) {
3981 size += data->br_stack->nr
3982 * sizeof(struct perf_branch_entry);
3983 }
3984 header->size += size;
3985 }
3952} 3986}
3953 3987
3954static void perf_event_output(struct perf_event *event, 3988static void perf_event_output(struct perf_event *event,
@@ -5935,6 +5969,40 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,
5935 if (attr->read_format & ~(PERF_FORMAT_MAX-1)) 5969 if (attr->read_format & ~(PERF_FORMAT_MAX-1))
5936 return -EINVAL; 5970 return -EINVAL;
5937 5971
5972 if (attr->sample_type & PERF_SAMPLE_BRANCH_STACK) {
5973 u64 mask = attr->branch_sample_type;
5974
5975 /* only using defined bits */
5976 if (mask & ~(PERF_SAMPLE_BRANCH_MAX-1))
5977 return -EINVAL;
5978
5979 /* at least one branch bit must be set */
5980 if (!(mask & ~PERF_SAMPLE_BRANCH_PLM_ALL))
5981 return -EINVAL;
5982
5983 /* kernel level capture: check permissions */
5984 if ((mask & PERF_SAMPLE_BRANCH_PERM_PLM)
5985 && perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN))
5986 return -EACCES;
5987
5988 /* propagate priv level, when not set for branch */
5989 if (!(mask & PERF_SAMPLE_BRANCH_PLM_ALL)) {
5990
5991 /* exclude_kernel checked on syscall entry */
5992 if (!attr->exclude_kernel)
5993 mask |= PERF_SAMPLE_BRANCH_KERNEL;
5994
5995 if (!attr->exclude_user)
5996 mask |= PERF_SAMPLE_BRANCH_USER;
5997
5998 if (!attr->exclude_hv)
5999 mask |= PERF_SAMPLE_BRANCH_HV;
6000 /*
6001 * adjust user setting (for HW filter setup)
6002 */
6003 attr->branch_sample_type = mask;
6004 }
6005 }
5938out: 6006out:
5939 return ret; 6007 return ret;
5940 6008