op_model_amd.c 11.3 KB
Newer Older
1
/*
2
 * @file op_model_amd.c
3
 * athlon / K7 / K8 / Family 10h model-specific MSR operations
L
Linus Torvalds 已提交
4
 *
5
 * @remark Copyright 2002-2009 OProfile authors
L
Linus Torvalds 已提交
6 7 8 9 10
 * @remark Read the file COPYING
 *
 * @author John Levon
 * @author Philippe Elie
 * @author Graydon Hoare
11
 * @author Robert Richter <robert.richter@amd.com>
12
 * @author Barry Kasindorf
13
 */
L
Linus Torvalds 已提交
14 15

#include <linux/oprofile.h>
16 17 18
#include <linux/device.h>
#include <linux/pci.h>

L
Linus Torvalds 已提交
19 20
#include <asm/ptrace.h>
#include <asm/msr.h>
21
#include <asm/nmi.h>
22

L
Linus Torvalds 已提交
23 24 25
#include "op_x86_model.h"
#include "op_counter.h"

26 27
#define NUM_COUNTERS 4
#define NUM_CONTROLS 4
28
#define OP_EVENT_MASK			0x0FFF
29
#define OP_CTR_OVERFLOW			(1ULL<<31)
30 31

#define MSR_AMD_EVENTSEL_RESERVED	((0xFFFFFCF0ULL<<32)|(1ULL<<21))
L
Linus Torvalds 已提交
32

33 34 35 36
static unsigned long reset_value[NUM_COUNTERS];

#ifdef CONFIG_OPROFILE_IBS

37 38 39 40
/* IbsFetchCtl bits/masks */
#define IBS_FETCH_HIGH_VALID_BIT	(1UL << 17)	/* bit 49 */
#define IBS_FETCH_HIGH_ENABLE		(1UL << 16)	/* bit 48 */
#define IBS_FETCH_LOW_MAX_CNT_MASK	0x0000FFFFUL	/* MaxCnt mask */
41

42 43 44
/*IbsOpCtl bits */
#define IBS_OP_LOW_VALID_BIT		(1ULL<<18)	/* bit 18 */
#define IBS_OP_LOW_ENABLE		(1ULL<<17)	/* bit 17 */
45

46 47
#define IBS_FETCH_SIZE	6
#define IBS_OP_SIZE	12
48

49
static int has_ibs;	/* AMD Family10h and later */
50 51 52 53 54 55 56 57 58 59 60

struct op_ibs_config {
	unsigned long op_enabled;
	unsigned long fetch_enabled;
	unsigned long max_cnt_fetch;
	unsigned long max_cnt_op;
	unsigned long rand_en;
	unsigned long dispatched_ops;
};

static struct op_ibs_config ibs_config;
61

62 63
#endif

64
/* functions for op_amd_spec */
65

66
static void op_amd_fill_in_addresses(struct op_msrs * const msrs)
L
Linus Torvalds 已提交
67
{
68 69
	int i;

70
	for (i = 0; i < NUM_COUNTERS; i++) {
71 72
		if (reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i))
			msrs->counters[i].addr = MSR_K7_PERFCTR0 + i;
73 74 75 76
		else
			msrs->counters[i].addr = 0;
	}

77
	for (i = 0; i < NUM_CONTROLS; i++) {
78 79
		if (reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i))
			msrs->controls[i].addr = MSR_K7_EVNTSEL0 + i;
80 81 82
		else
			msrs->controls[i].addr = 0;
	}
L
Linus Torvalds 已提交
83 84
}

85 86
static void op_amd_setup_ctrs(struct op_x86_model_spec const *model,
			      struct op_msrs const * const msrs)
L
Linus Torvalds 已提交
87
{
88
	u64 val;
L
Linus Torvalds 已提交
89
	int i;
90

L
Linus Torvalds 已提交
91
	/* clear all counters */
92
	for (i = 0 ; i < NUM_CONTROLS; ++i) {
93
		if (unlikely(!CTRL_IS_RESERVED(msrs, i)))
94
			continue;
95 96 97
		rdmsrl(msrs->controls[i].addr, val);
		val &= model->reserved;
		wrmsrl(msrs->controls[i].addr, val);
L
Linus Torvalds 已提交
98
	}
99

L
Linus Torvalds 已提交
100
	/* avoid a false detection of ctr overflows in NMI handler */
101
	for (i = 0; i < NUM_COUNTERS; ++i) {
102
		if (unlikely(!CTR_IS_RESERVED(msrs, i)))
103
			continue;
104
		wrmsr(msrs->counters[i].addr, -1, -1);
L
Linus Torvalds 已提交
105 106 107
	}

	/* enable active counters */
108 109 110
	for (i = 0; i < NUM_COUNTERS; ++i) {
		if ((counter_config[i].enabled) && (CTR_IS_RESERVED(msrs, i))) {
			reset_value[i] = counter_config[i].count;
111
			wrmsr(msrs->counters[i].addr, -(unsigned int)counter_config[i].count, -1);
112 113 114 115
			rdmsrl(msrs->controls[i].addr, val);
			val &= model->reserved;
			val |= op_x86_get_ctrl(model, &counter_config[i]);
			wrmsrl(msrs->controls[i].addr, val);
116 117
		} else {
			reset_value[i] = 0;
L
Linus Torvalds 已提交
118 119 120 121
		}
	}
}

122 123
#ifdef CONFIG_OPROFILE_IBS

124 125 126
static inline int
op_amd_handle_ibs(struct pt_regs * const regs,
		  struct op_msrs const * const msrs)
L
Linus Torvalds 已提交
127
{
128 129 130
	u32 low, high;
	u64 msr;
	struct op_entry entry;
L
Linus Torvalds 已提交
131

132
	if (!has_ibs)
133
		return 1;
L
Linus Torvalds 已提交
134

135
	if (ibs_config.fetch_enabled) {
136
		rdmsr(MSR_AMD64_IBSFETCHCTL, low, high);
137
		if (high & IBS_FETCH_HIGH_VALID_BIT) {
138
			rdmsrl(MSR_AMD64_IBSFETCHLINAD, msr);
139 140 141 142 143 144
			oprofile_write_reserve(&entry, regs, msr,
					       IBS_FETCH_CODE, IBS_FETCH_SIZE);
			oprofile_add_data(&entry, (u32)msr);
			oprofile_add_data(&entry, (u32)(msr >> 32));
			oprofile_add_data(&entry, low);
			oprofile_add_data(&entry, high);
145
			rdmsrl(MSR_AMD64_IBSFETCHPHYSAD, msr);
146 147 148
			oprofile_add_data(&entry, (u32)msr);
			oprofile_add_data(&entry, (u32)(msr >> 32));
			oprofile_write_commit(&entry);
149

R
Robert Richter 已提交
150
			/* reenable the IRQ */
151 152 153
			high &= ~IBS_FETCH_HIGH_VALID_BIT;
			high |= IBS_FETCH_HIGH_ENABLE;
			low &= IBS_FETCH_LOW_MAX_CNT_MASK;
154 155 156 157
			wrmsr(MSR_AMD64_IBSFETCHCTL, low, high);
		}
	}

158
	if (ibs_config.op_enabled) {
159
		rdmsr(MSR_AMD64_IBSOPCTL, low, high);
160
		if (low & IBS_OP_LOW_VALID_BIT) {
161
			rdmsrl(MSR_AMD64_IBSOPRIP, msr);
162 163 164 165
			oprofile_write_reserve(&entry, regs, msr,
					       IBS_OP_CODE, IBS_OP_SIZE);
			oprofile_add_data(&entry, (u32)msr);
			oprofile_add_data(&entry, (u32)(msr >> 32));
166
			rdmsrl(MSR_AMD64_IBSOPDATA, msr);
167 168
			oprofile_add_data(&entry, (u32)msr);
			oprofile_add_data(&entry, (u32)(msr >> 32));
169
			rdmsrl(MSR_AMD64_IBSOPDATA2, msr);
170 171
			oprofile_add_data(&entry, (u32)msr);
			oprofile_add_data(&entry, (u32)(msr >> 32));
172
			rdmsrl(MSR_AMD64_IBSOPDATA3, msr);
173 174
			oprofile_add_data(&entry, (u32)msr);
			oprofile_add_data(&entry, (u32)(msr >> 32));
175
			rdmsrl(MSR_AMD64_IBSDCLINAD, msr);
176 177
			oprofile_add_data(&entry, (u32)msr);
			oprofile_add_data(&entry, (u32)(msr >> 32));
178
			rdmsrl(MSR_AMD64_IBSDCPHYSAD, msr);
179 180 181
			oprofile_add_data(&entry, (u32)msr);
			oprofile_add_data(&entry, (u32)(msr >> 32));
			oprofile_write_commit(&entry);
182 183

			/* reenable the IRQ */
184
			high = 0;
185 186
			low &= ~IBS_OP_LOW_VALID_BIT;
			low |= IBS_OP_LOW_ENABLE;
187 188 189 190
			wrmsr(MSR_AMD64_IBSOPCTL, low, high);
		}
	}

L
Linus Torvalds 已提交
191 192 193
	return 1;
}

194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
static inline void op_amd_start_ibs(void)
{
	unsigned int low, high;
	if (has_ibs && ibs_config.fetch_enabled) {
		low = (ibs_config.max_cnt_fetch >> 4) & 0xFFFF;
		high = ((ibs_config.rand_en & 0x1) << 25) /* bit 57 */
			+ IBS_FETCH_HIGH_ENABLE;
		wrmsr(MSR_AMD64_IBSFETCHCTL, low, high);
	}

	if (has_ibs && ibs_config.op_enabled) {
		low = ((ibs_config.max_cnt_op >> 4) & 0xFFFF)
			+ ((ibs_config.dispatched_ops & 0x1) << 19) /* bit 19 */
			+ IBS_OP_LOW_ENABLE;
		high = 0;
		wrmsr(MSR_AMD64_IBSOPCTL, low, high);
	}
}

static void op_amd_stop_ibs(void)
{
	unsigned int low, high;
	if (has_ibs && ibs_config.fetch_enabled) {
		/* clear max count and enable */
		low = 0;
		high = 0;
		wrmsr(MSR_AMD64_IBSFETCHCTL, low, high);
	}

	if (has_ibs && ibs_config.op_enabled) {
		/* clear max count and enable */
		low = 0;
		high = 0;
		wrmsr(MSR_AMD64_IBSOPCTL, low, high);
	}
}

#else

static inline int op_amd_handle_ibs(struct pt_regs * const regs,
				    struct op_msrs const * const msrs) { }
static inline void op_amd_start_ibs(void) { }
static inline void op_amd_stop_ibs(void) { }

238 239
#endif

240 241 242
static int op_amd_check_ctrs(struct pt_regs * const regs,
			     struct op_msrs const * const msrs)
{
243
	u64 val;
244 245
	int i;

246 247
	for (i = 0 ; i < NUM_COUNTERS; ++i) {
		if (!reset_value[i])
248
			continue;
249 250 251 252 253 254
		rdmsrl(msrs->counters[i].addr, val);
		/* bit is clear if overflowed: */
		if (val & OP_CTR_OVERFLOW)
			continue;
		oprofile_add_sample(regs, i);
		wrmsr(msrs->counters[i].addr, -(unsigned int)reset_value[i], -1);
255 256 257 258 259 260 261
	}

	op_amd_handle_ibs(regs, msrs);

	/* See op_model_ppro.c */
	return 1;
}
262

263
static void op_amd_start(struct op_msrs const * const msrs)
L
Linus Torvalds 已提交
264
{
265
	u64 val;
L
Linus Torvalds 已提交
266
	int i;
267 268
	for (i = 0 ; i < NUM_COUNTERS ; ++i) {
		if (reset_value[i]) {
269 270 271
			rdmsrl(msrs->controls[i].addr, val);
			val |= ARCH_PERFMON_EVENTSEL0_ENABLE;
			wrmsrl(msrs->controls[i].addr, val);
L
Linus Torvalds 已提交
272 273
		}
	}
274

275
	op_amd_start_ibs();
L
Linus Torvalds 已提交
276 277
}

278
static void op_amd_stop(struct op_msrs const * const msrs)
L
Linus Torvalds 已提交
279
{
280
	u64 val;
L
Linus Torvalds 已提交
281 282
	int i;

R
Robert Richter 已提交
283 284 285 286
	/*
	 * Subtle: stop on all counters to avoid race with setting our
	 * pm callback
	 */
287 288
	for (i = 0 ; i < NUM_COUNTERS ; ++i) {
		if (!reset_value[i])
289
			continue;
290 291 292
		rdmsrl(msrs->controls[i].addr, val);
		val &= ~ARCH_PERFMON_EVENTSEL0_ENABLE;
		wrmsrl(msrs->controls[i].addr, val);
L
Linus Torvalds 已提交
293
	}
294

295
	op_amd_stop_ibs();
L
Linus Torvalds 已提交
296 297
}

298
static void op_amd_shutdown(struct op_msrs const * const msrs)
299 300 301
{
	int i;

302
	for (i = 0 ; i < NUM_COUNTERS ; ++i) {
303
		if (CTR_IS_RESERVED(msrs, i))
304 305
			release_perfctr_nmi(MSR_K7_PERFCTR0 + i);
	}
306
	for (i = 0 ; i < NUM_CONTROLS ; ++i) {
307
		if (CTRL_IS_RESERVED(msrs, i))
308 309 310
			release_evntsel_nmi(MSR_K7_EVNTSEL0 + i);
	}
}
L
Linus Torvalds 已提交
311

312
#ifdef CONFIG_OPROFILE_IBS
313

314 315
static u8 ibs_eilvt_off;

316 317
static inline void apic_init_ibs_nmi_per_cpu(void *arg)
{
318
	ibs_eilvt_off = setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_NMI, 0);
319 320 321 322 323 324 325
}

static inline void apic_clear_ibs_nmi_per_cpu(void *arg)
{
	setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1);
}

326
static int init_ibs_nmi(void)
327 328 329 330 331 332 333 334
{
#define IBSCTL_LVTOFFSETVAL		(1 << 8)
#define IBSCTL				0x1cc
	struct pci_dev *cpu_cfg;
	int nodes;
	u32 value = 0;

	/* per CPU setup */
335
	on_each_cpu(apic_init_ibs_nmi_per_cpu, NULL, 1);
336 337 338 339 340 341 342 343 344 345 346 347 348 349

	nodes = 0;
	cpu_cfg = NULL;
	do {
		cpu_cfg = pci_get_device(PCI_VENDOR_ID_AMD,
					 PCI_DEVICE_ID_AMD_10H_NB_MISC,
					 cpu_cfg);
		if (!cpu_cfg)
			break;
		++nodes;
		pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off
				       | IBSCTL_LVTOFFSETVAL);
		pci_read_config_dword(cpu_cfg, IBSCTL, &value);
		if (value != (ibs_eilvt_off | IBSCTL_LVTOFFSETVAL)) {
350
			pci_dev_put(cpu_cfg);
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
			printk(KERN_DEBUG "Failed to setup IBS LVT offset, "
				"IBSCTL = 0x%08x", value);
			return 1;
		}
	} while (1);

	if (!nodes) {
		printk(KERN_DEBUG "No CPU node configured for IBS");
		return 1;
	}

#ifdef CONFIG_NUMA
	/* Sanity check */
	/* Works only for 64bit with proper numa implementation. */
	if (nodes != num_possible_nodes()) {
		printk(KERN_DEBUG "Failed to setup CPU node(s) for IBS, "
			"found: %d, expected %d",
			nodes, num_possible_nodes());
		return 1;
	}
#endif
	return 0;
}

375 376 377
/* uninitialize the APIC for the IBS interrupts if needed */
static void clear_ibs_nmi(void)
{
378
	if (has_ibs)
379 380 381
		on_each_cpu(apic_clear_ibs_nmi_per_cpu, NULL, 1);
}

R
Robert Richter 已提交
382
/* initialize the APIC for the IBS interrupts if available */
383
static void ibs_init(void)
384
{
385
	has_ibs = boot_cpu_has(X86_FEATURE_IBS);
386

387
	if (!has_ibs)
388 389
		return;

390
	if (init_ibs_nmi()) {
391
		has_ibs = 0;
392 393 394 395
		return;
	}

	printk(KERN_INFO "oprofile: AMD IBS detected\n");
396 397
}

398
static void ibs_exit(void)
399
{
400
	if (!has_ibs)
401 402 403
		return;

	clear_ibs_nmi();
404 405
}

R
Robert Richter 已提交
406
static int (*create_arch_files)(struct super_block *sb, struct dentry *root);
407

R
Robert Richter 已提交
408
static int setup_ibs_files(struct super_block *sb, struct dentry *root)
409 410
{
	struct dentry *dir;
411 412 413 414 415 416 417 418
	int ret = 0;

	/* architecture specific files */
	if (create_arch_files)
		ret = create_arch_files(sb, root);

	if (ret)
		return ret;
419

420
	if (!has_ibs)
421 422 423
		return ret;

	/* model specific files */
424 425 426 427 428 429 430

	/* setup some reasonable defaults */
	ibs_config.max_cnt_fetch = 250000;
	ibs_config.fetch_enabled = 0;
	ibs_config.max_cnt_op = 250000;
	ibs_config.op_enabled = 0;
	ibs_config.dispatched_ops = 1;
431 432

	dir = oprofilefs_mkdir(sb, root, "ibs_fetch");
433
	oprofilefs_create_ulong(sb, dir, "enable",
434
				&ibs_config.fetch_enabled);
435
	oprofilefs_create_ulong(sb, dir, "max_count",
436 437 438 439
				&ibs_config.max_cnt_fetch);
	oprofilefs_create_ulong(sb, dir, "rand_enable",
				&ibs_config.rand_en);

440
	dir = oprofilefs_mkdir(sb, root, "ibs_op");
441
	oprofilefs_create_ulong(sb, dir, "enable",
442
				&ibs_config.op_enabled);
443
	oprofilefs_create_ulong(sb, dir, "max_count",
444
				&ibs_config.max_cnt_op);
445
	oprofilefs_create_ulong(sb, dir, "dispatched_ops",
446
				&ibs_config.dispatched_ops);
447 448

	return 0;
449 450
}

451 452
static int op_amd_init(struct oprofile_operations *ops)
{
453
	ibs_init();
454 455
	create_arch_files = ops->create_files;
	ops->create_files = setup_ibs_files;
456 457 458 459 460
	return 0;
}

static void op_amd_exit(void)
{
461
	ibs_exit();
462 463
}

464 465 466 467 468 469 470 471 472 473 474 475
#else

/* no IBS support */

static int op_amd_init(struct oprofile_operations *ops)
{
	return 0;
}

static void op_amd_exit(void) {}

#endif /* CONFIG_OPROFILE_IBS */
476

477
struct op_x86_model_spec const op_amd_spec = {
R
Robert Richter 已提交
478 479
	.num_counters		= NUM_COUNTERS,
	.num_controls		= NUM_CONTROLS,
480 481 482 483
	.reserved		= MSR_AMD_EVENTSEL_RESERVED,
	.event_mask		= OP_EVENT_MASK,
	.init			= op_amd_init,
	.exit			= op_amd_exit,
R
Robert Richter 已提交
484 485 486 487 488
	.fill_in_addresses	= &op_amd_fill_in_addresses,
	.setup_ctrs		= &op_amd_setup_ctrs,
	.check_ctrs		= &op_amd_check_ctrs,
	.start			= &op_amd_start,
	.stop			= &op_amd_stop,
489
	.shutdown		= &op_amd_shutdown,
L
Linus Torvalds 已提交
490
};