Date: Thu, 23 Jun 2016 09:03:52 +0000 (UTC) From: Sepherosa Ziehau <sephe@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org Subject: svn commit: r302135 - stable/10/sys/dev/hyperv/vmbus Message-ID: <201606230903.u5N93qjj065877@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: sephe Date: Thu Jun 23 09:03:52 2016 New Revision: 302135 URL: https://svnweb.freebsd.org/changeset/base/302135 Log: MFC 300987,300988,300989,300992,300993,300994,301009 300987 hyperv/et: Fix STIMER0 operations. - Make sure that STIMER0 is disabled before writting to it, since writing to an enabled STIMER will result in undefined behaviour. - It is unnecessary to reconfigure STIMER0 upon each et_start(). - Make sure that MSR_HV_REF_TIME_COUNT will not return 0, since writing 0 to STIMER_COUNT will disable the target STIMER. MFC after: 1 week Sponsored by: Microsoft OSTC Differential Revision: https://reviews.freebsd.org/D6573 300988 hyperv/vmbus: Move SINT settings to vmbus_var.h While I'm here remove the event timer's dependency on hv_vmbus_priv.h MFC after: 1 week Sponsored by: Microsoft OSTC Differential Revision: https://reviews.freebsd.org/D6574 300989 hyperv/et: Make sure only one event timer will be registered This nullifies the need to use softc. MFC after: 1 week Sponsored by: Microsoft OSTC Differential Revision: https://reviews.freebsd.org/D6591 300992 hyperv: Move timer frequency definition to common place. And cleanup event timer period settings. MFC after: 1 week Sponsored by: Microsoft OSTC Differential Revision: https://reviews.freebsd.org/D6597 300993 hyperv/et: Device renaming; consistent w/ other Hyper-V utils While I'm here, prefix function names w/ vmbus, since unlike Hyper-V timecounter, Hyper-V event timer will not work w/o vmbus. MFC after: 1 week Sponsored by: Microsoft OSTC Differential Revision: https://reviews.freebsd.org/D6598 300994 hyperv/et: Allow Hyper-V event timer be disabled MFC after: 1 week Sponsored by: Microsoft OSTC Differential Revision: https://reviews.freebsd.org/D6599 301009 hyperv/vmbus: Process event timer before checking events And update comment. MFC after: 1 week Sponsored by: Microsoft OSTC Differential Revision: https://reviews.freebsd.org/D6600 Modified: stable/10/sys/dev/hyperv/vmbus/hv_connection.c stable/10/sys/dev/hyperv/vmbus/hv_et.c stable/10/sys/dev/hyperv/vmbus/hv_hv.c stable/10/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c stable/10/sys/dev/hyperv/vmbus/hv_vmbus_priv.h stable/10/sys/dev/hyperv/vmbus/hyperv_var.h stable/10/sys/dev/hyperv/vmbus/vmbus_var.h Directory Properties: stable/10/ (props changed) Modified: stable/10/sys/dev/hyperv/vmbus/hv_connection.c ============================================================================== --- stable/10/sys/dev/hyperv/vmbus/hv_connection.c Thu Jun 23 09:02:50 2016 (r302134) +++ stable/10/sys/dev/hyperv/vmbus/hv_connection.c Thu Jun 23 09:03:52 2016 (r302135) @@ -340,7 +340,7 @@ vmbus_event_proc(struct vmbus_softc *sc, * On Host with Win8 or above, the event page can be checked directly * to get the id of the channel that has the pending interrupt. */ - event = VMBUS_PCPU_GET(sc, event_flag, cpu) + HV_VMBUS_MESSAGE_SINT; + event = VMBUS_PCPU_GET(sc, event_flag, cpu) + VMBUS_SINT_MESSAGE; vmbus_event_flags_proc(event->flagsul, VMBUS_PCPU_GET(sc, event_flag_cnt, cpu)); } @@ -350,7 +350,7 @@ vmbus_event_proc_compat(struct vmbus_sof { hv_vmbus_synic_event_flags *event; - event = VMBUS_PCPU_GET(sc, event_flag, cpu) + HV_VMBUS_MESSAGE_SINT; + event = VMBUS_PCPU_GET(sc, event_flag, cpu) + VMBUS_SINT_MESSAGE; if (atomic_testandclear_int(&event->flags32[0], 0)) { vmbus_event_flags_proc( hv_vmbus_g_connection.recv_interrupt_page, Modified: stable/10/sys/dev/hyperv/vmbus/hv_et.c ============================================================================== --- stable/10/sys/dev/hyperv/vmbus/hv_et.c Thu Jun 23 09:02:50 2016 (r302134) +++ stable/10/sys/dev/hyperv/vmbus/hv_et.c Thu Jun 23 09:03:52 2016 (r302135) @@ -37,16 +37,16 @@ __FBSDID("$FreeBSD$"); #include <sys/time.h> #include <sys/timeet.h> -#include <dev/hyperv/vmbus/hv_vmbus_priv.h> +#include <machine/cpu.h> + #include <dev/hyperv/vmbus/hyperv_reg.h> #include <dev/hyperv/vmbus/hyperv_var.h> +#include <dev/hyperv/vmbus/vmbus_var.h> -#define HV_TIMER_FREQUENCY (10 * 1000 * 1000LL) /* 100ns period */ -#define HV_MAX_DELTA_TICKS 0xffffffffLL -#define HV_MIN_DELTA_TICKS 1LL +#define VMBUS_ET_NAME "hvet" #define MSR_HV_STIMER0_CFG_SINT \ - ((((uint64_t)HV_VMBUS_TIMER_SINT) << MSR_HV_STIMER_CFG_SINT_SHIFT) & \ + ((((uint64_t)VMBUS_SINT_TIMER) << MSR_HV_STIMER_CFG_SINT_SHIFT) & \ MSR_HV_STIMER_CFG_SINT_MASK) /* @@ -59,117 +59,140 @@ __FBSDID("$FreeBSD$"); CPUID_HV_MSR_SYNIC | \ CPUID_HV_MSR_SYNTIMER) -static struct eventtimer *et; +static struct eventtimer vmbus_et; -static inline uint64_t -sbintime2tick(sbintime_t time) +static __inline uint64_t +hyperv_sbintime2count(sbintime_t time) { struct timespec val; val = sbttots(time); - return val.tv_sec * HV_TIMER_FREQUENCY + val.tv_nsec / 100; + return (val.tv_sec * HYPERV_TIMER_FREQ) + + (val.tv_nsec / HYPERV_TIMER_NS_FACTOR); } static int -hv_et_start(struct eventtimer *et, sbintime_t firsttime, sbintime_t periodtime) +vmbus_et_start(struct eventtimer *et __unused, sbintime_t first, + sbintime_t period __unused) { - uint64_t current, config; - - config = MSR_HV_STIMER_CFG_AUTOEN | MSR_HV_STIMER0_CFG_SINT; + uint64_t current; current = rdmsr(MSR_HV_TIME_REF_COUNT); - current += sbintime2tick(firsttime); - - wrmsr(MSR_HV_STIMER0_CONFIG, config); + current += hyperv_sbintime2count(first); wrmsr(MSR_HV_STIMER0_COUNT, current); return (0); } -static int -hv_et_stop(struct eventtimer *et) -{ - wrmsr(MSR_HV_STIMER0_CONFIG, 0); - wrmsr(MSR_HV_STIMER0_COUNT, 0); - - return (0); -} - void -hv_et_intr(struct trapframe *frame) +vmbus_et_intr(struct trapframe *frame) { struct trapframe *oldframe; struct thread *td; - if (et->et_active) { + if (vmbus_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); + vmbus_et.et_event_cb(&vmbus_et, vmbus_et.et_arg); td->td_intr_frame = oldframe; td->td_intr_nesting_level--; } } static void -hv_et_identify(driver_t *driver, device_t parent) +vmbus_et_identify(driver_t *driver, device_t parent) { - if (device_find_child(parent, "hv_et", -1) != NULL || + if (device_get_unit(parent) != 0 || + device_find_child(parent, VMBUS_ET_NAME, -1) != NULL || (hyperv_features & CPUID_HV_ET_MASK) != CPUID_HV_ET_MASK) return; - device_add_child(parent, "hv_et", -1); + device_add_child(parent, VMBUS_ET_NAME, -1); } static int -hv_et_probe(device_t dev) +vmbus_et_probe(device_t dev) { + if (resource_disabled(VMBUS_ET_NAME, 0)) + return (ENXIO); + device_set_desc(dev, "Hyper-V event timer"); return (BUS_PROBE_NOWILDCARD); } +static void +vmbus_et_config(void *arg __unused) +{ + /* + * Make sure that STIMER0 is really disabled before writing + * to STIMER0_CONFIG. + * + * "Writing to the configuration register of a timer that + * is already enabled may result in undefined behaviour." + */ + for (;;) { + uint64_t val; + + /* Stop counting, and this also implies disabling STIMER0 */ + wrmsr(MSR_HV_STIMER0_COUNT, 0); + + val = rdmsr(MSR_HV_STIMER0_CONFIG); + if ((val & MSR_HV_STIMER_CFG_ENABLE) == 0) + break; + cpu_spinwait(); + } + wrmsr(MSR_HV_STIMER0_CONFIG, + MSR_HV_STIMER_CFG_AUTOEN | MSR_HV_STIMER0_CFG_SINT); +} + static int -hv_et_attach(device_t dev) +vmbus_et_attach(device_t dev) { - /* XXX: need allocate SINT and remove global et */ - et = device_get_softc(dev); + /* TODO: use independent IDT vector */ - et->et_name = "Hyper-V"; - et->et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU; - et->et_quality = 1000; - et->et_frequency = HV_TIMER_FREQUENCY; - et->et_min_period = HV_MIN_DELTA_TICKS * ((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 = dev; + vmbus_et.et_name = "Hyper-V"; + vmbus_et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU; + vmbus_et.et_quality = 1000; + vmbus_et.et_frequency = HYPERV_TIMER_FREQ; + vmbus_et.et_min_period = (0x00000001ULL << 32) / HYPERV_TIMER_FREQ; + vmbus_et.et_max_period = (0xfffffffeULL << 32) / HYPERV_TIMER_FREQ; + vmbus_et.et_start = vmbus_et_start; + + /* + * Delay a bit to make sure that MSR_HV_TIME_REF_COUNT will + * not return 0, since writing 0 to STIMER0_COUNT will disable + * STIMER0. + */ + DELAY(100); + smp_rendezvous(NULL, vmbus_et_config, NULL, NULL); - return (et_register(et)); + return (et_register(&vmbus_et)); } static int -hv_et_detach(device_t dev) +vmbus_et_detach(device_t dev) { - return (et_deregister(et)); + return (et_deregister(&vmbus_et)); } -static device_method_t hv_et_methods[] = { - DEVMETHOD(device_identify, hv_et_identify), - DEVMETHOD(device_probe, hv_et_probe), - DEVMETHOD(device_attach, hv_et_attach), - DEVMETHOD(device_detach, hv_et_detach), +static device_method_t vmbus_et_methods[] = { + DEVMETHOD(device_identify, vmbus_et_identify), + DEVMETHOD(device_probe, vmbus_et_probe), + DEVMETHOD(device_attach, vmbus_et_attach), + DEVMETHOD(device_detach, vmbus_et_detach), DEVMETHOD_END }; -static driver_t hv_et_driver = { - "hv_et", - hv_et_methods, - sizeof(struct eventtimer) +static driver_t vmbus_et_driver = { + VMBUS_ET_NAME, + vmbus_et_methods, + 0 }; -static devclass_t hv_et_devclass; -DRIVER_MODULE(hv_et, vmbus, hv_et_driver, hv_et_devclass, NULL, 0); +static devclass_t vmbus_et_devclass; +DRIVER_MODULE(hv_et, vmbus, vmbus_et_driver, vmbus_et_devclass, NULL, NULL); MODULE_VERSION(hv_et, 1); Modified: stable/10/sys/dev/hyperv/vmbus/hv_hv.c ============================================================================== --- stable/10/sys/dev/hyperv/vmbus/hv_hv.c Thu Jun 23 09:02:50 2016 (r302134) +++ stable/10/sys/dev/hyperv/vmbus/hv_hv.c Thu Jun 23 09:03:52 2016 (r302135) @@ -49,8 +49,6 @@ __FBSDID("$FreeBSD$"); #include <dev/hyperv/vmbus/hyperv_var.h> #include <dev/hyperv/vmbus/vmbus_var.h> -#define HV_NANOSECONDS_PER_SEC 1000000000L - #define HYPERV_FREEBSD_BUILD 0ULL #define HYPERV_FREEBSD_VERSION ((uint64_t)__FreeBSD_version) #define HYPERV_FREEBSD_OSID 0ULL @@ -87,7 +85,7 @@ static struct timecounter hyperv_timecou .tc_get_timecount = hyperv_get_timecount, .tc_poll_pps = NULL, .tc_counter_mask = 0xffffffff, - .tc_frequency = HV_NANOSECONDS_PER_SEC/100, + .tc_frequency = HYPERV_TIMER_FREQ, .tc_name = "Hyper-V", .tc_quality = 2000, .tc_flags = 0, Modified: stable/10/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c ============================================================================== --- stable/10/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c Thu Jun 23 09:02:50 2016 (r302134) +++ stable/10/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c Thu Jun 23 09:03:52 2016 (r302135) @@ -80,7 +80,7 @@ vmbus_msg_task(void *xsc, int pending __ struct vmbus_softc *sc = xsc; hv_vmbus_message *msg; - msg = VMBUS_PCPU_GET(sc, message, curcpu) + HV_VMBUS_MESSAGE_SINT; + msg = VMBUS_PCPU_GET(sc, message, curcpu) + VMBUS_SINT_MESSAGE; for (;;) { const hv_vmbus_channel_msg_table_entry *entry; hv_vmbus_channel_msg_header *hdr; @@ -124,34 +124,23 @@ handled: } } -/** - * @brief Interrupt filter routine for VMBUS. - * - * The purpose of this routine is to determine the type of VMBUS protocol - * message to process - an event or a channel message. - */ static inline int hv_vmbus_isr(struct vmbus_softc *sc, struct trapframe *frame, int cpu) { hv_vmbus_message *msg, *msg_base; - /* - * The Windows team has advised that we check for events - * before checking for messages. This is the way they do it - * in Windows when running as a guest in Hyper-V - */ - sc->vmbus_event_proc(sc, cpu); - - /* Check if there are actual msgs to be process */ msg_base = VMBUS_PCPU_GET(sc, message, cpu); - msg = msg_base + HV_VMBUS_TIMER_SINT; - /* we call eventtimer process the message */ + /* + * Check event timer. + * + * TODO: move this to independent IDT vector. + */ + msg = msg_base + VMBUS_SINT_TIMER; if (msg->header.message_type == HV_MESSAGE_TIMER_EXPIRED) { msg->header.message_type = HV_MESSAGE_TYPE_NONE; - /* call intrrupt handler of event timer */ - hv_et_intr(frame); + vmbus_et_intr(frame); /* * Make sure the write to message_type (ie set to @@ -175,8 +164,20 @@ hv_vmbus_isr(struct vmbus_softc *sc, str } } - msg = msg_base + HV_VMBUS_MESSAGE_SINT; - if (msg->header.message_type != HV_MESSAGE_TYPE_NONE) { + /* + * Check events. Hot path for network and storage I/O data; high rate. + * + * NOTE: + * As recommended by the Windows guest fellows, we check events before + * checking messages. + */ + sc->vmbus_event_proc(sc, cpu); + + /* + * Check messages. Mainly management stuffs; ultra low rate. + */ + msg = msg_base + VMBUS_SINT_MESSAGE; + if (__predict_false(msg->header.message_type != HV_MESSAGE_TYPE_NONE)) { taskqueue_enqueue(VMBUS_PCPU_GET(sc, message_tq, cpu), VMBUS_PCPU_PTR(sc, message_task, cpu)); } @@ -254,7 +255,7 @@ vmbus_synic_setup(void *xsc) /* * Configure and unmask SINT for message and event flags. */ - sint = MSR_HV_SINT0 + HV_VMBUS_MESSAGE_SINT; + sint = MSR_HV_SINT0 + VMBUS_SINT_MESSAGE; orig = rdmsr(sint); val = sc->vmbus_idtvec | MSR_HV_SINT_AUTOEOI | (orig & MSR_HV_SINT_RSVD_MASK); @@ -263,7 +264,7 @@ vmbus_synic_setup(void *xsc) /* * Configure and unmask SINT for timer. */ - sint = MSR_HV_SINT0 + HV_VMBUS_TIMER_SINT; + sint = MSR_HV_SINT0 + VMBUS_SINT_TIMER; orig = rdmsr(sint); val = sc->vmbus_idtvec | MSR_HV_SINT_AUTOEOI | (orig & MSR_HV_SINT_RSVD_MASK); @@ -292,14 +293,14 @@ vmbus_synic_teardown(void *arg) /* * Mask message and event flags SINT. */ - sint = MSR_HV_SINT0 + HV_VMBUS_MESSAGE_SINT; + sint = MSR_HV_SINT0 + VMBUS_SINT_MESSAGE; orig = rdmsr(sint); wrmsr(sint, orig | MSR_HV_SINT_MASKED); /* * Mask timer SINT. */ - sint = MSR_HV_SINT0 + HV_VMBUS_TIMER_SINT; + sint = MSR_HV_SINT0 + VMBUS_SINT_TIMER; orig = rdmsr(sint); wrmsr(sint, orig | MSR_HV_SINT_MASKED); Modified: stable/10/sys/dev/hyperv/vmbus/hv_vmbus_priv.h ============================================================================== --- stable/10/sys/dev/hyperv/vmbus/hv_vmbus_priv.h Thu Jun 23 09:02:50 2016 (r302134) +++ stable/10/sys/dev/hyperv/vmbus/hv_vmbus_priv.h Thu Jun 23 09:03:52 2016 (r302135) @@ -189,8 +189,6 @@ enum { HV_VMBUS_EVENT_PORT_ID = 2, HV_VMBUS_MONITOR_CONNECTION_ID = 3, HV_VMBUS_MONITOR_PORT_ID = 3, - HV_VMBUS_MESSAGE_SINT = 2, - HV_VMBUS_TIMER_SINT = 4, }; #define HV_PRESENT_BIT 0x80000000 @@ -542,12 +540,6 @@ int hv_vmbus_disconnect(void); int hv_vmbus_post_message(void *buffer, size_t buf_size); int hv_vmbus_set_event(hv_vmbus_channel *channel); -/** - * Event Timer interfaces - */ -void hv_et_init(void); -void hv_et_intr(struct trapframe*); - /* Wait for device creation */ void vmbus_scan(void); Modified: stable/10/sys/dev/hyperv/vmbus/hyperv_var.h ============================================================================== --- stable/10/sys/dev/hyperv/vmbus/hyperv_var.h Thu Jun 23 09:02:50 2016 (r302134) +++ stable/10/sys/dev/hyperv/vmbus/hyperv_var.h Thu Jun 23 09:03:52 2016 (r302135) @@ -29,6 +29,12 @@ #ifndef _HYPERV_VAR_H_ #define _HYPERV_VAR_H_ +#ifndef NANOSEC +#define NANOSEC 1000000000ULL +#endif +#define HYPERV_TIMER_NS_FACTOR 100ULL +#define HYPERV_TIMER_FREQ (NANOSEC / HYPERV_TIMER_NS_FACTOR) + extern u_int hyperv_features; extern u_int hyperv_recommends; Modified: stable/10/sys/dev/hyperv/vmbus/vmbus_var.h ============================================================================== --- stable/10/sys/dev/hyperv/vmbus/vmbus_var.h Thu Jun 23 09:02:50 2016 (r302134) +++ stable/10/sys/dev/hyperv/vmbus/vmbus_var.h Thu Jun 23 09:03:52 2016 (r302135) @@ -30,8 +30,21 @@ #define _VMBUS_VAR_H_ #include <sys/param.h> +#include <sys/taskqueue.h> + #include <dev/hyperv/include/hyperv_busdma.h> +/* + * NOTE: DO NOT CHANGE THIS. + */ +#define VMBUS_SINT_MESSAGE 2 +/* + * NOTE: + * - DO NOT set it to the same value as VMBUS_SINT_MESSAGE. + * - DO NOT set it to 0. + */ +#define VMBUS_SINT_TIMER 4 + struct vmbus_pcpu_data { u_long *intr_cnt; /* Hyper-V interrupt counter */ struct vmbus_message *message; /* shared messages */ @@ -77,8 +90,13 @@ vmbus_get_device(void) #define VMBUS_PCPU_GET(sc, field, cpu) (sc)->vmbus_pcpu[(cpu)].field #define VMBUS_PCPU_PTR(sc, field, cpu) &(sc)->vmbus_pcpu[(cpu)].field +struct hv_vmbus_channel; +struct trapframe; + void vmbus_on_channel_open(const struct hv_vmbus_channel *); void vmbus_event_proc(struct vmbus_softc *, int); void vmbus_event_proc_compat(struct vmbus_softc *, int); +void vmbus_et_intr(struct trapframe *); + #endif /* !_VMBUS_VAR_H_ */
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201606230903.u5N93qjj065877>