nmi_int.c 16.9 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3
/**
 * @file nmi_int.c
 *
4
 * @remark Copyright 2002-2009 OProfile authors
Linus Torvalds's avatar
Linus Torvalds committed
5 6 7
 * @remark Read the file COPYING
 *
 * @author John Levon <levon@movementarian.org>
8
 * @author Robert Richter <robert.richter@amd.com>
9 10 11
 * @author Barry Kasindorf <barry.kasindorf@amd.com>
 * @author Jason Yeh <jason.yeh@amd.com>
 * @author Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
Linus Torvalds's avatar
Linus Torvalds committed
12 13 14 15 16 17
 */

#include <linux/init.h>
#include <linux/notifier.h>
#include <linux/smp.h>
#include <linux/oprofile.h>
18
#include <linux/syscore_ops.h>
Linus Torvalds's avatar
Linus Torvalds committed
19
#include <linux/slab.h>
20
#include <linux/moduleparam.h>
21
#include <linux/kdebug.h>
22
#include <linux/cpu.h>
Linus Torvalds's avatar
Linus Torvalds committed
23 24 25
#include <asm/nmi.h>
#include <asm/msr.h>
#include <asm/apic.h>
26

Linus Torvalds's avatar
Linus Torvalds committed
27 28
#include "op_counter.h"
#include "op_x86_model.h"
29

30
static struct op_x86_model_spec *model;
31 32
static DEFINE_PER_CPU(struct op_msrs, cpu_msrs);
static DEFINE_PER_CPU(unsigned long, saved_lvtpc);
33

34 35 36
/* must be protected with get_online_cpus()/put_online_cpus(): */
static int nmi_enabled;
static int ctr_running;
Linus Torvalds's avatar
Linus Torvalds committed
37

38 39
struct op_counter_config counter_config[OP_MAX_COUNTER];

40 41 42 43 44 45 46 47 48 49 50 51
/* common functions */

u64 op_x86_get_ctrl(struct op_x86_model_spec const *model,
		    struct op_counter_config *counter_config)
{
	u64 val = 0;
	u16 event = (u16)counter_config->event;

	val |= ARCH_PERFMON_EVENTSEL_INT;
	val |= counter_config->user ? ARCH_PERFMON_EVENTSEL_USR : 0;
	val |= counter_config->kernel ? ARCH_PERFMON_EVENTSEL_OS : 0;
	val |= (counter_config->unit_mask & 0xFF) << 8;
52 53 54 55
	counter_config->extra &= (ARCH_PERFMON_EVENTSEL_INV |
				  ARCH_PERFMON_EVENTSEL_EDGE |
				  ARCH_PERFMON_EVENTSEL_CMASK);
	val |= counter_config->extra;
56 57
	event &= model->event_mask ? model->event_mask : 0xFF;
	val |= event & 0xFF;
58
	val |= (u64)(event & 0x0F00) << 24;
59 60 61 62 63

	return val;
}


64 65 66 67 68 69 70 71 72
static int profile_exceptions_notify(unsigned int val, struct pt_regs *regs)
{
	if (ctr_running)
		model->check_ctrs(regs, &__get_cpu_var(cpu_msrs));
	else if (!nmi_enabled)
		return NMI_DONE;
	else
		model->stop(&__get_cpu_var(cpu_msrs));
	return NMI_HANDLED;
Linus Torvalds's avatar
Linus Torvalds committed
73
}
74

75
static void nmi_cpu_save_registers(struct op_msrs *msrs)
Linus Torvalds's avatar
Linus Torvalds committed
76
{
77 78
	struct op_msr *counters = msrs->counters;
	struct op_msr *controls = msrs->controls;
Linus Torvalds's avatar
Linus Torvalds committed
79 80
	unsigned int i;

81
	for (i = 0; i < model->num_counters; ++i) {
82 83
		if (counters[i].addr)
			rdmsrl(counters[i].addr, counters[i].saved);
Linus Torvalds's avatar
Linus Torvalds committed
84
	}
85

86
	for (i = 0; i < model->num_controls; ++i) {
87 88
		if (controls[i].addr)
			rdmsrl(controls[i].addr, controls[i].saved);
Linus Torvalds's avatar
Linus Torvalds committed
89 90 91
	}
}

92 93 94
static void nmi_cpu_start(void *dummy)
{
	struct op_msrs const *msrs = &__get_cpu_var(cpu_msrs);
95 96 97 98
	if (!msrs->controls)
		WARN_ON_ONCE(1);
	else
		model->start(msrs);
99 100 101 102
}

static int nmi_start(void)
{
103 104
	get_online_cpus();
	ctr_running = 1;
105 106 107
	/* make ctr_running visible to the nmi handler: */
	smp_mb();
	on_each_cpu(nmi_cpu_start, NULL, 1);
108
	put_online_cpus();
109 110 111 112 113 114
	return 0;
}

static void nmi_cpu_stop(void *dummy)
{
	struct op_msrs const *msrs = &__get_cpu_var(cpu_msrs);
115 116 117 118
	if (!msrs->controls)
		WARN_ON_ONCE(1);
	else
		model->stop(msrs);
119 120 121 122
}

static void nmi_stop(void)
{
123
	get_online_cpus();
124
	on_each_cpu(nmi_cpu_stop, NULL, 1);
125 126
	ctr_running = 0;
	put_online_cpus();
127 128
}

129 130 131 132
#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX

static DEFINE_PER_CPU(int, switch_index);

133 134 135 136 137
static inline int has_mux(void)
{
	return !!model->switch_ctrl;
}

138 139
inline int op_x86_phys_to_virt(int phys)
{
140
	return __this_cpu_read(switch_index) + phys;
141 142
}

143 144 145 146 147
inline int op_x86_virt_to_phys(int virt)
{
	return virt % model->num_counters;
}

148 149 150
static void nmi_shutdown_mux(void)
{
	int i;
151 152 153 154

	if (!has_mux())
		return;

155 156 157 158 159 160 161 162 163 164 165 166
	for_each_possible_cpu(i) {
		kfree(per_cpu(cpu_msrs, i).multiplex);
		per_cpu(cpu_msrs, i).multiplex = NULL;
		per_cpu(switch_index, i) = 0;
	}
}

static int nmi_setup_mux(void)
{
	size_t multiplex_size =
		sizeof(struct op_msr) * model->num_virt_counters;
	int i;
167 168 169 170

	if (!has_mux())
		return 1;

171 172
	for_each_possible_cpu(i) {
		per_cpu(cpu_msrs, i).multiplex =
173
			kzalloc(multiplex_size, GFP_KERNEL);
174 175 176
		if (!per_cpu(cpu_msrs, i).multiplex)
			return 0;
	}
177

178 179 180
	return 1;
}

181 182 183 184 185
static void nmi_cpu_setup_mux(int cpu, struct op_msrs const * const msrs)
{
	int i;
	struct op_msr *multiplex = msrs->multiplex;

186 187 188
	if (!has_mux())
		return;

189 190 191 192 193 194 195 196 197 198 199
	for (i = 0; i < model->num_virt_counters; ++i) {
		if (counter_config[i].enabled) {
			multiplex[i].saved = -(u64)counter_config[i].count;
		} else {
			multiplex[i].saved = 0;
		}
	}

	per_cpu(switch_index, cpu) = 0;
}

200 201
static void nmi_cpu_save_mpx_registers(struct op_msrs *msrs)
{
202
	struct op_msr *counters = msrs->counters;
203 204 205 206 207
	struct op_msr *multiplex = msrs->multiplex;
	int i;

	for (i = 0; i < model->num_counters; ++i) {
		int virt = op_x86_phys_to_virt(i);
208 209
		if (counters[i].addr)
			rdmsrl(counters[i].addr, multiplex[virt].saved);
210 211 212 213 214
	}
}

static void nmi_cpu_restore_mpx_registers(struct op_msrs *msrs)
{
215
	struct op_msr *counters = msrs->counters;
216 217 218 219 220
	struct op_msr *multiplex = msrs->multiplex;
	int i;

	for (i = 0; i < model->num_counters; ++i) {
		int virt = op_x86_phys_to_virt(i);
221 222
		if (counters[i].addr)
			wrmsrl(counters[i].addr, multiplex[virt].saved);
223 224 225
	}
}

226 227 228 229 230 231 232 233 234 235 236
static void nmi_cpu_switch(void *dummy)
{
	int cpu = smp_processor_id();
	int si = per_cpu(switch_index, cpu);
	struct op_msrs *msrs = &per_cpu(cpu_msrs, cpu);

	nmi_cpu_stop(NULL);
	nmi_cpu_save_mpx_registers(msrs);

	/* move to next set */
	si += model->num_counters;
237
	if ((si >= model->num_virt_counters) || (counter_config[si].count == 0))
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
		per_cpu(switch_index, cpu) = 0;
	else
		per_cpu(switch_index, cpu) = si;

	model->switch_ctrl(model, msrs);
	nmi_cpu_restore_mpx_registers(msrs);

	nmi_cpu_start(NULL);
}


/*
 * Quick check to see if multiplexing is necessary.
 * The check should be sufficient since counters are used
 * in ordre.
 */
static int nmi_multiplex_on(void)
{
	return counter_config[model->num_counters].count ? 0 : -EINVAL;
}

static int nmi_switch_event(void)
{
261
	if (!has_mux())
262 263 264 265
		return -ENOSYS;		/* not implemented */
	if (nmi_multiplex_on() < 0)
		return -EINVAL;		/* not necessary */

266 267 268 269
	get_online_cpus();
	if (ctr_running)
		on_each_cpu(nmi_cpu_switch, NULL, 1);
	put_online_cpus();
270 271 272 273

	return 0;
}

274 275 276 277 278 279
static inline void mux_init(struct oprofile_operations *ops)
{
	if (has_mux())
		ops->switch_events = nmi_switch_event;
}

280 281 282 283 284 285 286 287 288 289
static void mux_clone(int cpu)
{
	if (!has_mux())
		return;

	memcpy(per_cpu(cpu_msrs, cpu).multiplex,
	       per_cpu(cpu_msrs, 0).multiplex,
	       sizeof(struct op_msr) * model->num_virt_counters);
}

290 291 292
#else

inline int op_x86_phys_to_virt(int phys) { return phys; }
293
inline int op_x86_virt_to_phys(int virt) { return virt; }
294 295
static inline void nmi_shutdown_mux(void) { }
static inline int nmi_setup_mux(void) { return 1; }
296 297
static inline void
nmi_cpu_setup_mux(int cpu, struct op_msrs const * const msrs) { }
298
static inline void mux_init(struct oprofile_operations *ops) { }
299
static void mux_clone(int cpu) { }
300 301 302

#endif

Linus Torvalds's avatar
Linus Torvalds committed
303 304 305
static void free_msrs(void)
{
	int i;
306
	for_each_possible_cpu(i) {
307 308 309 310
		kfree(per_cpu(cpu_msrs, i).counters);
		per_cpu(cpu_msrs, i).counters = NULL;
		kfree(per_cpu(cpu_msrs, i).controls);
		per_cpu(cpu_msrs, i).controls = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
311
	}
312
	nmi_shutdown_mux();
Linus Torvalds's avatar
Linus Torvalds committed
313 314 315 316 317 318 319
}

static int allocate_msrs(void)
{
	size_t controls_size = sizeof(struct op_msr) * model->num_controls;
	size_t counters_size = sizeof(struct op_msr) * model->num_counters;

320
	int i;
Chris Wright's avatar
Chris Wright committed
321
	for_each_possible_cpu(i) {
322
		per_cpu(cpu_msrs, i).counters = kzalloc(counters_size,
323 324
							GFP_KERNEL);
		if (!per_cpu(cpu_msrs, i).counters)
325
			goto fail;
326
		per_cpu(cpu_msrs, i).controls = kzalloc(controls_size,
327 328
							GFP_KERNEL);
		if (!per_cpu(cpu_msrs, i).controls)
329
			goto fail;
Linus Torvalds's avatar
Linus Torvalds committed
330 331
	}

332 333 334
	if (!nmi_setup_mux())
		goto fail;

335
	return 1;
336 337 338 339

fail:
	free_msrs();
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
340 341
}

342
static void nmi_cpu_setup(void *dummy)
Linus Torvalds's avatar
Linus Torvalds committed
343 344
{
	int cpu = smp_processor_id();
345
	struct op_msrs *msrs = &per_cpu(cpu_msrs, cpu);
346
	nmi_cpu_save_registers(msrs);
347
	raw_spin_lock(&oprofilefs_lock);
348
	model->setup_ctrs(model, msrs);
349
	nmi_cpu_setup_mux(cpu, msrs);
350
	raw_spin_unlock(&oprofilefs_lock);
351
	per_cpu(saved_lvtpc, cpu) = apic_read(APIC_LVTPC);
Linus Torvalds's avatar
Linus Torvalds committed
352 353 354
	apic_write(APIC_LVTPC, APIC_DM_NMI);
}

355
static void nmi_cpu_restore_registers(struct op_msrs *msrs)
Linus Torvalds's avatar
Linus Torvalds committed
356
{
357 358
	struct op_msr *counters = msrs->counters;
	struct op_msr *controls = msrs->controls;
Linus Torvalds's avatar
Linus Torvalds committed
359 360
	unsigned int i;

361
	for (i = 0; i < model->num_controls; ++i) {
362 363
		if (controls[i].addr)
			wrmsrl(controls[i].addr, controls[i].saved);
Linus Torvalds's avatar
Linus Torvalds committed
364
	}
365

366
	for (i = 0; i < model->num_counters; ++i) {
367 368
		if (counters[i].addr)
			wrmsrl(counters[i].addr, counters[i].saved);
Linus Torvalds's avatar
Linus Torvalds committed
369 370 371
	}
}

372
static void nmi_cpu_shutdown(void *dummy)
Linus Torvalds's avatar
Linus Torvalds committed
373 374 375
{
	unsigned int v;
	int cpu = smp_processor_id();
376
	struct op_msrs *msrs = &per_cpu(cpu_msrs, cpu);
377

Linus Torvalds's avatar
Linus Torvalds committed
378 379 380 381 382 383 384
	/* restoring APIC_LVTPC can trigger an apic error because the delivery
	 * mode and vector nr combination can be illegal. That's by design: on
	 * power on apic lvt contain a zero vector nr which are legal only for
	 * NMI delivery mode. So inhibit apic err before restoring lvtpc
	 */
	v = apic_read(APIC_LVTERR);
	apic_write(APIC_LVTERR, v | APIC_LVT_MASKED);
385
	apic_write(APIC_LVTPC, per_cpu(saved_lvtpc, cpu));
Linus Torvalds's avatar
Linus Torvalds committed
386
	apic_write(APIC_LVTERR, v);
387
	nmi_cpu_restore_registers(msrs);
Linus Torvalds's avatar
Linus Torvalds committed
388 389
}

390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
static void nmi_cpu_up(void *dummy)
{
	if (nmi_enabled)
		nmi_cpu_setup(dummy);
	if (ctr_running)
		nmi_cpu_start(dummy);
}

static void nmi_cpu_down(void *dummy)
{
	if (ctr_running)
		nmi_cpu_stop(dummy);
	if (nmi_enabled)
		nmi_cpu_shutdown(dummy);
}

406
static int nmi_create_files(struct super_block *sb, struct dentry *root)
Linus Torvalds's avatar
Linus Torvalds committed
407 408 409
{
	unsigned int i;

410
	for (i = 0; i < model->num_virt_counters; ++i) {
411
		struct dentry *dir;
412
		char buf[4];
413 414

		/* quick little hack to _not_ expose a counter if it is not
415 416 417 418
		 * available for use.  This should protect userspace app.
		 * NOTE:  assumes 1:1 mapping here (that counters are organized
		 *        sequentially in their struct assignment).
		 */
419
		if (!avail_to_resrv_perfctr_nmi_bit(op_x86_virt_to_phys(i)))
420 421
			continue;

422
		snprintf(buf,  sizeof(buf), "%d", i);
Linus Torvalds's avatar
Linus Torvalds committed
423
		dir = oprofilefs_mkdir(sb, root, buf);
424 425 426 427 428 429
		oprofilefs_create_ulong(sb, dir, "enabled", &counter_config[i].enabled);
		oprofilefs_create_ulong(sb, dir, "event", &counter_config[i].event);
		oprofilefs_create_ulong(sb, dir, "count", &counter_config[i].count);
		oprofilefs_create_ulong(sb, dir, "unit_mask", &counter_config[i].unit_mask);
		oprofilefs_create_ulong(sb, dir, "kernel", &counter_config[i].kernel);
		oprofilefs_create_ulong(sb, dir, "user", &counter_config[i].user);
430
		oprofilefs_create_ulong(sb, dir, "extra", &counter_config[i].extra);
Linus Torvalds's avatar
Linus Torvalds committed
431 432 433 434
	}

	return 0;
}
435

436 437 438 439 440 441 442
static int oprofile_cpu_notifier(struct notifier_block *b, unsigned long action,
				 void *data)
{
	int cpu = (unsigned long)data;
	switch (action) {
	case CPU_DOWN_FAILED:
	case CPU_ONLINE:
443
		smp_call_function_single(cpu, nmi_cpu_up, NULL, 0);
444 445
		break;
	case CPU_DOWN_PREPARE:
446
		smp_call_function_single(cpu, nmi_cpu_down, NULL, 1);
447 448 449 450 451 452 453 454 455
		break;
	}
	return NOTIFY_DONE;
}

static struct notifier_block oprofile_cpu_nb = {
	.notifier_call = oprofile_cpu_notifier
};

456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
static int nmi_setup(void)
{
	int err = 0;
	int cpu;

	if (!allocate_msrs())
		return -ENOMEM;

	/* We need to serialize save and setup for HT because the subset
	 * of msrs are distinct for save and setup operations
	 */

	/* Assume saved/restored counters are the same on all CPUs */
	err = model->fill_in_addresses(&per_cpu(cpu_msrs, 0));
	if (err)
		goto fail;

	for_each_possible_cpu(cpu) {
		if (!cpu)
			continue;

		memcpy(per_cpu(cpu_msrs, cpu).counters,
		       per_cpu(cpu_msrs, 0).counters,
		       sizeof(struct op_msr) * model->num_counters);

		memcpy(per_cpu(cpu_msrs, cpu).controls,
		       per_cpu(cpu_msrs, 0).controls,
		       sizeof(struct op_msr) * model->num_controls);

		mux_clone(cpu);
	}

	nmi_enabled = 0;
	ctr_running = 0;
490 491
	/* make variables visible to the nmi handler: */
	smp_mb();
492 493
	err = register_nmi_handler(NMI_LOCAL, profile_exceptions_notify,
					0, "oprofile");
494 495 496 497
	if (err)
		goto fail;

	get_online_cpus();
498
	register_cpu_notifier(&oprofile_cpu_nb);
499
	nmi_enabled = 1;
500 501 502
	/* make nmi_enabled visible to the nmi handler: */
	smp_mb();
	on_each_cpu(nmi_cpu_setup, NULL, 1);
503 504 505 506 507 508 509 510 511 512 513 514 515
	put_online_cpus();

	return 0;
fail:
	free_msrs();
	return err;
}

static void nmi_shutdown(void)
{
	struct op_msrs *msrs;

	get_online_cpus();
516
	unregister_cpu_notifier(&oprofile_cpu_nb);
517 518 519 520
	on_each_cpu(nmi_cpu_shutdown, NULL, 1);
	nmi_enabled = 0;
	ctr_running = 0;
	put_online_cpus();
521 522
	/* make variables visible to the nmi handler: */
	smp_mb();
523
	unregister_nmi_handler(NMI_LOCAL, "oprofile");
524 525 526 527 528 529
	msrs = &get_cpu_var(cpu_msrs);
	model->shutdown(msrs);
	free_msrs();
	put_cpu_var(cpu_msrs);
}

530 531
#ifdef CONFIG_PM

532
static int nmi_suspend(void)
533 534 535 536 537 538 539
{
	/* Only one CPU left, just stop that one */
	if (nmi_enabled == 1)
		nmi_cpu_stop(NULL);
	return 0;
}

540
static void nmi_resume(void)
541 542 543 544 545
{
	if (nmi_enabled == 1)
		nmi_cpu_start(NULL);
}

546
static struct syscore_ops oprofile_syscore_ops = {
547 548 549 550
	.resume		= nmi_resume,
	.suspend	= nmi_suspend,
};

551
static void __init init_suspend_resume(void)
552
{
553
	register_syscore_ops(&oprofile_syscore_ops);
554 555
}

556
static void exit_suspend_resume(void)
557
{
558
	unregister_syscore_ops(&oprofile_syscore_ops);
559 560 561
}

#else
562

563 564
static inline void init_suspend_resume(void) { }
static inline void exit_suspend_resume(void) { }
565

566 567
#endif /* CONFIG_PM */

568
static int __init p4_init(char **cpu_type)
Linus Torvalds's avatar
Linus Torvalds committed
569 570 571
{
	__u8 cpu_model = boot_cpu_data.x86_model;

572
	if (cpu_model > 6 || cpu_model == 5)
Linus Torvalds's avatar
Linus Torvalds committed
573 574 575 576 577 578 579 580
		return 0;

#ifndef CONFIG_SMP
	*cpu_type = "i386/p4";
	model = &op_p4_spec;
	return 1;
#else
	switch (smp_num_siblings) {
581 582 583 584 585 586 587 588 589
	case 1:
		*cpu_type = "i386/p4";
		model = &op_p4_spec;
		return 1;

	case 2:
		*cpu_type = "i386/p4-ht";
		model = &op_p4_ht2_spec;
		return 1;
Linus Torvalds's avatar
Linus Torvalds committed
590 591 592 593 594 595 596 597
	}
#endif

	printk(KERN_INFO "oprofile: P4 HyperThreading detected with > 2 threads\n");
	printk(KERN_INFO "oprofile: Reverting to timer mode.\n");
	return 0;
}

598 599 600 601 602 603 604 605 606
enum __force_cpu_type {
	reserved = 0,		/* do not force */
	timer,
	arch_perfmon,
};

static int force_cpu_type;

static int set_cpu_type(const char *str, struct kernel_param *kp)
607
{
608 609 610 611 612
	if (!strcmp(str, "timer")) {
		force_cpu_type = timer;
		printk(KERN_INFO "oprofile: forcing NMI timer mode\n");
	} else if (!strcmp(str, "arch_perfmon")) {
		force_cpu_type = arch_perfmon;
613
		printk(KERN_INFO "oprofile: forcing architectural perfmon\n");
614 615
	} else {
		force_cpu_type = 0;
616 617 618 619
	}

	return 0;
}
620
module_param_call(cpu_type, set_cpu_type, NULL, NULL, 0);
621

622
static int __init ppro_init(char **cpu_type)
Linus Torvalds's avatar
Linus Torvalds committed
623 624
{
	__u8 cpu_model = boot_cpu_data.x86_model;
625
	struct op_x86_model_spec *spec = &op_ppro_spec;	/* default */
Linus Torvalds's avatar
Linus Torvalds committed
626

627
	if (force_cpu_type == arch_perfmon && cpu_has_arch_perfmon)
628 629
		return 0;

630 631 632 633 634 635 636 637 638 639 640 641
	/*
	 * Documentation on identifying Intel processors by CPU family
	 * and model can be found in the Intel Software Developer's
	 * Manuals (SDM):
	 *
	 *  http://www.intel.com/products/processor/manuals/
	 *
	 * As of May 2010 the documentation for this was in the:
	 * "Intel 64 and IA-32 Architectures Software Developer's
	 * Manual Volume 3B: System Programming Guide", "Table B-1
	 * CPUID Signature Values of DisplayFamily_DisplayModel".
	 */
642 643 644 645 646 647 648 649
	switch (cpu_model) {
	case 0 ... 2:
		*cpu_type = "i386/ppro";
		break;
	case 3 ... 5:
		*cpu_type = "i386/pii";
		break;
	case 6 ... 8:
650
	case 10 ... 11:
651 652 653
		*cpu_type = "i386/piii";
		break;
	case 9:
654
	case 13:
655 656 657
		*cpu_type = "i386/p6_mobile";
		break;
	case 14:
658
		*cpu_type = "i386/core";
659
		break;
660 661 662
	case 0x0f:
	case 0x16:
	case 0x17:
663
	case 0x1d:
664 665
		*cpu_type = "i386/core_2";
		break;
666
	case 0x1a:
667
	case 0x1e:
668
	case 0x2e:
669
		spec = &op_arch_perfmon_spec;
670 671
		*cpu_type = "i386/core_i7";
		break;
672
	case 0x1c:
673 674
		*cpu_type = "i386/atom";
		break;
675 676
	default:
		/* Unknown */
Linus Torvalds's avatar
Linus Torvalds committed
677 678 679
		return 0;
	}

680
	model = spec;
Linus Torvalds's avatar
Linus Torvalds committed
681 682 683
	return 1;
}

684
int __init op_nmi_init(struct oprofile_operations *ops)
Linus Torvalds's avatar
Linus Torvalds committed
685 686 687
{
	__u8 vendor = boot_cpu_data.x86_vendor;
	__u8 family = boot_cpu_data.x86;
688
	char *cpu_type = NULL;
689
	int ret = 0;
Linus Torvalds's avatar
Linus Torvalds committed
690 691 692

	if (!cpu_has_apic)
		return -ENODEV;
693

694 695 696
	if (force_cpu_type == timer)
		return -ENODEV;

Linus Torvalds's avatar
Linus Torvalds committed
697
	switch (vendor) {
698 699
	case X86_VENDOR_AMD:
		/* Needs to be at least an Athlon (or hammer in 32bit mode) */
Linus Torvalds's avatar
Linus Torvalds committed
700

701 702 703 704 705
		switch (family) {
		case 6:
			cpu_type = "i386/athlon";
			break;
		case 0xf:
706 707 708 709
			/*
			 * Actually it could be i386/hammer too, but
			 * give user space an consistent name.
			 */
710 711 712 713 714
			cpu_type = "x86-64/hammer";
			break;
		case 0x10:
			cpu_type = "x86-64/family10";
			break;
715 716 717
		case 0x11:
			cpu_type = "x86-64/family11h";
			break;
718 719 720
		case 0x12:
			cpu_type = "x86-64/family12h";
			break;
721 722 723
		case 0x14:
			cpu_type = "x86-64/family14h";
			break;
724 725 726
		case 0x15:
			cpu_type = "x86-64/family15h";
			break;
727 728
		default:
			return -ENODEV;
729
		}
730
		model = &op_amd_spec;
731 732 733 734 735 736
		break;

	case X86_VENDOR_INTEL:
		switch (family) {
			/* Pentium IV */
		case 0xf:
737
			p4_init(&cpu_type);
Linus Torvalds's avatar
Linus Torvalds committed
738
			break;
739 740 741

			/* A P6-class processor */
		case 6:
742
			ppro_init(&cpu_type);
Linus Torvalds's avatar
Linus Torvalds committed
743 744 745
			break;

		default:
746
			break;
747
		}
748

749 750 751 752
		if (cpu_type)
			break;

		if (!cpu_has_arch_perfmon)
753
			return -ENODEV;
754 755 756 757

		/* use arch perfmon as fallback */
		cpu_type = "i386/arch_perfmon";
		model = &op_arch_perfmon_spec;
758 759 760 761
		break;

	default:
		return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
762 763
	}

764
	/* default values, can be overwritten by model */
765 766 767 768 769 770
	ops->create_files	= nmi_create_files;
	ops->setup		= nmi_setup;
	ops->shutdown		= nmi_shutdown;
	ops->start		= nmi_start;
	ops->stop		= nmi_stop;
	ops->cpu_type		= cpu_type;
771

772 773 774 775 776
	if (model->init)
		ret = model->init(ops);
	if (ret)
		return ret;

777 778 779
	if (!model->num_virt_counters)
		model->num_virt_counters = model->num_counters;

780 781
	mux_init(ops);

782
	init_suspend_resume();
783

Linus Torvalds's avatar
Linus Torvalds committed
784 785 786 787
	printk(KERN_INFO "oprofile: using NMI interrupt.\n");
	return 0;
}

788
void op_nmi_exit(void)
Linus Torvalds's avatar
Linus Torvalds committed
789
{
790
	exit_suspend_resume();
Linus Torvalds's avatar
Linus Torvalds committed
791
}