Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 22 Feb 2017 08:26:51 +0000 (UTC)
From:      Dexuan Cui <dexuan@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org
Subject:   svn commit: r314091 - stable/11/sys/dev/hyperv/netvsc
Message-ID:  <201702220826.v1M8QpS4052992@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: dexuan
Date: Wed Feb 22 08:26:51 2017
New Revision: 314091
URL: https://svnweb.freebsd.org/changeset/base/314091

Log:
  MFC 312688
  
  Approved by:	sephe (mentor)
  
  r312688
      hyperv/hn: add the support for VF drivers (SR-IOV)
  
      Hyper-V's NIC SR-IOV implementation needs a Hyper-V synthetic NIC and
      a VF NIC to work together (both NICs have the same MAC address), mainly to
      support seamless live migration.
  
      When the VF device becomes UP (or DOWN), the synthetic NIC driver needs
      to switch the data path from the synthetic NIC to the VF (or the opposite).
  
      Note: multicast/broadcast packets are still received through the synthetic
      NIC and we need to inject the packets through the VF interface (if the VF is
      UP), even if the synthetic NIC is DOWN (so we need to force the rxfilter
      to be NDIS_PACKET_TYPE_PROMISCUOUS, when the VF is UP).
  
      Reviewed by:	sephe
      Approved by:	sephe (mentor)
      Sponsored by:	Microsoft
      Differential Revision:	https://reviews.freebsd.org/D8964

Modified:
  stable/11/sys/dev/hyperv/netvsc/hn_nvs.c
  stable/11/sys/dev/hyperv/netvsc/hn_nvs.h
  stable/11/sys/dev/hyperv/netvsc/if_hn.c
  stable/11/sys/dev/hyperv/netvsc/if_hnreg.h
  stable/11/sys/dev/hyperv/netvsc/if_hnvar.h
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/sys/dev/hyperv/netvsc/hn_nvs.c
==============================================================================
--- stable/11/sys/dev/hyperv/netvsc/hn_nvs.c	Wed Feb 22 08:02:24 2017	(r314090)
+++ stable/11/sys/dev/hyperv/netvsc/hn_nvs.c	Wed Feb 22 08:26:51 2017	(r314091)
@@ -500,6 +500,8 @@ hn_nvs_conf_ndis(struct hn_softc *sc, in
 	conf.nvs_type = HN_NVS_TYPE_NDIS_CONF;
 	conf.nvs_mtu = mtu;
 	conf.nvs_caps = HN_NVS_NDIS_CONF_VLAN;
+	if (sc->hn_nvs_ver >= HN_NVS_VERSION_5)
+		conf.nvs_caps |= HN_NVS_NDIS_CONF_SRIOV;
 
 	/* NOTE: No response. */
 	error = hn_nvs_req_send(sc, &conf, sizeof(conf));
@@ -719,3 +721,15 @@ hn_nvs_send_rndis_ctrl(struct vmbus_chan
 	return hn_nvs_send_rndis_sglist(chan, HN_NVS_RNDIS_MTYPE_CTRL,
 	    sndc, gpa, gpa_cnt);
 }
+
+void
+hn_nvs_set_datapath(struct hn_softc *sc, uint32_t path)
+{
+	struct hn_nvs_datapath dp;
+
+	memset(&dp, 0, sizeof(dp));
+	dp.nvs_type = HN_NVS_TYPE_SET_DATAPATH;
+	dp.nvs_active_path = path;
+
+	hn_nvs_req_send(sc, &dp, sizeof(dp));
+}

Modified: stable/11/sys/dev/hyperv/netvsc/hn_nvs.h
==============================================================================
--- stable/11/sys/dev/hyperv/netvsc/hn_nvs.h	Wed Feb 22 08:02:24 2017	(r314090)
+++ stable/11/sys/dev/hyperv/netvsc/hn_nvs.h	Wed Feb 22 08:26:51 2017	(r314091)
@@ -100,6 +100,7 @@ void		hn_nvs_sent_xact(struct hn_nvs_sen
 int		hn_nvs_send_rndis_ctrl(struct vmbus_channel *chan,
 		    struct hn_nvs_sendctx *sndc, struct vmbus_gpa *gpa,
 		    int gpa_cnt);
+void		hn_nvs_set_datapath(struct hn_softc *sc, uint32_t path);
 
 extern struct hn_nvs_sendctx	hn_nvs_sendctx_none;
 

Modified: stable/11/sys/dev/hyperv/netvsc/if_hn.c
==============================================================================
--- stable/11/sys/dev/hyperv/netvsc/if_hn.c	Wed Feb 22 08:02:24 2017	(r314090)
+++ stable/11/sys/dev/hyperv/netvsc/if_hn.c	Wed Feb 22 08:26:51 2017	(r314091)
@@ -77,6 +77,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/systm.h>
 #include <sys/taskqueue.h>
 #include <sys/buf_ring.h>
+#include <sys/eventhandler.h>
 
 #include <machine/atomic.h>
 #include <machine/in_cksum.h>
@@ -84,6 +85,7 @@ __FBSDID("$FreeBSD$");
 #include <net/bpf.h>
 #include <net/ethernet.h>
 #include <net/if.h>
+#include <net/if_dl.h>
 #include <net/if_media.h>
 #include <net/if_types.h>
 #include <net/if_var.h>
@@ -216,6 +218,11 @@ struct hn_rxinfo {
 	uint32_t			hash_value;
 };
 
+struct hn_update_vf {
+	struct hn_rx_ring	*rxr;
+	struct ifnet		*vf;
+};
+
 #define HN_RXINFO_VLAN			0x0001
 #define HN_RXINFO_CSUM			0x0002
 #define HN_RXINFO_HASHINF		0x0004
@@ -295,7 +302,7 @@ static int			hn_txagg_pktmax_sysctl(SYSC
 static int			hn_txagg_align_sysctl(SYSCTL_HANDLER_ARGS);
 static int			hn_polling_sysctl(SYSCTL_HANDLER_ARGS);
 
-static void			hn_stop(struct hn_softc *);
+static void			hn_stop(struct hn_softc *, bool);
 static void			hn_init_locked(struct hn_softc *);
 static int			hn_chan_attach(struct hn_softc *,
 				    struct vmbus_channel *);
@@ -707,7 +714,8 @@ hn_rxfilter_config(struct hn_softc *sc)
 
 	HN_LOCK_ASSERT(sc);
 
-	if (ifp->if_flags & IFF_PROMISC) {
+	if ((ifp->if_flags & IFF_PROMISC) ||
+	    (sc->hn_flags & HN_FLAG_VF)) {
 		filter = NDIS_PACKET_TYPE_PROMISCUOUS;
 	} else {
 		filter = NDIS_PACKET_TYPE_DIRECTED;
@@ -896,6 +904,119 @@ hn_ifmedia_sts(struct ifnet *ifp, struct
 	ifmr->ifm_active |= IFM_10G_T | IFM_FDX;
 }
 
+static void
+hn_update_vf_task(void *arg, int pending __unused)
+{
+	struct hn_update_vf *uv = arg;
+
+	uv->rxr->hn_vf = uv->vf;
+}
+
+static void
+hn_update_vf(struct hn_softc *sc, struct ifnet *vf)
+{
+	struct hn_rx_ring *rxr;
+	struct hn_update_vf uv;
+	struct task task;
+	int i;
+
+	HN_LOCK_ASSERT(sc);
+
+	TASK_INIT(&task, 0, hn_update_vf_task, &uv);
+
+	for (i = 0; i < sc->hn_rx_ring_cnt; ++i) {
+		rxr = &sc->hn_rx_ring[i];
+
+		if (i < sc->hn_rx_ring_inuse) {
+			uv.rxr = rxr;
+			uv.vf = vf;
+			vmbus_chan_run_task(rxr->hn_chan, &task);
+		} else {
+			rxr->hn_vf = vf;
+		}
+	}
+}
+
+static void
+hn_set_vf(struct hn_softc *sc, struct ifnet *ifp, bool vf)
+{
+	struct ifnet *hn_ifp;
+
+	HN_LOCK(sc);
+
+	if (!(sc->hn_flags & HN_FLAG_SYNTH_ATTACHED))
+		goto out;
+
+	hn_ifp = sc->hn_ifp;
+
+	if (ifp == hn_ifp)
+		goto out;
+
+	if (ifp->if_alloctype != IFT_ETHER)
+		goto out;
+
+	/* Ignore lagg/vlan interfaces */
+	if (strcmp(ifp->if_dname, "lagg") == 0 ||
+	    strcmp(ifp->if_dname, "vlan") == 0)
+		goto out;
+
+	if (bcmp(IF_LLADDR(ifp), IF_LLADDR(hn_ifp), ETHER_ADDR_LEN) != 0)
+		goto out;
+
+	/* Now we're sure 'ifp' is a real VF device. */
+	if (vf) {
+		if (sc->hn_flags & HN_FLAG_VF)
+			goto out;
+
+		sc->hn_flags |= HN_FLAG_VF;
+		hn_rxfilter_config(sc);
+	} else {
+		if (!(sc->hn_flags & HN_FLAG_VF))
+			goto out;
+
+		sc->hn_flags &= ~HN_FLAG_VF;
+		if (sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING)
+			hn_rxfilter_config(sc);
+		else
+			hn_set_rxfilter(sc, NDIS_PACKET_TYPE_NONE);
+	}
+
+	hn_nvs_set_datapath(sc,
+	    vf ? HN_NVS_DATAPATH_VF : HN_NVS_DATAPATH_SYNTHETIC);
+
+	hn_update_vf(sc, vf ? ifp : NULL);
+
+	if (vf) {
+		hn_suspend_mgmt(sc);
+		sc->hn_link_flags &=
+		    ~(HN_LINK_FLAG_LINKUP | HN_LINK_FLAG_NETCHG);
+		if_link_state_change(sc->hn_ifp, LINK_STATE_DOWN);
+	} else {
+		hn_resume_mgmt(sc);
+	}
+
+	if (bootverbose)
+		if_printf(hn_ifp, "Data path is switched %s %s\n",
+		    vf ? "to" : "from", if_name(ifp));
+out:
+	HN_UNLOCK(sc);
+}
+
+static void
+hn_ifnet_event(void *arg, struct ifnet *ifp, int event)
+{
+	if (event != IFNET_EVENT_UP && event != IFNET_EVENT_DOWN)
+		return;
+
+	hn_set_vf(arg, ifp, event == IFNET_EVENT_UP);
+}
+
+static void
+hn_ifaddr_event(void *arg, struct ifnet *ifp)
+{
+	hn_set_vf(arg, ifp, ifp->if_flags & IFF_UP);
+}
+
 /* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */
 static const struct hyperv_guid g_net_vsc_device_type = {
 	.hv_guid = {0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46,
@@ -1221,6 +1342,12 @@ hn_attach(device_t dev)
 	sc->hn_mgmt_taskq = sc->hn_mgmt_taskq0;
 	hn_update_link_status(sc);
 
+	sc->hn_ifnet_evthand = EVENTHANDLER_REGISTER(ifnet_event,
+	    hn_ifnet_event, sc, EVENTHANDLER_PRI_ANY);
+
+	sc->hn_ifaddr_evthand = EVENTHANDLER_REGISTER(ifaddr_event,
+	    hn_ifaddr_event, sc, EVENTHANDLER_PRI_ANY);
+
 	return (0);
 failed:
 	if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED)
@@ -1235,6 +1362,11 @@ hn_detach(device_t dev)
 	struct hn_softc *sc = device_get_softc(dev);
 	struct ifnet *ifp = sc->hn_ifp;
 
+	if (sc->hn_ifaddr_evthand != NULL)
+		EVENTHANDLER_DEREGISTER(ifaddr_event, sc->hn_ifaddr_evthand);
+	if (sc->hn_ifnet_evthand != NULL)
+		EVENTHANDLER_DEREGISTER(ifnet_event, sc->hn_ifnet_evthand);
+
 	if (sc->hn_xact != NULL && vmbus_chan_is_revoked(sc->hn_prichan)) {
 		/*
 		 * In case that the vmbus missed the orphan handler
@@ -1247,7 +1379,7 @@ hn_detach(device_t dev)
 		HN_LOCK(sc);
 		if (sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) {
 			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
-				hn_stop(sc);
+				hn_stop(sc, true);
 			/*
 			 * NOTE:
 			 * hn_stop() only suspends data, so managment
@@ -2124,11 +2256,14 @@ static int
 hn_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen,
     const struct hn_rxinfo *info)
 {
-	struct ifnet *ifp = rxr->hn_ifp;
+	struct ifnet *ifp;
 	struct mbuf *m_new;
 	int size, do_lro = 0, do_csum = 1;
 	int hash_type;
 
+	/* If the VF is active, inject the packet through the VF */
+	ifp = rxr->hn_vf ? rxr->hn_vf : rxr->hn_ifp;
+
 	if (dlen <= MHLEN) {
 		m_new = m_gethdr(M_NOWAIT, MT_DATA);
 		if (m_new == NULL) {
@@ -2439,7 +2574,7 @@ hn_ioctl(struct ifnet *ifp, u_long cmd, 
 			}
 		} else {
 			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
-				hn_stop(sc);
+				hn_stop(sc, false);
 		}
 		sc->hn_if_flags = ifp->if_flags;
 
@@ -2529,7 +2664,7 @@ hn_ioctl(struct ifnet *ifp, u_long cmd, 
 }
 
 static void
-hn_stop(struct hn_softc *sc)
+hn_stop(struct hn_softc *sc, bool detaching)
 {
 	struct ifnet *ifp = sc->hn_ifp;
 	int i;
@@ -2550,6 +2685,13 @@ hn_stop(struct hn_softc *sc)
 	atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE);
 	for (i = 0; i < sc->hn_tx_ring_inuse; ++i)
 		sc->hn_tx_ring[i].hn_oactive = 0;
+
+	/*
+	 * If the VF is active, make sure the filter is not 0, even if
+	 * the synthetic NIC is down.
+	 */
+	if (!detaching && (sc->hn_flags & HN_FLAG_VF))
+		hn_rxfilter_config(sc);
 }
 
 static void
@@ -4894,7 +5036,8 @@ hn_suspend(struct hn_softc *sc)
 	/* Disable polling. */
 	hn_polling(sc, 0);
 
-	if (sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING)
+	if ((sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) ||
+	    (sc->hn_flags & HN_FLAG_VF))
 		hn_suspend_data(sc);
 	hn_suspend_mgmt(sc);
 }
@@ -4983,9 +5126,18 @@ static void
 hn_resume(struct hn_softc *sc)
 {
 
-	if (sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING)
+	if ((sc->hn_ifp->if_drv_flags & IFF_DRV_RUNNING) ||
+	    (sc->hn_flags & HN_FLAG_VF))
 		hn_resume_data(sc);
-	hn_resume_mgmt(sc);
+
+	/*
+	 * When the VF is activated, the synthetic interface is changed
+	 * to DOWN in hn_set_vf(). Here, if the VF is still active, we
+	 * don't call hn_resume_mgmt() until the VF is deactivated in
+	 * hn_set_vf().
+	 */
+	if (!(sc->hn_flags & HN_FLAG_VF))
+		hn_resume_mgmt(sc);
 
 	/*
 	 * Re-enable polling if this interface is running and

Modified: stable/11/sys/dev/hyperv/netvsc/if_hnreg.h
==============================================================================
--- stable/11/sys/dev/hyperv/netvsc/if_hnreg.h	Wed Feb 22 08:02:24 2017	(r314090)
+++ stable/11/sys/dev/hyperv/netvsc/if_hnreg.h	Wed Feb 22 08:26:51 2017	(r314091)
@@ -133,6 +133,17 @@ struct hn_nvs_ndis_init {
 } __packed;
 CTASSERT(sizeof(struct hn_nvs_ndis_init) >= HN_NVS_REQSIZE_MIN);
 
+#define HN_NVS_DATAPATH_SYNTHETIC	0
+#define HN_NVS_DATAPATH_VF		1
+
+/* No response */
+struct hn_nvs_datapath {
+	uint32_t	nvs_type;	/* HN_NVS_TYPE_SET_DATAPATH */
+	uint32_t	nvs_active_path;/* HN_NVS_DATAPATH_* */
+	uint32_t	nvs_rsvd[6];
+} __packed;
+CTASSERT(sizeof(struct hn_nvs_datapath) >= HN_NVS_REQSIZE_MIN);
+
 struct hn_nvs_rxbuf_conn {
 	uint32_t	nvs_type;	/* HN_NVS_TYPE_RXBUF_CONN */
 	uint32_t	nvs_gpadl;	/* RXBUF vmbus GPADL */

Modified: stable/11/sys/dev/hyperv/netvsc/if_hnvar.h
==============================================================================
--- stable/11/sys/dev/hyperv/netvsc/if_hnvar.h	Wed Feb 22 08:02:24 2017	(r314090)
+++ stable/11/sys/dev/hyperv/netvsc/if_hnvar.h	Wed Feb 22 08:26:51 2017	(r314091)
@@ -59,6 +59,7 @@ struct hn_tx_ring;
 
 struct hn_rx_ring {
 	struct ifnet	*hn_ifp;
+	struct ifnet	*hn_vf;		/* SR-IOV VF */
 	struct hn_tx_ring *hn_txr;
 	void		*hn_pktbuf;
 	int		hn_pktbuf_len;
@@ -234,6 +235,9 @@ struct hn_softc {
 	int			hn_rss_ind_size;
 	uint32_t		hn_rss_hash;	/* NDIS_HASH_ */
 	struct ndis_rssprm_toeplitz hn_rss;
+
+	eventhandler_tag	hn_ifaddr_evthand;
+	eventhandler_tag	hn_ifnet_evthand;
 };
 
 #define HN_FLAG_RXBUF_CONNECTED		0x0001
@@ -244,6 +248,7 @@ struct hn_softc {
 #define HN_FLAG_NO_SLEEPING		0x0020
 #define HN_FLAG_RXBUF_REF		0x0040
 #define HN_FLAG_CHIM_REF		0x0080
+#define HN_FLAG_VF			0x0100
 
 #define HN_FLAG_ERRORS			(HN_FLAG_RXBUF_REF | HN_FLAG_CHIM_REF)
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201702220826.v1M8QpS4052992>