svn commit: r302135 - stable/10/sys/dev/hyperv/vmbus

Sepherosa Ziehau sephe at FreeBSD.org
Thu Jun 23 09:03:54 UTC 2016


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_ */


More information about the svn-src-all mailing list