Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
* 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (106 commits) perf kvm: Fix copy & paste error in description perf script: Kill script_spec__delete perf top: Fix a memory leak perf stat: Introduce get_ratio_color() helper perf session: Remove impossible condition check perf tools: Fix feature-bits rework fallout, remove unused variable perf script: Add generic perl handler to process events perf tools: Use for_each_set_bit() to iterate over feature flags perf tools: Unify handling of features when writing feature section perf report: Accept fifos as input file perf tools: Moving code in some files perf tools: Fix out-of-bound access to struct perf_session perf tools: Continue processing header on unknown features perf tools: Improve macros for struct feature_ops perf: builtin-record: Document and check that mmap_pages must be a power of two. perf: builtin-record: Provide advice if mmap'ing fails with EPERM. perf tools: Fix truncated annotation perf script: look up thread using tid instead of pid perf tools: Look up thread names for system wide profiling perf tools: Fix comm for processes with named threads ...
This commit is contained in:
@@ -2,5 +2,5 @@ ifdef CONFIG_FUNCTION_TRACER
|
||||
CFLAGS_REMOVE_core.o = -pg
|
||||
endif
|
||||
|
||||
obj-y := core.o ring_buffer.o
|
||||
obj-y := core.o ring_buffer.o callchain.o
|
||||
obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
|
||||
|
||||
191
kernel/events/callchain.c
Normal file
191
kernel/events/callchain.c
Normal file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Performance events callchain code, extracted from core.c:
|
||||
*
|
||||
* Copyright (C) 2008 Thomas Gleixner <tglx@linutronix.de>
|
||||
* Copyright (C) 2008-2011 Red Hat, Inc., Ingo Molnar
|
||||
* Copyright (C) 2008-2011 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
|
||||
* Copyright <20> 2009 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com>
|
||||
*
|
||||
* For licensing details see kernel-base/COPYING
|
||||
*/
|
||||
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/slab.h>
|
||||
#include "internal.h"
|
||||
|
||||
struct callchain_cpus_entries {
|
||||
struct rcu_head rcu_head;
|
||||
struct perf_callchain_entry *cpu_entries[0];
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(int, callchain_recursion[PERF_NR_CONTEXTS]);
|
||||
static atomic_t nr_callchain_events;
|
||||
static DEFINE_MUTEX(callchain_mutex);
|
||||
static struct callchain_cpus_entries *callchain_cpus_entries;
|
||||
|
||||
|
||||
__weak void perf_callchain_kernel(struct perf_callchain_entry *entry,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
}
|
||||
|
||||
__weak void perf_callchain_user(struct perf_callchain_entry *entry,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
}
|
||||
|
||||
static void release_callchain_buffers_rcu(struct rcu_head *head)
|
||||
{
|
||||
struct callchain_cpus_entries *entries;
|
||||
int cpu;
|
||||
|
||||
entries = container_of(head, struct callchain_cpus_entries, rcu_head);
|
||||
|
||||
for_each_possible_cpu(cpu)
|
||||
kfree(entries->cpu_entries[cpu]);
|
||||
|
||||
kfree(entries);
|
||||
}
|
||||
|
||||
static void release_callchain_buffers(void)
|
||||
{
|
||||
struct callchain_cpus_entries *entries;
|
||||
|
||||
entries = callchain_cpus_entries;
|
||||
rcu_assign_pointer(callchain_cpus_entries, NULL);
|
||||
call_rcu(&entries->rcu_head, release_callchain_buffers_rcu);
|
||||
}
|
||||
|
||||
static int alloc_callchain_buffers(void)
|
||||
{
|
||||
int cpu;
|
||||
int size;
|
||||
struct callchain_cpus_entries *entries;
|
||||
|
||||
/*
|
||||
* We can't use the percpu allocation API for data that can be
|
||||
* accessed from NMI. Use a temporary manual per cpu allocation
|
||||
* until that gets sorted out.
|
||||
*/
|
||||
size = offsetof(struct callchain_cpus_entries, cpu_entries[nr_cpu_ids]);
|
||||
|
||||
entries = kzalloc(size, GFP_KERNEL);
|
||||
if (!entries)
|
||||
return -ENOMEM;
|
||||
|
||||
size = sizeof(struct perf_callchain_entry) * PERF_NR_CONTEXTS;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
entries->cpu_entries[cpu] = kmalloc_node(size, GFP_KERNEL,
|
||||
cpu_to_node(cpu));
|
||||
if (!entries->cpu_entries[cpu])
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rcu_assign_pointer(callchain_cpus_entries, entries);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
for_each_possible_cpu(cpu)
|
||||
kfree(entries->cpu_entries[cpu]);
|
||||
kfree(entries);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int get_callchain_buffers(void)
|
||||
{
|
||||
int err = 0;
|
||||
int count;
|
||||
|
||||
mutex_lock(&callchain_mutex);
|
||||
|
||||
count = atomic_inc_return(&nr_callchain_events);
|
||||
if (WARN_ON_ONCE(count < 1)) {
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (count > 1) {
|
||||
/* If the allocation failed, give up */
|
||||
if (!callchain_cpus_entries)
|
||||
err = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
err = alloc_callchain_buffers();
|
||||
if (err)
|
||||
release_callchain_buffers();
|
||||
exit:
|
||||
mutex_unlock(&callchain_mutex);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void put_callchain_buffers(void)
|
||||
{
|
||||
if (atomic_dec_and_mutex_lock(&nr_callchain_events, &callchain_mutex)) {
|
||||
release_callchain_buffers();
|
||||
mutex_unlock(&callchain_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static struct perf_callchain_entry *get_callchain_entry(int *rctx)
|
||||
{
|
||||
int cpu;
|
||||
struct callchain_cpus_entries *entries;
|
||||
|
||||
*rctx = get_recursion_context(__get_cpu_var(callchain_recursion));
|
||||
if (*rctx == -1)
|
||||
return NULL;
|
||||
|
||||
entries = rcu_dereference(callchain_cpus_entries);
|
||||
if (!entries)
|
||||
return NULL;
|
||||
|
||||
cpu = smp_processor_id();
|
||||
|
||||
return &entries->cpu_entries[cpu][*rctx];
|
||||
}
|
||||
|
||||
static void
|
||||
put_callchain_entry(int rctx)
|
||||
{
|
||||
put_recursion_context(__get_cpu_var(callchain_recursion), rctx);
|
||||
}
|
||||
|
||||
struct perf_callchain_entry *perf_callchain(struct pt_regs *regs)
|
||||
{
|
||||
int rctx;
|
||||
struct perf_callchain_entry *entry;
|
||||
|
||||
|
||||
entry = get_callchain_entry(&rctx);
|
||||
if (rctx == -1)
|
||||
return NULL;
|
||||
|
||||
if (!entry)
|
||||
goto exit_put;
|
||||
|
||||
entry->nr = 0;
|
||||
|
||||
if (!user_mode(regs)) {
|
||||
perf_callchain_store(entry, PERF_CONTEXT_KERNEL);
|
||||
perf_callchain_kernel(entry, regs);
|
||||
if (current->mm)
|
||||
regs = task_pt_regs(current);
|
||||
else
|
||||
regs = NULL;
|
||||
}
|
||||
|
||||
if (regs) {
|
||||
perf_callchain_store(entry, PERF_CONTEXT_USER);
|
||||
perf_callchain_user(entry, regs);
|
||||
}
|
||||
|
||||
exit_put:
|
||||
put_callchain_entry(rctx);
|
||||
|
||||
return entry;
|
||||
}
|
||||
@@ -128,7 +128,7 @@ enum event_type_t {
|
||||
* perf_sched_events : >0 events exist
|
||||
* perf_cgroup_events: >0 per-cpu cgroup events exist on this cpu
|
||||
*/
|
||||
struct jump_label_key perf_sched_events __read_mostly;
|
||||
struct jump_label_key_deferred perf_sched_events __read_mostly;
|
||||
static DEFINE_PER_CPU(atomic_t, perf_cgroup_events);
|
||||
|
||||
static atomic_t nr_mmap_events __read_mostly;
|
||||
@@ -1130,6 +1130,8 @@ event_sched_out(struct perf_event *event,
|
||||
if (!is_software_event(event))
|
||||
cpuctx->active_oncpu--;
|
||||
ctx->nr_active--;
|
||||
if (event->attr.freq && event->attr.sample_freq)
|
||||
ctx->nr_freq--;
|
||||
if (event->attr.exclusive || !cpuctx->active_oncpu)
|
||||
cpuctx->exclusive = 0;
|
||||
}
|
||||
@@ -1325,6 +1327,7 @@ retry:
|
||||
}
|
||||
raw_spin_unlock_irq(&ctx->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(perf_event_disable);
|
||||
|
||||
static void perf_set_shadow_time(struct perf_event *event,
|
||||
struct perf_event_context *ctx,
|
||||
@@ -1406,6 +1409,8 @@ event_sched_in(struct perf_event *event,
|
||||
if (!is_software_event(event))
|
||||
cpuctx->active_oncpu++;
|
||||
ctx->nr_active++;
|
||||
if (event->attr.freq && event->attr.sample_freq)
|
||||
ctx->nr_freq++;
|
||||
|
||||
if (event->attr.exclusive)
|
||||
cpuctx->exclusive = 1;
|
||||
@@ -1662,8 +1667,7 @@ retry:
|
||||
* Note: this works for group members as well as group leaders
|
||||
* since the non-leader members' sibling_lists will be empty.
|
||||
*/
|
||||
static void __perf_event_mark_enabled(struct perf_event *event,
|
||||
struct perf_event_context *ctx)
|
||||
static void __perf_event_mark_enabled(struct perf_event *event)
|
||||
{
|
||||
struct perf_event *sub;
|
||||
u64 tstamp = perf_event_time(event);
|
||||
@@ -1701,7 +1705,7 @@ static int __perf_event_enable(void *info)
|
||||
*/
|
||||
perf_cgroup_set_timestamp(current, ctx);
|
||||
|
||||
__perf_event_mark_enabled(event, ctx);
|
||||
__perf_event_mark_enabled(event);
|
||||
|
||||
if (!event_filter_match(event)) {
|
||||
if (is_cgroup_event(event))
|
||||
@@ -1782,7 +1786,7 @@ void perf_event_enable(struct perf_event *event)
|
||||
|
||||
retry:
|
||||
if (!ctx->is_active) {
|
||||
__perf_event_mark_enabled(event, ctx);
|
||||
__perf_event_mark_enabled(event);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -1809,6 +1813,7 @@ retry:
|
||||
out:
|
||||
raw_spin_unlock_irq(&ctx->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(perf_event_enable);
|
||||
|
||||
int perf_event_refresh(struct perf_event *event, int refresh)
|
||||
{
|
||||
@@ -2327,6 +2332,9 @@ static void perf_ctx_adjust_freq(struct perf_event_context *ctx, u64 period)
|
||||
u64 interrupts, now;
|
||||
s64 delta;
|
||||
|
||||
if (!ctx->nr_freq)
|
||||
return;
|
||||
|
||||
list_for_each_entry_rcu(event, &ctx->event_list, event_entry) {
|
||||
if (event->state != PERF_EVENT_STATE_ACTIVE)
|
||||
continue;
|
||||
@@ -2382,12 +2390,14 @@ static void perf_rotate_context(struct perf_cpu_context *cpuctx)
|
||||
{
|
||||
u64 interval = (u64)cpuctx->jiffies_interval * TICK_NSEC;
|
||||
struct perf_event_context *ctx = NULL;
|
||||
int rotate = 0, remove = 1;
|
||||
int rotate = 0, remove = 1, freq = 0;
|
||||
|
||||
if (cpuctx->ctx.nr_events) {
|
||||
remove = 0;
|
||||
if (cpuctx->ctx.nr_events != cpuctx->ctx.nr_active)
|
||||
rotate = 1;
|
||||
if (cpuctx->ctx.nr_freq)
|
||||
freq = 1;
|
||||
}
|
||||
|
||||
ctx = cpuctx->task_ctx;
|
||||
@@ -2395,33 +2405,40 @@ static void perf_rotate_context(struct perf_cpu_context *cpuctx)
|
||||
remove = 0;
|
||||
if (ctx->nr_events != ctx->nr_active)
|
||||
rotate = 1;
|
||||
if (ctx->nr_freq)
|
||||
freq = 1;
|
||||
}
|
||||
|
||||
if (!rotate && !freq)
|
||||
goto done;
|
||||
|
||||
perf_ctx_lock(cpuctx, cpuctx->task_ctx);
|
||||
perf_pmu_disable(cpuctx->ctx.pmu);
|
||||
perf_ctx_adjust_freq(&cpuctx->ctx, interval);
|
||||
if (ctx)
|
||||
perf_ctx_adjust_freq(ctx, interval);
|
||||
|
||||
if (!rotate)
|
||||
goto done;
|
||||
if (freq) {
|
||||
perf_ctx_adjust_freq(&cpuctx->ctx, interval);
|
||||
if (ctx)
|
||||
perf_ctx_adjust_freq(ctx, interval);
|
||||
}
|
||||
|
||||
cpu_ctx_sched_out(cpuctx, EVENT_FLEXIBLE);
|
||||
if (ctx)
|
||||
ctx_sched_out(ctx, cpuctx, EVENT_FLEXIBLE);
|
||||
if (rotate) {
|
||||
cpu_ctx_sched_out(cpuctx, EVENT_FLEXIBLE);
|
||||
if (ctx)
|
||||
ctx_sched_out(ctx, cpuctx, EVENT_FLEXIBLE);
|
||||
|
||||
rotate_ctx(&cpuctx->ctx);
|
||||
if (ctx)
|
||||
rotate_ctx(ctx);
|
||||
rotate_ctx(&cpuctx->ctx);
|
||||
if (ctx)
|
||||
rotate_ctx(ctx);
|
||||
|
||||
perf_event_sched_in(cpuctx, ctx, current);
|
||||
perf_event_sched_in(cpuctx, ctx, current);
|
||||
}
|
||||
|
||||
perf_pmu_enable(cpuctx->ctx.pmu);
|
||||
perf_ctx_unlock(cpuctx, cpuctx->task_ctx);
|
||||
|
||||
done:
|
||||
if (remove)
|
||||
list_del_init(&cpuctx->rotation_list);
|
||||
|
||||
perf_pmu_enable(cpuctx->ctx.pmu);
|
||||
perf_ctx_unlock(cpuctx, cpuctx->task_ctx);
|
||||
}
|
||||
|
||||
void perf_event_task_tick(void)
|
||||
@@ -2448,7 +2465,7 @@ static int event_enable_on_exec(struct perf_event *event,
|
||||
if (event->state >= PERF_EVENT_STATE_INACTIVE)
|
||||
return 0;
|
||||
|
||||
__perf_event_mark_enabled(event, ctx);
|
||||
__perf_event_mark_enabled(event);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -2480,13 +2497,7 @@ static void perf_event_enable_on_exec(struct perf_event_context *ctx)
|
||||
raw_spin_lock(&ctx->lock);
|
||||
task_ctx_sched_out(ctx);
|
||||
|
||||
list_for_each_entry(event, &ctx->pinned_groups, group_entry) {
|
||||
ret = event_enable_on_exec(event, ctx);
|
||||
if (ret)
|
||||
enabled = 1;
|
||||
}
|
||||
|
||||
list_for_each_entry(event, &ctx->flexible_groups, group_entry) {
|
||||
list_for_each_entry(event, &ctx->event_list, event_entry) {
|
||||
ret = event_enable_on_exec(event, ctx);
|
||||
if (ret)
|
||||
enabled = 1;
|
||||
@@ -2573,215 +2584,6 @@ static u64 perf_event_read(struct perf_event *event)
|
||||
return perf_event_count(event);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callchain support
|
||||
*/
|
||||
|
||||
struct callchain_cpus_entries {
|
||||
struct rcu_head rcu_head;
|
||||
struct perf_callchain_entry *cpu_entries[0];
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(int, callchain_recursion[PERF_NR_CONTEXTS]);
|
||||
static atomic_t nr_callchain_events;
|
||||
static DEFINE_MUTEX(callchain_mutex);
|
||||
struct callchain_cpus_entries *callchain_cpus_entries;
|
||||
|
||||
|
||||
__weak void perf_callchain_kernel(struct perf_callchain_entry *entry,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
}
|
||||
|
||||
__weak void perf_callchain_user(struct perf_callchain_entry *entry,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
}
|
||||
|
||||
static void release_callchain_buffers_rcu(struct rcu_head *head)
|
||||
{
|
||||
struct callchain_cpus_entries *entries;
|
||||
int cpu;
|
||||
|
||||
entries = container_of(head, struct callchain_cpus_entries, rcu_head);
|
||||
|
||||
for_each_possible_cpu(cpu)
|
||||
kfree(entries->cpu_entries[cpu]);
|
||||
|
||||
kfree(entries);
|
||||
}
|
||||
|
||||
static void release_callchain_buffers(void)
|
||||
{
|
||||
struct callchain_cpus_entries *entries;
|
||||
|
||||
entries = callchain_cpus_entries;
|
||||
rcu_assign_pointer(callchain_cpus_entries, NULL);
|
||||
call_rcu(&entries->rcu_head, release_callchain_buffers_rcu);
|
||||
}
|
||||
|
||||
static int alloc_callchain_buffers(void)
|
||||
{
|
||||
int cpu;
|
||||
int size;
|
||||
struct callchain_cpus_entries *entries;
|
||||
|
||||
/*
|
||||
* We can't use the percpu allocation API for data that can be
|
||||
* accessed from NMI. Use a temporary manual per cpu allocation
|
||||
* until that gets sorted out.
|
||||
*/
|
||||
size = offsetof(struct callchain_cpus_entries, cpu_entries[nr_cpu_ids]);
|
||||
|
||||
entries = kzalloc(size, GFP_KERNEL);
|
||||
if (!entries)
|
||||
return -ENOMEM;
|
||||
|
||||
size = sizeof(struct perf_callchain_entry) * PERF_NR_CONTEXTS;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
entries->cpu_entries[cpu] = kmalloc_node(size, GFP_KERNEL,
|
||||
cpu_to_node(cpu));
|
||||
if (!entries->cpu_entries[cpu])
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rcu_assign_pointer(callchain_cpus_entries, entries);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
for_each_possible_cpu(cpu)
|
||||
kfree(entries->cpu_entries[cpu]);
|
||||
kfree(entries);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int get_callchain_buffers(void)
|
||||
{
|
||||
int err = 0;
|
||||
int count;
|
||||
|
||||
mutex_lock(&callchain_mutex);
|
||||
|
||||
count = atomic_inc_return(&nr_callchain_events);
|
||||
if (WARN_ON_ONCE(count < 1)) {
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (count > 1) {
|
||||
/* If the allocation failed, give up */
|
||||
if (!callchain_cpus_entries)
|
||||
err = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
err = alloc_callchain_buffers();
|
||||
if (err)
|
||||
release_callchain_buffers();
|
||||
exit:
|
||||
mutex_unlock(&callchain_mutex);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void put_callchain_buffers(void)
|
||||
{
|
||||
if (atomic_dec_and_mutex_lock(&nr_callchain_events, &callchain_mutex)) {
|
||||
release_callchain_buffers();
|
||||
mutex_unlock(&callchain_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static int get_recursion_context(int *recursion)
|
||||
{
|
||||
int rctx;
|
||||
|
||||
if (in_nmi())
|
||||
rctx = 3;
|
||||
else if (in_irq())
|
||||
rctx = 2;
|
||||
else if (in_softirq())
|
||||
rctx = 1;
|
||||
else
|
||||
rctx = 0;
|
||||
|
||||
if (recursion[rctx])
|
||||
return -1;
|
||||
|
||||
recursion[rctx]++;
|
||||
barrier();
|
||||
|
||||
return rctx;
|
||||
}
|
||||
|
||||
static inline void put_recursion_context(int *recursion, int rctx)
|
||||
{
|
||||
barrier();
|
||||
recursion[rctx]--;
|
||||
}
|
||||
|
||||
static struct perf_callchain_entry *get_callchain_entry(int *rctx)
|
||||
{
|
||||
int cpu;
|
||||
struct callchain_cpus_entries *entries;
|
||||
|
||||
*rctx = get_recursion_context(__get_cpu_var(callchain_recursion));
|
||||
if (*rctx == -1)
|
||||
return NULL;
|
||||
|
||||
entries = rcu_dereference(callchain_cpus_entries);
|
||||
if (!entries)
|
||||
return NULL;
|
||||
|
||||
cpu = smp_processor_id();
|
||||
|
||||
return &entries->cpu_entries[cpu][*rctx];
|
||||
}
|
||||
|
||||
static void
|
||||
put_callchain_entry(int rctx)
|
||||
{
|
||||
put_recursion_context(__get_cpu_var(callchain_recursion), rctx);
|
||||
}
|
||||
|
||||
static struct perf_callchain_entry *perf_callchain(struct pt_regs *regs)
|
||||
{
|
||||
int rctx;
|
||||
struct perf_callchain_entry *entry;
|
||||
|
||||
|
||||
entry = get_callchain_entry(&rctx);
|
||||
if (rctx == -1)
|
||||
return NULL;
|
||||
|
||||
if (!entry)
|
||||
goto exit_put;
|
||||
|
||||
entry->nr = 0;
|
||||
|
||||
if (!user_mode(regs)) {
|
||||
perf_callchain_store(entry, PERF_CONTEXT_KERNEL);
|
||||
perf_callchain_kernel(entry, regs);
|
||||
if (current->mm)
|
||||
regs = task_pt_regs(current);
|
||||
else
|
||||
regs = NULL;
|
||||
}
|
||||
|
||||
if (regs) {
|
||||
perf_callchain_store(entry, PERF_CONTEXT_USER);
|
||||
perf_callchain_user(entry, regs);
|
||||
}
|
||||
|
||||
exit_put:
|
||||
put_callchain_entry(rctx);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the perf_event context in a task_struct:
|
||||
*/
|
||||
@@ -2946,7 +2748,7 @@ static void free_event(struct perf_event *event)
|
||||
|
||||
if (!event->parent) {
|
||||
if (event->attach_state & PERF_ATTACH_TASK)
|
||||
jump_label_dec(&perf_sched_events);
|
||||
jump_label_dec_deferred(&perf_sched_events);
|
||||
if (event->attr.mmap || event->attr.mmap_data)
|
||||
atomic_dec(&nr_mmap_events);
|
||||
if (event->attr.comm)
|
||||
@@ -2957,7 +2759,7 @@ static void free_event(struct perf_event *event)
|
||||
put_callchain_buffers();
|
||||
if (is_cgroup_event(event)) {
|
||||
atomic_dec(&per_cpu(perf_cgroup_events, event->cpu));
|
||||
jump_label_dec(&perf_sched_events);
|
||||
jump_label_dec_deferred(&perf_sched_events);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4820,7 +4622,6 @@ static void perf_swevent_overflow(struct perf_event *event, u64 overflow,
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
int throttle = 0;
|
||||
|
||||
data->period = event->hw.last_period;
|
||||
if (!overflow)
|
||||
overflow = perf_swevent_set_period(event);
|
||||
|
||||
@@ -4854,6 +4655,12 @@ static void perf_swevent_event(struct perf_event *event, u64 nr,
|
||||
if (!is_sampling_event(event))
|
||||
return;
|
||||
|
||||
if ((event->attr.sample_type & PERF_SAMPLE_PERIOD) && !event->attr.freq) {
|
||||
data->period = nr;
|
||||
return perf_swevent_overflow(event, 1, data, regs);
|
||||
} else
|
||||
data->period = event->hw.last_period;
|
||||
|
||||
if (nr == 1 && hwc->sample_period == 1 && !event->attr.freq)
|
||||
return perf_swevent_overflow(event, 1, data, regs);
|
||||
|
||||
@@ -5981,7 +5788,7 @@ done:
|
||||
|
||||
if (!event->parent) {
|
||||
if (event->attach_state & PERF_ATTACH_TASK)
|
||||
jump_label_inc(&perf_sched_events);
|
||||
jump_label_inc(&perf_sched_events.key);
|
||||
if (event->attr.mmap || event->attr.mmap_data)
|
||||
atomic_inc(&nr_mmap_events);
|
||||
if (event->attr.comm)
|
||||
@@ -6219,7 +6026,7 @@ SYSCALL_DEFINE5(perf_event_open,
|
||||
* - that may need work on context switch
|
||||
*/
|
||||
atomic_inc(&per_cpu(perf_cgroup_events, event->cpu));
|
||||
jump_label_inc(&perf_sched_events);
|
||||
jump_label_inc(&perf_sched_events.key);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -7065,6 +6872,9 @@ void __init perf_event_init(void)
|
||||
|
||||
ret = init_hw_breakpoint();
|
||||
WARN(ret, "hw_breakpoint initialization failed with: %d", ret);
|
||||
|
||||
/* do not patch jump label more than once per second */
|
||||
jump_label_rate_limit(&perf_sched_events, HZ);
|
||||
}
|
||||
|
||||
static int __init perf_event_sysfs_init(void)
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#ifndef _KERNEL_EVENTS_INTERNAL_H
|
||||
#define _KERNEL_EVENTS_INTERNAL_H
|
||||
|
||||
#include <linux/hardirq.h>
|
||||
|
||||
/* Buffer handling */
|
||||
|
||||
#define RING_BUFFER_WRITABLE 0x01
|
||||
|
||||
struct ring_buffer {
|
||||
@@ -67,7 +71,7 @@ static inline int page_order(struct ring_buffer *rb)
|
||||
}
|
||||
#endif
|
||||
|
||||
static unsigned long perf_data_size(struct ring_buffer *rb)
|
||||
static inline unsigned long perf_data_size(struct ring_buffer *rb)
|
||||
{
|
||||
return rb->nr_pages << (PAGE_SHIFT + page_order(rb));
|
||||
}
|
||||
@@ -96,4 +100,37 @@ __output_copy(struct perf_output_handle *handle,
|
||||
} while (len);
|
||||
}
|
||||
|
||||
/* Callchain handling */
|
||||
extern struct perf_callchain_entry *perf_callchain(struct pt_regs *regs);
|
||||
extern int get_callchain_buffers(void);
|
||||
extern void put_callchain_buffers(void);
|
||||
|
||||
static inline int get_recursion_context(int *recursion)
|
||||
{
|
||||
int rctx;
|
||||
|
||||
if (in_nmi())
|
||||
rctx = 3;
|
||||
else if (in_irq())
|
||||
rctx = 2;
|
||||
else if (in_softirq())
|
||||
rctx = 1;
|
||||
else
|
||||
rctx = 0;
|
||||
|
||||
if (recursion[rctx])
|
||||
return -1;
|
||||
|
||||
recursion[rctx]++;
|
||||
barrier();
|
||||
|
||||
return rctx;
|
||||
}
|
||||
|
||||
static inline void put_recursion_context(int *recursion, int rctx)
|
||||
{
|
||||
barrier();
|
||||
recursion[rctx]--;
|
||||
}
|
||||
|
||||
#endif /* _KERNEL_EVENTS_INTERNAL_H */
|
||||
|
||||
Reference in New Issue
Block a user