aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/Kconfig12
-rw-r--r--arch/x86/oprofile/nmi_int.c162
-rw-r--r--arch/x86/oprofile/op_counter.h2
-rw-r--r--arch/x86/oprofile/op_model_amd.c110
-rw-r--r--arch/x86/oprofile/op_model_p4.c4
-rw-r--r--arch/x86/oprofile/op_model_ppro.c2
-rw-r--r--arch/x86/oprofile/op_x86_model.h7
-rw-r--r--drivers/oprofile/oprof.c78
-rw-r--r--drivers/oprofile/oprof.h2
-rw-r--r--drivers/oprofile/oprofile_files.c43
-rw-r--r--drivers/oprofile/oprofile_stats.c10
-rw-r--r--include/linux/oprofile.h3
12 files changed, 415 insertions, 20 deletions
diff --git a/arch/Kconfig b/arch/Kconfig
index 99193b160232..beea3ccebb5e 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -30,6 +30,18 @@ config OPROFILE_IBS
30 30
31 If unsure, say N. 31 If unsure, say N.
32 32
33config OPROFILE_EVENT_MULTIPLEX
34 bool "OProfile multiplexing support (EXPERIMENTAL)"
35 default n
36 depends on OPROFILE && X86
37 help
38 The number of hardware counters is limited. The multiplexing
39 feature enables OProfile to gather more events than counters
40 are provided by the hardware. This is realized by switching
41 between events at an user specified time interval.
42
43 If unsure, say N.
44
33config HAVE_OPROFILE 45config HAVE_OPROFILE
34 bool 46 bool
35 47
diff --git a/arch/x86/oprofile/nmi_int.c b/arch/x86/oprofile/nmi_int.c
index fca8dc94531e..e54f6a0b35ac 100644
--- a/arch/x86/oprofile/nmi_int.c
+++ b/arch/x86/oprofile/nmi_int.c
@@ -1,11 +1,14 @@
1/** 1/**
2 * @file nmi_int.c 2 * @file nmi_int.c
3 * 3 *
4 * @remark Copyright 2002-2008 OProfile authors 4 * @remark Copyright 2002-2009 OProfile authors
5 * @remark Read the file COPYING 5 * @remark Read the file COPYING
6 * 6 *
7 * @author John Levon <levon@movementarian.org> 7 * @author John Levon <levon@movementarian.org>
8 * @author Robert Richter <robert.richter@amd.com> 8 * @author Robert Richter <robert.richter@amd.com>
9 * @author Barry Kasindorf <barry.kasindorf@amd.com>
10 * @author Jason Yeh <jason.yeh@amd.com>
11 * @author Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
9 */ 12 */
10 13
11#include <linux/init.h> 14#include <linux/init.h>
@@ -24,6 +27,12 @@
24#include "op_counter.h" 27#include "op_counter.h"
25#include "op_x86_model.h" 28#include "op_x86_model.h"
26 29
30
31#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
32DEFINE_PER_CPU(int, switch_index);
33#endif
34
35
27static struct op_x86_model_spec const *model; 36static struct op_x86_model_spec const *model;
28static DEFINE_PER_CPU(struct op_msrs, cpu_msrs); 37static DEFINE_PER_CPU(struct op_msrs, cpu_msrs);
29static DEFINE_PER_CPU(unsigned long, saved_lvtpc); 38static DEFINE_PER_CPU(unsigned long, saved_lvtpc);
@@ -31,6 +40,13 @@ static DEFINE_PER_CPU(unsigned long, saved_lvtpc);
31/* 0 == registered but off, 1 == registered and on */ 40/* 0 == registered but off, 1 == registered and on */
32static int nmi_enabled = 0; 41static int nmi_enabled = 0;
33 42
43
44#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
45extern atomic_t multiplex_counter;
46#endif
47
48struct op_counter_config counter_config[OP_MAX_COUNTER];
49
34/* common functions */ 50/* common functions */
35 51
36u64 op_x86_get_ctrl(struct op_x86_model_spec const *model, 52u64 op_x86_get_ctrl(struct op_x86_model_spec const *model,
@@ -95,6 +111,11 @@ static void free_msrs(void)
95 per_cpu(cpu_msrs, i).counters = NULL; 111 per_cpu(cpu_msrs, i).counters = NULL;
96 kfree(per_cpu(cpu_msrs, i).controls); 112 kfree(per_cpu(cpu_msrs, i).controls);
97 per_cpu(cpu_msrs, i).controls = NULL; 113 per_cpu(cpu_msrs, i).controls = NULL;
114
115#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
116 kfree(per_cpu(cpu_msrs, i).multiplex);
117 per_cpu(cpu_msrs, i).multiplex = NULL;
118#endif
98 } 119 }
99} 120}
100 121
@@ -103,6 +124,9 @@ static int allocate_msrs(void)
103 int success = 1; 124 int success = 1;
104 size_t controls_size = sizeof(struct op_msr) * model->num_controls; 125 size_t controls_size = sizeof(struct op_msr) * model->num_controls;
105 size_t counters_size = sizeof(struct op_msr) * model->num_counters; 126 size_t counters_size = sizeof(struct op_msr) * model->num_counters;
127#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
128 size_t multiplex_size = sizeof(struct op_msr) * model->num_virt_counters;
129#endif
106 130
107 int i; 131 int i;
108 for_each_possible_cpu(i) { 132 for_each_possible_cpu(i) {
@@ -118,6 +142,14 @@ static int allocate_msrs(void)
118 success = 0; 142 success = 0;
119 break; 143 break;
120 } 144 }
145#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
146 per_cpu(cpu_msrs, i).multiplex =
147 kmalloc(multiplex_size, GFP_KERNEL);
148 if (!per_cpu(cpu_msrs, i).multiplex) {
149 success = 0;
150 break;
151 }
152#endif
121 } 153 }
122 154
123 if (!success) 155 if (!success)
@@ -126,6 +158,25 @@ static int allocate_msrs(void)
126 return success; 158 return success;
127} 159}
128 160
161#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
162
163static void nmi_setup_cpu_mux(struct op_msrs const * const msrs)
164{
165 int i;
166 struct op_msr *multiplex = msrs->multiplex;
167
168 for (i = 0; i < model->num_virt_counters; ++i) {
169 if (counter_config[i].enabled) {
170 multiplex[i].saved = -(u64)counter_config[i].count;
171 } else {
172 multiplex[i].addr = 0;
173 multiplex[i].saved = 0;
174 }
175 }
176}
177
178#endif
179
129static void nmi_cpu_setup(void *dummy) 180static void nmi_cpu_setup(void *dummy)
130{ 181{
131 int cpu = smp_processor_id(); 182 int cpu = smp_processor_id();
@@ -133,6 +184,9 @@ static void nmi_cpu_setup(void *dummy)
133 nmi_cpu_save_registers(msrs); 184 nmi_cpu_save_registers(msrs);
134 spin_lock(&oprofilefs_lock); 185 spin_lock(&oprofilefs_lock);
135 model->setup_ctrs(model, msrs); 186 model->setup_ctrs(model, msrs);
187#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
188 nmi_setup_cpu_mux(msrs);
189#endif
136 spin_unlock(&oprofilefs_lock); 190 spin_unlock(&oprofilefs_lock);
137 per_cpu(saved_lvtpc, cpu) = apic_read(APIC_LVTPC); 191 per_cpu(saved_lvtpc, cpu) = apic_read(APIC_LVTPC);
138 apic_write(APIC_LVTPC, APIC_DM_NMI); 192 apic_write(APIC_LVTPC, APIC_DM_NMI);
@@ -173,14 +227,52 @@ static int nmi_setup(void)
173 memcpy(per_cpu(cpu_msrs, cpu).controls, 227 memcpy(per_cpu(cpu_msrs, cpu).controls,
174 per_cpu(cpu_msrs, 0).controls, 228 per_cpu(cpu_msrs, 0).controls,
175 sizeof(struct op_msr) * model->num_controls); 229 sizeof(struct op_msr) * model->num_controls);
230#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
231 memcpy(per_cpu(cpu_msrs, cpu).multiplex,
232 per_cpu(cpu_msrs, 0).multiplex,
233 sizeof(struct op_msr) * model->num_virt_counters);
234#endif
176 } 235 }
177
178 } 236 }
179 on_each_cpu(nmi_cpu_setup, NULL, 1); 237 on_each_cpu(nmi_cpu_setup, NULL, 1);
180 nmi_enabled = 1; 238 nmi_enabled = 1;
181 return 0; 239 return 0;
182} 240}
183 241
242#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
243
244static void nmi_cpu_save_mpx_registers(struct op_msrs *msrs)
245{
246 unsigned int si = __get_cpu_var(switch_index);
247 struct op_msr *multiplex = msrs->multiplex;
248 unsigned int i;
249
250 for (i = 0; i < model->num_counters; ++i) {
251 int offset = i + si;
252 if (multiplex[offset].addr) {
253 rdmsrl(multiplex[offset].addr,
254 multiplex[offset].saved);
255 }
256 }
257}
258
259static void nmi_cpu_restore_mpx_registers(struct op_msrs *msrs)
260{
261 unsigned int si = __get_cpu_var(switch_index);
262 struct op_msr *multiplex = msrs->multiplex;
263 unsigned int i;
264
265 for (i = 0; i < model->num_counters; ++i) {
266 int offset = i + si;
267 if (multiplex[offset].addr) {
268 wrmsrl(multiplex[offset].addr,
269 multiplex[offset].saved);
270 }
271 }
272}
273
274#endif
275
184static void nmi_cpu_restore_registers(struct op_msrs *msrs) 276static void nmi_cpu_restore_registers(struct op_msrs *msrs)
185{ 277{
186 struct op_msr *counters = msrs->counters; 278 struct op_msr *counters = msrs->counters;
@@ -214,6 +306,9 @@ static void nmi_cpu_shutdown(void *dummy)
214 apic_write(APIC_LVTPC, per_cpu(saved_lvtpc, cpu)); 306 apic_write(APIC_LVTPC, per_cpu(saved_lvtpc, cpu));
215 apic_write(APIC_LVTERR, v); 307 apic_write(APIC_LVTERR, v);
216 nmi_cpu_restore_registers(msrs); 308 nmi_cpu_restore_registers(msrs);
309#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
310 __get_cpu_var(switch_index) = 0;
311#endif
217} 312}
218 313
219static void nmi_shutdown(void) 314static void nmi_shutdown(void)
@@ -252,16 +347,15 @@ static void nmi_stop(void)
252 on_each_cpu(nmi_cpu_stop, NULL, 1); 347 on_each_cpu(nmi_cpu_stop, NULL, 1);
253} 348}
254 349
255struct op_counter_config counter_config[OP_MAX_COUNTER];
256
257static int nmi_create_files(struct super_block *sb, struct dentry *root) 350static int nmi_create_files(struct super_block *sb, struct dentry *root)
258{ 351{
259 unsigned int i; 352 unsigned int i;
260 353
261 for (i = 0; i < model->num_counters; ++i) { 354 for (i = 0; i < model->num_virt_counters; ++i) {
262 struct dentry *dir; 355 struct dentry *dir;
263 char buf[4]; 356 char buf[4];
264 357
358#ifndef CONFIG_OPROFILE_EVENT_MULTIPLEX
265 /* quick little hack to _not_ expose a counter if it is not 359 /* quick little hack to _not_ expose a counter if it is not
266 * available for use. This should protect userspace app. 360 * available for use. This should protect userspace app.
267 * NOTE: assumes 1:1 mapping here (that counters are organized 361 * NOTE: assumes 1:1 mapping here (that counters are organized
@@ -269,6 +363,7 @@ static int nmi_create_files(struct super_block *sb, struct dentry *root)
269 */ 363 */
270 if (unlikely(!avail_to_resrv_perfctr_nmi_bit(i))) 364 if (unlikely(!avail_to_resrv_perfctr_nmi_bit(i)))
271 continue; 365 continue;
366#endif /* CONFIG_OPROFILE_EVENT_MULTIPLEX */
272 367
273 snprintf(buf, sizeof(buf), "%d", i); 368 snprintf(buf, sizeof(buf), "%d", i);
274 dir = oprofilefs_mkdir(sb, root, buf); 369 dir = oprofilefs_mkdir(sb, root, buf);
@@ -283,6 +378,57 @@ static int nmi_create_files(struct super_block *sb, struct dentry *root)
283 return 0; 378 return 0;
284} 379}
285 380
381#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
382
383static void nmi_cpu_switch(void *dummy)
384{
385 int cpu = smp_processor_id();
386 int si = per_cpu(switch_index, cpu);
387 struct op_msrs *msrs = &per_cpu(cpu_msrs, cpu);
388
389 nmi_cpu_stop(NULL);
390 nmi_cpu_save_mpx_registers(msrs);
391
392 /* move to next set */
393 si += model->num_counters;
394 if ((si > model->num_virt_counters) || (counter_config[si].count == 0))
395 per_cpu(switch_index, cpu) = 0;
396 else
397 per_cpu(switch_index, cpu) = si;
398
399 model->switch_ctrl(model, msrs);
400 nmi_cpu_restore_mpx_registers(msrs);
401
402 nmi_cpu_start(NULL);
403}
404
405
406/*
407 * Quick check to see if multiplexing is necessary.
408 * The check should be sufficient since counters are used
409 * in ordre.
410 */
411static int nmi_multiplex_on(void)
412{
413 return counter_config[model->num_counters].count ? 0 : -EINVAL;
414}
415
416static int nmi_switch_event(void)
417{
418 if (!model->switch_ctrl)
419 return -ENOSYS; /* not implemented */
420 if (nmi_multiplex_on() < 0)
421 return -EINVAL; /* not necessary */
422
423 on_each_cpu(nmi_cpu_switch, NULL, 1);
424
425 atomic_inc(&multiplex_counter);
426
427 return 0;
428}
429
430#endif
431
286#ifdef CONFIG_SMP 432#ifdef CONFIG_SMP
287static int oprofile_cpu_notifier(struct notifier_block *b, unsigned long action, 433static int oprofile_cpu_notifier(struct notifier_block *b, unsigned long action,
288 void *data) 434 void *data)
@@ -516,12 +662,18 @@ int __init op_nmi_init(struct oprofile_operations *ops)
516 register_cpu_notifier(&oprofile_cpu_nb); 662 register_cpu_notifier(&oprofile_cpu_nb);
517#endif 663#endif
518 /* default values, can be overwritten by model */ 664 /* default values, can be overwritten by model */
665#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
666 __raw_get_cpu_var(switch_index) = 0;
667#endif
519 ops->create_files = nmi_create_files; 668 ops->create_files = nmi_create_files;
520 ops->setup = nmi_setup; 669 ops->setup = nmi_setup;
521 ops->shutdown = nmi_shutdown; 670 ops->shutdown = nmi_shutdown;
522 ops->start = nmi_start; 671 ops->start = nmi_start;
523 ops->stop = nmi_stop; 672 ops->stop = nmi_stop;
524 ops->cpu_type = cpu_type; 673 ops->cpu_type = cpu_type;
674#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
675 ops->switch_events = nmi_switch_event;
676#endif
525 677
526 if (model->init) 678 if (model->init)
527 ret = model->init(ops); 679 ret = model->init(ops);
diff --git a/arch/x86/oprofile/op_counter.h b/arch/x86/oprofile/op_counter.h
index 91b6a116165e..e28398df0df2 100644
--- a/arch/x86/oprofile/op_counter.h
+++ b/arch/x86/oprofile/op_counter.h
@@ -10,7 +10,7 @@
10#ifndef OP_COUNTER_H 10#ifndef OP_COUNTER_H
11#define OP_COUNTER_H 11#define OP_COUNTER_H
12 12
13#define OP_MAX_COUNTER 8 13#define OP_MAX_COUNTER 32
14 14
15/* Per-perfctr configuration as set via 15/* Per-perfctr configuration as set via
16 * oprofilefs. 16 * oprofilefs.
diff --git a/arch/x86/oprofile/op_model_amd.c b/arch/x86/oprofile/op_model_amd.c
index f676f8825a3f..fdbed3a0c877 100644
--- a/arch/x86/oprofile/op_model_amd.c
+++ b/arch/x86/oprofile/op_model_amd.c
@@ -9,12 +9,15 @@
9 * @author Philippe Elie 9 * @author Philippe Elie
10 * @author Graydon Hoare 10 * @author Graydon Hoare
11 * @author Robert Richter <robert.richter@amd.com> 11 * @author Robert Richter <robert.richter@amd.com>
12 * @author Barry Kasindorf 12 * @author Barry Kasindorf <barry.kasindorf@amd.com>
13 * @author Jason Yeh <jason.yeh@amd.com>
14 * @author Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
13 */ 15 */
14 16
15#include <linux/oprofile.h> 17#include <linux/oprofile.h>
16#include <linux/device.h> 18#include <linux/device.h>
17#include <linux/pci.h> 19#include <linux/pci.h>
20#include <linux/percpu.h>
18 21
19#include <asm/ptrace.h> 22#include <asm/ptrace.h>
20#include <asm/msr.h> 23#include <asm/msr.h>
@@ -25,12 +28,23 @@
25 28
26#define NUM_COUNTERS 4 29#define NUM_COUNTERS 4
27#define NUM_CONTROLS 4 30#define NUM_CONTROLS 4
31#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
32#define NUM_VIRT_COUNTERS 32
33#define NUM_VIRT_CONTROLS 32
34#else
35#define NUM_VIRT_COUNTERS NUM_COUNTERS
36#define NUM_VIRT_CONTROLS NUM_CONTROLS
37#endif
38
28#define OP_EVENT_MASK 0x0FFF 39#define OP_EVENT_MASK 0x0FFF
29#define OP_CTR_OVERFLOW (1ULL<<31) 40#define OP_CTR_OVERFLOW (1ULL<<31)
30 41
31#define MSR_AMD_EVENTSEL_RESERVED ((0xFFFFFCF0ULL<<32)|(1ULL<<21)) 42#define MSR_AMD_EVENTSEL_RESERVED ((0xFFFFFCF0ULL<<32)|(1ULL<<21))
32 43
33static unsigned long reset_value[NUM_COUNTERS]; 44static unsigned long reset_value[NUM_VIRT_COUNTERS];
45#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
46DECLARE_PER_CPU(int, switch_index);
47#endif
34 48
35#ifdef CONFIG_OPROFILE_IBS 49#ifdef CONFIG_OPROFILE_IBS
36 50
@@ -82,6 +96,16 @@ static void op_amd_fill_in_addresses(struct op_msrs * const msrs)
82 else 96 else
83 msrs->controls[i].addr = 0; 97 msrs->controls[i].addr = 0;
84 } 98 }
99
100#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
101 for (i = 0; i < NUM_VIRT_COUNTERS; i++) {
102 int hw_counter = i % NUM_CONTROLS;
103 if (reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i))
104 msrs->multiplex[i].addr = MSR_K7_PERFCTR0 + hw_counter;
105 else
106 msrs->multiplex[i].addr = 0;
107 }
108#endif
85} 109}
86 110
87static void op_amd_setup_ctrs(struct op_x86_model_spec const *model, 111static void op_amd_setup_ctrs(struct op_x86_model_spec const *model,
@@ -90,6 +114,15 @@ static void op_amd_setup_ctrs(struct op_x86_model_spec const *model,
90 u64 val; 114 u64 val;
91 int i; 115 int i;
92 116
117 /* setup reset_value */
118 for (i = 0; i < NUM_VIRT_COUNTERS; ++i) {
119 if (counter_config[i].enabled) {
120 reset_value[i] = counter_config[i].count;
121 } else {
122 reset_value[i] = 0;
123 }
124 }
125
93 /* clear all counters */ 126 /* clear all counters */
94 for (i = 0; i < NUM_CONTROLS; ++i) { 127 for (i = 0; i < NUM_CONTROLS; ++i) {
95 if (unlikely(!msrs->controls[i].addr)) 128 if (unlikely(!msrs->controls[i].addr))
@@ -108,20 +141,49 @@ static void op_amd_setup_ctrs(struct op_x86_model_spec const *model,
108 141
109 /* enable active counters */ 142 /* enable active counters */
110 for (i = 0; i < NUM_COUNTERS; ++i) { 143 for (i = 0; i < NUM_COUNTERS; ++i) {
111 if (counter_config[i].enabled && msrs->counters[i].addr) { 144#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
112 reset_value[i] = counter_config[i].count; 145 int offset = i + __get_cpu_var(switch_index);
113 wrmsrl(msrs->counters[i].addr, 146#else
114 -(u64)counter_config[i].count); 147 int offset = i;
148#endif
149 if (counter_config[offset].enabled && msrs->counters[i].addr) {
150 /* setup counter registers */
151 wrmsrl(msrs->counters[i].addr, -(u64)reset_value[offset]);
152
153 /* setup control registers */
115 rdmsrl(msrs->controls[i].addr, val); 154 rdmsrl(msrs->controls[i].addr, val);
116 val &= model->reserved; 155 val &= model->reserved;
117 val |= op_x86_get_ctrl(model, &counter_config[i]); 156 val |= op_x86_get_ctrl(model, &counter_config[offset]);
157 wrmsrl(msrs->controls[i].addr, val);
158 }
159 }
160}
161
162
163#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
164
165static void op_amd_switch_ctrl(struct op_x86_model_spec const *model,
166 struct op_msrs const * const msrs)
167{
168 u64 val;
169 int i;
170
171 /* enable active counters */
172 for (i = 0; i < NUM_COUNTERS; ++i) {
173 int offset = i + __get_cpu_var(switch_index);
174 if (counter_config[offset].enabled) {
175 /* setup control registers */
176 rdmsrl(msrs->controls[i].addr, val);
177 val &= model->reserved;
178 val |= op_x86_get_ctrl(model, &counter_config[offset]);
118 wrmsrl(msrs->controls[i].addr, val); 179 wrmsrl(msrs->controls[i].addr, val);
119 } else {
120 reset_value[i] = 0;
121 } 180 }
122 } 181 }
123} 182}
124 183
184#endif
185
186
125#ifdef CONFIG_OPROFILE_IBS 187#ifdef CONFIG_OPROFILE_IBS
126 188
127static inline int 189static inline int
@@ -230,14 +292,19 @@ static int op_amd_check_ctrs(struct pt_regs * const regs,
230 int i; 292 int i;
231 293
232 for (i = 0; i < NUM_COUNTERS; ++i) { 294 for (i = 0; i < NUM_COUNTERS; ++i) {
233 if (!reset_value[i]) 295#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
296 int offset = i + __get_cpu_var(switch_index);
297#else
298 int offset = i;
299#endif
300 if (!reset_value[offset])
234 continue; 301 continue;
235 rdmsrl(msrs->counters[i].addr, val); 302 rdmsrl(msrs->counters[i].addr, val);
236 /* bit is clear if overflowed: */ 303 /* bit is clear if overflowed: */
237 if (val & OP_CTR_OVERFLOW) 304 if (val & OP_CTR_OVERFLOW)
238 continue; 305 continue;
239 oprofile_add_sample(regs, i); 306 oprofile_add_sample(regs, offset);
240 wrmsrl(msrs->counters[i].addr, -(u64)reset_value[i]); 307 wrmsrl(msrs->counters[i].addr, -(u64)reset_value[offset]);
241 } 308 }
242 309
243 op_amd_handle_ibs(regs, msrs); 310 op_amd_handle_ibs(regs, msrs);
@@ -250,8 +317,14 @@ static void op_amd_start(struct op_msrs const * const msrs)
250{ 317{
251 u64 val; 318 u64 val;
252 int i; 319 int i;
320
253 for (i = 0; i < NUM_COUNTERS; ++i) { 321 for (i = 0; i < NUM_COUNTERS; ++i) {
254 if (reset_value[i]) { 322#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
323 int offset = i + __get_cpu_var(switch_index);
324#else
325 int offset = i;
326#endif
327 if (reset_value[offset]) {
255 rdmsrl(msrs->controls[i].addr, val); 328 rdmsrl(msrs->controls[i].addr, val);
256 val |= ARCH_PERFMON_EVENTSEL0_ENABLE; 329 val |= ARCH_PERFMON_EVENTSEL0_ENABLE;
257 wrmsrl(msrs->controls[i].addr, val); 330 wrmsrl(msrs->controls[i].addr, val);
@@ -271,7 +344,11 @@ static void op_amd_stop(struct op_msrs const * const msrs)
271 * pm callback 344 * pm callback
272 */ 345 */
273 for (i = 0; i < NUM_COUNTERS; ++i) { 346 for (i = 0; i < NUM_COUNTERS; ++i) {
347#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
348 if (!reset_value[i + per_cpu(switch_index, smp_processor_id())])
349#else
274 if (!reset_value[i]) 350 if (!reset_value[i])
351#endif
275 continue; 352 continue;
276 rdmsrl(msrs->controls[i].addr, val); 353 rdmsrl(msrs->controls[i].addr, val);
277 val &= ~ARCH_PERFMON_EVENTSEL0_ENABLE; 354 val &= ~ARCH_PERFMON_EVENTSEL0_ENABLE;
@@ -289,7 +366,7 @@ static void op_amd_shutdown(struct op_msrs const * const msrs)
289 if (msrs->counters[i].addr) 366 if (msrs->counters[i].addr)
290 release_perfctr_nmi(MSR_K7_PERFCTR0 + i); 367 release_perfctr_nmi(MSR_K7_PERFCTR0 + i);
291 } 368 }
292 for (i = 0; i < NUM_CONTROLS; ++i) { 369 for (i = 0; i < NUM_COUNTERS; ++i) {
293 if (msrs->controls[i].addr) 370 if (msrs->controls[i].addr)
294 release_evntsel_nmi(MSR_K7_EVNTSEL0 + i); 371 release_evntsel_nmi(MSR_K7_EVNTSEL0 + i);
295 } 372 }
@@ -463,6 +540,8 @@ static void op_amd_exit(void) {}
463struct op_x86_model_spec const op_amd_spec = { 540struct op_x86_model_spec const op_amd_spec = {
464 .num_counters = NUM_COUNTERS, 541 .num_counters = NUM_COUNTERS,
465 .num_controls = NUM_CONTROLS, 542 .num_controls = NUM_CONTROLS,
543 .num_virt_counters = NUM_VIRT_COUNTERS,
544 .num_virt_controls = NUM_VIRT_CONTROLS,
466 .reserved = MSR_AMD_EVENTSEL_RESERVED, 545 .reserved = MSR_AMD_EVENTSEL_RESERVED,
467 .event_mask = OP_EVENT_MASK, 546 .event_mask = OP_EVENT_MASK,
468 .init = op_amd_init, 547 .init = op_amd_init,
@@ -473,4 +552,7 @@ struct op_x86_model_spec const op_amd_spec = {
473 .start = &op_amd_start, 552 .start = &op_amd_start,
474 .stop = &op_amd_stop, 553 .stop = &op_amd_stop,
475 .shutdown = &op_amd_shutdown, 554 .shutdown = &op_amd_shutdown,
555#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
556 .switch_ctrl = &op_amd_switch_ctrl,
557#endif
476}; 558};
diff --git a/arch/x86/oprofile/op_model_p4.c b/arch/x86/oprofile/op_model_p4.c
index 5921b7fc724b..65b9237cde8b 100644
--- a/arch/x86/oprofile/op_model_p4.c
+++ b/arch/x86/oprofile/op_model_p4.c
@@ -698,6 +698,8 @@ static void p4_shutdown(struct op_msrs const * const msrs)
698struct op_x86_model_spec const op_p4_ht2_spec = { 698struct op_x86_model_spec const op_p4_ht2_spec = {
699 .num_counters = NUM_COUNTERS_HT2, 699 .num_counters = NUM_COUNTERS_HT2,
700 .num_controls = NUM_CONTROLS_HT2, 700 .num_controls = NUM_CONTROLS_HT2,
701 .num_virt_counters = NUM_COUNTERS_HT2,
702 .num_virt_controls = NUM_CONTROLS_HT2,
701 .fill_in_addresses = &p4_fill_in_addresses, 703 .fill_in_addresses = &p4_fill_in_addresses,
702 .setup_ctrs = &p4_setup_ctrs, 704 .setup_ctrs = &p4_setup_ctrs,
703 .check_ctrs = &p4_check_ctrs, 705 .check_ctrs = &p4_check_ctrs,
@@ -710,6 +712,8 @@ struct op_x86_model_spec const op_p4_ht2_spec = {
710struct op_x86_model_spec const op_p4_spec = { 712struct op_x86_model_spec const op_p4_spec = {
711 .num_counters = NUM_COUNTERS_NON_HT, 713 .num_counters = NUM_COUNTERS_NON_HT,
712 .num_controls = NUM_CONTROLS_NON_HT, 714 .num_controls = NUM_CONTROLS_NON_HT,
715 .num_virt_counters = NUM_COUNTERS_NON_HT,
716 .num_virt_controls = NUM_CONTROLS_NON_HT,
713 .fill_in_addresses = &p4_fill_in_addresses, 717 .fill_in_addresses = &p4_fill_in_addresses,
714 .setup_ctrs = &p4_setup_ctrs, 718 .setup_ctrs = &p4_setup_ctrs,
715 .check_ctrs = &p4_check_ctrs, 719 .check_ctrs = &p4_check_ctrs,
diff --git a/arch/x86/oprofile/op_model_ppro.c b/arch/x86/oprofile/op_model_ppro.c
index 570d717c3308..098cbca5c0b0 100644
--- a/arch/x86/oprofile/op_model_ppro.c
+++ b/arch/x86/oprofile/op_model_ppro.c
@@ -206,6 +206,8 @@ static void ppro_shutdown(struct op_msrs const * const msrs)
206struct op_x86_model_spec const op_ppro_spec = { 206struct op_x86_model_spec const op_ppro_spec = {
207 .num_counters = 2, 207 .num_counters = 2,
208 .num_controls = 2, 208 .num_controls = 2,
209 .num_virt_counters = 2,
210 .num_virt_controls = 2,
209 .reserved = MSR_PPRO_EVENTSEL_RESERVED, 211 .reserved = MSR_PPRO_EVENTSEL_RESERVED,
210 .fill_in_addresses = &ppro_fill_in_addresses, 212 .fill_in_addresses = &ppro_fill_in_addresses,
211 .setup_ctrs = &ppro_setup_ctrs, 213 .setup_ctrs = &ppro_setup_ctrs,
diff --git a/arch/x86/oprofile/op_x86_model.h b/arch/x86/oprofile/op_x86_model.h
index 505489873b9d..0d07d23cb062 100644
--- a/arch/x86/oprofile/op_x86_model.h
+++ b/arch/x86/oprofile/op_x86_model.h
@@ -23,6 +23,7 @@ struct op_msr {
23struct op_msrs { 23struct op_msrs {
24 struct op_msr *counters; 24 struct op_msr *counters;
25 struct op_msr *controls; 25 struct op_msr *controls;
26 struct op_msr *multiplex;
26}; 27};
27 28
28struct pt_regs; 29struct pt_regs;
@@ -35,6 +36,8 @@ struct oprofile_operations;
35struct op_x86_model_spec { 36struct op_x86_model_spec {
36 unsigned int num_counters; 37 unsigned int num_counters;
37 unsigned int num_controls; 38 unsigned int num_controls;
39 unsigned int num_virt_counters;
40 unsigned int num_virt_controls;
38 u64 reserved; 41 u64 reserved;
39 u16 event_mask; 42 u16 event_mask;
40 int (*init)(struct oprofile_operations *ops); 43 int (*init)(struct oprofile_operations *ops);
@@ -47,6 +50,10 @@ struct op_x86_model_spec {
47 void (*start)(struct op_msrs const * const msrs); 50 void (*start)(struct op_msrs const * const msrs);
48 void (*stop)(struct op_msrs const * const msrs); 51 void (*stop)(struct op_msrs const * const msrs);
49 void (*shutdown)(struct op_msrs const * const msrs); 52 void (*shutdown)(struct op_msrs const * const msrs);
53#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
54 void (*switch_ctrl)(struct op_x86_model_spec const *model,
55 struct op_msrs const * const msrs);
56#endif
50}; 57};
51 58
52struct op_counter_config; 59struct op_counter_config;
diff --git a/drivers/oprofile/oprof.c b/drivers/oprofile/oprof.c
index 3cffce90f82a..7bc64af7cf99 100644
--- a/drivers/oprofile/oprof.c
+++ b/drivers/oprofile/oprof.c
@@ -12,6 +12,8 @@
12#include <linux/init.h> 12#include <linux/init.h>
13#include <linux/oprofile.h> 13#include <linux/oprofile.h>
14#include <linux/moduleparam.h> 14#include <linux/moduleparam.h>
15#include <linux/workqueue.h>
16#include <linux/time.h>
15#include <asm/mutex.h> 17#include <asm/mutex.h>
16 18
17#include "oprof.h" 19#include "oprof.h"
@@ -27,6 +29,15 @@ unsigned long oprofile_backtrace_depth;
27static unsigned long is_setup; 29static unsigned long is_setup;
28static DEFINE_MUTEX(start_mutex); 30static DEFINE_MUTEX(start_mutex);
29 31
32#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
33
34static void switch_worker(struct work_struct *work);
35static DECLARE_DELAYED_WORK(switch_work, switch_worker);
36unsigned long timeout_jiffies;
37#define MULTIPLEXING_TIMER_DEFAULT 1
38
39#endif
40
30/* timer 41/* timer
31 0 - use performance monitoring hardware if available 42 0 - use performance monitoring hardware if available
32 1 - use the timer int mechanism regardless 43 1 - use the timer int mechanism regardless
@@ -87,6 +98,20 @@ out:
87 return err; 98 return err;
88} 99}
89 100
101#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
102
103static void start_switch_worker(void)
104{
105 schedule_delayed_work(&switch_work, timeout_jiffies);
106}
107
108static void switch_worker(struct work_struct *work)
109{
110 if (!oprofile_ops.switch_events())
111 start_switch_worker();
112}
113
114#endif
90 115
91/* Actually start profiling (echo 1>/dev/oprofile/enable) */ 116/* Actually start profiling (echo 1>/dev/oprofile/enable) */
92int oprofile_start(void) 117int oprofile_start(void)
@@ -108,6 +133,11 @@ int oprofile_start(void)
108 if ((err = oprofile_ops.start())) 133 if ((err = oprofile_ops.start()))
109 goto out; 134 goto out;
110 135
136#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
137 if (oprofile_ops.switch_events)
138 start_switch_worker();
139#endif
140
111 oprofile_started = 1; 141 oprofile_started = 1;
112out: 142out:
113 mutex_unlock(&start_mutex); 143 mutex_unlock(&start_mutex);
@@ -123,6 +153,11 @@ void oprofile_stop(void)
123 goto out; 153 goto out;
124 oprofile_ops.stop(); 154 oprofile_ops.stop();
125 oprofile_started = 0; 155 oprofile_started = 0;
156
157#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
158 cancel_delayed_work_sync(&switch_work);
159#endif
160
126 /* wake up the daemon to read what remains */ 161 /* wake up the daemon to read what remains */
127 wake_up_buffer_waiter(); 162 wake_up_buffer_waiter();
128out: 163out:
@@ -155,6 +190,36 @@ post_sync:
155 mutex_unlock(&start_mutex); 190 mutex_unlock(&start_mutex);
156} 191}
157 192
193#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
194
195/* User inputs in ms, converts to jiffies */
196int oprofile_set_timeout(unsigned long val_msec)
197{
198 int err = 0;
199
200 mutex_lock(&start_mutex);
201
202 if (oprofile_started) {
203 err = -EBUSY;
204 goto out;
205 }
206
207 if (!oprofile_ops.switch_events) {
208 err = -EINVAL;
209 goto out;
210 }
211
212 timeout_jiffies = msecs_to_jiffies(val_msec);
213 if (timeout_jiffies == MAX_JIFFY_OFFSET)
214 timeout_jiffies = msecs_to_jiffies(MULTIPLEXING_TIMER_DEFAULT);
215
216out:
217 mutex_unlock(&start_mutex);
218 return err;
219
220}
221
222#endif
158 223
159int oprofile_set_backtrace(unsigned long val) 224int oprofile_set_backtrace(unsigned long val)
160{ 225{
@@ -179,10 +244,23 @@ out:
179 return err; 244 return err;
180} 245}
181 246
247#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
248
249static void __init oprofile_multiplexing_init(void)
250{
251 timeout_jiffies = msecs_to_jiffies(MULTIPLEXING_TIMER_DEFAULT);
252}
253
254#endif
255
182static int __init oprofile_init(void) 256static int __init oprofile_init(void)
183{ 257{
184 int err; 258 int err;
185 259
260#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
261 oprofile_multiplexing_init();
262#endif
263
186 err = oprofile_arch_init(&oprofile_ops); 264 err = oprofile_arch_init(&oprofile_ops);
187 265
188 if (err < 0 || timer) { 266 if (err < 0 || timer) {
diff --git a/drivers/oprofile/oprof.h b/drivers/oprofile/oprof.h
index c288d3c24b50..ee38abcc74f3 100644
--- a/drivers/oprofile/oprof.h
+++ b/drivers/oprofile/oprof.h
@@ -27,6 +27,7 @@ extern unsigned long oprofile_buffer_watershed;
27extern struct oprofile_operations oprofile_ops; 27extern struct oprofile_operations oprofile_ops;
28extern unsigned long oprofile_started; 28extern unsigned long oprofile_started;
29extern unsigned long oprofile_backtrace_depth; 29extern unsigned long oprofile_backtrace_depth;
30extern unsigned long timeout_jiffies;
30 31
31struct super_block; 32struct super_block;
32struct dentry; 33struct dentry;
@@ -35,5 +36,6 @@ void oprofile_create_files(struct super_block *sb, struct dentry *root);
35void oprofile_timer_init(struct oprofile_operations *ops); 36void oprofile_timer_init(struct oprofile_operations *ops);
36 37
37int oprofile_set_backtrace(unsigned long depth); 38int oprofile_set_backtrace(unsigned long depth);
39int oprofile_set_timeout(unsigned long time);
38 40
39#endif /* OPROF_H */ 41#endif /* OPROF_H */
diff --git a/drivers/oprofile/oprofile_files.c b/drivers/oprofile/oprofile_files.c
index 5d36ffc30dd5..468ec3e4f856 100644
--- a/drivers/oprofile/oprofile_files.c
+++ b/drivers/oprofile/oprofile_files.c
@@ -9,6 +9,7 @@
9 9
10#include <linux/fs.h> 10#include <linux/fs.h>
11#include <linux/oprofile.h> 11#include <linux/oprofile.h>
12#include <linux/jiffies.h>
12 13
13#include "event_buffer.h" 14#include "event_buffer.h"
14#include "oprofile_stats.h" 15#include "oprofile_stats.h"
@@ -22,6 +23,45 @@ unsigned long oprofile_buffer_size;
22unsigned long oprofile_cpu_buffer_size; 23unsigned long oprofile_cpu_buffer_size;
23unsigned long oprofile_buffer_watershed; 24unsigned long oprofile_buffer_watershed;
24 25
26#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
27
28static ssize_t timeout_read(struct file *file, char __user *buf,
29 size_t count, loff_t *offset)
30{
31 return oprofilefs_ulong_to_user(jiffies_to_msecs(timeout_jiffies),
32 buf, count, offset);
33}
34
35
36static ssize_t timeout_write(struct file *file, char const __user *buf,
37 size_t count, loff_t *offset)
38{
39 unsigned long val;
40 int retval;
41
42 if (*offset)
43 return -EINVAL;
44
45 retval = oprofilefs_ulong_from_user(&val, buf, count);
46 if (retval)
47 return retval;
48
49 retval = oprofile_set_timeout(val);
50
51 if (retval)
52 return retval;
53 return count;
54}
55
56
57static const struct file_operations timeout_fops = {
58 .read = timeout_read,
59 .write = timeout_write,
60};
61
62#endif
63
64
25static ssize_t depth_read(struct file *file, char __user *buf, size_t count, loff_t *offset) 65static ssize_t depth_read(struct file *file, char __user *buf, size_t count, loff_t *offset)
26{ 66{
27 return oprofilefs_ulong_to_user(oprofile_backtrace_depth, buf, count, 67 return oprofilefs_ulong_to_user(oprofile_backtrace_depth, buf, count,
@@ -139,6 +179,9 @@ void oprofile_create_files(struct super_block *sb, struct dentry *root)
139 oprofilefs_create_file(sb, root, "cpu_type", &cpu_type_fops); 179 oprofilefs_create_file(sb, root, "cpu_type", &cpu_type_fops);
140 oprofilefs_create_file(sb, root, "backtrace_depth", &depth_fops); 180 oprofilefs_create_file(sb, root, "backtrace_depth", &depth_fops);
141 oprofilefs_create_file(sb, root, "pointer_size", &pointer_size_fops); 181 oprofilefs_create_file(sb, root, "pointer_size", &pointer_size_fops);
182#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
183 oprofilefs_create_file(sb, root, "time_slice", &timeout_fops);
184#endif
142 oprofile_create_stats_files(sb, root); 185 oprofile_create_stats_files(sb, root);
143 if (oprofile_ops.create_files) 186 if (oprofile_ops.create_files)
144 oprofile_ops.create_files(sb, root); 187 oprofile_ops.create_files(sb, root);
diff --git a/drivers/oprofile/oprofile_stats.c b/drivers/oprofile/oprofile_stats.c
index 3c2270a8300c..77a57a6792f6 100644
--- a/drivers/oprofile/oprofile_stats.c
+++ b/drivers/oprofile/oprofile_stats.c
@@ -16,6 +16,9 @@
16#include "cpu_buffer.h" 16#include "cpu_buffer.h"
17 17
18struct oprofile_stat_struct oprofile_stats; 18struct oprofile_stat_struct oprofile_stats;
19#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
20atomic_t multiplex_counter;
21#endif
19 22
20void oprofile_reset_stats(void) 23void oprofile_reset_stats(void)
21{ 24{
@@ -34,6 +37,9 @@ void oprofile_reset_stats(void)
34 atomic_set(&oprofile_stats.sample_lost_no_mapping, 0); 37 atomic_set(&oprofile_stats.sample_lost_no_mapping, 0);
35 atomic_set(&oprofile_stats.event_lost_overflow, 0); 38 atomic_set(&oprofile_stats.event_lost_overflow, 0);
36 atomic_set(&oprofile_stats.bt_lost_no_mapping, 0); 39 atomic_set(&oprofile_stats.bt_lost_no_mapping, 0);
40#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
41 atomic_set(&multiplex_counter, 0);
42#endif
37} 43}
38 44
39 45
@@ -76,4 +82,8 @@ void oprofile_create_stats_files(struct super_block *sb, struct dentry *root)
76 &oprofile_stats.event_lost_overflow); 82 &oprofile_stats.event_lost_overflow);
77 oprofilefs_create_ro_atomic(sb, dir, "bt_lost_no_mapping", 83 oprofilefs_create_ro_atomic(sb, dir, "bt_lost_no_mapping",
78 &oprofile_stats.bt_lost_no_mapping); 84 &oprofile_stats.bt_lost_no_mapping);
85#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
86 oprofilefs_create_ro_atomic(sb, dir, "multiplex_counter",
87 &multiplex_counter);
88#endif
79} 89}
diff --git a/include/linux/oprofile.h b/include/linux/oprofile.h
index d68d2ed94f15..5171639ecf0f 100644
--- a/include/linux/oprofile.h
+++ b/include/linux/oprofile.h
@@ -67,6 +67,9 @@ struct oprofile_operations {
67 67
68 /* Initiate a stack backtrace. Optional. */ 68 /* Initiate a stack backtrace. Optional. */
69 void (*backtrace)(struct pt_regs * const regs, unsigned int depth); 69 void (*backtrace)(struct pt_regs * const regs, unsigned int depth);
70
71 /* Multiplex between different events. Optional. */
72 int (*switch_events)(void);
70 /* CPU identification string. */ 73 /* CPU identification string. */
71 char * cpu_type; 74 char * cpu_type;
72}; 75};