mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 03:36:19 +00:00
perf_counter: Dynamically allocate tasks' perf_counter_context struct
This replaces the struct perf_counter_context in the task_struct with a pointer to a dynamically allocated perf_counter_context struct. The main reason for doing is this is to allow us to transfer a perf_counter_context from one task to another when we do lazy PMU switching in a later patch. This has a few side-benefits: the task_struct becomes a little smaller, we save some memory because only tasks that have perf_counters attached get a perf_counter_context allocated for them, and we can remove the inclusion of <linux/perf_counter.h> in sched.h, meaning that we don't end up recompiling nearly everything whenever perf_counter.h changes. The perf_counter_context structures are reference-counted and freed when the last reference is dropped. A context can have references from its task and the counters on its task. Counters can outlive the task so it is possible that a context will be freed well after its task has exited. Contexts are allocated on fork if the parent had a context, or otherwise the first time that a per-task counter is created on a task. In the latter case, we set the context pointer in the task struct locklessly using an atomic compare-and-exchange operation in case we raced with some other task in creating a context for the subject task. This also removes the task pointer from the perf_counter struct. The task pointer was not used anywhere and would make it harder to move a context from one task to another. Anything that needed to know which task a counter was attached to was already using counter->ctx->task. The __perf_counter_init_context function moves up in perf_counter.c so that it can be called from find_get_context, and now initializes the refcount, but is otherwise unchanged. We were potentially calling list_del_counter twice: once from __perf_counter_exit_task when the task exits and once from __perf_counter_remove_from_context when the counter's fd gets closed. This adds a check in list_del_counter so it doesn't do anything if the counter has already been removed from the lists. Since perf_counter_task_sched_in doesn't do anything if the task doesn't have a context, and leaves cpuctx->task_ctx = NULL, this adds code to __perf_install_in_context to set cpuctx->task_ctx if necessary, i.e. in the case where the current task adds the first counter to itself and thus creates a context for itself. This also adds similar code to __perf_counter_enable to handle a similar situation which can arise when the counters have been disabled using prctl; that also leaves cpuctx->task_ctx = NULL. [ Impact: refactor counter context management to prepare for new feature ] Signed-off-by: Paul Mackerras <paulus@samba.org> Acked-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com> Cc: Marcelo Tosatti <mtosatti@redhat.com> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> LKML-Reference: <18966.10075.781053.231153@cargo.ozlabs.ibm.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
parent
34adc80622
commit
a63eaf34ae
7 changed files with 145 additions and 101 deletions
|
@ -14,6 +14,7 @@
|
||||||
* Mikael Pettersson : PM converted to driver model.
|
* Mikael Pettersson : PM converted to driver model.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/perf_counter.h>
|
||||||
#include <linux/kernel_stat.h>
|
#include <linux/kernel_stat.h>
|
||||||
#include <linux/mc146818rtc.h>
|
#include <linux/mc146818rtc.h>
|
||||||
#include <linux/acpi_pmtmr.h>
|
#include <linux/acpi_pmtmr.h>
|
||||||
|
|
|
@ -108,18 +108,6 @@ extern struct group_info init_groups;
|
||||||
|
|
||||||
extern struct cred init_cred;
|
extern struct cred init_cred;
|
||||||
|
|
||||||
#ifdef CONFIG_PERF_COUNTERS
|
|
||||||
# define INIT_PERF_COUNTERS(tsk) \
|
|
||||||
.perf_counter_ctx.counter_list = \
|
|
||||||
LIST_HEAD_INIT(tsk.perf_counter_ctx.counter_list), \
|
|
||||||
.perf_counter_ctx.event_list = \
|
|
||||||
LIST_HEAD_INIT(tsk.perf_counter_ctx.event_list), \
|
|
||||||
.perf_counter_ctx.lock = \
|
|
||||||
__SPIN_LOCK_UNLOCKED(tsk.perf_counter_ctx.lock),
|
|
||||||
#else
|
|
||||||
# define INIT_PERF_COUNTERS(tsk)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* INIT_TASK is used to set up the first task table, touch at
|
* INIT_TASK is used to set up the first task table, touch at
|
||||||
* your own risk!. Base=0, limit=0x1fffff (=2MB)
|
* your own risk!. Base=0, limit=0x1fffff (=2MB)
|
||||||
|
@ -183,7 +171,6 @@ extern struct cred init_cred;
|
||||||
}, \
|
}, \
|
||||||
.dirties = INIT_PROP_LOCAL_SINGLE(dirties), \
|
.dirties = INIT_PROP_LOCAL_SINGLE(dirties), \
|
||||||
INIT_IDS \
|
INIT_IDS \
|
||||||
INIT_PERF_COUNTERS(tsk) \
|
|
||||||
INIT_TRACE_IRQFLAGS \
|
INIT_TRACE_IRQFLAGS \
|
||||||
INIT_LOCKDEP \
|
INIT_LOCKDEP \
|
||||||
INIT_FTRACE_GRAPH \
|
INIT_FTRACE_GRAPH \
|
||||||
|
|
|
@ -449,7 +449,6 @@ struct perf_counter {
|
||||||
struct hw_perf_counter hw;
|
struct hw_perf_counter hw;
|
||||||
|
|
||||||
struct perf_counter_context *ctx;
|
struct perf_counter_context *ctx;
|
||||||
struct task_struct *task;
|
|
||||||
struct file *filp;
|
struct file *filp;
|
||||||
|
|
||||||
struct perf_counter *parent;
|
struct perf_counter *parent;
|
||||||
|
@ -498,7 +497,6 @@ struct perf_counter {
|
||||||
* Used as a container for task counters and CPU counters as well:
|
* Used as a container for task counters and CPU counters as well:
|
||||||
*/
|
*/
|
||||||
struct perf_counter_context {
|
struct perf_counter_context {
|
||||||
#ifdef CONFIG_PERF_COUNTERS
|
|
||||||
/*
|
/*
|
||||||
* Protect the states of the counters in the list,
|
* Protect the states of the counters in the list,
|
||||||
* nr_active, and the list:
|
* nr_active, and the list:
|
||||||
|
@ -516,6 +514,7 @@ struct perf_counter_context {
|
||||||
int nr_counters;
|
int nr_counters;
|
||||||
int nr_active;
|
int nr_active;
|
||||||
int is_active;
|
int is_active;
|
||||||
|
atomic_t refcount;
|
||||||
struct task_struct *task;
|
struct task_struct *task;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -523,7 +522,6 @@ struct perf_counter_context {
|
||||||
*/
|
*/
|
||||||
u64 time;
|
u64 time;
|
||||||
u64 timestamp;
|
u64 timestamp;
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -71,7 +71,6 @@ struct sched_param {
|
||||||
#include <linux/path.h>
|
#include <linux/path.h>
|
||||||
#include <linux/compiler.h>
|
#include <linux/compiler.h>
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
#include <linux/perf_counter.h>
|
|
||||||
#include <linux/pid.h>
|
#include <linux/pid.h>
|
||||||
#include <linux/percpu.h>
|
#include <linux/percpu.h>
|
||||||
#include <linux/topology.h>
|
#include <linux/topology.h>
|
||||||
|
@ -99,6 +98,7 @@ struct robust_list_head;
|
||||||
struct bio;
|
struct bio;
|
||||||
struct bts_tracer;
|
struct bts_tracer;
|
||||||
struct fs_struct;
|
struct fs_struct;
|
||||||
|
struct perf_counter_context;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* List of flags we want to share for kernel threads,
|
* List of flags we want to share for kernel threads,
|
||||||
|
@ -1387,7 +1387,9 @@ struct task_struct {
|
||||||
struct list_head pi_state_list;
|
struct list_head pi_state_list;
|
||||||
struct futex_pi_state *pi_state_cache;
|
struct futex_pi_state *pi_state_cache;
|
||||||
#endif
|
#endif
|
||||||
struct perf_counter_context perf_counter_ctx;
|
#ifdef CONFIG_PERF_COUNTERS
|
||||||
|
struct perf_counter_context *perf_counter_ctxp;
|
||||||
|
#endif
|
||||||
#ifdef CONFIG_NUMA
|
#ifdef CONFIG_NUMA
|
||||||
struct mempolicy *mempolicy;
|
struct mempolicy *mempolicy;
|
||||||
short il_next;
|
short il_next;
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
#include <linux/tracehook.h>
|
#include <linux/tracehook.h>
|
||||||
#include <linux/fs_struct.h>
|
#include <linux/fs_struct.h>
|
||||||
#include <linux/init_task.h>
|
#include <linux/init_task.h>
|
||||||
|
#include <linux/perf_counter.h>
|
||||||
#include <trace/sched.h>
|
#include <trace/sched.h>
|
||||||
|
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
|
@ -159,7 +160,7 @@ static void delayed_put_task_struct(struct rcu_head *rhp)
|
||||||
struct task_struct *tsk = container_of(rhp, struct task_struct, rcu);
|
struct task_struct *tsk = container_of(rhp, struct task_struct, rcu);
|
||||||
|
|
||||||
#ifdef CONFIG_PERF_COUNTERS
|
#ifdef CONFIG_PERF_COUNTERS
|
||||||
WARN_ON_ONCE(!list_empty(&tsk->perf_counter_ctx.counter_list));
|
WARN_ON_ONCE(tsk->perf_counter_ctxp);
|
||||||
#endif
|
#endif
|
||||||
trace_sched_process_free(tsk);
|
trace_sched_process_free(tsk);
|
||||||
put_task_struct(tsk);
|
put_task_struct(tsk);
|
||||||
|
|
|
@ -63,6 +63,7 @@
|
||||||
#include <linux/fs_struct.h>
|
#include <linux/fs_struct.h>
|
||||||
#include <trace/sched.h>
|
#include <trace/sched.h>
|
||||||
#include <linux/magic.h>
|
#include <linux/magic.h>
|
||||||
|
#include <linux/perf_counter.h>
|
||||||
|
|
||||||
#include <asm/pgtable.h>
|
#include <asm/pgtable.h>
|
||||||
#include <asm/pgalloc.h>
|
#include <asm/pgalloc.h>
|
||||||
|
|
|
@ -97,6 +97,17 @@ void perf_enable(void)
|
||||||
hw_perf_enable();
|
hw_perf_enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void get_ctx(struct perf_counter_context *ctx)
|
||||||
|
{
|
||||||
|
atomic_inc(&ctx->refcount);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void put_ctx(struct perf_counter_context *ctx)
|
||||||
|
{
|
||||||
|
if (atomic_dec_and_test(&ctx->refcount))
|
||||||
|
kfree(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
list_add_counter(struct perf_counter *counter, struct perf_counter_context *ctx)
|
list_add_counter(struct perf_counter *counter, struct perf_counter_context *ctx)
|
||||||
{
|
{
|
||||||
|
@ -118,11 +129,17 @@ list_add_counter(struct perf_counter *counter, struct perf_counter_context *ctx)
|
||||||
ctx->nr_counters++;
|
ctx->nr_counters++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove a counter from the lists for its context.
|
||||||
|
* Must be called with counter->mutex and ctx->mutex held.
|
||||||
|
*/
|
||||||
static void
|
static void
|
||||||
list_del_counter(struct perf_counter *counter, struct perf_counter_context *ctx)
|
list_del_counter(struct perf_counter *counter, struct perf_counter_context *ctx)
|
||||||
{
|
{
|
||||||
struct perf_counter *sibling, *tmp;
|
struct perf_counter *sibling, *tmp;
|
||||||
|
|
||||||
|
if (list_empty(&counter->list_entry))
|
||||||
|
return;
|
||||||
ctx->nr_counters--;
|
ctx->nr_counters--;
|
||||||
|
|
||||||
list_del_init(&counter->list_entry);
|
list_del_init(&counter->list_entry);
|
||||||
|
@ -216,8 +233,6 @@ static void __perf_counter_remove_from_context(void *info)
|
||||||
|
|
||||||
counter_sched_out(counter, cpuctx, ctx);
|
counter_sched_out(counter, cpuctx, ctx);
|
||||||
|
|
||||||
counter->task = NULL;
|
|
||||||
|
|
||||||
list_del_counter(counter, ctx);
|
list_del_counter(counter, ctx);
|
||||||
|
|
||||||
if (!ctx->task) {
|
if (!ctx->task) {
|
||||||
|
@ -279,7 +294,6 @@ retry:
|
||||||
*/
|
*/
|
||||||
if (!list_empty(&counter->list_entry)) {
|
if (!list_empty(&counter->list_entry)) {
|
||||||
list_del_counter(counter, ctx);
|
list_del_counter(counter, ctx);
|
||||||
counter->task = NULL;
|
|
||||||
}
|
}
|
||||||
spin_unlock_irq(&ctx->lock);
|
spin_unlock_irq(&ctx->lock);
|
||||||
}
|
}
|
||||||
|
@ -568,11 +582,17 @@ static void __perf_install_in_context(void *info)
|
||||||
* If this is a task context, we need to check whether it is
|
* If this is a task context, we need to check whether it is
|
||||||
* the current task context of this cpu. If not it has been
|
* the current task context of this cpu. If not it has been
|
||||||
* scheduled out before the smp call arrived.
|
* scheduled out before the smp call arrived.
|
||||||
|
* Or possibly this is the right context but it isn't
|
||||||
|
* on this cpu because it had no counters.
|
||||||
*/
|
*/
|
||||||
if (ctx->task && cpuctx->task_ctx != ctx)
|
if (ctx->task && cpuctx->task_ctx != ctx) {
|
||||||
return;
|
if (cpuctx->task_ctx || ctx->task != current)
|
||||||
|
return;
|
||||||
|
cpuctx->task_ctx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&ctx->lock, flags);
|
spin_lock_irqsave(&ctx->lock, flags);
|
||||||
|
ctx->is_active = 1;
|
||||||
update_context_time(ctx);
|
update_context_time(ctx);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -653,7 +673,6 @@ perf_install_in_context(struct perf_counter_context *ctx,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
counter->task = task;
|
|
||||||
retry:
|
retry:
|
||||||
task_oncpu_function_call(task, __perf_install_in_context,
|
task_oncpu_function_call(task, __perf_install_in_context,
|
||||||
counter);
|
counter);
|
||||||
|
@ -693,10 +712,14 @@ static void __perf_counter_enable(void *info)
|
||||||
* If this is a per-task counter, need to check whether this
|
* If this is a per-task counter, need to check whether this
|
||||||
* counter's task is the current task on this cpu.
|
* counter's task is the current task on this cpu.
|
||||||
*/
|
*/
|
||||||
if (ctx->task && cpuctx->task_ctx != ctx)
|
if (ctx->task && cpuctx->task_ctx != ctx) {
|
||||||
return;
|
if (cpuctx->task_ctx || ctx->task != current)
|
||||||
|
return;
|
||||||
|
cpuctx->task_ctx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&ctx->lock, flags);
|
spin_lock_irqsave(&ctx->lock, flags);
|
||||||
|
ctx->is_active = 1;
|
||||||
update_context_time(ctx);
|
update_context_time(ctx);
|
||||||
|
|
||||||
counter->prev_state = counter->state;
|
counter->prev_state = counter->state;
|
||||||
|
@ -852,10 +875,10 @@ void __perf_counter_sched_out(struct perf_counter_context *ctx,
|
||||||
void perf_counter_task_sched_out(struct task_struct *task, int cpu)
|
void perf_counter_task_sched_out(struct task_struct *task, int cpu)
|
||||||
{
|
{
|
||||||
struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu);
|
struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu);
|
||||||
struct perf_counter_context *ctx = &task->perf_counter_ctx;
|
struct perf_counter_context *ctx = task->perf_counter_ctxp;
|
||||||
struct pt_regs *regs;
|
struct pt_regs *regs;
|
||||||
|
|
||||||
if (likely(!cpuctx->task_ctx))
|
if (likely(!ctx || !cpuctx->task_ctx))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
update_context_time(ctx);
|
update_context_time(ctx);
|
||||||
|
@ -871,6 +894,8 @@ static void __perf_counter_task_sched_out(struct perf_counter_context *ctx)
|
||||||
{
|
{
|
||||||
struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
|
struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context);
|
||||||
|
|
||||||
|
if (!cpuctx->task_ctx)
|
||||||
|
return;
|
||||||
__perf_counter_sched_out(ctx, cpuctx);
|
__perf_counter_sched_out(ctx, cpuctx);
|
||||||
cpuctx->task_ctx = NULL;
|
cpuctx->task_ctx = NULL;
|
||||||
}
|
}
|
||||||
|
@ -969,8 +994,10 @@ __perf_counter_sched_in(struct perf_counter_context *ctx,
|
||||||
void perf_counter_task_sched_in(struct task_struct *task, int cpu)
|
void perf_counter_task_sched_in(struct task_struct *task, int cpu)
|
||||||
{
|
{
|
||||||
struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu);
|
struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu);
|
||||||
struct perf_counter_context *ctx = &task->perf_counter_ctx;
|
struct perf_counter_context *ctx = task->perf_counter_ctxp;
|
||||||
|
|
||||||
|
if (likely(!ctx))
|
||||||
|
return;
|
||||||
__perf_counter_sched_in(ctx, cpuctx, cpu);
|
__perf_counter_sched_in(ctx, cpuctx, cpu);
|
||||||
cpuctx->task_ctx = ctx;
|
cpuctx->task_ctx = ctx;
|
||||||
}
|
}
|
||||||
|
@ -985,11 +1012,11 @@ static void perf_counter_cpu_sched_in(struct perf_cpu_context *cpuctx, int cpu)
|
||||||
int perf_counter_task_disable(void)
|
int perf_counter_task_disable(void)
|
||||||
{
|
{
|
||||||
struct task_struct *curr = current;
|
struct task_struct *curr = current;
|
||||||
struct perf_counter_context *ctx = &curr->perf_counter_ctx;
|
struct perf_counter_context *ctx = curr->perf_counter_ctxp;
|
||||||
struct perf_counter *counter;
|
struct perf_counter *counter;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
if (likely(!ctx->nr_counters))
|
if (!ctx || !ctx->nr_counters)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
local_irq_save(flags);
|
local_irq_save(flags);
|
||||||
|
@ -1020,12 +1047,12 @@ int perf_counter_task_disable(void)
|
||||||
int perf_counter_task_enable(void)
|
int perf_counter_task_enable(void)
|
||||||
{
|
{
|
||||||
struct task_struct *curr = current;
|
struct task_struct *curr = current;
|
||||||
struct perf_counter_context *ctx = &curr->perf_counter_ctx;
|
struct perf_counter_context *ctx = curr->perf_counter_ctxp;
|
||||||
struct perf_counter *counter;
|
struct perf_counter *counter;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int cpu;
|
int cpu;
|
||||||
|
|
||||||
if (likely(!ctx->nr_counters))
|
if (!ctx || !ctx->nr_counters)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
local_irq_save(flags);
|
local_irq_save(flags);
|
||||||
|
@ -1128,19 +1155,23 @@ void perf_counter_task_tick(struct task_struct *curr, int cpu)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
cpuctx = &per_cpu(perf_cpu_context, cpu);
|
cpuctx = &per_cpu(perf_cpu_context, cpu);
|
||||||
ctx = &curr->perf_counter_ctx;
|
ctx = curr->perf_counter_ctxp;
|
||||||
|
|
||||||
perf_adjust_freq(&cpuctx->ctx);
|
perf_adjust_freq(&cpuctx->ctx);
|
||||||
perf_adjust_freq(ctx);
|
if (ctx)
|
||||||
|
perf_adjust_freq(ctx);
|
||||||
|
|
||||||
perf_counter_cpu_sched_out(cpuctx);
|
perf_counter_cpu_sched_out(cpuctx);
|
||||||
__perf_counter_task_sched_out(ctx);
|
if (ctx)
|
||||||
|
__perf_counter_task_sched_out(ctx);
|
||||||
|
|
||||||
rotate_ctx(&cpuctx->ctx);
|
rotate_ctx(&cpuctx->ctx);
|
||||||
rotate_ctx(ctx);
|
if (ctx)
|
||||||
|
rotate_ctx(ctx);
|
||||||
|
|
||||||
perf_counter_cpu_sched_in(cpuctx, cpu);
|
perf_counter_cpu_sched_in(cpuctx, cpu);
|
||||||
perf_counter_task_sched_in(curr, cpu);
|
if (ctx)
|
||||||
|
perf_counter_task_sched_in(curr, cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1176,6 +1207,22 @@ static u64 perf_counter_read(struct perf_counter *counter)
|
||||||
return atomic64_read(&counter->count);
|
return atomic64_read(&counter->count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize the perf_counter context in a task_struct:
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
__perf_counter_init_context(struct perf_counter_context *ctx,
|
||||||
|
struct task_struct *task)
|
||||||
|
{
|
||||||
|
memset(ctx, 0, sizeof(*ctx));
|
||||||
|
spin_lock_init(&ctx->lock);
|
||||||
|
mutex_init(&ctx->mutex);
|
||||||
|
INIT_LIST_HEAD(&ctx->counter_list);
|
||||||
|
INIT_LIST_HEAD(&ctx->event_list);
|
||||||
|
atomic_set(&ctx->refcount, 1);
|
||||||
|
ctx->task = task;
|
||||||
|
}
|
||||||
|
|
||||||
static void put_context(struct perf_counter_context *ctx)
|
static void put_context(struct perf_counter_context *ctx)
|
||||||
{
|
{
|
||||||
if (ctx->task)
|
if (ctx->task)
|
||||||
|
@ -1186,6 +1233,7 @@ static struct perf_counter_context *find_get_context(pid_t pid, int cpu)
|
||||||
{
|
{
|
||||||
struct perf_cpu_context *cpuctx;
|
struct perf_cpu_context *cpuctx;
|
||||||
struct perf_counter_context *ctx;
|
struct perf_counter_context *ctx;
|
||||||
|
struct perf_counter_context *tctx;
|
||||||
struct task_struct *task;
|
struct task_struct *task;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1225,15 +1273,36 @@ static struct perf_counter_context *find_get_context(pid_t pid, int cpu)
|
||||||
if (!task)
|
if (!task)
|
||||||
return ERR_PTR(-ESRCH);
|
return ERR_PTR(-ESRCH);
|
||||||
|
|
||||||
ctx = &task->perf_counter_ctx;
|
|
||||||
ctx->task = task;
|
|
||||||
|
|
||||||
/* Reuse ptrace permission checks for now. */
|
/* Reuse ptrace permission checks for now. */
|
||||||
if (!ptrace_may_access(task, PTRACE_MODE_READ)) {
|
if (!ptrace_may_access(task, PTRACE_MODE_READ)) {
|
||||||
put_context(ctx);
|
put_task_struct(task);
|
||||||
return ERR_PTR(-EACCES);
|
return ERR_PTR(-EACCES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx = task->perf_counter_ctxp;
|
||||||
|
if (!ctx) {
|
||||||
|
ctx = kmalloc(sizeof(struct perf_counter_context), GFP_KERNEL);
|
||||||
|
if (!ctx) {
|
||||||
|
put_task_struct(task);
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
}
|
||||||
|
__perf_counter_init_context(ctx, task);
|
||||||
|
/*
|
||||||
|
* Make sure other cpus see correct values for *ctx
|
||||||
|
* once task->perf_counter_ctxp is visible to them.
|
||||||
|
*/
|
||||||
|
smp_wmb();
|
||||||
|
tctx = cmpxchg(&task->perf_counter_ctxp, NULL, ctx);
|
||||||
|
if (tctx) {
|
||||||
|
/*
|
||||||
|
* We raced with some other task; use
|
||||||
|
* the context they set.
|
||||||
|
*/
|
||||||
|
kfree(ctx);
|
||||||
|
ctx = tctx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1242,6 +1311,7 @@ static void free_counter_rcu(struct rcu_head *head)
|
||||||
struct perf_counter *counter;
|
struct perf_counter *counter;
|
||||||
|
|
||||||
counter = container_of(head, struct perf_counter, rcu_head);
|
counter = container_of(head, struct perf_counter, rcu_head);
|
||||||
|
put_ctx(counter->ctx);
|
||||||
kfree(counter);
|
kfree(counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2247,7 +2317,7 @@ static void perf_counter_comm_event(struct perf_comm_event *comm_event)
|
||||||
perf_counter_comm_ctx(&cpuctx->ctx, comm_event);
|
perf_counter_comm_ctx(&cpuctx->ctx, comm_event);
|
||||||
put_cpu_var(perf_cpu_context);
|
put_cpu_var(perf_cpu_context);
|
||||||
|
|
||||||
perf_counter_comm_ctx(¤t->perf_counter_ctx, comm_event);
|
perf_counter_comm_ctx(current->perf_counter_ctxp, comm_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void perf_counter_comm(struct task_struct *task)
|
void perf_counter_comm(struct task_struct *task)
|
||||||
|
@ -2256,7 +2326,9 @@ void perf_counter_comm(struct task_struct *task)
|
||||||
|
|
||||||
if (!atomic_read(&nr_comm_tracking))
|
if (!atomic_read(&nr_comm_tracking))
|
||||||
return;
|
return;
|
||||||
|
if (!current->perf_counter_ctxp)
|
||||||
|
return;
|
||||||
|
|
||||||
comm_event = (struct perf_comm_event){
|
comm_event = (struct perf_comm_event){
|
||||||
.task = task,
|
.task = task,
|
||||||
.event = {
|
.event = {
|
||||||
|
@ -2372,7 +2444,7 @@ got_name:
|
||||||
perf_counter_mmap_ctx(&cpuctx->ctx, mmap_event);
|
perf_counter_mmap_ctx(&cpuctx->ctx, mmap_event);
|
||||||
put_cpu_var(perf_cpu_context);
|
put_cpu_var(perf_cpu_context);
|
||||||
|
|
||||||
perf_counter_mmap_ctx(¤t->perf_counter_ctx, mmap_event);
|
perf_counter_mmap_ctx(current->perf_counter_ctxp, mmap_event);
|
||||||
|
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
}
|
}
|
||||||
|
@ -2384,6 +2456,8 @@ void perf_counter_mmap(unsigned long addr, unsigned long len,
|
||||||
|
|
||||||
if (!atomic_read(&nr_mmap_tracking))
|
if (!atomic_read(&nr_mmap_tracking))
|
||||||
return;
|
return;
|
||||||
|
if (!current->perf_counter_ctxp)
|
||||||
|
return;
|
||||||
|
|
||||||
mmap_event = (struct perf_mmap_event){
|
mmap_event = (struct perf_mmap_event){
|
||||||
.file = file,
|
.file = file,
|
||||||
|
@ -2985,6 +3059,7 @@ perf_counter_alloc(struct perf_counter_hw_event *hw_event,
|
||||||
counter->group_leader = group_leader;
|
counter->group_leader = group_leader;
|
||||||
counter->pmu = NULL;
|
counter->pmu = NULL;
|
||||||
counter->ctx = ctx;
|
counter->ctx = ctx;
|
||||||
|
get_ctx(ctx);
|
||||||
|
|
||||||
counter->state = PERF_COUNTER_STATE_INACTIVE;
|
counter->state = PERF_COUNTER_STATE_INACTIVE;
|
||||||
if (hw_event->disabled)
|
if (hw_event->disabled)
|
||||||
|
@ -3149,21 +3224,6 @@ err_put_context:
|
||||||
goto out_fput;
|
goto out_fput;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize the perf_counter context in a task_struct:
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
__perf_counter_init_context(struct perf_counter_context *ctx,
|
|
||||||
struct task_struct *task)
|
|
||||||
{
|
|
||||||
memset(ctx, 0, sizeof(*ctx));
|
|
||||||
spin_lock_init(&ctx->lock);
|
|
||||||
mutex_init(&ctx->mutex);
|
|
||||||
INIT_LIST_HEAD(&ctx->counter_list);
|
|
||||||
INIT_LIST_HEAD(&ctx->event_list);
|
|
||||||
ctx->task = task;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* inherit a counter from parent task to child task:
|
* inherit a counter from parent task to child task:
|
||||||
*/
|
*/
|
||||||
|
@ -3195,7 +3255,6 @@ inherit_counter(struct perf_counter *parent_counter,
|
||||||
/*
|
/*
|
||||||
* Link it up in the child's context:
|
* Link it up in the child's context:
|
||||||
*/
|
*/
|
||||||
child_counter->task = child;
|
|
||||||
add_counter_to_ctx(child_counter, child_ctx);
|
add_counter_to_ctx(child_counter, child_ctx);
|
||||||
|
|
||||||
child_counter->parent = parent_counter;
|
child_counter->parent = parent_counter;
|
||||||
|
@ -3294,40 +3353,15 @@ __perf_counter_exit_task(struct task_struct *child,
|
||||||
struct perf_counter *parent_counter;
|
struct perf_counter *parent_counter;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we do not self-reap then we have to wait for the
|
* Protect against concurrent operations on child_counter
|
||||||
* child task to unschedule (it will happen for sure),
|
* due its fd getting closed, etc.
|
||||||
* so that its counter is at its final count. (This
|
|
||||||
* condition triggers rarely - child tasks usually get
|
|
||||||
* off their CPU before the parent has a chance to
|
|
||||||
* get this far into the reaping action)
|
|
||||||
*/
|
*/
|
||||||
if (child != current) {
|
mutex_lock(&child_counter->mutex);
|
||||||
wait_task_inactive(child, 0);
|
|
||||||
update_counter_times(child_counter);
|
|
||||||
list_del_counter(child_counter, child_ctx);
|
|
||||||
} else {
|
|
||||||
struct perf_cpu_context *cpuctx;
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
/*
|
update_counter_times(child_counter);
|
||||||
* Disable and unlink this counter.
|
list_del_counter(child_counter, child_ctx);
|
||||||
*
|
|
||||||
* Be careful about zapping the list - IRQ/NMI context
|
|
||||||
* could still be processing it:
|
|
||||||
*/
|
|
||||||
local_irq_save(flags);
|
|
||||||
perf_disable();
|
|
||||||
|
|
||||||
cpuctx = &__get_cpu_var(perf_cpu_context);
|
mutex_unlock(&child_counter->mutex);
|
||||||
|
|
||||||
group_sched_out(child_counter, cpuctx, child_ctx);
|
|
||||||
update_counter_times(child_counter);
|
|
||||||
|
|
||||||
list_del_counter(child_counter, child_ctx);
|
|
||||||
|
|
||||||
perf_enable();
|
|
||||||
local_irq_restore(flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
parent_counter = child_counter->parent;
|
parent_counter = child_counter->parent;
|
||||||
/*
|
/*
|
||||||
|
@ -3346,19 +3380,29 @@ __perf_counter_exit_task(struct task_struct *child,
|
||||||
*
|
*
|
||||||
* Note: we may be running in child context, but the PID is not hashed
|
* Note: we may be running in child context, but the PID is not hashed
|
||||||
* anymore so new counters will not be added.
|
* anymore so new counters will not be added.
|
||||||
|
* (XXX not sure that is true when we get called from flush_old_exec.
|
||||||
|
* -- paulus)
|
||||||
*/
|
*/
|
||||||
void perf_counter_exit_task(struct task_struct *child)
|
void perf_counter_exit_task(struct task_struct *child)
|
||||||
{
|
{
|
||||||
struct perf_counter *child_counter, *tmp;
|
struct perf_counter *child_counter, *tmp;
|
||||||
struct perf_counter_context *child_ctx;
|
struct perf_counter_context *child_ctx;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
WARN_ON_ONCE(child != current);
|
WARN_ON_ONCE(child != current);
|
||||||
|
|
||||||
child_ctx = &child->perf_counter_ctx;
|
child_ctx = child->perf_counter_ctxp;
|
||||||
|
|
||||||
if (likely(!child_ctx->nr_counters))
|
if (likely(!child_ctx))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
local_irq_save(flags);
|
||||||
|
__perf_counter_task_sched_out(child_ctx);
|
||||||
|
child->perf_counter_ctxp = NULL;
|
||||||
|
local_irq_restore(flags);
|
||||||
|
|
||||||
|
mutex_lock(&child_ctx->mutex);
|
||||||
|
|
||||||
again:
|
again:
|
||||||
list_for_each_entry_safe(child_counter, tmp, &child_ctx->counter_list,
|
list_for_each_entry_safe(child_counter, tmp, &child_ctx->counter_list,
|
||||||
list_entry)
|
list_entry)
|
||||||
|
@ -3371,6 +3415,10 @@ again:
|
||||||
*/
|
*/
|
||||||
if (!list_empty(&child_ctx->counter_list))
|
if (!list_empty(&child_ctx->counter_list))
|
||||||
goto again;
|
goto again;
|
||||||
|
|
||||||
|
mutex_unlock(&child_ctx->mutex);
|
||||||
|
|
||||||
|
put_ctx(child_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -3382,19 +3430,25 @@ void perf_counter_init_task(struct task_struct *child)
|
||||||
struct perf_counter *counter;
|
struct perf_counter *counter;
|
||||||
struct task_struct *parent = current;
|
struct task_struct *parent = current;
|
||||||
|
|
||||||
child_ctx = &child->perf_counter_ctx;
|
child->perf_counter_ctxp = NULL;
|
||||||
parent_ctx = &parent->perf_counter_ctx;
|
|
||||||
|
|
||||||
__perf_counter_init_context(child_ctx, child);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is executed from the parent task context, so inherit
|
* This is executed from the parent task context, so inherit
|
||||||
* counters that have been marked for cloning:
|
* counters that have been marked for cloning.
|
||||||
|
* First allocate and initialize a context for the child.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (likely(!parent_ctx->nr_counters))
|
child_ctx = kmalloc(sizeof(struct perf_counter_context), GFP_KERNEL);
|
||||||
|
if (!child_ctx)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
parent_ctx = parent->perf_counter_ctxp;
|
||||||
|
if (likely(!parent_ctx || !parent_ctx->nr_counters))
|
||||||
|
return;
|
||||||
|
|
||||||
|
__perf_counter_init_context(child_ctx, child);
|
||||||
|
child->perf_counter_ctxp = child_ctx;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Lock the parent list. No need to lock the child - not PID
|
* Lock the parent list. No need to lock the child - not PID
|
||||||
* hashed yet and not running, so nobody can access it.
|
* hashed yet and not running, so nobody can access it.
|
||||||
|
|
Loading…
Reference in a new issue