forked from Minki/linux
df156f90a0
When kvm guest uses kvmclock, it may hang on vcpu hot-plug. This is caused by an overflow in pvclock_get_nsec_offset, u64 delta = tsc - shadow->tsc_timestamp; which in turn is caused by an undefined values from percpu hv_clock that hasn't been initialized yet. Uninitialized clock on being booted cpu is accessed from start_secondary -> smp_callin -> smp_store_cpu_info -> identify_secondary_cpu -> mtrr_ap_init -> mtrr_restore -> stop_machine_from_inactive_cpu -> queue_stop_cpus_work ... -> sched_clock -> kvm_clock_read which is well before x86_cpuinit.setup_percpu_clockev call in start_secondary, where percpu clock is initialized. This patch introduces a hook that allows to setup/initialize per_cpu clock early and avoid overflow due to reading - undefined values - old values if cpu was offlined and then onlined again Another possible early user of this clock source is ftrace that accesses it to get timestamps for ring buffer entries. So if mtrr_ap_init is moved from identify_secondary_cpu to past x86_cpuinit.setup_percpu_clockev in start_secondary, ftrace may cause the same overflow/hang on cpu hot-plug anyway. More complete description of the problem: https://lkml.org/lkml/2012/2/2/101 Credits to Marcelo Tosatti <mtosatti@redhat.com> for hook idea. Acked-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Igor Mammedov <imammedo@redhat.com> Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com> Signed-off-by: Avi Kivity <avi@redhat.com>
121 lines
3.1 KiB
C
121 lines
3.1 KiB
C
/*
|
|
* Copyright (C) 2009 Thomas Gleixner <tglx@linutronix.de>
|
|
*
|
|
* For licencing details see kernel-base/COPYING
|
|
*/
|
|
#include <linux/init.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/module.h>
|
|
#include <linux/pci.h>
|
|
|
|
#include <asm/bios_ebda.h>
|
|
#include <asm/paravirt.h>
|
|
#include <asm/pci_x86.h>
|
|
#include <asm/pci.h>
|
|
#include <asm/mpspec.h>
|
|
#include <asm/setup.h>
|
|
#include <asm/apic.h>
|
|
#include <asm/e820.h>
|
|
#include <asm/time.h>
|
|
#include <asm/irq.h>
|
|
#include <asm/pat.h>
|
|
#include <asm/tsc.h>
|
|
#include <asm/iommu.h>
|
|
#include <asm/mach_traps.h>
|
|
|
|
void __cpuinit x86_init_noop(void) { }
|
|
void __init x86_init_uint_noop(unsigned int unused) { }
|
|
void __init x86_init_pgd_noop(pgd_t *unused) { }
|
|
int __init iommu_init_noop(void) { return 0; }
|
|
void iommu_shutdown_noop(void) { }
|
|
void wallclock_init_noop(void) { }
|
|
|
|
/*
|
|
* The platform setup functions are preset with the default functions
|
|
* for standard PC hardware.
|
|
*/
|
|
struct x86_init_ops x86_init __initdata = {
|
|
|
|
.resources = {
|
|
.probe_roms = probe_roms,
|
|
.reserve_resources = reserve_standard_io_resources,
|
|
.memory_setup = default_machine_specific_memory_setup,
|
|
},
|
|
|
|
.mpparse = {
|
|
.mpc_record = x86_init_uint_noop,
|
|
.setup_ioapic_ids = x86_init_noop,
|
|
.mpc_apic_id = default_mpc_apic_id,
|
|
.smp_read_mpc_oem = default_smp_read_mpc_oem,
|
|
.mpc_oem_bus_info = default_mpc_oem_bus_info,
|
|
.find_smp_config = default_find_smp_config,
|
|
.get_smp_config = default_get_smp_config,
|
|
},
|
|
|
|
.irqs = {
|
|
.pre_vector_init = init_ISA_irqs,
|
|
.intr_init = native_init_IRQ,
|
|
.trap_init = x86_init_noop,
|
|
},
|
|
|
|
.oem = {
|
|
.arch_setup = x86_init_noop,
|
|
.banner = default_banner,
|
|
},
|
|
|
|
.mapping = {
|
|
.pagetable_reserve = native_pagetable_reserve,
|
|
},
|
|
|
|
.paging = {
|
|
.pagetable_setup_start = native_pagetable_setup_start,
|
|
.pagetable_setup_done = native_pagetable_setup_done,
|
|
},
|
|
|
|
.timers = {
|
|
.setup_percpu_clockev = setup_boot_APIC_clock,
|
|
.tsc_pre_init = x86_init_noop,
|
|
.timer_init = hpet_time_init,
|
|
.wallclock_init = x86_init_noop,
|
|
},
|
|
|
|
.iommu = {
|
|
.iommu_init = iommu_init_noop,
|
|
},
|
|
|
|
.pci = {
|
|
.init = x86_default_pci_init,
|
|
.init_irq = x86_default_pci_init_irq,
|
|
.fixup_irqs = x86_default_pci_fixup_irqs,
|
|
},
|
|
};
|
|
|
|
struct x86_cpuinit_ops x86_cpuinit __cpuinitdata = {
|
|
.early_percpu_clock_init = x86_init_noop,
|
|
.setup_percpu_clockev = setup_secondary_APIC_clock,
|
|
.fixup_cpu_id = x86_default_fixup_cpu_id,
|
|
};
|
|
|
|
static void default_nmi_init(void) { };
|
|
static int default_i8042_detect(void) { return 1; };
|
|
|
|
struct x86_platform_ops x86_platform = {
|
|
.calibrate_tsc = native_calibrate_tsc,
|
|
.wallclock_init = wallclock_init_noop,
|
|
.get_wallclock = mach_get_cmos_time,
|
|
.set_wallclock = mach_set_rtc_mmss,
|
|
.iommu_shutdown = iommu_shutdown_noop,
|
|
.is_untracked_pat_range = is_ISA_range,
|
|
.nmi_init = default_nmi_init,
|
|
.get_nmi_reason = default_get_nmi_reason,
|
|
.i8042_detect = default_i8042_detect
|
|
};
|
|
|
|
EXPORT_SYMBOL_GPL(x86_platform);
|
|
struct x86_msi_ops x86_msi = {
|
|
.setup_msi_irqs = native_setup_msi_irqs,
|
|
.teardown_msi_irq = native_teardown_msi_irq,
|
|
.teardown_msi_irqs = default_teardown_msi_irqs,
|
|
.restore_msi_irqs = default_restore_msi_irqs,
|
|
};
|