op_model_amd.c 13.6 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-2008 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
L
Linus Torvalds 已提交
28

29 30 31
#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 已提交
32 33
#define CTR_OVERFLOWED(n) (!((n) & (1U<<31)))

34 35 36
#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 已提交
37 38
#define CTRL_SET_ACTIVE(n) (n |= (1<<22))
#define CTRL_SET_INACTIVE(n) (n &= ~(1<<22))
39 40
#define CTRL_CLEAR_LO(x) (x &= (1<<21))
#define CTRL_CLEAR_HI(x) (x &= 0xfffffcf0)
L
Linus Torvalds 已提交
41
#define CTRL_SET_ENABLE(val) (val |= 1<<20)
42 43
#define CTRL_SET_USR(val, u) (val |= ((u & 1) << 16))
#define CTRL_SET_KERN(val, k) (val |= ((k & 1) << 17))
L
Linus Torvalds 已提交
44
#define CTRL_SET_UM(val, m) (val |= (m << 8))
45 46 47 48
#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 已提交
49

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

#ifdef CONFIG_OPROFILE_IBS

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

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

/* Codes used in cpu_buffer.c */
64
/* This produces duplicate code, need to be fixed */
65 66 67
#define IBS_FETCH_BEGIN 3
#define IBS_OP_BEGIN    4

68 69
/* The function interface needs to be fixed, something like add
   data. Should then be added to linux/oprofile.h. */
70 71
extern void
oprofile_add_ibs_sample(struct pt_regs *const regs,
R
Robert Richter 已提交
72
			unsigned int *const ibs_sample, int ibs_code);
73

74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
struct ibs_fetch_sample {
	/* MSRC001_1031 IBS Fetch Linear Address Register */
	unsigned int ibs_fetch_lin_addr_low;
	unsigned int ibs_fetch_lin_addr_high;
	/* MSRC001_1030 IBS Fetch Control Register */
	unsigned int ibs_fetch_ctl_low;
	unsigned int ibs_fetch_ctl_high;
	/* MSRC001_1032 IBS Fetch Physical Address Register */
	unsigned int ibs_fetch_phys_addr_low;
	unsigned int ibs_fetch_phys_addr_high;
};

struct ibs_op_sample {
	/* MSRC001_1034 IBS Op Logical Address Register (IbsRIP) */
	unsigned int ibs_op_rip_low;
	unsigned int ibs_op_rip_high;
	/* MSRC001_1035 IBS Op Data Register */
	unsigned int ibs_op_data1_low;
	unsigned int ibs_op_data1_high;
	/* MSRC001_1036 IBS Op Data 2 Register */
	unsigned int ibs_op_data2_low;
	unsigned int ibs_op_data2_high;
	/* MSRC001_1037 IBS Op Data 3 Register */
	unsigned int ibs_op_data3_low;
	unsigned int ibs_op_data3_high;
	/* MSRC001_1038 IBS DC Linear Address Register (IbsDcLinAd) */
	unsigned int ibs_dc_linear_low;
	unsigned int ibs_dc_linear_high;
	/* MSRC001_1039 IBS DC Physical Address Register (IbsDcPhysAd) */
	unsigned int ibs_dc_phys_low;
	unsigned int ibs_dc_phys_high;
};

/*
 * unitialize the APIC for the IBS interrupts if needed on AMD Family10h+
*/
static void clear_ibs_nmi(void);

static int ibs_allowed;	/* AMD Family10h and later */

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

125 126
#endif

127
/* functions for op_amd_spec */
128

129
static void op_amd_fill_in_addresses(struct op_msrs * const msrs)
L
Linus Torvalds 已提交
130
{
131 132
	int i;

133
	for (i = 0; i < NUM_COUNTERS; i++) {
134 135
		if (reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i))
			msrs->counters[i].addr = MSR_K7_PERFCTR0 + i;
136 137 138 139
		else
			msrs->counters[i].addr = 0;
	}

140
	for (i = 0; i < NUM_CONTROLS; i++) {
141 142
		if (reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i))
			msrs->controls[i].addr = MSR_K7_EVNTSEL0 + i;
143 144 145
		else
			msrs->controls[i].addr = 0;
	}
L
Linus Torvalds 已提交
146 147
}

148

149
static void op_amd_setup_ctrs(struct op_msrs const * const msrs)
L
Linus Torvalds 已提交
150 151 152
{
	unsigned int low, high;
	int i;
153

L
Linus Torvalds 已提交
154
	/* clear all counters */
155
	for (i = 0 ; i < NUM_CONTROLS; ++i) {
156
		if (unlikely(!CTRL_IS_RESERVED(msrs, i)))
157
			continue;
L
Linus Torvalds 已提交
158
		CTRL_READ(low, high, msrs, i);
159 160
		CTRL_CLEAR_LO(low);
		CTRL_CLEAR_HI(high);
L
Linus Torvalds 已提交
161 162
		CTRL_WRITE(low, high, msrs, i);
	}
163

L
Linus Torvalds 已提交
164
	/* avoid a false detection of ctr overflows in NMI handler */
165
	for (i = 0; i < NUM_COUNTERS; ++i) {
166
		if (unlikely(!CTR_IS_RESERVED(msrs, i)))
167
			continue;
L
Linus Torvalds 已提交
168 169 170 171
		CTR_WRITE(1, msrs, i);
	}

	/* enable active counters */
172 173 174 175 176
	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 已提交
177 178

			CTRL_READ(low, high, msrs, i);
179 180
			CTRL_CLEAR_LO(low);
			CTRL_CLEAR_HI(high);
L
Linus Torvalds 已提交
181
			CTRL_SET_ENABLE(low);
182 183 184 185 186
			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);
187 188 189
			CTRL_SET_HOST_ONLY(high, 0);
			CTRL_SET_GUEST_ONLY(high, 0);

L
Linus Torvalds 已提交
190
			CTRL_WRITE(low, high, msrs, i);
191 192
		} else {
			reset_value[i] = 0;
L
Linus Torvalds 已提交
193 194 195 196
		}
	}
}

197 198
#ifdef CONFIG_OPROFILE_IBS

199 200 201
static inline int
op_amd_handle_ibs(struct pt_regs * const regs,
		  struct op_msrs const * const msrs)
L
Linus Torvalds 已提交
202 203
{
	unsigned int low, high;
204 205
	struct ibs_fetch_sample ibs_fetch;
	struct ibs_op_sample ibs_op;
L
Linus Torvalds 已提交
206

207 208
	if (!ibs_allowed)
		return 1;
L
Linus Torvalds 已提交
209

210
	if (ibs_config.fetch_enabled) {
211
		rdmsr(MSR_AMD64_IBSFETCHCTL, low, high);
212
		if (high & IBS_FETCH_HIGH_VALID_BIT) {
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
			ibs_fetch.ibs_fetch_ctl_high = high;
			ibs_fetch.ibs_fetch_ctl_low = low;
			rdmsr(MSR_AMD64_IBSFETCHLINAD, low, high);
			ibs_fetch.ibs_fetch_lin_addr_high = high;
			ibs_fetch.ibs_fetch_lin_addr_low = low;
			rdmsr(MSR_AMD64_IBSFETCHPHYSAD, low, high);
			ibs_fetch.ibs_fetch_phys_addr_high = high;
			ibs_fetch.ibs_fetch_phys_addr_low = low;

			oprofile_add_ibs_sample(regs,
						(unsigned int *)&ibs_fetch,
						IBS_FETCH_BEGIN);

			/*reenable the IRQ */
			rdmsr(MSR_AMD64_IBSFETCHCTL, low, high);
228 229 230
			high &= ~IBS_FETCH_HIGH_VALID_BIT;
			high |= IBS_FETCH_HIGH_ENABLE;
			low &= IBS_FETCH_LOW_MAX_CNT_MASK;
231 232 233 234
			wrmsr(MSR_AMD64_IBSFETCHCTL, low, high);
		}
	}

235
	if (ibs_config.op_enabled) {
236
		rdmsr(MSR_AMD64_IBSOPCTL, low, high);
237
		if (low & IBS_OP_LOW_VALID_BIT) {
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
			rdmsr(MSR_AMD64_IBSOPRIP, low, high);
			ibs_op.ibs_op_rip_low = low;
			ibs_op.ibs_op_rip_high = high;
			rdmsr(MSR_AMD64_IBSOPDATA, low, high);
			ibs_op.ibs_op_data1_low = low;
			ibs_op.ibs_op_data1_high = high;
			rdmsr(MSR_AMD64_IBSOPDATA2, low, high);
			ibs_op.ibs_op_data2_low = low;
			ibs_op.ibs_op_data2_high = high;
			rdmsr(MSR_AMD64_IBSOPDATA3, low, high);
			ibs_op.ibs_op_data3_low = low;
			ibs_op.ibs_op_data3_high = high;
			rdmsr(MSR_AMD64_IBSDCLINAD, low, high);
			ibs_op.ibs_dc_linear_low = low;
			ibs_op.ibs_dc_linear_high = high;
			rdmsr(MSR_AMD64_IBSDCPHYSAD, low, high);
			ibs_op.ibs_dc_phys_low = low;
			ibs_op.ibs_dc_phys_high = high;

			/* reenable the IRQ */
			oprofile_add_ibs_sample(regs,
						(unsigned int *)&ibs_op,
						IBS_OP_BEGIN);
			rdmsr(MSR_AMD64_IBSOPCTL, low, high);
262
			high = 0;
263 264
			low &= ~IBS_OP_LOW_VALID_BIT;
			low |= IBS_OP_LOW_ENABLE;
265 266 267 268
			wrmsr(MSR_AMD64_IBSOPCTL, low, high);
		}
	}

L
Linus Torvalds 已提交
269 270 271
	return 1;
}

272 273
#endif

274 275 276 277 278 279
static int op_amd_check_ctrs(struct pt_regs * const regs,
			     struct op_msrs const * const msrs)
{
	unsigned int low, high;
	int i;

280 281
	for (i = 0 ; i < NUM_COUNTERS; ++i) {
		if (!reset_value[i])
282 283 284
			continue;
		CTR_READ(low, high, msrs, i);
		if (CTR_OVERFLOWED(low)) {
285 286
			oprofile_add_sample(regs, i);
			CTR_WRITE(reset_value[i], msrs, i);
287 288 289
		}
	}

290
#ifdef CONFIG_OPROFILE_IBS
291
	op_amd_handle_ibs(regs, msrs);
292
#endif
293 294 295 296

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

298
static void op_amd_start(struct op_msrs const * const msrs)
L
Linus Torvalds 已提交
299 300 301
{
	unsigned int low, high;
	int i;
302 303
	for (i = 0 ; i < NUM_COUNTERS ; ++i) {
		if (reset_value[i]) {
L
Linus Torvalds 已提交
304 305 306 307 308
			CTRL_READ(low, high, msrs, i);
			CTRL_SET_ACTIVE(low);
			CTRL_WRITE(low, high, msrs, i);
		}
	}
309 310

#ifdef CONFIG_OPROFILE_IBS
311 312
	if (ibs_allowed && ibs_config.fetch_enabled) {
		low = (ibs_config.max_cnt_fetch >> 4) & 0xFFFF;
313 314
		high = ((ibs_config.rand_en & 0x1) << 25) /* bit 57 */
			+ IBS_FETCH_HIGH_ENABLE;
315 316 317 318
		wrmsr(MSR_AMD64_IBSFETCHCTL, low, high);
	}

	if (ibs_allowed && ibs_config.op_enabled) {
319 320 321
		low = ((ibs_config.max_cnt_op >> 4) & 0xFFFF)
			+ ((ibs_config.dispatched_ops & 0x1) << 19) /* bit 19 */
			+ IBS_OP_LOW_ENABLE;
322 323 324
		high = 0;
		wrmsr(MSR_AMD64_IBSOPCTL, low, high);
	}
325
#endif
L
Linus Torvalds 已提交
326 327 328
}


329
static void op_amd_stop(struct op_msrs const * const msrs)
L
Linus Torvalds 已提交
330
{
331
	unsigned int low, high;
L
Linus Torvalds 已提交
332 333 334 335
	int i;

	/* Subtle: stop on all counters to avoid race with
	 * setting our pm callback */
336 337
	for (i = 0 ; i < NUM_COUNTERS ; ++i) {
		if (!reset_value[i])
338
			continue;
L
Linus Torvalds 已提交
339 340 341 342
		CTRL_READ(low, high, msrs, i);
		CTRL_SET_INACTIVE(low);
		CTRL_WRITE(low, high, msrs, i);
	}
343

344
#ifdef CONFIG_OPROFILE_IBS
345 346 347 348 349 350 351 352 353 354 355
	if (ibs_allowed && ibs_config.fetch_enabled) {
		low = 0;		/* clear max count and enable */
		high = 0;
		wrmsr(MSR_AMD64_IBSFETCHCTL, low, high);
	}

	if (ibs_allowed && ibs_config.op_enabled) {
		low = 0;		/* clear max count and enable */
		high = 0;
		wrmsr(MSR_AMD64_IBSOPCTL, low, high);
	}
356
#endif
L
Linus Torvalds 已提交
357 358
}

359
static void op_amd_shutdown(struct op_msrs const * const msrs)
360 361 362
{
	int i;

363
	for (i = 0 ; i < NUM_COUNTERS ; ++i) {
364
		if (CTR_IS_RESERVED(msrs, i))
365 366
			release_perfctr_nmi(MSR_K7_PERFCTR0 + i);
	}
367
	for (i = 0 ; i < NUM_CONTROLS ; ++i) {
368
		if (CTRL_IS_RESERVED(msrs, i))
369 370 371
			release_evntsel_nmi(MSR_K7_EVNTSEL0 + i);
	}
}
L
Linus Torvalds 已提交
372

373
#ifndef CONFIG_OPROFILE_IBS
374 375 376 377 378 379 380 381 382 383 384 385

/* no IBS support */

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

static void op_amd_exit(void) {}

#else

386 387
static u8 ibs_eilvt_off;

388 389
static inline void apic_init_ibs_nmi_per_cpu(void *arg)
{
390
	ibs_eilvt_off = setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_NMI, 0);
391 392 393 394 395 396 397
}

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

398 399 400 401 402 403 404 405 406
static int pfm_amd64_setup_eilvt(void)
{
#define IBSCTL_LVTOFFSETVAL		(1 << 8)
#define IBSCTL				0x1cc
	struct pci_dev *cpu_cfg;
	int nodes;
	u32 value = 0;

	/* per CPU setup */
407
	on_each_cpu(apic_init_ibs_nmi_per_cpu, NULL, 1);
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445

	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)) {
			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;
}

446 447
/*
 * initialize the APIC for the IBS interrupts
448
 * if available (AMD Family10h rev B0 and later)
449 450 451 452 453 454 455 456
 */
static void setup_ibs(void)
{
	ibs_allowed = boot_cpu_has(X86_FEATURE_IBS);

	if (!ibs_allowed)
		return;

457
	if (pfm_amd64_setup_eilvt()) {
458
		ibs_allowed = 0;
459 460 461 462
		return;
	}

	printk(KERN_INFO "oprofile: AMD IBS detected\n");
463 464 465 466 467 468 469 470 471
}


/*
 * unitialize the APIC for the IBS interrupts if needed on AMD Family10h
 * rev B0 and later */
static void clear_ibs_nmi(void)
{
	if (ibs_allowed)
472
		on_each_cpu(apic_clear_ibs_nmi_per_cpu, NULL, 1);
473 474
}

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

R
Robert Richter 已提交
477
static int setup_ibs_files(struct super_block *sb, struct dentry *root)
478 479
{
	struct dentry *dir;
480 481 482 483 484 485 486 487
	int ret = 0;

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

	if (ret)
		return ret;
488 489

	if (!ibs_allowed)
490 491 492
		return ret;

	/* model specific files */
493 494 495 496 497 498 499

	/* 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;
500 501

	dir = oprofilefs_mkdir(sb, root, "ibs_fetch");
502
	oprofilefs_create_ulong(sb, dir, "enable",
503
				&ibs_config.fetch_enabled);
504
	oprofilefs_create_ulong(sb, dir, "max_count",
505 506 507 508
				&ibs_config.max_cnt_fetch);
	oprofilefs_create_ulong(sb, dir, "rand_enable",
				&ibs_config.rand_en);

509
	dir = oprofilefs_mkdir(sb, root, "ibs_op");
510
	oprofilefs_create_ulong(sb, dir, "enable",
511
				&ibs_config.op_enabled);
512
	oprofilefs_create_ulong(sb, dir, "max_count",
513
				&ibs_config.max_cnt_op);
514
	oprofilefs_create_ulong(sb, dir, "dispatched_ops",
515
				&ibs_config.dispatched_ops);
516 517

	return 0;
518 519
}

520 521
static int op_amd_init(struct oprofile_operations *ops)
{
522 523 524
	setup_ibs();
	create_arch_files = ops->create_files;
	ops->create_files = setup_ibs_files;
525 526 527 528 529
	return 0;
}

static void op_amd_exit(void)
{
530
	clear_ibs_nmi();
531 532
}

533 534
#endif

535
struct op_x86_model_spec const op_amd_spec = {
R
Robert Richter 已提交
536 537 538 539 540 541 542 543 544 545
	.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 已提交
546
};