Commit 1a2a76c2 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'x86-urgent-2020-02-09' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86 fixes from Thomas Gleixner:
 "A set of fixes for X86:

   - Ensure that the PIT is set up when the local APIC is disable or
     configured in legacy mode. This is caused by an ordering issue
     introduced in the recent changes which skip PIT initialization when
     the TSC and APIC frequencies are already known.

   - Handle malformed SRAT tables during early ACPI parsing which caused
     an infinite loop anda boot hang.

   - Fix a long standing race in the affinity setting code which affects
     PCI devices with non-maskable MSI interrupts. The problem is caused
     by the non-atomic writes of the MSI address (destination APIC id)
     and data (vector) fields which the device uses to construct the MSI
     message. The non-atomic writes are mandated by PCI.

     If both fields change and the device raises an interrupt after
     writing address and before writing data, then the MSI block
     constructs a inconsistent message which causes interrupts to be
     lost and subsequent malfunction of the device.

     The fix is to redirect the interrupt to the new vector on the
     current CPU first and then switch it over to the new target CPU.
     This allows to observe an eventually raised interrupt in the
     transitional stage (old CPU, new vector) to be observed in the APIC
     IRR and retriggered on the new target CPU and the new vector.

     The potential spurious interrupts caused by this are harmless and
     can in the worst case expose a buggy driver (all handlers have to
     be able to deal with spurious interrupts as they can and do happen
     for various reasons).

   - Add the missing suspend/resume mechanism for the HYPERV hypercall
     page which prevents resume hibernation on HYPERV guests. This
     change got lost before the merge window.

   - Mask the IOAPIC before disabling the local APIC to prevent
     potentially stale IOAPIC remote IRR bits which cause stale
     interrupt lines after resume"

* tag 'x86-urgent-2020-02-09' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86/apic: Mask IOAPIC entries when disabling the local APIC
  x86/hyperv: Suspend/resume the hypercall page for hibernation
  x86/apic/msi: Plug non-maskable MSI affinity race
  x86/boot: Handle malformed SRAT tables during early ACPI parsing
  x86/timer: Don't skip PIT setup when APIC is disabled or in legacy mode
parents f4137760 0f378d73
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -393,7 +393,13 @@ int count_immovable_mem_regions(void)
	table = table_addr + sizeof(struct acpi_table_srat);

	while (table + sizeof(struct acpi_subtable_header) < table_end) {

		sub_table = (struct acpi_subtable_header *)table;
		if (!sub_table->length) {
			debug_putstr("Invalid zero length SRAT subtable.\n");
			return 0;
		}

		if (sub_table->type == ACPI_SRAT_TYPE_MEMORY_AFFINITY) {
			struct acpi_srat_mem_affinity *ma;

+50 −0
Original line number Diff line number Diff line
@@ -21,11 +21,15 @@
#include <linux/hyperv.h>
#include <linux/slab.h>
#include <linux/cpuhotplug.h>
#include <linux/syscore_ops.h>
#include <clocksource/hyperv_timer.h>

void *hv_hypercall_pg;
EXPORT_SYMBOL_GPL(hv_hypercall_pg);

/* Storage to save the hypercall page temporarily for hibernation */
static void *hv_hypercall_pg_saved;

u32 *hv_vp_index;
EXPORT_SYMBOL_GPL(hv_vp_index);

@@ -246,6 +250,48 @@ static int __init hv_pci_init(void)
	return 1;
}

static int hv_suspend(void)
{
	union hv_x64_msr_hypercall_contents hypercall_msr;

	/*
	 * Reset the hypercall page as it is going to be invalidated
	 * accross hibernation. Setting hv_hypercall_pg to NULL ensures
	 * that any subsequent hypercall operation fails safely instead of
	 * crashing due to an access of an invalid page. The hypercall page
	 * pointer is restored on resume.
	 */
	hv_hypercall_pg_saved = hv_hypercall_pg;
	hv_hypercall_pg = NULL;

	/* Disable the hypercall page in the hypervisor */
	rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
	hypercall_msr.enable = 0;
	wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);

	return 0;
}

static void hv_resume(void)
{
	union hv_x64_msr_hypercall_contents hypercall_msr;

	/* Re-enable the hypercall page */
	rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
	hypercall_msr.enable = 1;
	hypercall_msr.guest_physical_address =
		vmalloc_to_pfn(hv_hypercall_pg_saved);
	wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);

	hv_hypercall_pg = hv_hypercall_pg_saved;
	hv_hypercall_pg_saved = NULL;
}

static struct syscore_ops hv_syscore_ops = {
	.suspend	= hv_suspend,
	.resume		= hv_resume,
};

/*
 * This function is to be invoked early in the boot sequence after the
 * hypervisor has been detected.
@@ -330,6 +376,8 @@ void __init hyperv_init(void)

	x86_init.pci.arch_init = hv_pci_init;

	register_syscore_ops(&hv_syscore_ops);

	return;

remove_cpuhp_state:
@@ -349,6 +397,8 @@ void hyperv_cleanup(void)
{
	union hv_x64_msr_hypercall_contents hypercall_msr;

	unregister_syscore_ops(&hv_syscore_ops);

	/* Reset our OS id */
	wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0);

+10 −0
Original line number Diff line number Diff line
@@ -140,6 +140,7 @@ extern void apic_soft_disable(void);
extern void lapic_shutdown(void);
extern void sync_Arb_IDs(void);
extern void init_bsp_APIC(void);
extern void apic_intr_mode_select(void);
extern void apic_intr_mode_init(void);
extern void init_apic_mappings(void);
void register_lapic_address(unsigned long address);
@@ -188,6 +189,7 @@ static inline void disable_local_APIC(void) { }
# define setup_secondary_APIC_clock x86_init_noop
static inline void lapic_update_tsc_freq(void) { }
static inline void init_bsp_APIC(void) { }
static inline void apic_intr_mode_select(void) { }
static inline void apic_intr_mode_init(void) { }
static inline void lapic_assign_system_vectors(void) { }
static inline void lapic_assign_legacy_vector(unsigned int i, bool r) { }
@@ -452,6 +454,14 @@ static inline void ack_APIC_irq(void)
	apic_eoi();
}


static inline bool lapic_vector_set_in_irr(unsigned int vector)
{
	u32 irr = apic_read(APIC_IRR + (vector / 32 * 0x10));

	return !!(irr & (1U << (vector % 32)));
}

static inline unsigned default_get_apic_id(unsigned long x)
{
	unsigned int ver = GET_APIC_VERSION(apic_read(APIC_LVR));
+2 −0
Original line number Diff line number Diff line
@@ -51,12 +51,14 @@ struct x86_init_resources {
 *				are set up.
 * @intr_init:			interrupt init code
 * @trap_init:			platform specific trap setup
 * @intr_mode_select:		interrupt delivery mode selection
 * @intr_mode_init:		interrupt delivery mode setup
 */
struct x86_init_irqs {
	void (*pre_vector_init)(void);
	void (*intr_init)(void);
	void (*trap_init)(void);
	void (*intr_mode_select)(void);
	void (*intr_mode_init)(void);
};

+25 −5
Original line number Diff line number Diff line
@@ -830,8 +830,17 @@ bool __init apic_needs_pit(void)
	if (!tsc_khz || !cpu_khz)
		return true;

	/* Is there an APIC at all? */
	if (!boot_cpu_has(X86_FEATURE_APIC))
	/* Is there an APIC at all or is it disabled? */
	if (!boot_cpu_has(X86_FEATURE_APIC) || disable_apic)
		return true;

	/*
	 * If interrupt delivery mode is legacy PIC or virtual wire without
	 * configuration, the local APIC timer wont be set up. Make sure
	 * that the PIT is initialized.
	 */
	if (apic_intr_mode == APIC_PIC ||
	    apic_intr_mode == APIC_VIRTUAL_WIRE_NO_CONFIG)
		return true;

	/* Virt guests may lack ARAT, but still have DEADLINE */
@@ -1322,7 +1331,7 @@ void __init sync_Arb_IDs(void)

enum apic_intr_mode_id apic_intr_mode __ro_after_init;

static int __init apic_intr_mode_select(void)
static int __init __apic_intr_mode_select(void)
{
	/* Check kernel option */
	if (disable_apic) {
@@ -1384,6 +1393,12 @@ static int __init apic_intr_mode_select(void)
	return APIC_SYMMETRIC_IO;
}

/* Select the interrupt delivery mode for the BSP */
void __init apic_intr_mode_select(void)
{
	apic_intr_mode = __apic_intr_mode_select();
}

/*
 * An initial setup of the virtual wire mode.
 */
@@ -1440,8 +1455,6 @@ void __init apic_intr_mode_init(void)
{
	bool upmode = IS_ENABLED(CONFIG_UP_LATE_INIT);

	apic_intr_mode = apic_intr_mode_select();

	switch (apic_intr_mode) {
	case APIC_PIC:
		pr_info("APIC: Keep in PIC mode(8259)\n");
@@ -2626,6 +2639,13 @@ static int lapic_suspend(void)
#endif

	local_irq_save(flags);

	/*
	 * Mask IOAPIC before disabling the local APIC to prevent stale IRR
	 * entries on some implementations.
	 */
	mask_ioapic_entries();

	disable_local_APIC();

	irq_remapping_disable();
Loading