op_model_amd.c 12.5 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
#include "op_x86_model.h"
#include "op_counter.h"
25
#include "../../../drivers/oprofile/cpu_buffer.h"
L
Linus Torvalds 已提交
26

27 28
#define NUM_COUNTERS 4
#define NUM_CONTROLS 4
L
Linus Torvalds 已提交
29

30 31 32
#define CTR_IS_RESERVED(msrs, c) (msrs->counters[(c)].addr ? 1 : 0)
#define CTR_READ(l, h, msrs, c) do {rdmsr(msrs->counters[(c)].addr, (l), (h)); } while (0)
#define CTR_WRITE(l, msrs, c) do {wrmsr(msrs->counters[(c)].addr, -(unsigned int)(l), -1); } while (0)
L
Linus Torvalds 已提交
33 34
#define CTR_OVERFLOWED(n) (!((n) & (1U<<31)))

35 36 37
#define CTRL_IS_RESERVED(msrs, c) (msrs->controls[(c)].addr ? 1 : 0)
#define CTRL_READ(l, h, msrs, c) do {rdmsr(msrs->controls[(c)].addr, (l), (h)); } while (0)
#define CTRL_WRITE(l, h, msrs, c) do {wrmsr(msrs->controls[(c)].addr, (l), (h)); } while (0)
L
Linus Torvalds 已提交
38 39
#define CTRL_SET_ACTIVE(n) (n |= (1<<22))
#define CTRL_SET_INACTIVE(n) (n &= ~(1<<22))
40 41
#define CTRL_CLEAR_LO(x) (x &= (1<<21))
#define CTRL_CLEAR_HI(x) (x &= 0xfffffcf0)
L
Linus Torvalds 已提交
42
#define CTRL_SET_ENABLE(val) (val |= 1<<20)
43 44
#define CTRL_SET_USR(val, u) (val |= ((u & 1) << 16))
#define CTRL_SET_KERN(val, k) (val |= ((k & 1) << 17))
L
Linus Torvalds 已提交
45
#define CTRL_SET_UM(val, m) (val |= (m << 8))
46 47 48 49
#define CTRL_SET_EVENT_LOW(val, e) (val |= (e & 0xff))
#define CTRL_SET_EVENT_HIGH(val, e) (val |= ((e >> 8) & 0xf))
#define CTRL_SET_HOST_ONLY(val, h) (val |= ((h & 1) << 9))
#define CTRL_SET_GUEST_ONLY(val, h) (val |= ((h & 1) << 8))
L
Linus Torvalds 已提交
50

51 52 53 54
static unsigned long reset_value[NUM_COUNTERS];

#ifdef CONFIG_OPROFILE_IBS

55 56 57 58
/* 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 */
59

60 61 62
/*IbsOpCtl bits */
#define IBS_OP_LOW_VALID_BIT		(1ULL<<18)	/* bit 18 */
#define IBS_OP_LOW_ENABLE		(1ULL<<17)	/* bit 17 */
63

R
Robert Richter 已提交
64 65 66 67
/*
 * The function interface needs to be fixed, something like add
 * data. Should then be added to linux/oprofile.h.
 */
68 69 70
extern
void oprofile_add_data(struct op_entry *entry, struct pt_regs * const regs,
		       unsigned long pc, int code, int size);
71

72 73
#define IBS_FETCH_SIZE	6
#define IBS_OP_SIZE	12
74

75
static int has_ibs;	/* AMD Family10h and later */
76 77 78 79 80 81 82 83 84 85 86

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;
87

88 89
#endif

90
/* functions for op_amd_spec */
91

92
static void op_amd_fill_in_addresses(struct op_msrs * const msrs)
L
Linus Torvalds 已提交
93
{
94 95
	int i;

96
	for (i = 0; i < NUM_COUNTERS; i++) {
97 98
		if (reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i))
			msrs->counters[i].addr = MSR_K7_PERFCTR0 + i;
99 100 101 102
		else
			msrs->counters[i].addr = 0;
	}

103
	for (i = 0; i < NUM_CONTROLS; i++) {
104 105
		if (reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i))
			msrs->controls[i].addr = MSR_K7_EVNTSEL0 + i;
106 107 108
		else
			msrs->controls[i].addr = 0;
	}
L
Linus Torvalds 已提交
109 110
}

111

112
static void op_amd_setup_ctrs(struct op_msrs const * const msrs)
L
Linus Torvalds 已提交
113 114 115
{
	unsigned int low, high;
	int i;
116

L
Linus Torvalds 已提交
117
	/* clear all counters */
118
	for (i = 0 ; i < NUM_CONTROLS; ++i) {
119
		if (unlikely(!CTRL_IS_RESERVED(msrs, i)))
120
			continue;
L
Linus Torvalds 已提交
121
		CTRL_READ(low, high, msrs, i);
122 123
		CTRL_CLEAR_LO(low);
		CTRL_CLEAR_HI(high);
L
Linus Torvalds 已提交
124 125
		CTRL_WRITE(low, high, msrs, i);
	}
126

L
Linus Torvalds 已提交
127
	/* avoid a false detection of ctr overflows in NMI handler */
128
	for (i = 0; i < NUM_COUNTERS; ++i) {
129
		if (unlikely(!CTR_IS_RESERVED(msrs, i)))
130
			continue;
L
Linus Torvalds 已提交
131 132 133 134
		CTR_WRITE(1, msrs, i);
	}

	/* enable active counters */
135 136 137 138 139
	for (i = 0; i < NUM_COUNTERS; ++i) {
		if ((counter_config[i].enabled) && (CTR_IS_RESERVED(msrs, i))) {
			reset_value[i] = counter_config[i].count;

			CTR_WRITE(counter_config[i].count, msrs, i);
L
Linus Torvalds 已提交
140 141

			CTRL_READ(low, high, msrs, i);
142 143
			CTRL_CLEAR_LO(low);
			CTRL_CLEAR_HI(high);
L
Linus Torvalds 已提交
144
			CTRL_SET_ENABLE(low);
145 146 147 148 149
			CTRL_SET_USR(low, counter_config[i].user);
			CTRL_SET_KERN(low, counter_config[i].kernel);
			CTRL_SET_UM(low, counter_config[i].unit_mask);
			CTRL_SET_EVENT_LOW(low, counter_config[i].event);
			CTRL_SET_EVENT_HIGH(high, counter_config[i].event);
150 151 152
			CTRL_SET_HOST_ONLY(high, 0);
			CTRL_SET_GUEST_ONLY(high, 0);

L
Linus Torvalds 已提交
153
			CTRL_WRITE(low, high, msrs, i);
154 155
		} else {
			reset_value[i] = 0;
L
Linus Torvalds 已提交
156 157 158 159
		}
	}
}

160 161
#ifdef CONFIG_OPROFILE_IBS

162 163 164
static inline int
op_amd_handle_ibs(struct pt_regs * const regs,
		  struct op_msrs const * const msrs)
L
Linus Torvalds 已提交
165
{
166 167 168
	u32 low, high;
	u64 msr;
	struct op_entry entry;
L
Linus Torvalds 已提交
169

170
	if (!has_ibs)
171
		return 1;
L
Linus Torvalds 已提交
172

173
	if (ibs_config.fetch_enabled) {
174
		rdmsr(MSR_AMD64_IBSFETCHCTL, low, high);
175
		if (high & IBS_FETCH_HIGH_VALID_BIT) {
176 177 178 179 180 181 182 183 184 185 186
			rdmsrl(MSR_AMD64_IBSFETCHLINAD, msr);
			oprofile_add_data(&entry, regs, msr, IBS_FETCH_CODE,
					  IBS_FETCH_SIZE);
			op_cpu_buffer_add_data(&entry, (u32)msr);
			op_cpu_buffer_add_data(&entry, (u32)(msr >> 32));
			op_cpu_buffer_add_data(&entry, low);
			op_cpu_buffer_add_data(&entry, high);
			rdmsrl(MSR_AMD64_IBSFETCHPHYSAD, msr);
			op_cpu_buffer_add_data(&entry, (u32)msr);
			op_cpu_buffer_add_data(&entry, (u32)(msr >> 32));
			op_cpu_buffer_write_commit(&entry);
187

R
Robert Richter 已提交
188
			/* reenable the IRQ */
189 190 191
			high &= ~IBS_FETCH_HIGH_VALID_BIT;
			high |= IBS_FETCH_HIGH_ENABLE;
			low &= IBS_FETCH_LOW_MAX_CNT_MASK;
192 193 194 195
			wrmsr(MSR_AMD64_IBSFETCHCTL, low, high);
		}
	}

196
	if (ibs_config.op_enabled) {
197
		rdmsr(MSR_AMD64_IBSOPCTL, low, high);
198
		if (low & IBS_OP_LOW_VALID_BIT) {
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
			rdmsrl(MSR_AMD64_IBSOPRIP, msr);
			oprofile_add_data(&entry, regs, msr, IBS_OP_CODE,
					  IBS_OP_SIZE);
			op_cpu_buffer_add_data(&entry, (u32)msr);
			op_cpu_buffer_add_data(&entry, (u32)(msr >> 32));
			rdmsrl(MSR_AMD64_IBSOPDATA, msr);
			op_cpu_buffer_add_data(&entry, (u32)msr);
			op_cpu_buffer_add_data(&entry, (u32)(msr >> 32));
			rdmsrl(MSR_AMD64_IBSOPDATA2, msr);
			op_cpu_buffer_add_data(&entry, (u32)msr);
			op_cpu_buffer_add_data(&entry, (u32)(msr >> 32));
			rdmsrl(MSR_AMD64_IBSOPDATA3, msr);
			op_cpu_buffer_add_data(&entry, (u32)msr);
			op_cpu_buffer_add_data(&entry, (u32)(msr >> 32));
			rdmsrl(MSR_AMD64_IBSDCLINAD, msr);
			op_cpu_buffer_add_data(&entry, (u32)msr);
			op_cpu_buffer_add_data(&entry, (u32)(msr >> 32));
			rdmsrl(MSR_AMD64_IBSDCPHYSAD, msr);
			op_cpu_buffer_add_data(&entry, (u32)msr);
			op_cpu_buffer_add_data(&entry, (u32)(msr >> 32));
			op_cpu_buffer_write_commit(&entry);
220 221

			/* reenable the IRQ */
222
			high = 0;
223 224
			low &= ~IBS_OP_LOW_VALID_BIT;
			low |= IBS_OP_LOW_ENABLE;
225 226 227 228
			wrmsr(MSR_AMD64_IBSOPCTL, low, high);
		}
	}

L
Linus Torvalds 已提交
229 230 231
	return 1;
}

232 233
#endif

234 235 236 237 238 239
static int op_amd_check_ctrs(struct pt_regs * const regs,
			     struct op_msrs const * const msrs)
{
	unsigned int low, high;
	int i;

240 241
	for (i = 0 ; i < NUM_COUNTERS; ++i) {
		if (!reset_value[i])
242 243 244
			continue;
		CTR_READ(low, high, msrs, i);
		if (CTR_OVERFLOWED(low)) {
245 246
			oprofile_add_sample(regs, i);
			CTR_WRITE(reset_value[i], msrs, i);
247 248 249
		}
	}

250
#ifdef CONFIG_OPROFILE_IBS
251
	op_amd_handle_ibs(regs, msrs);
252
#endif
253 254 255 256

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

258
static void op_amd_start(struct op_msrs const * const msrs)
L
Linus Torvalds 已提交
259 260 261
{
	unsigned int low, high;
	int i;
262 263
	for (i = 0 ; i < NUM_COUNTERS ; ++i) {
		if (reset_value[i]) {
L
Linus Torvalds 已提交
264 265 266 267 268
			CTRL_READ(low, high, msrs, i);
			CTRL_SET_ACTIVE(low);
			CTRL_WRITE(low, high, msrs, i);
		}
	}
269 270

#ifdef CONFIG_OPROFILE_IBS
271
	if (has_ibs && ibs_config.fetch_enabled) {
272
		low = (ibs_config.max_cnt_fetch >> 4) & 0xFFFF;
273 274
		high = ((ibs_config.rand_en & 0x1) << 25) /* bit 57 */
			+ IBS_FETCH_HIGH_ENABLE;
275 276 277
		wrmsr(MSR_AMD64_IBSFETCHCTL, low, high);
	}

278
	if (has_ibs && ibs_config.op_enabled) {
279 280 281
		low = ((ibs_config.max_cnt_op >> 4) & 0xFFFF)
			+ ((ibs_config.dispatched_ops & 0x1) << 19) /* bit 19 */
			+ IBS_OP_LOW_ENABLE;
282 283 284
		high = 0;
		wrmsr(MSR_AMD64_IBSOPCTL, low, high);
	}
285
#endif
L
Linus Torvalds 已提交
286 287 288
}


289
static void op_amd_stop(struct op_msrs const * const msrs)
L
Linus Torvalds 已提交
290
{
291
	unsigned int low, high;
L
Linus Torvalds 已提交
292 293
	int i;

R
Robert Richter 已提交
294 295 296 297
	/*
	 * Subtle: stop on all counters to avoid race with setting our
	 * pm callback
	 */
298 299
	for (i = 0 ; i < NUM_COUNTERS ; ++i) {
		if (!reset_value[i])
300
			continue;
L
Linus Torvalds 已提交
301 302 303 304
		CTRL_READ(low, high, msrs, i);
		CTRL_SET_INACTIVE(low);
		CTRL_WRITE(low, high, msrs, i);
	}
305

306
#ifdef CONFIG_OPROFILE_IBS
307
	if (has_ibs && ibs_config.fetch_enabled) {
R
Robert Richter 已提交
308 309
		/* clear max count and enable */
		low = 0;
310 311 312 313
		high = 0;
		wrmsr(MSR_AMD64_IBSFETCHCTL, low, high);
	}

314
	if (has_ibs && ibs_config.op_enabled) {
R
Robert Richter 已提交
315 316
		/* clear max count and enable */
		low = 0;
317 318 319
		high = 0;
		wrmsr(MSR_AMD64_IBSOPCTL, low, high);
	}
320
#endif
L
Linus Torvalds 已提交
321 322
}

323
static void op_amd_shutdown(struct op_msrs const * const msrs)
324 325 326
{
	int i;

327
	for (i = 0 ; i < NUM_COUNTERS ; ++i) {
328
		if (CTR_IS_RESERVED(msrs, i))
329 330
			release_perfctr_nmi(MSR_K7_PERFCTR0 + i);
	}
331
	for (i = 0 ; i < NUM_CONTROLS ; ++i) {
332
		if (CTRL_IS_RESERVED(msrs, i))
333 334 335
			release_evntsel_nmi(MSR_K7_EVNTSEL0 + i);
	}
}
L
Linus Torvalds 已提交
336

337
#ifdef CONFIG_OPROFILE_IBS
338

339 340
static u8 ibs_eilvt_off;

341 342
static inline void apic_init_ibs_nmi_per_cpu(void *arg)
{
343
	ibs_eilvt_off = setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_NMI, 0);
344 345 346 347 348 349 350
}

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

351
static int init_ibs_nmi(void)
352 353 354 355 356 357 358 359
{
#define IBSCTL_LVTOFFSETVAL		(1 << 8)
#define IBSCTL				0x1cc
	struct pci_dev *cpu_cfg;
	int nodes;
	u32 value = 0;

	/* per CPU setup */
360
	on_each_cpu(apic_init_ibs_nmi_per_cpu, NULL, 1);
361 362 363 364 365 366 367 368 369 370 371 372 373 374

	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)) {
375
			pci_dev_put(cpu_cfg);
376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
			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;
}

400 401 402
/* uninitialize the APIC for the IBS interrupts if needed */
static void clear_ibs_nmi(void)
{
403
	if (has_ibs)
404 405 406
		on_each_cpu(apic_clear_ibs_nmi_per_cpu, NULL, 1);
}

R
Robert Richter 已提交
407
/* initialize the APIC for the IBS interrupts if available */
408
static void ibs_init(void)
409
{
410
	has_ibs = boot_cpu_has(X86_FEATURE_IBS);
411

412
	if (!has_ibs)
413 414
		return;

415
	if (init_ibs_nmi()) {
416
		has_ibs = 0;
417 418 419 420
		return;
	}

	printk(KERN_INFO "oprofile: AMD IBS detected\n");
421 422
}

423
static void ibs_exit(void)
424
{
425
	if (!has_ibs)
426 427 428
		return;

	clear_ibs_nmi();
429 430
}

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

R
Robert Richter 已提交
433
static int setup_ibs_files(struct super_block *sb, struct dentry *root)
434 435
{
	struct dentry *dir;
436 437 438 439 440 441 442 443
	int ret = 0;

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

	if (ret)
		return ret;
444

445
	if (!has_ibs)
446 447 448
		return ret;

	/* model specific files */
449 450 451 452 453 454 455

	/* 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;
456 457

	dir = oprofilefs_mkdir(sb, root, "ibs_fetch");
458
	oprofilefs_create_ulong(sb, dir, "enable",
459
				&ibs_config.fetch_enabled);
460
	oprofilefs_create_ulong(sb, dir, "max_count",
461 462 463 464
				&ibs_config.max_cnt_fetch);
	oprofilefs_create_ulong(sb, dir, "rand_enable",
				&ibs_config.rand_en);

465
	dir = oprofilefs_mkdir(sb, root, "ibs_op");
466
	oprofilefs_create_ulong(sb, dir, "enable",
467
				&ibs_config.op_enabled);
468
	oprofilefs_create_ulong(sb, dir, "max_count",
469
				&ibs_config.max_cnt_op);
470
	oprofilefs_create_ulong(sb, dir, "dispatched_ops",
471
				&ibs_config.dispatched_ops);
472 473

	return 0;
474 475
}

476 477
static int op_amd_init(struct oprofile_operations *ops)
{
478
	ibs_init();
479 480
	create_arch_files = ops->create_files;
	ops->create_files = setup_ibs_files;
481 482 483 484 485
	return 0;
}

static void op_amd_exit(void)
{
486
	ibs_exit();
487 488
}

489 490 491 492 493 494 495 496 497 498 499 500
#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 */
501

502
struct op_x86_model_spec const op_amd_spec = {
R
Robert Richter 已提交
503 504 505 506 507 508 509 510 511 512
	.init			= op_amd_init,
	.exit			= op_amd_exit,
	.num_counters		= NUM_COUNTERS,
	.num_controls		= NUM_CONTROLS,
	.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,
	.shutdown		= &op_amd_shutdown
L
Linus Torvalds 已提交
513
};