Files
xen/xen/arch/x86/oprofile/nmi_int.c
T
Jan Beulich c1811b988f x86/oprofile: switch to xv[mz]alloc_array()
Use the more "modern" forms, thus doing away with effectively open-
coding xmalloc_array() at the same time. While there is a difference in
generated code, as xmalloc_bytes() forces SMP_CACHE_BYTES alignment, if
code really cared about such higher than default alignment, it should
request so explicitly.

While at it also use XVFREE() instead of open-coding it, or instead of
leaving a dangling pointer, and change loop induction variable types.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Roger Pau Monné <roger.pau@cloud.com>
2025-07-29 11:28:13 +02:00

486 lines
10 KiB
C

/**
* @file nmi_int.c
*
* @remark Copyright 2002 OProfile authors
* @remark Read the file COPYING
*
* @author John Levon <levon@movementarian.org>
*
* Modified for Xen: by Aravind Menon & Jose Renato Santos
* These modifications are:
* Copyright (C) 2005 Hewlett-Packard Co.
*/
#include <xen/event.h>
#include <xen/types.h>
#include <xen/errno.h>
#include <xen/init.h>
#include <xen/param.h>
#include <xen/string.h>
#include <xen/delay.h>
#include <xen/xenoprof.h>
#include <xen/xvmalloc.h>
#include <public/xenoprof.h>
#include <asm/msr.h>
#include <asm/apic.h>
#include <asm/regs.h>
#include <asm/current.h>
#include <asm/nmi.h>
#include "op_counter.h"
#include "op_x86_model.h"
struct op_counter_config counter_config[OP_MAX_COUNTER];
struct op_ibs_config ibs_config;
struct op_x86_model_spec const *__read_mostly model;
static struct op_msrs cpu_msrs[NR_CPUS];
static unsigned long saved_lvtpc[NR_CPUS];
static const char *cpu_type;
static DEFINE_PER_CPU(struct vcpu *, nmi_cont_vcpu);
static int passive_domain_msr_op_checks(unsigned int msr, int *typep, int *indexp)
{
struct vpmu_struct *vpmu = vcpu_vpmu(current);
if ( model == NULL )
return 0;
if ( model->is_arch_pmu_msr == NULL )
return 0;
if ( !model->is_arch_pmu_msr(msr, typep, indexp) )
return 0;
if ( !vpmu_is_set(vpmu, VPMU_PASSIVE_DOMAIN_ALLOCATED) )
if ( ! model->allocated_msr(current) )
return 0;
return 1;
}
int passive_domain_do_rdmsr(unsigned int msr, uint64_t *msr_content)
{
int type, index;
if ( !passive_domain_msr_op_checks(msr, &type, &index))
return 0;
model->load_msr(current, type, index, msr_content);
return 1;
}
int passive_domain_do_wrmsr(unsigned int msr, uint64_t msr_content)
{
int type, index;
if ( !passive_domain_msr_op_checks(msr, &type, &index))
return 0;
model->save_msr(current, type, index, msr_content);
return 1;
}
void passive_domain_destroy(struct vcpu *v)
{
struct vpmu_struct *vpmu = vcpu_vpmu(v);
if ( vpmu_is_set(vpmu, VPMU_PASSIVE_DOMAIN_ALLOCATED) )
model->free_msr(v);
}
bool nmi_oprofile_send_virq(void)
{
struct vcpu *v = xchg(&this_cpu(nmi_cont_vcpu), NULL);
if (v)
send_guest_vcpu_virq(v, VIRQ_XENOPROF);
return v;
}
static int cf_check nmi_callback(const struct cpu_user_regs *regs, int cpu)
{
int xen_mode, ovf;
ovf = model->check_ctrs(cpu, &cpu_msrs[cpu], regs);
xen_mode = ring_0(regs);
if (ovf && is_active(current->domain) && !xen_mode &&
!this_cpu(nmi_cont_vcpu)) {
this_cpu(nmi_cont_vcpu) = current;
trigger_nmi_continuation();
}
if ( ovf == 2 )
current->arch.nmi_pending = true;
return 1;
}
static void nmi_cpu_save_registers(struct op_msrs *msrs)
{
unsigned int const nr_ctrs = model->num_counters;
unsigned int const nr_ctrls = model->num_controls;
struct op_msr *counters = msrs->counters;
struct op_msr *controls = msrs->controls;
unsigned int i;
for (i = 0; i < nr_ctrs; ++i) {
rdmsrl(counters[i].addr, counters[i].value);
}
for (i = 0; i < nr_ctrls; ++i) {
rdmsrl(controls[i].addr, controls[i].value);
}
}
static void cf_check nmi_save_registers(void *dummy)
{
int cpu = smp_processor_id();
struct op_msrs * msrs = &cpu_msrs[cpu];
model->fill_in_addresses(msrs);
nmi_cpu_save_registers(msrs);
}
static void free_msrs(void)
{
unsigned int i;
for (i = 0; i < nr_cpu_ids; ++i) {
XVFREE(cpu_msrs[i].counters);
XVFREE(cpu_msrs[i].controls);
}
}
static int allocate_msrs(void)
{
unsigned int i;
int success = 1;
for_each_online_cpu (i) {
cpu_msrs[i].counters = xvmalloc_array(struct op_msr,
model->num_counters);
if (!cpu_msrs[i].counters) {
success = 0;
break;
}
cpu_msrs[i].controls = xvmalloc_array(struct op_msr,
model->num_controls);
if (!cpu_msrs[i].controls) {
success = 0;
break;
}
}
if (!success)
free_msrs();
return success;
}
static void cf_check nmi_cpu_setup(void *dummy)
{
int cpu = smp_processor_id();
struct op_msrs * msrs = &cpu_msrs[cpu];
model->setup_ctrs(msrs);
}
int nmi_setup_events(void)
{
on_each_cpu(nmi_cpu_setup, NULL, 1);
return 0;
}
int nmi_reserve_counters(void)
{
if (!allocate_msrs())
return -ENOMEM;
/*
* We need to be careful to install our NMI handler
* without actually triggering any NMIs as this will
* break the core code horrifically.
*/
if (reserve_lapic_nmi() < 0) {
free_msrs();
return -EBUSY;
}
/* We need to serialize save and setup for HT because the subset
* of msrs are distinct for save and setup operations
*/
on_each_cpu(nmi_save_registers, NULL, 1);
return 0;
}
int nmi_enable_virq(void)
{
set_nmi_callback(nmi_callback);
return 0;
}
void nmi_disable_virq(void)
{
unset_nmi_callback();
}
static void nmi_restore_registers(struct op_msrs * msrs)
{
unsigned int const nr_ctrs = model->num_counters;
unsigned int const nr_ctrls = model->num_controls;
struct op_msr * counters = msrs->counters;
struct op_msr * controls = msrs->controls;
unsigned int i;
for (i = 0; i < nr_ctrls; ++i) {
wrmsrl(controls[i].addr, controls[i].value);
}
for (i = 0; i < nr_ctrs; ++i) {
wrmsrl(counters[i].addr, counters[i].value);
}
}
static void cf_check nmi_cpu_shutdown(void *dummy)
{
int cpu = smp_processor_id();
struct op_msrs * msrs = &cpu_msrs[cpu];
nmi_restore_registers(msrs);
}
void nmi_release_counters(void)
{
on_each_cpu(nmi_cpu_shutdown, NULL, 1);
release_lapic_nmi();
free_msrs();
}
static void cf_check nmi_cpu_start(void *dummy)
{
int cpu = smp_processor_id();
struct op_msrs const * msrs = &cpu_msrs[cpu];
saved_lvtpc[cpu] = apic_read(APIC_LVTPC);
apic_write(APIC_LVTPC, APIC_DM_NMI);
model->start(msrs);
}
int nmi_start(void)
{
on_each_cpu(nmi_cpu_start, NULL, 1);
return 0;
}
static void cf_check nmi_cpu_stop(void *dummy)
{
unsigned int v;
int cpu = smp_processor_id();
struct op_msrs const * msrs = &cpu_msrs[cpu];
model->stop(msrs);
/* 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
*/
if ( (apic_read(APIC_LVTPC) & APIC_DM_MASK) != APIC_DM_NMI
|| (apic_read(APIC_LVTPC) & APIC_LVT_MASKED) )
{
printk("nmi_stop: APIC not good %ul\n", apic_read(APIC_LVTPC));
mdelay(5000);
}
v = apic_read(APIC_LVTERR);
apic_write(APIC_LVTERR, v | APIC_LVT_MASKED);
apic_write(APIC_LVTPC, saved_lvtpc[cpu]);
apic_write(APIC_LVTERR, v);
}
void nmi_stop(void)
{
on_each_cpu(nmi_cpu_stop, NULL, 1);
}
static int __init p4_init(const char **cpu_type)
{
unsigned int cpu_model = current_cpu_data.x86_model;
if ((cpu_model > 6) || (cpu_model == 5)) {
printk("xenoprof: Initialization failed. "
"Intel processor model %u for pentium 4 family is not "
"supported\n", cpu_model);
return 0;
}
switch (current_cpu_data.x86_num_siblings) {
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;
}
printk("Xenoprof ERROR: P4 HyperThreading detected with > 2 threads\n");
return 0;
}
static int force_arch_perfmon;
static int __init cf_check force_cpu_type(const char *str)
{
if (!strcmp(str, "arch_perfmon")) {
force_arch_perfmon = 1;
printk(KERN_INFO "oprofile: forcing architectural perfmon\n");
}
else
return -EINVAL;
return 0;
}
custom_param("cpu_type", force_cpu_type);
static int __init ppro_init(const char **cpu_type)
{
if (force_arch_perfmon && cpu_has_arch_perfmon)
return 0;
switch (current_cpu_data.x86_model) {
case 14:
*cpu_type = "i386/core";
break;
case 15:
*cpu_type = "i386/core_2";
ppro_has_global_ctrl = 1;
break;
default:
/* Unknown */
return 0;
}
model = &op_ppro_spec;
return 1;
}
static int __init arch_perfmon_init(const char **cpu_type)
{
if (!cpu_has_arch_perfmon)
return 0;
*cpu_type = "i386/arch_perfmon";
model = &op_arch_perfmon_spec;
arch_perfmon_setup_counters();
ppro_has_global_ctrl = 1;
return 1;
}
static int __init cf_check nmi_init(void)
{
unsigned int vendor = current_cpu_data.x86_vendor;
unsigned int family = current_cpu_data.x86;
if (!cpu_has_apic) {
printk("xenoprof: Initialization failed. No APIC\n");
return -ENODEV;
}
switch (vendor) {
case X86_VENDOR_AMD:
/* Needs to be at least an Athlon (or hammer in 32bit mode) */
switch (family) {
default:
printk("xenoprof: Initialization failed. "
"AMD processor family %u is not "
"supported\n", family);
return -ENODEV;
case 0xf:
model = &op_athlon_spec;
cpu_type = "x86-64/hammer";
break;
case 0x10:
model = &op_athlon_spec;
cpu_type = "x86-64/family10";
ibs_init();
break;
case 0x11:
model = &op_athlon_spec;
cpu_type = "x86-64/family11h";
break;
case 0x12:
model = &op_athlon_spec;
cpu_type = "x86-64/family12h";
break;
case 0x14:
model = &op_athlon_spec;
cpu_type = "x86-64/family14h";
break;
case 0x15:
model = &op_amd_fam15h_spec;
cpu_type = "x86-64/family15h";
break;
case 0x16:
model = &op_athlon_spec;
cpu_type = "x86-64/family16h";
break;
}
break;
case X86_VENDOR_INTEL:
switch (family) {
/* Pentium IV */
case 0xf:
p4_init(&cpu_type);
break;
/* A P6-class processor */
case 6:
ppro_init(&cpu_type);
break;
default:
break;
}
if (!cpu_type && !arch_perfmon_init(&cpu_type)) {
printk("xenoprof: Initialization failed. "
"Intel processor family %u model %d is not supported\n",
family, current_cpu_data.x86_model);
return -ENODEV;
}
break;
default:
printk("xenoprof: Initialization failed. "
"Unsupported processor. Unknown vendor %u\n",
vendor);
return -ENODEV;
}
return 0;
}
__initcall(nmi_init);
int xenoprof_arch_init(int *num_events, char *_cpu_type)
{
if (cpu_type == NULL)
return -ENODEV;
*num_events = model->num_counters;
strlcpy(_cpu_type, cpu_type, XENOPROF_CPU_TYPE_SIZE);
return 0;
}