diff options
| author | Thomas Gleixner <tglx@linutronix.de> | 2008-12-04 14:12:29 -0500 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2008-12-08 09:47:03 -0500 |
| commit | 0793a61d4df8daeac6492dbf8d2f3e5713caae5e (patch) | |
| tree | cc9603eb8daffeb7ace521c42a6a44db164ac551 /kernel | |
| parent | b5aa97e83bcc31a96374d18f5452d53909a16c90 (diff) | |
performance counters: core code
Implement the core kernel bits of Performance Counters subsystem.
The Linux Performance Counter subsystem provides an abstraction of
performance counter hardware capabilities. It provides per task and per
CPU counters, and it provides event capabilities on top of those.
Performance counters are accessed via special file descriptors.
There's one file descriptor per virtual counter used.
The special file descriptor is opened via the perf_counter_open()
system call:
int
perf_counter_open(u32 hw_event_type,
u32 hw_event_period,
u32 record_type,
pid_t pid,
int cpu);
The syscall returns the new fd. The fd can be used via the normal
VFS system calls: read() can be used to read the counter, fcntl()
can be used to set the blocking mode, etc.
Multiple counters can be kept open at a time, and the counters
can be poll()ed.
See more details in Documentation/perf-counters.txt.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/Makefile | 1 | ||||
| -rw-r--r-- | kernel/fork.c | 1 | ||||
| -rw-r--r-- | kernel/perf_counter.c | 943 | ||||
| -rw-r--r-- | kernel/sched.c | 24 | ||||
| -rw-r--r-- | kernel/sys_ni.c | 3 |
5 files changed, 972 insertions, 0 deletions
diff --git a/kernel/Makefile b/kernel/Makefile index 19fad003b19d..1f184a1dc406 100644 --- a/kernel/Makefile +++ b/kernel/Makefile | |||
| @@ -89,6 +89,7 @@ obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o | |||
| 89 | obj-$(CONFIG_FUNCTION_TRACER) += trace/ | 89 | obj-$(CONFIG_FUNCTION_TRACER) += trace/ |
| 90 | obj-$(CONFIG_TRACING) += trace/ | 90 | obj-$(CONFIG_TRACING) += trace/ |
| 91 | obj-$(CONFIG_SMP) += sched_cpupri.o | 91 | obj-$(CONFIG_SMP) += sched_cpupri.o |
| 92 | obj-$(CONFIG_PERF_COUNTERS) += perf_counter.o | ||
| 92 | 93 | ||
| 93 | ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y) | 94 | ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y) |
| 94 | # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is | 95 | # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is |
diff --git a/kernel/fork.c b/kernel/fork.c index 2a372a0e206f..441fadff1fa4 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
| @@ -975,6 +975,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
| 975 | goto fork_out; | 975 | goto fork_out; |
| 976 | 976 | ||
| 977 | rt_mutex_init_task(p); | 977 | rt_mutex_init_task(p); |
| 978 | perf_counter_init_task(p); | ||
| 978 | 979 | ||
| 979 | #ifdef CONFIG_PROVE_LOCKING | 980 | #ifdef CONFIG_PROVE_LOCKING |
| 980 | DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled); | 981 | DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled); |
diff --git a/kernel/perf_counter.c b/kernel/perf_counter.c new file mode 100644 index 000000000000..20508f053658 --- /dev/null +++ b/kernel/perf_counter.c | |||
| @@ -0,0 +1,943 @@ | |||
| 1 | /* | ||
| 2 | * Performance counter core code | ||
| 3 | * | ||
| 4 | * Copyright(C) 2008 Thomas Gleixner <tglx@linutronix.de> | ||
| 5 | * Copyright(C) 2008 Red Hat, Inc., Ingo Molnar | ||
| 6 | * | ||
| 7 | * For licencing details see kernel-base/COPYING | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include <linux/fs.h> | ||
| 11 | #include <linux/cpu.h> | ||
| 12 | #include <linux/smp.h> | ||
| 13 | #include <linux/poll.h> | ||
| 14 | #include <linux/sysfs.h> | ||
| 15 | #include <linux/ptrace.h> | ||
| 16 | #include <linux/percpu.h> | ||
| 17 | #include <linux/uaccess.h> | ||
| 18 | #include <linux/syscalls.h> | ||
| 19 | #include <linux/anon_inodes.h> | ||
| 20 | #include <linux/perf_counter.h> | ||
| 21 | |||
| 22 | /* | ||
| 23 | * Each CPU has a list of per CPU counters: | ||
| 24 | */ | ||
| 25 | DEFINE_PER_CPU(struct perf_cpu_context, perf_cpu_context); | ||
| 26 | |||
| 27 | int perf_max_counters __read_mostly; | ||
| 28 | static int perf_reserved_percpu __read_mostly; | ||
| 29 | static int perf_overcommit __read_mostly = 1; | ||
| 30 | |||
| 31 | /* | ||
| 32 | * Mutex for (sysadmin-configurable) counter reservations: | ||
| 33 | */ | ||
| 34 | static DEFINE_MUTEX(perf_resource_mutex); | ||
| 35 | |||
| 36 | /* | ||
| 37 | * Architecture provided APIs - weak aliases: | ||
| 38 | */ | ||
| 39 | |||
| 40 | int __weak hw_perf_counter_init(struct perf_counter *counter, u32 hw_event_type) | ||
| 41 | { | ||
| 42 | return -EINVAL; | ||
| 43 | } | ||
| 44 | |||
| 45 | void __weak hw_perf_counter_enable(struct perf_counter *counter) { } | ||
| 46 | void __weak hw_perf_counter_disable(struct perf_counter *counter) { } | ||
| 47 | void __weak hw_perf_counter_read(struct perf_counter *counter) { } | ||
| 48 | void __weak hw_perf_disable_all(void) { } | ||
| 49 | void __weak hw_perf_enable_all(void) { } | ||
| 50 | void __weak hw_perf_counter_setup(void) { } | ||
| 51 | |||
| 52 | #if BITS_PER_LONG == 64 | ||
| 53 | |||
| 54 | /* | ||
| 55 | * Read the cached counter in counter safe against cross CPU / NMI | ||
| 56 | * modifications. 64 bit version - no complications. | ||
| 57 | */ | ||
| 58 | static inline u64 perf_read_counter_safe(struct perf_counter *counter) | ||
| 59 | { | ||
| 60 | return (u64) atomic64_read(&counter->count); | ||
| 61 | } | ||
| 62 | |||
| 63 | #else | ||
| 64 | |||
| 65 | /* | ||
| 66 | * Read the cached counter in counter safe against cross CPU / NMI | ||
| 67 | * modifications. 32 bit version. | ||
| 68 | */ | ||
| 69 | static u64 perf_read_counter_safe(struct perf_counter *counter) | ||
| 70 | { | ||
| 71 | u32 cntl, cnth; | ||
| 72 | |||
| 73 | local_irq_disable(); | ||
| 74 | do { | ||
| 75 | cnth = atomic_read(&counter->count32[1]); | ||
| 76 | cntl = atomic_read(&counter->count32[0]); | ||
| 77 | } while (cnth != atomic_read(&counter->count32[1])); | ||
| 78 | |||
| 79 | local_irq_enable(); | ||
| 80 | |||
| 81 | return cntl | ((u64) cnth) << 32; | ||
| 82 | } | ||
| 83 | |||
| 84 | #endif | ||
| 85 | |||
| 86 | /* | ||
| 87 | * Cross CPU call to remove a performance counter | ||
| 88 | * | ||
| 89 | * We disable the counter on the hardware level first. After that we | ||
| 90 | * remove it from the context list. | ||
| 91 | */ | ||
| 92 | static void __perf_remove_from_context(void *info) | ||
| 93 | { | ||
| 94 | struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context); | ||
| 95 | struct perf_counter *counter = info; | ||
| 96 | struct perf_counter_context *ctx = counter->ctx; | ||
| 97 | |||
| 98 | /* | ||
| 99 | * If this is a task context, we need to check whether it is | ||
| 100 | * the current task context of this cpu. If not it has been | ||
| 101 | * scheduled out before the smp call arrived. | ||
| 102 | */ | ||
| 103 | if (ctx->task && cpuctx->task_ctx != ctx) | ||
| 104 | return; | ||
| 105 | |||
| 106 | spin_lock(&ctx->lock); | ||
| 107 | |||
| 108 | if (counter->active) { | ||
| 109 | hw_perf_counter_disable(counter); | ||
| 110 | counter->active = 0; | ||
| 111 | ctx->nr_active--; | ||
| 112 | cpuctx->active_oncpu--; | ||
| 113 | counter->task = NULL; | ||
| 114 | } | ||
| 115 | ctx->nr_counters--; | ||
| 116 | |||
| 117 | /* | ||
| 118 | * Protect the list operation against NMI by disabling the | ||
| 119 | * counters on a global level. NOP for non NMI based counters. | ||
| 120 | */ | ||
| 121 | hw_perf_disable_all(); | ||
| 122 | list_del_init(&counter->list); | ||
| 123 | hw_perf_enable_all(); | ||
| 124 | |||
| 125 | if (!ctx->task) { | ||
| 126 | /* | ||
| 127 | * Allow more per task counters with respect to the | ||
| 128 | * reservation: | ||
| 129 | */ | ||
| 130 | cpuctx->max_pertask = | ||
| 131 | min(perf_max_counters - ctx->nr_counters, | ||
| 132 | perf_max_counters - perf_reserved_percpu); | ||
| 133 | } | ||
| 134 | |||
| 135 | spin_unlock(&ctx->lock); | ||
| 136 | } | ||
| 137 | |||
| 138 | |||
| 139 | /* | ||
| 140 | * Remove the counter from a task's (or a CPU's) list of counters. | ||
| 141 | * | ||
| 142 | * Must be called with counter->mutex held. | ||
| 143 | * | ||
| 144 | * CPU counters are removed with a smp call. For task counters we only | ||
| 145 | * call when the task is on a CPU. | ||
| 146 | */ | ||
| 147 | static void perf_remove_from_context(struct perf_counter *counter) | ||
| 148 | { | ||
| 149 | struct perf_counter_context *ctx = counter->ctx; | ||
| 150 | struct task_struct *task = ctx->task; | ||
| 151 | |||
| 152 | if (!task) { | ||
| 153 | /* | ||
| 154 | * Per cpu counters are removed via an smp call and | ||
| 155 | * the removal is always sucessful. | ||
| 156 | */ | ||
| 157 | smp_call_function_single(counter->cpu, | ||
| 158 | __perf_remove_from_context, | ||
| 159 | counter, 1); | ||
| 160 | return; | ||
| 161 | } | ||
| 162 | |||
| 163 | retry: | ||
| 164 | task_oncpu_function_call(task, __perf_remove_from_context, | ||
| 165 | counter); | ||
| 166 | |||
| 167 | spin_lock_irq(&ctx->lock); | ||
