static atomic_t nr_comm_counters __read_mostly;
/*
- * 0 - not paranoid
- * 1 - disallow cpu counters to unpriv
- * 2 - disallow kernel profiling to unpriv
+ * perf counter paranoia level:
+ * 0 - not paranoid
+ * 1 - disallow cpu counters to unpriv
+ * 2 - disallow kernel profiling to unpriv
*/
-int sysctl_perf_counter_paranoid __read_mostly; /* do we need to be privileged */
+int sysctl_perf_counter_paranoid __read_mostly;
static inline bool perf_paranoid_cpu(void)
{
}
int sysctl_perf_counter_mlock __read_mostly = 512; /* 'free' kb per user */
-int sysctl_perf_counter_limit __read_mostly = 100000; /* max NMIs per second */
+
+/*
+ * max perf counter sample rate
+ */
+int sysctl_perf_counter_sample_rate __read_mostly = 100000;
static atomic64_t perf_counter_id;
int do_switch = 1;
regs = task_pt_regs(task);
- perf_swcounter_event(PERF_COUNT_CONTEXT_SWITCHES, 1, 1, regs, 0);
+ perf_swcounter_event(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, 1, regs, 0);
if (likely(!ctx || !cpuctx->task_ctx))
return;
if (interrupts == MAX_INTERRUPTS) {
perf_log_throttle(counter, 1);
counter->pmu->unthrottle(counter);
- interrupts = 2*sysctl_perf_counter_limit/HZ;
+ interrupts = 2*sysctl_perf_counter_sample_rate/HZ;
}
if (!counter->attr.freq || !counter->attr.sample_freq)
spin_lock_irq(&ctx->lock);
if (counter->attr.freq) {
- if (value > sysctl_perf_counter_limit) {
+ if (value > sysctl_perf_counter_sample_rate) {
ret = -EINVAL;
goto unlock;
}
struct {
struct perf_event_header header;
u64 time;
+ u64 id;
} throttle_event = {
.header = {
.type = PERF_EVENT_THROTTLE + 1,
.misc = 0,
.size = sizeof(throttle_event),
},
- .time = sched_clock(),
+ .time = sched_clock(),
+ .id = counter->id,
};
ret = perf_output_begin(&handle, counter, sizeof(throttle_event), 1, 0);
} else {
if (hwc->interrupts != MAX_INTERRUPTS) {
hwc->interrupts++;
- if (HZ * hwc->interrupts > (u64)sysctl_perf_counter_limit) {
+ if (HZ * hwc->interrupts >
+ (u64)sysctl_perf_counter_sample_rate) {
hwc->interrupts = MAX_INTERRUPTS;
perf_log_throttle(counter, 0);
ret = 1;
}
static int perf_swcounter_match(struct perf_counter *counter,
- enum perf_event_types type,
+ enum perf_type_id type,
u32 event, struct pt_regs *regs)
{
if (!perf_swcounter_is_counting(counter))
}
static void perf_swcounter_ctx_event(struct perf_counter_context *ctx,
- enum perf_event_types type, u32 event,
+ enum perf_type_id type, u32 event,
u64 nr, int nmi, struct pt_regs *regs,
u64 addr)
{
return &cpuctx->recursion[0];
}
-static void __perf_swcounter_event(enum perf_event_types type, u32 event,
+static void __perf_swcounter_event(enum perf_type_id type, u32 event,
u64 nr, int nmi, struct pt_regs *regs,
u64 addr)
{
struct perf_counter_context *ctx;
perf_swcounter_ctx_event(&cpuctx->ctx, PERF_TYPE_SOFTWARE,
- PERF_COUNT_CPU_MIGRATIONS,
+ PERF_COUNT_SW_CPU_MIGRATIONS,
1, 1, NULL, 0);
ctx = perf_pin_task_context(task);
if (ctx) {
perf_swcounter_ctx_event(ctx, PERF_TYPE_SOFTWARE,
- PERF_COUNT_CPU_MIGRATIONS,
+ PERF_COUNT_SW_CPU_MIGRATIONS,
1, 1, NULL, 0);
perf_unpin_context(ctx);
}
* events.
*/
switch (counter->attr.config) {
- case PERF_COUNT_CPU_CLOCK:
+ case PERF_COUNT_SW_CPU_CLOCK:
pmu = &perf_ops_cpu_clock;
break;
- case PERF_COUNT_TASK_CLOCK:
+ case PERF_COUNT_SW_TASK_CLOCK:
/*
* If the user instantiates this as a per-cpu counter,
* use the cpu_clock counter instead.
pmu = &perf_ops_cpu_clock;
break;
- case PERF_COUNT_PAGE_FAULTS:
- case PERF_COUNT_PAGE_FAULTS_MIN:
- case PERF_COUNT_PAGE_FAULTS_MAJ:
- case PERF_COUNT_CONTEXT_SWITCHES:
- case PERF_COUNT_CPU_MIGRATIONS:
+ case PERF_COUNT_SW_PAGE_FAULTS:
+ case PERF_COUNT_SW_PAGE_FAULTS_MIN:
+ case PERF_COUNT_SW_PAGE_FAULTS_MAJ:
+ case PERF_COUNT_SW_CONTEXT_SWITCHES:
+ case PERF_COUNT_SW_CPU_MIGRATIONS:
pmu = &perf_ops_generic;
break;
}
if (attr->inherit && (attr->sample_type & PERF_SAMPLE_GROUP))
goto done;
- if (attr->type == PERF_TYPE_RAW) {
- pmu = hw_perf_counter_init(counter);
- goto done;
- }
-
switch (attr->type) {
+ case PERF_TYPE_RAW:
case PERF_TYPE_HARDWARE:
case PERF_TYPE_HW_CACHE:
pmu = hw_perf_counter_init(counter);
case PERF_TYPE_TRACEPOINT:
pmu = tp_perf_counter_init(counter);
break;
+
+ default:
+ break;
}
done:
err = 0;
return counter;
}
+static int perf_copy_attr(struct perf_counter_attr __user *uattr,
+ struct perf_counter_attr *attr)
+{
+ int ret;
+ u32 size;
+
+ if (!access_ok(VERIFY_WRITE, uattr, PERF_ATTR_SIZE_VER0))
+ return -EFAULT;
+
+ /*
+ * zero the full structure, so that a short copy will be nice.
+ */
+ memset(attr, 0, sizeof(*attr));
+
+ ret = get_user(size, &uattr->size);
+ if (ret)
+ return ret;
+
+ if (size > PAGE_SIZE) /* silly large */
+ goto err_size;
+
+ if (!size) /* abi compat */
+ size = PERF_ATTR_SIZE_VER0;
+
+ if (size < PERF_ATTR_SIZE_VER0)
+ goto err_size;
+
+ /*
+ * If we're handed a bigger struct than we know of,
+ * ensure all the unknown bits are 0.
+ */
+ if (size > sizeof(*attr)) {
+ unsigned long val;
+ unsigned long __user *addr;
+ unsigned long __user *end;
+
+ addr = PTR_ALIGN((void __user *)uattr + sizeof(*attr),
+ sizeof(unsigned long));
+ end = PTR_ALIGN((void __user *)uattr + size,
+ sizeof(unsigned long));
+
+ for (; addr < end; addr += sizeof(unsigned long)) {
+ ret = get_user(val, addr);
+ if (ret)
+ return ret;
+ if (val)
+ goto err_size;
+ }
+ }
+
+ ret = copy_from_user(attr, uattr, size);
+ if (ret)
+ return -EFAULT;
+
+ /*
+ * If the type exists, the corresponding creation will verify
+ * the attr->config.
+ */
+ if (attr->type >= PERF_TYPE_MAX)
+ return -EINVAL;
+
+ if (attr->__reserved_1 || attr->__reserved_2 || attr->__reserved_3)
+ return -EINVAL;
+
+ if (attr->sample_type & ~(PERF_SAMPLE_MAX-1))
+ return -EINVAL;
+
+ if (attr->read_format & ~(PERF_FORMAT_MAX-1))
+ return -EINVAL;
+
+out:
+ return ret;
+
+err_size:
+ put_user(sizeof(*attr), &uattr->size);
+ ret = -E2BIG;
+ goto out;
+}
+
/**
* sys_perf_counter_open - open a performance counter, associate it to a task/cpu
*
* @group_fd: group leader counter fd
*/
SYSCALL_DEFINE5(perf_counter_open,
- const struct perf_counter_attr __user *, attr_uptr,
+ struct perf_counter_attr __user *, attr_uptr,
pid_t, pid, int, cpu, int, group_fd, unsigned long, flags)
{
struct perf_counter *counter, *group_leader;
if (flags)
return -EINVAL;
- if (copy_from_user(&attr, attr_uptr, sizeof(attr)) != 0)
- return -EFAULT;
+ ret = perf_copy_attr(attr_uptr, &attr);
+ if (ret)
+ return ret;
if (!attr.exclude_kernel) {
if (perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN))
return -EACCES;
}
+ if (attr.freq) {
+ if (attr.sample_freq > sysctl_perf_counter_sample_rate)
+ return -EINVAL;
+ }
+
/*
* Get the target context (task or percpu):
*/