Received: by 10.223.176.46 with SMTP id f43csp3453095wra; Mon, 22 Jan 2018 14:42:35 -0800 (PST) X-Google-Smtp-Source: AH8x2253QujCz884Del60qQbGeTHaLsrN1m+prlB9owvp5EjKn0DBjc13Zuk1p6eNt5RKsZe1gUx X-Received: by 10.107.138.13 with SMTP id m13mr751854iod.81.1516660955343; Mon, 22 Jan 2018 14:42:35 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1516660955; cv=none; d=google.com; s=arc-20160816; b=WNZuX+QvhlD3bHX6s//6Q1MEsZhVHyaTQUk4cyt2jOzPJv1xC+JK22hQc3dy1ELyK4 dn/7bj9364npTssagVeRx8Wwr8R95x8s/l6gKknGg5kd/NWZJPfFx8lA3CPZT6Qmbapn dpx6pBAq14T8Xlq19DQz1AVlzkjmmblzdQPDk3srZamaGOb8XzIHCDcmyZ59K7THoG9B zlFACMDELvxfcvieZCHpZaje3xzqzj4EE8fDVjWJg58wCtL1swa23gztdYK+EZtk6ak4 u8Wf8LjwMXKw6sAdz46neS1NTzsEFWNgE/BzIb7fHnJ7GpIKygw6ZTmMm23RY9l+bO8q zC9A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:reply-to:message-id:date:subject:cc:to :from:arc-authentication-results; bh=QZfBQTXa5XV0YhJqZZNBMnrAEVSQ6jxgJQUvcl8TaCs=; b=uriOTBEJkDABVrA1E5M/WhHpFOx+33H6xzsy3rXB3HTzwaFCKqhq3X8UVw84o8jEZn xd96jJu0YG31BP+pCmMzAOxb1kGMnFcUx7aTKcNWDBe6xVZG4RERLYNTgfFMA7JEwZ4O xC7XwheQmTVRycWlK4lalcoWppB+0FvHRMhBmsan1Xjfrur9HKzjZvZeEWzek/usJAS4 XNhQSSIJwso82wuXzaVzO8BxMgqx3ppUrZ4E1mNd5SZA0+kdmwgDA9fW7o/0Tdc//hXD vzbhjbJU0ZY/UdtHLfU8YLqPq2PfYz01ZwmfMV+sLtlztTb0TC/IN4B0ZondCeTNmvAX Dpxg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=exchange.microsoft.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 13si6966428itx.111.2018.01.22.14.42.20; Mon, 22 Jan 2018 14:42:35 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=exchange.microsoft.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751173AbeAVWl7 (ORCPT + 99 others); Mon, 22 Jan 2018 17:41:59 -0500 Received: from a2nlsmtp01-05.prod.iad2.secureserver.net ([198.71.225.49]:38742 "EHLO a2nlsmtp01-05.prod.iad2.secureserver.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751037AbeAVWl5 (ORCPT ); Mon, 22 Jan 2018 17:41:57 -0500 X-Greylist: delayed 675 seconds by postgrey-1.27 at vger.kernel.org; Mon, 22 Jan 2018 17:41:57 EST Received: from linuxonhyperv.com ([107.180.71.197]) by : HOSTING RELAY : with SMTP id dkayetqXSl4AQdkayeRKUZ; Mon, 22 Jan 2018 15:29:41 -0700 x-originating-ip: 107.180.71.197 Received: from mikelley by linuxonhyperv.com with local (Exim 4.89_1) (envelope-from ) id 1edkay-0000zP-LG; Mon, 22 Jan 2018 15:29:40 -0700 From: mikelley@exchange.microsoft.com To: gregkh@linuxfoundation.org, linux-kernel@vger.kernel.org, devel@linuxdriverproject.org, olaf@aepfle.de, apw@canonical.com, vkuznets@redhat.com, jasowang@redhat.com, leann.ogasawara@canonical.com, marcelo.cerri@canonical.com, sthemmin@microsoft.com, kys@microsoft.com, x86@kernel.org, jrp@pun.org Cc: Michael Kelley Subject: [PATCH v2 char-misc 1/1] Drivers: hv: vmbus: Implement Direct Mode for stimer0 Date: Mon, 22 Jan 2018 15:29:29 -0700 Message-Id: <20180122222929.3762-1-mikelley@exchange.microsoft.com> X-Mailer: git-send-email 2.15.1 Reply-To: mikelley@microsoft.com X-CMAE-Envelope: MS4wfJsjbhtatoW4T0EX+zTJssP0JUGHu+tYlmba2ASYTU9XlVD3So0HtLZiNQx+14mtZMU90AZsUEsAsGR/GLq8jfCCf3cPZv4WqVbyzqsVO3F2xe1grd2l r6T33tik2xAhdCTlUIVQm8JKMLeI/IH5iKihpRwX3CBHhFBrrHO9mNXhrkpmN3u2yyC9DN+jjPZNQB0FjE2DhisZH1aJg+DDhystF+mf+xkkf++5YO3TmViH ogH2dyrop22GvxMxu22DPlWmICNQ77LyZ1NMsjCXsWRrd9RG7IA8WOlP/14GZ+6Ge0zd8TJbBhOa1yekOd5r+KY7MatS6SmJ+g0LQdxg+yCCpxeX7ZZPh0ug twchp/X6up83AkW6xW3YgFXxvziUgkb8ilOqG8tkwxvJvTXT5w+2ooLwVZS8tZYHdK6XC9yJjN0GB3lrsV49IAt2hB0qD6KvT5x4rB08tjMZYv4mv+nW9aq0 hkQqqtwQb56hyai7nZYfpEoyI/thA8t1qvPUsjPpCt/2QkSwdfZaHNyWPOf/4YrkEu/HLCKnTnrdaHzLUQ8qFaX9va3v5NS0RpZCwe5qaI+MISXszO8O1qxx nX6H8BX+rLuPlsUztIocy7vmVU0UmXsJJg0wS2FYoAMjvw== Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Michael Kelley The 2016 version of Hyper-V offers the option to operate the guest VM per-vcpu stimer's in Direct Mode, which means the timer interupts on its own vector rather than queueing a VMbus message. Direct Mode reduces timer processing overhead in both the hypervisor and the guest, and avoids having timer interrupts pollute the VMbus interrupt stream for the synthetic NIC and storage. This patch enables Direct Mode by default on stimer0 when running on a version of Hyper-V that supports it, with a hv_vmbus module parameter for disabling Direct Mode and reverting to the old behavior. In prep for coming support of Hyper-V on ARM64, the arch independent portion of the code contains calls to routines that will be populated on ARM64 but are not needed and do nothing on x86. Signed-off-by: Michael Kelley --- Changes since v1: * Major rework to allocate and use a fixed interrupt vector * Fixed block comment style * Removed minor changes unrelated to Direct Mode --- arch/x86/entry/entry_32.S | 3 ++ arch/x86/entry/entry_64.S | 2 ++ arch/x86/include/asm/hardirq.h | 3 ++ arch/x86/include/asm/irq_vectors.h | 7 +++- arch/x86/include/asm/mshyperv.h | 12 +++++++ arch/x86/include/uapi/asm/hyperv.h | 3 ++ arch/x86/kernel/cpu/mshyperv.c | 41 ++++++++++++++++++++++- arch/x86/kernel/irq.c | 9 +++++ drivers/hv/hv.c | 67 ++++++++++++++++++++++++++++++++++++-- drivers/hv/hyperv_vmbus.h | 4 ++- 10 files changed, 146 insertions(+), 5 deletions(-) diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S index ace8f32..1814991 100644 --- a/arch/x86/entry/entry_32.S +++ b/arch/x86/entry/entry_32.S @@ -882,6 +882,9 @@ BUILD_INTERRUPT3(xen_hvm_callback_vector, HYPERVISOR_CALLBACK_VECTOR, BUILD_INTERRUPT3(hyperv_callback_vector, HYPERVISOR_CALLBACK_VECTOR, hyperv_vector_handler) +BUILD_INTERRUPT3(hv_stimer0_callback_vector, HYPERV_STIMER0_VECTOR, + hv_stimer0_vector_handler) + #endif /* CONFIG_HYPERV */ ENTRY(page_fault) diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S index f048e38..23afac9 100644 --- a/arch/x86/entry/entry_64.S +++ b/arch/x86/entry/entry_64.S @@ -1227,6 +1227,8 @@ apicinterrupt3 HYPERVISOR_CALLBACK_VECTOR \ #if IS_ENABLED(CONFIG_HYPERV) apicinterrupt3 HYPERVISOR_CALLBACK_VECTOR \ hyperv_callback_vector hyperv_vector_handler +apicinterrupt3 HYPERV_STIMER0_VECTOR \ + hv_stimer0_callback_vector hv_stimer0_vector_handler #endif /* CONFIG_HYPERV */ idtentry debug do_debug has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK diff --git a/arch/x86/include/asm/hardirq.h b/arch/x86/include/asm/hardirq.h index 51cc979..c788343 100644 --- a/arch/x86/include/asm/hardirq.h +++ b/arch/x86/include/asm/hardirq.h @@ -38,6 +38,9 @@ #if IS_ENABLED(CONFIG_HYPERV) || defined(CONFIG_XEN) unsigned int irq_hv_callback_count; #endif +#if IS_ENABLED(CONFIG_HYPERV) + unsigned int hyperv_stimer0_count; +#endif } ____cacheline_aligned irq_cpustat_t; DECLARE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat); diff --git a/arch/x86/include/asm/irq_vectors.h b/arch/x86/include/asm/irq_vectors.h index 67421f6..6accf0b 100644 --- a/arch/x86/include/asm/irq_vectors.h +++ b/arch/x86/include/asm/irq_vectors.h @@ -103,7 +103,12 @@ #endif #define MANAGED_IRQ_SHUTDOWN_VECTOR 0xef -#define LOCAL_TIMER_VECTOR 0xee + +#if IS_ENABLED(CONFIG_HYPERV) +#define HYPERV_STIMER0_VECTOR 0xee +#endif + +#define LOCAL_TIMER_VECTOR 0xed #define NR_VECTORS 256 diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index b623a42..aed9185 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -171,6 +171,18 @@ static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type) void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs)); void hv_remove_crash_handler(void); +/* + * Routines for stimer0 Direct Mode handling. + * On x86/x64, there are no percpu actions to take. + */ +void hv_stimer0_callback_vector(void); +int hv_setup_stimer0_irq(int *irq, int *vector, void (*handler)(void)); +void hv_remove_stimer0_irq(int irq); + +static inline void hv_enable_stimer0_percpu_irq(int irq) {} +static inline void hv_disable_stimer0_percpu_irq(int irq) {} + + #if IS_ENABLED(CONFIG_HYPERV) extern struct clocksource *hyperv_cs; extern void *hv_hypercall_pg; diff --git a/arch/x86/include/uapi/asm/hyperv.h b/arch/x86/include/uapi/asm/hyperv.h index 1a5bfea..7213cb8 100644 --- a/arch/x86/include/uapi/asm/hyperv.h +++ b/arch/x86/include/uapi/asm/hyperv.h @@ -74,6 +74,9 @@ /* Crash MSR available */ #define HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE (1 << 10) +/* stimer Direct Mode is available */ +#define HV_X64_STIMER_DIRECT_MODE_AVAILABLE (1 << 19) + /* * Feature identification: EBX indicates which flags were specified at * partition creation. The format is the same as the partition creation diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index 85eb5fc..dfcb8d2 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -37,6 +37,7 @@ #if IS_ENABLED(CONFIG_HYPERV) static void (*vmbus_handler)(void); +static void (*hv_stimer0_handler)(void); static void (*hv_kexec_handler)(void); static void (*hv_crash_handler)(struct pt_regs *regs); @@ -69,6 +70,41 @@ void hv_remove_vmbus_irq(void) EXPORT_SYMBOL_GPL(hv_setup_vmbus_irq); EXPORT_SYMBOL_GPL(hv_remove_vmbus_irq); +/* + * Routines to do per-architecture handling of stimer0 + * interrupts when in Direct Mode + */ + +__visible void __irq_entry hv_stimer0_vector_handler(struct pt_regs *regs) +{ + struct pt_regs *old_regs = set_irq_regs(regs); + + entering_irq(); + inc_irq_stat(hyperv_stimer0_count); + if (hv_stimer0_handler) + hv_stimer0_handler(); + ack_APIC_irq(); + + exiting_irq(); + set_irq_regs(old_regs); +} + +int hv_setup_stimer0_irq(int *irq, int *vector, void (*handler)(void)) +{ + *vector = HYPERV_STIMER0_VECTOR; + *irq = 0; /* Unused on x86/x64 */ + hv_stimer0_handler = handler; + return 0; +} +EXPORT_SYMBOL_GPL(hv_setup_stimer0_irq); + +void hv_remove_stimer0_irq(int irq) +{ + /* We have no way to deallocate the interrupt gate */ + hv_stimer0_handler = NULL; +} +EXPORT_SYMBOL_GPL(hv_remove_stimer0_irq); + void hv_setup_kexec_handler(void (*handler)(void)) { hv_kexec_handler = handler; @@ -249,8 +285,11 @@ static void __init ms_hyperv_init_platform(void) */ x86_platform.apic_post_init = hyperv_init; hyperv_setup_mmu_ops(); - /* Setup the IDT for hypervisor callback */ + /* Setup the IDTs for hypervisor callback and for stimer0 */ alloc_intr_gate(HYPERVISOR_CALLBACK_VECTOR, hyperv_callback_vector); + if (ms_hyperv.misc_features & HV_X64_STIMER_DIRECT_MODE_AVAILABLE) + alloc_intr_gate(HYPERV_STIMER0_VECTOR, + hv_stimer0_callback_vector); #endif } diff --git a/arch/x86/kernel/irq.c b/arch/x86/kernel/irq.c index 68e1867..e1154f0 100644 --- a/arch/x86/kernel/irq.c +++ b/arch/x86/kernel/irq.c @@ -142,6 +142,15 @@ int arch_show_interrupts(struct seq_file *p, int prec) seq_puts(p, " Hypervisor callback interrupts\n"); } #endif +#if IS_ENABLED(CONFIG_HYPERV) + if (test_bit(HYPERV_STIMER0_VECTOR, system_vectors)) { + seq_printf(p, "%*s: ", prec, "HVS"); + for_each_online_cpu(j) + seq_printf(p, "%10u ", + irq_stats(j)->hyperv_stimer0_count); + seq_puts(p, " Hyper-V stimer0 interrupts\n"); + } +#endif seq_printf(p, "%*s: %10u\n", prec, "ERR", atomic_read(&irq_err_count)); #if defined(CONFIG_X86_IO_APIC) seq_printf(p, "%*s: %10u\n", prec, "MIS", atomic_read(&irq_mis_count)); diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index fe96aab..3b11381 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -27,8 +27,9 @@ #include #include #include -#include +#include #include +#include #include #include #include "hyperv_vmbus.h" @@ -38,6 +39,22 @@ struct hv_context hv_context = { .synic_initialized = false, }; +/* + * If false, we're using the old mechanism for stimer0 interrupts + * where it sends a VMbus message when it expires. The old + * mechanism is used if Direct Mode is explicitly disabled + * by the module parameter, or on older versions of Hyper-V + * that don't support Direct Mode. While Hyper-V provides + * four stimer's per CPU, Linux uses only stimer0. + */ +static bool direct_mode_enabled = true; +module_param(direct_mode_enabled, bool, 0444); +MODULE_PARM_DESC(direct_mode_enabled, + "Set to N to disable stimer Direct Mode."); + +static int stimer0_irq; +static int stimer0_vector; + #define HV_TIMER_FREQUENCY (10 * 1000 * 1000) /* 100ns period */ #define HV_MAX_MAX_DELTA_TICKS 0xffffffff #define HV_MIN_DELTA_TICKS 1 @@ -53,6 +70,8 @@ int hv_init(void) if (!hv_context.cpu_context) return -ENOMEM; + if (!(ms_hyperv.misc_features & HV_X64_STIMER_DIRECT_MODE_AVAILABLE)) + direct_mode_enabled = false; return 0; } @@ -91,6 +110,21 @@ int hv_post_message(union hv_connection_id connection_id, return status & 0xFFFF; } +/* + * ISR for when stimer0 is operating in Direct Mode. Direct Mode + * does not use VMbus or any VMbus messages, so process here and not + * in the VMbus driver code. + */ + +static void hv_stimer0_isr(void) +{ + struct hv_per_cpu_context *hv_cpu; + + hv_cpu = this_cpu_ptr(hv_context.cpu_context); + hv_cpu->clk_evt->event_handler(hv_cpu->clk_evt); + add_interrupt_randomness(stimer0_vector, 0); +} + static int hv_ce_set_next_event(unsigned long delta, struct clock_event_device *evt) { @@ -108,6 +142,8 @@ static int hv_ce_shutdown(struct clock_event_device *evt) { hv_init_timer(HV_X64_MSR_STIMER0_COUNT, 0); hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, 0); + if (direct_mode_enabled) + hv_disable_stimer0_percpu_irq(stimer0_irq); return 0; } @@ -116,9 +152,29 @@ static int hv_ce_set_oneshot(struct clock_event_device *evt) { union hv_timer_config timer_cfg; + timer_cfg.as_uint64 = 0; timer_cfg.enable = 1; timer_cfg.auto_enable = 1; - timer_cfg.sintx = VMBUS_MESSAGE_SINT; + if (direct_mode_enabled) + /* + * When it expires, the timer will directly interrupt + * on the specified hardware vector/IRQ. + */ + { + timer_cfg.direct_mode = 1; + timer_cfg.apic_vector = stimer0_vector; + hv_enable_stimer0_percpu_irq(stimer0_irq); + } + else + /* + * When it expires, the timer will generate a VMbus message, + * to be handled by the normal VMbus interrupt handler. + */ + { + timer_cfg.direct_mode = 0; + timer_cfg.sintx = VMBUS_MESSAGE_SINT; + } + hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, timer_cfg.as_uint64); return 0; @@ -191,6 +247,10 @@ int hv_synic_alloc(void) INIT_LIST_HEAD(&hv_cpu->chan_list); } + if (direct_mode_enabled && hv_setup_stimer0_irq( + &stimer0_irq, &stimer0_vector, hv_stimer0_isr)) + goto err; + return 0; err: return -ENOMEM; @@ -292,6 +352,9 @@ void hv_synic_clockevents_cleanup(void) if (!(ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE)) return; + if (direct_mode_enabled) + hv_remove_stimer0_irq(stimer0_irq); + for_each_present_cpu(cpu) { struct hv_per_cpu_context *hv_cpu = per_cpu_ptr(hv_context.cpu_context, cpu); diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 22300ec..36d34fe 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -57,7 +57,9 @@ u64 periodic:1; u64 lazy:1; u64 auto_enable:1; - u64 reserved_z0:12; + u64 apic_vector:8; + u64 direct_mode:1; + u64 reserved_z0:3; u64 sintx:4; u64 reserved_z1:44; }; -- 1.8.3.1