amd_nonfatal.c
来自「xen虚拟机源代码安装包」· C语言 代码 · 共 304 行
C
304 行
/* * MCA implementation for AMD CPUs * Copyright (c) 2007 Advanced Micro Devices, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *//* K8 common MCA documentation published at * * AMD64 Architecture Programmer's Manual Volume 2: * System Programming * Publication # 24593 Revision: 3.12 * Issue Date: September 2006 * * URL: * http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/24593.pdf *//* The related documentation for K8 Revisions A - E is: * * BIOS and Kernel Developer's Guide for * AMD Athlon 64 and AMD Opteron Processors * Publication # 26094 Revision: 3.30 * Issue Date: February 2006 * * URL: * http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/26094.PDF *//* The related documentation for K8 Revisions F - G is: * * BIOS and Kernel Developer's Guide for * AMD NPT Family 0Fh Processors * Publication # 32559 Revision: 3.04 * Issue Date: December 2006 * * URL: * http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/32559.pdf */#include <xen/config.h>#include <xen/init.h>#include <xen/types.h>#include <xen/kernel.h>#include <xen/smp.h>#include <xen/timer.h>#include <xen/event.h>#include <asm/processor.h> #include <asm/system.h>#include <asm/msr.h>#include "mce.h"#include "x86_mca.h"static struct timer mce_timer;#define MCE_PERIOD MILLISECS(15000)#define MCE_MIN MILLISECS(2000)#define MCE_MAX MILLISECS(30000)static s_time_t period = MCE_PERIOD;static int hw_threshold = 0;static int adjust = 0;/* The polling service routine: * Collects information of correctable errors and notifies * Dom0 via an event. */void mce_amd_checkregs(void *info){ struct vcpu *vcpu = current; struct mc_info *mc_data; struct mcinfo_global mc_global; struct mcinfo_bank mc_info; uint64_t status, addrv, miscv; unsigned int i; unsigned int event_enabled; unsigned int cpu_nr; int error_found; /* We don't need a slot yet. Only allocate one on error. */ mc_data = NULL; cpu_nr = smp_processor_id(); event_enabled = guest_enabled_event(dom0->vcpu[0], VIRQ_MCA); error_found = 0; memset(&mc_global, 0, sizeof(mc_global)); mc_global.common.type = MC_TYPE_GLOBAL; mc_global.common.size = sizeof(mc_global); mc_global.mc_domid = vcpu->domain->domain_id; /* impacted domain */ mc_global.mc_coreid = vcpu->processor; /* impacted physical cpu */ BUG_ON(cpu_nr != vcpu->processor); mc_global.mc_core_threadid = 0; mc_global.mc_vcpuid = vcpu->vcpu_id; /* impacted vcpu */#if 0 /* TODO: on which socket is this physical core? It's not clear to me how to figure this out. */ mc_global.mc_socketid = ???;#endif mc_global.mc_flags |= MC_FLAG_CORRECTABLE; rdmsrl(MSR_IA32_MCG_STATUS, mc_global.mc_gstatus); for (i = 0; i < nr_mce_banks; i++) { struct domain *d; rdmsrl(MSR_IA32_MC0_STATUS + i * 4, status); if (!(status & MCi_STATUS_VAL)) continue; if (mc_data == NULL) { /* Now we need a slot to fill in error telemetry. */ mc_data = x86_mcinfo_getptr(); BUG_ON(mc_data == NULL); x86_mcinfo_clear(mc_data); x86_mcinfo_add(mc_data, &mc_global); } memset(&mc_info, 0, sizeof(mc_info)); mc_info.common.type = MC_TYPE_BANK; mc_info.common.size = sizeof(mc_info); mc_info.mc_bank = i; mc_info.mc_status = status; /* Increase polling frequency */ error_found = 1; addrv = 0; if (status & MCi_STATUS_ADDRV) { rdmsrl(MSR_IA32_MC0_ADDR + i * 4, addrv); d = maddr_get_owner(addrv); if (d != NULL) mc_info.mc_domid = d->domain_id; } miscv = 0; if (status & MCi_STATUS_MISCV) rdmsrl(MSR_IA32_MC0_MISC + i * 4, miscv); mc_info.mc_addr = addrv; mc_info.mc_misc = miscv; x86_mcinfo_add(mc_data, &mc_info); if (mc_callback_bank_extended) mc_callback_bank_extended(mc_data, i, status); /* clear status */ wrmsrl(MSR_IA32_MC0_STATUS + i * 4, 0x0ULL); wmb(); } if (error_found > 0) { /* If Dom0 enabled the VIRQ_MCA event, then ... */ if (event_enabled) /* ... notify it. */ send_guest_global_virq(dom0, VIRQ_MCA); else /* ... or dump it */ x86_mcinfo_dump(mc_data); } adjust += error_found;}/* polling service routine invoker: * Adjust poll frequency at runtime. No error means slow polling frequency, * an error means higher polling frequency. * It uses hw threshold register introduced in AMD K8 RevF to detect * multiple correctable errors between two polls. In that case, * increase polling frequency higher than normal. */static void mce_amd_work_fn(void *data){ on_each_cpu(mce_amd_checkregs, data, 1, 1); if (adjust > 0) { if ( !guest_enabled_event(dom0->vcpu[0], VIRQ_MCA) ) { /* Dom0 did not enable VIRQ_MCA, so Xen is reporting. */ printk("MCE: polling routine found correctable error. " " Use mcelog to parse above error output.\n"); } } if (hw_threshold) { uint64_t value; uint32_t counter; rdmsrl(MSR_IA32_MC4_MISC, value); /* Only the error counter field is of interest * Bit field is described in AMD K8 BKDG chapter 6.4.5.5 */ counter = (value & 0xFFF00000000ULL) >> 32U; /* HW does not count *all* kinds of correctable errors. * Thus it is possible, that the polling routine finds an * correctable error even if the HW reports nothing. * However, the other way around is not possible (= BUG). */ if (counter > 0) { /* HW reported correctable errors, * the polling routine did not find... */ BUG_ON(adjust == 0); /* subtract 1 to not double count the error * from the polling service routine */ adjust += (counter - 1); /* Restart counter */ /* No interrupt, reset counter value */ value &= ~(0x60FFF00000000ULL); /* Counter enable */ value |= (1ULL << 51); wrmsrl(MSR_IA32_MC4_MISC, value); wmb(); } } if (adjust > 0) { /* Increase polling frequency */ adjust++; /* adjust == 1 must have an effect */ period /= adjust; } else { /* Decrease polling frequency */ period *= 2; } if (period > MCE_MAX) { /* limit: Poll at least every 30s */ period = MCE_MAX; } if (period < MCE_MIN) { /* limit: Poll every 2s. * When this is reached an uncorrectable error * is expected to happen, if Dom0 does nothing. */ period = MCE_MIN; } set_timer(&mce_timer, NOW() + period); adjust = 0;}void amd_nonfatal_mcheck_init(struct cpuinfo_x86 *c){ if (c->x86_vendor != X86_VENDOR_AMD) return; /* Assume we are on K8 or newer AMD CPU here */ /* The threshold bitfields in MSR_IA32_MC4_MISC has * been introduced along with the SVME feature bit. */ if (cpu_has(c, X86_FEATURE_SVME)) { uint64_t value; /* hw threshold registers present */ hw_threshold = 1; rdmsrl(MSR_IA32_MC4_MISC, value); if (value & (1ULL << 61)) { /* Locked bit */ /* Locked by BIOS. Not available for use */ hw_threshold = 0; } if (!(value & (1ULL << 63))) { /* Valid bit */ /* No CtrP present */ hw_threshold = 0; } else { if (!(value & (1ULL << 62))) { /* Counter Bit */ /* No counter field present */ hw_threshold = 0; } } if (hw_threshold) { /* No interrupt, reset counter value */ value &= ~(0x60FFF00000000ULL); /* Counter enable */ value |= (1ULL << 51); wrmsrl(MSR_IA32_MC4_MISC, value); /* serialize */ wmb(); printk(XENLOG_INFO "MCA: Use hw thresholding to adjust polling frequency\n"); } } init_timer(&mce_timer, mce_amd_work_fn, NULL, 0); set_timer(&mce_timer, NOW() + period); return;}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?