diff options
author | sephe <sephe@FreeBSD.org> | 2016-02-19 02:03:14 +0000 |
---|---|---|
committer | sephe <sephe@FreeBSD.org> | 2016-02-19 02:03:14 +0000 |
commit | 427bb5d10fd29ef038c6a4f6df3160e9c77f1b24 (patch) | |
tree | 9dc5d5a4a2fc61ccf8579a3b76d30b2b1b3a99cd /sys/dev/hyperv/vmbus | |
parent | bdaba1774dae36cadfb71e549a51d42677155f62 (diff) | |
download | FreeBSD-src-427bb5d10fd29ef038c6a4f6df3160e9c77f1b24.zip FreeBSD-src-427bb5d10fd29ef038c6a4f6df3160e9c77f1b24.tar.gz |
MFC [Hyper-V]: r293719-r293722, r293869-r293871, r293873-r293875, r293877
r293719 hyperv/hn: Implement LRO
r293720 hyperv/hn: Implement SIOC[SG]IFMEDIA support
r293721 hyperv/hn: Avoid mbuf cluster allocation, if the packet is small.
r293722 hyperv/hn: Removed unused netvsc_init()
r293869 hyperv/hn: Unbreak LINT-NOIP
r293870 hyperv: use x86 generic code to do the hypervisor detection
r293871 hyperv: remove unused vmbus definitions
r293873 hyperv: implement an event timer
r293874 hyperv: add interrupt counters
r293875 hyperv: set receive buffer size according to NVSP protocol version
r293877 Unbreak `make depend` with sys/modules/hyperv/vmbus after r293870
Approved by: re (glebius), adrian (mentor)
Sponsored by: Microsoft OSTC
Diffstat (limited to 'sys/dev/hyperv/vmbus')
-rw-r--r-- | sys/dev/hyperv/vmbus/hv_connection.c | 19 | ||||
-rw-r--r-- | sys/dev/hyperv/vmbus/hv_et.c | 131 | ||||
-rw-r--r-- | sys/dev/hyperv/vmbus/hv_hv.c | 61 | ||||
-rw-r--r-- | sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c | 78 | ||||
-rw-r--r-- | sys/dev/hyperv/vmbus/hv_vmbus_priv.h | 57 |
5 files changed, 234 insertions, 112 deletions
diff --git a/sys/dev/hyperv/vmbus/hv_connection.c b/sys/dev/hyperv/vmbus/hv_connection.c index cc83037..7496288 100644 --- a/sys/dev/hyperv/vmbus/hv_connection.c +++ b/sys/dev/hyperv/vmbus/hv_connection.c @@ -254,7 +254,7 @@ hv_vmbus_connect(void) { hv_vmbus_protocal_version = version; if (bootverbose) - printf("VMBUS: Portocal Version: %d.%d\n", + printf("VMBUS: Protocol Version: %d.%d\n", version >> 16, version & 0xFFFF); sema_destroy(&msg_info->wait_sema); @@ -426,12 +426,6 @@ VmbusProcessChannelEvent(uint32_t relid) // mtx_unlock(&channel->inbound_lock); } -#ifdef HV_DEBUG_INTR -extern uint32_t hv_intr_count; -extern uint32_t hv_vmbus_swintr_event_cpu[MAXCPU]; -extern uint32_t hv_vmbus_intr_cpu[MAXCPU]; -#endif - /** * Handler for events */ @@ -452,17 +446,6 @@ hv_vmbus_on_events(void *arg) KASSERT(cpu <= mp_maxid, ("VMBUS: hv_vmbus_on_events: " "cpu out of range!")); -#ifdef HV_DEBUG_INTR - int i; - hv_vmbus_swintr_event_cpu[cpu]++; - if (hv_intr_count % 10000 == 0) { - printf("VMBUS: Total interrupt %d\n", hv_intr_count); - for (i = 0; i < mp_ncpus; i++) - printf("VMBUS: hw cpu[%d]: %d, event sw intr cpu[%d]: %d\n", - i, hv_vmbus_intr_cpu[i], i, hv_vmbus_swintr_event_cpu[i]); - } -#endif - if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) || (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) { maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5; diff --git a/sys/dev/hyperv/vmbus/hv_et.c b/sys/dev/hyperv/vmbus/hv_et.c new file mode 100644 index 0000000..d961486 --- /dev/null +++ b/sys/dev/hyperv/vmbus/hv_et.c @@ -0,0 +1,131 @@ +/*- + * Copyright (c) 2015 Microsoft Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/proc.h> +#include <sys/systm.h> +#include <sys/smp.h> +#include <sys/time.h> +#include <sys/timeet.h> + +#include "hv_vmbus_priv.h" + +#define HV_TIMER_FREQUENCY (10 * 1000 * 1000LL) /* 100ns period */ +#define HV_MAX_DELTA_TICKS 0xffffffffLL +#define HV_MIN_DELTA_TICKS 1LL + +static struct eventtimer et; +static uint64_t periodticks[MAXCPU]; + +static inline uint64_t +sbintime2tick(sbintime_t time) +{ + struct timespec val; + + val = sbttots(time); + return val.tv_sec * HV_TIMER_FREQUENCY + val.tv_nsec / 100; +} + +static int +hv_et_start(struct eventtimer *et, sbintime_t firsttime, sbintime_t periodtime) +{ + union hv_timer_config timer_cfg; + uint64_t current; + + timer_cfg.as_uint64 = 0; + timer_cfg.auto_enable = 1; + timer_cfg.sintx = HV_VMBUS_MESSAGE_SINT; + + periodticks[curcpu] = sbintime2tick(periodtime); + if (firsttime == 0) + firsttime = periodtime; + + current = rdmsr(HV_X64_MSR_TIME_REF_COUNT); + current += sbintime2tick(firsttime); + + wrmsr(HV_X64_MSR_STIMER0_CONFIG, timer_cfg.as_uint64); + wrmsr(HV_X64_MSR_STIMER0_COUNT, current); + + return (0); +} + +static int +hv_et_stop(struct eventtimer *et) +{ + wrmsr(HV_X64_MSR_STIMER0_CONFIG, 0); + wrmsr(HV_X64_MSR_STIMER0_COUNT, 0); + + return (0); +} + +void +hv_et_intr(struct trapframe *frame) +{ + union hv_timer_config timer_cfg; + struct trapframe *oldframe; + struct thread *td; + + if (periodticks[curcpu] != 0) { + uint64_t tick = sbintime2tick(periodticks[curcpu]); + timer_cfg.as_uint64 = rdmsr(HV_X64_MSR_STIMER0_CONFIG); + timer_cfg.enable = 0; + timer_cfg.auto_enable = 1; + timer_cfg.periodic = 1; + periodticks[curcpu] = 0; + + wrmsr(HV_X64_MSR_STIMER0_CONFIG, timer_cfg.as_uint64); + wrmsr(HV_X64_MSR_STIMER0_COUNT, tick); + } + + if (et.et_active) { + td = curthread; + td->td_intr_nesting_level++; + oldframe = td->td_intr_frame; + td->td_intr_frame = frame; + et.et_event_cb(&et, et.et_arg); + td->td_intr_frame = oldframe; + td->td_intr_nesting_level--; + } +} + +void +hv_et_init(void) +{ + et.et_name = "HyperV"; + et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU | ET_FLAGS_PERIODIC; + et.et_quality = 1000; + et.et_frequency = HV_TIMER_FREQUENCY; + et.et_min_period = (1LL << 32) / HV_TIMER_FREQUENCY; + et.et_max_period = HV_MAX_DELTA_TICKS * ((1LL << 32) / HV_TIMER_FREQUENCY); + et.et_start = hv_et_start; + et.et_stop = hv_et_stop; + et.et_priv = &et; + et_register(&et); +} + diff --git a/sys/dev/hyperv/vmbus/hv_hv.c b/sys/dev/hyperv/vmbus/hv_hv.c index 84e2a5e..ca5641f 100644 --- a/sys/dev/hyperv/vmbus/hv_hv.c +++ b/sys/dev/hyperv/vmbus/hv_hv.c @@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$"); #include <sys/pcpu.h> #include <sys/timetc.h> #include <machine/bus.h> +#include <machine/md_var.h> #include <vm/vm.h> #include <vm/vm_param.h> #include <vm/pmap.h> @@ -44,23 +45,11 @@ __FBSDID("$FreeBSD$"); #include "hv_vmbus_priv.h" -#define HV_X64_MSR_GUEST_OS_ID 0x40000000 - -#define HV_X64_CPUID_MIN 0x40000005 -#define HV_X64_CPUID_MAX 0x4000ffff -#define HV_X64_MSR_TIME_REF_COUNT 0x40000020 - #define HV_NANOSECONDS_PER_SEC 1000000000L static u_int hv_get_timecount(struct timecounter *tc); -static inline void do_cpuid_inline(unsigned int op, unsigned int *eax, - unsigned int *ebx, unsigned int *ecx, unsigned int *edx) { - __asm__ __volatile__("cpuid" : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), - "=d" (*edx) : "0" (op), "c" (ecx)); -} - /** * Globals */ @@ -86,27 +75,10 @@ hv_get_timecount(struct timecounter *tc) int hv_vmbus_query_hypervisor_presence(void) { - u_int regs[4]; - int hyper_v_detected = 0; - - /* - * When Xen is detected and native Xen PV support is enabled, - * ignore Xen's HyperV emulation. - */ - if (vm_guest == VM_GUEST_XEN) + if (vm_guest != VM_GUEST_HV) return (0); - do_cpuid(1, regs); - if (regs[2] & 0x80000000) { /* if(a hypervisor is detected) */ - /* make sure this really is Hyper-V */ - /* we look at the CPUID info */ - do_cpuid(HV_X64_MSR_GUEST_OS_ID, regs); - hyper_v_detected = - regs[0] >= HV_X64_CPUID_MIN && - regs[0] <= HV_X64_CPUID_MAX && - !memcmp("Microsoft Hv", ®s[1], 12); - } - return (hyper_v_detected); + return (hv_high >= HV_X64_CPUID_MIN && hv_high <= HV_X64_CPUID_MAX); } /** @@ -115,10 +87,7 @@ hv_vmbus_query_hypervisor_presence(void) static int hv_vmbus_get_hypervisor_version(void) { - unsigned int eax; - unsigned int ebx; - unsigned int ecx; - unsigned int edx; + u_int regs[4]; unsigned int maxLeaf; unsigned int op; @@ -127,28 +96,16 @@ hv_vmbus_get_hypervisor_version(void) * Viridian is present * Query id and revision. */ - eax = 0; - ebx = 0; - ecx = 0; - edx = 0; op = HV_CPU_ID_FUNCTION_HV_VENDOR_AND_MAX_FUNCTION; - do_cpuid_inline(op, &eax, &ebx, &ecx, &edx); + do_cpuid(op, regs); - maxLeaf = eax; - eax = 0; - ebx = 0; - ecx = 0; - edx = 0; + maxLeaf = regs[0]; op = HV_CPU_ID_FUNCTION_HV_INTERFACE; - do_cpuid_inline(op, &eax, &ebx, &ecx, &edx); + do_cpuid(op, regs); if (maxLeaf >= HV_CPU_ID_FUNCTION_MS_HV_VERSION) { - eax = 0; - ebx = 0; - ecx = 0; - edx = 0; op = HV_CPU_ID_FUNCTION_MS_HV_VERSION; - do_cpuid_inline(op, &eax, &ebx, &ecx, &edx); + do_cpuid(op, regs); } return (maxLeaf); } @@ -255,6 +212,8 @@ hv_vmbus_init(void) hv_vmbus_g_context.hypercall_page = virt_addr; tc_init(&hv_timecounter); /* register virtual timecount */ + + hv_et_init(); return (0); diff --git a/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c b/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c index f7eae26..66a3f39 100644 --- a/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c +++ b/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$"); #include <sys/lock.h> #include <sys/malloc.h> #include <sys/module.h> +#include <sys/proc.h> #include <sys/sysctl.h> #include <sys/syslog.h> #include <sys/systm.h> @@ -60,13 +61,15 @@ __FBSDID("$FreeBSD$"); #include "hv_vmbus_priv.h" - -#define VMBUS_IRQ 0x5 +#include <contrib/dev/acpica/include/acpi.h> +#include "acpi_if.h" static device_t vmbus_devp; static int vmbus_inited; static hv_setup_args setup_args; /* only CPU 0 supported at this time */ +static char *vmbus_ids[] = { "VMBUS", NULL }; + /** * @brief Software interrupt thread routine to handle channel messages from * the hypervisor. @@ -151,7 +154,7 @@ handled: * message to process - an event or a channel message. */ static inline int -hv_vmbus_isr(void *unused) +hv_vmbus_isr(struct trapframe *frame) { int cpu; hv_vmbus_message* msg; @@ -191,41 +194,57 @@ hv_vmbus_isr(void *unused) page_addr = hv_vmbus_g_context.syn_ic_msg_page[cpu]; msg = (hv_vmbus_message*) page_addr + HV_VMBUS_MESSAGE_SINT; + /* we call eventtimer process the message */ + if (msg->header.message_type == HV_MESSAGE_TIMER_EXPIRED) { + msg->header.message_type = HV_MESSAGE_TYPE_NONE; + + /* + * Make sure the write to message_type (ie set to + * HV_MESSAGE_TYPE_NONE) happens before we read the + * message_pending and EOMing. Otherwise, the EOMing will + * not deliver any more messages + * since there is no empty slot + */ + wmb(); + + if (msg->header.message_flags.u.message_pending) { + /* + * This will cause message queue rescan to possibly + * deliver another msg from the hypervisor + */ + wrmsr(HV_X64_MSR_EOM, 0); + } + hv_et_intr(frame); + return (FILTER_HANDLED); + } + if (msg->header.message_type != HV_MESSAGE_TYPE_NONE) { swi_sched(hv_vmbus_g_context.msg_swintr[cpu], 0); } - return FILTER_HANDLED; + return (FILTER_HANDLED); } -#ifdef HV_DEBUG_INTR -uint32_t hv_intr_count = 0; -#endif uint32_t hv_vmbus_swintr_event_cpu[MAXCPU]; -uint32_t hv_vmbus_intr_cpu[MAXCPU]; +u_long *hv_vmbus_intr_cpu[MAXCPU]; void hv_vector_handler(struct trapframe *trap_frame) { -#ifdef HV_DEBUG_INTR int cpu; -#endif /* * Disable preemption. */ critical_enter(); -#ifdef HV_DEBUG_INTR /* * Do a little interrupt counting. */ cpu = PCPU_GET(cpuid); - hv_vmbus_intr_cpu[cpu]++; - hv_intr_count++; -#endif + (*hv_vmbus_intr_cpu[cpu])++; - hv_vmbus_isr(NULL); + hv_vmbus_isr(trap_frame); /* * Enable preemption. @@ -350,25 +369,15 @@ hv_vmbus_child_device_unregister(struct hv_device *child_dev) return(ret); } -static void -vmbus_identify(driver_t *driver, device_t parent) -{ - if (!hv_vmbus_query_hypervisor_presence()) - return; - - vm_guest = VM_GUEST_HV; - - BUS_ADD_CHILD(parent, 0, "vmbus", 0); -} - static int vmbus_probe(device_t dev) { - if(bootverbose) - device_printf(dev, "VMBUS: probe\n"); + if (ACPI_ID_PROBE(device_get_parent(dev), dev, vmbus_ids) == NULL || + device_get_unit(dev) != 0) + return (ENXIO); device_set_desc(dev, "Vmbus Devices"); - return (BUS_PROBE_NOWILDCARD); + return (BUS_PROBE_DEFAULT); } #ifdef HYPERV @@ -462,6 +471,7 @@ static int vmbus_bus_init(void) { int i, j, n, ret; + char buf[MAXCOMLEN + 1]; if (vmbus_inited) return (0); @@ -498,13 +508,15 @@ vmbus_bus_init(void) setup_args.vector = hv_vmbus_g_context.hv_cb_vector; CPU_FOREACH(j) { - hv_vmbus_intr_cpu[j] = 0; hv_vmbus_swintr_event_cpu[j] = 0; hv_vmbus_g_context.hv_event_intr_event[j] = NULL; hv_vmbus_g_context.hv_msg_intr_event[j] = NULL; hv_vmbus_g_context.event_swintr[j] = NULL; hv_vmbus_g_context.msg_swintr[j] = NULL; + snprintf(buf, sizeof(buf), "cpu%d:hyperv", j); + intrcnt_add(buf, &hv_vmbus_intr_cpu[j]); + for (i = 0; i < 2; i++) setup_args.page_buffers[2 * j + i] = NULL; } @@ -723,7 +735,6 @@ vmbus_modevent(module_t mod, int what, void *arg) static device_method_t vmbus_methods[] = { /** Device interface */ - DEVMETHOD(device_identify, vmbus_identify), DEVMETHOD(device_probe, vmbus_probe), DEVMETHOD(device_attach, vmbus_attach), DEVMETHOD(device_detach, vmbus_detach), @@ -745,8 +756,9 @@ static driver_t vmbus_driver = { driver_name, vmbus_methods,0, }; devclass_t vmbus_devclass; -DRIVER_MODULE(vmbus, nexus, vmbus_driver, vmbus_devclass, vmbus_modevent, 0); -MODULE_VERSION(vmbus,1); +DRIVER_MODULE(vmbus, acpi, vmbus_driver, vmbus_devclass, vmbus_modevent, 0); +MODULE_DEPEND(vmbus, acpi, 1, 1, 1); +MODULE_VERSION(vmbus, 1); /* We want to be started after SMP is initialized */ SYSINIT(vmb_init, SI_SUB_SMP + 1, SI_ORDER_FIRST, vmbus_init, NULL); diff --git a/sys/dev/hyperv/vmbus/hv_vmbus_priv.h b/sys/dev/hyperv/vmbus/hv_vmbus_priv.h index 0503d06..74fe824 100644 --- a/sys/dev/hyperv/vmbus/hv_vmbus_priv.h +++ b/sys/dev/hyperv/vmbus/hv_vmbus_priv.h @@ -359,11 +359,6 @@ typedef struct { struct sema control_sema; } hv_vmbus_connection; -/* - * Declare the MSR used to identify the guest OS - */ -#define HV_X64_MSR_GUEST_OS_ID 0x40000000 - typedef union { uint64_t as_uint64_t; struct { @@ -380,10 +375,6 @@ typedef union { } u; } hv_vmbus_x64_msr_guest_os_id_contents; -/* - * Declare the MSR used to setup pages used to communicate with the hypervisor - */ -#define HV_X64_MSR_HYPERCALL 0x40000001 typedef union { uint64_t as_uint64_t; @@ -513,6 +504,22 @@ typedef union { } hv_vmbus_synic_sint; /* + * Timer configuration register. + */ +union hv_timer_config { + uint64_t as_uint64; + struct { + uint64_t enable:1; + uint64_t periodic:1; + uint64_t lazy:1; + uint64_t auto_enable:1; + uint64_t reserved_z0:12; + uint64_t sintx:4; + uint64_t reserved_z1:44; + }; +}; + +/* * Define syn_ic control register */ typedef union _hv_vmbus_synic_scontrol { @@ -542,8 +549,21 @@ typedef union { uint32_t flags32[HV_EVENT_FLAGS_DWORD_COUNT]; } hv_vmbus_synic_event_flags; +#define HV_X64_CPUID_MIN (0x40000005) +#define HV_X64_CPUID_MAX (0x4000ffff) + +/* + * Declare the MSR used to identify the guest OS + */ +#define HV_X64_MSR_GUEST_OS_ID (0x40000000) +/* + * Declare the MSR used to setup pages used to communicate with the hypervisor + */ +#define HV_X64_MSR_HYPERCALL (0x40000001) /* MSR used to provide vcpu index */ -#define HV_X64_MSR_VP_INDEX (0x40000002) +#define HV_X64_MSR_VP_INDEX (0x40000002) + +#define HV_X64_MSR_TIME_REF_COUNT (0x40000020) /* * Define synthetic interrupt controller model specific registers @@ -572,6 +592,18 @@ typedef union { #define HV_X64_MSR_SINT15 (0x4000009F) /* + * Synthetic Timer MSRs. Four timers per vcpu. + */ +#define HV_X64_MSR_STIMER0_CONFIG 0x400000B0 +#define HV_X64_MSR_STIMER0_COUNT 0x400000B1 +#define HV_X64_MSR_STIMER1_CONFIG 0x400000B2 +#define HV_X64_MSR_STIMER1_COUNT 0x400000B3 +#define HV_X64_MSR_STIMER2_CONFIG 0x400000B4 +#define HV_X64_MSR_STIMER2_COUNT 0x400000B5 +#define HV_X64_MSR_STIMER3_CONFIG 0x400000B6 +#define HV_X64_MSR_STIMER3_COUNT 0x400000B7 + +/* * Declare the various hypercall operations */ typedef enum { @@ -678,6 +710,11 @@ int hv_vmbus_post_message(void *buffer, size_t buf_size); int hv_vmbus_set_event(hv_vmbus_channel *channel); void hv_vmbus_on_events(void *); +/** + * Event Timer interfaces + */ +void hv_et_init(void); +void hv_et_intr(struct trapframe*); /* * The guest OS needs to register the guest ID with the hypervisor. |