diff options
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); | ||
| 168 | /* | ||
| 169 | * If the context is active we need to retry the smp call. | ||
| 170 | */ | ||
| 171 | if (ctx->nr_active && !list_empty(&counter->list)) { | ||
| 172 | spin_unlock_irq(&ctx->lock); | ||
| 173 | goto retry; | ||
| 174 | } | ||
| 175 | |||
| 176 | /* | ||
| 177 | * The lock prevents that this context is scheduled in so we | ||
