Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 17 Oct 2016 08:20:17 +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-11@freebsd.org
Subject:   svn commit: r307502 - stable/11/sys/dev/hyperv/netvsc
Message-ID:  <201610170820.u9H8KHol029504@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: sephe
Date: Mon Oct 17 08:20:17 2016
New Revision: 307502
URL: https://svnweb.freebsd.org/changeset/base/307502

Log:
  MFC 305578-305581
  
  305578
      hyperv/hn: Pull vmbus channel open up.
  
      While I'm here, pull up the channel callback related code too.
  
      Sponsored by:   Microsoft
      Differential Revision:  https://reviews.freebsd.org/D7805
  
  305579
      hyperv/hn: Push RXBUF size adjustment down.
  
      It is not used in other places.
  
      Sponsored by:   Microsoft
      Differential Revision:  https://reviews.freebsd.org/D7806
  
  305580
      hyperv/hn: Factor out function to do NVS initialization.
  
      Sponsored by:   Microsoft
      Differential Revision:  https://reviews.freebsd.org/D7807
  
  305581
      hyperv/hn: Pass MTU around.
  
      Sponsored by:   Microsoft
      Differential Revision:  https://reviews.freebsd.org/D7808

Modified:
  stable/11/sys/dev/hyperv/netvsc/hv_net_vsc.c
  stable/11/sys/dev/hyperv/netvsc/hv_net_vsc.h
  stable/11/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
  stable/11/sys/dev/hyperv/netvsc/hv_rndis_filter.c
  stable/11/sys/dev/hyperv/netvsc/hv_rndis_filter.h
  stable/11/sys/dev/hyperv/netvsc/if_hnreg.h
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/sys/dev/hyperv/netvsc/hv_net_vsc.c
==============================================================================
--- stable/11/sys/dev/hyperv/netvsc/hv_net_vsc.c	Mon Oct 17 08:17:06 2016	(r307501)
+++ stable/11/sys/dev/hyperv/netvsc/hv_net_vsc.c	Mon Oct 17 08:20:17 2016	(r307502)
@@ -57,20 +57,11 @@ MALLOC_DEFINE(M_NETVSC, "netvsc", "Hyper
 /*
  * Forward declarations
  */
-static void hv_nv_on_channel_callback(struct vmbus_channel *chan,
-    void *xrxr);
 static int  hv_nv_init_send_buffer_with_net_vsp(struct hn_softc *sc);
-static int  hv_nv_init_rx_buffer_with_net_vsp(struct hn_softc *, int);
+static int  hv_nv_init_rx_buffer_with_net_vsp(struct hn_softc *);
 static int  hv_nv_destroy_send_buffer(struct hn_softc *sc);
 static int  hv_nv_destroy_rx_buffer(struct hn_softc *sc);
-static int  hv_nv_connect_to_vsp(struct hn_softc *sc);
-static void hv_nv_on_send_completion(struct hn_softc *sc,
-    struct vmbus_channel *, const struct vmbus_chanpkt_hdr *pkt);
-static void hv_nv_on_receive_completion(struct vmbus_channel *chan,
-    uint64_t tid);
-static void hv_nv_on_receive(struct hn_softc *sc,
-    struct hn_rx_ring *rxr, struct vmbus_channel *chan,
-    const struct vmbus_chanpkt_hdr *pkt);
+static int  hv_nv_connect_to_vsp(struct hn_softc *sc, int mtu);
 static void hn_nvs_sent_none(struct hn_send_ctx *sndc,
     struct hn_softc *, struct vmbus_channel *chan,
     const void *, int);
@@ -78,6 +69,13 @@ static void hn_nvs_sent_none(struct hn_s
 struct hn_send_ctx	hn_send_ctx_none =
     HN_SEND_CTX_INITIALIZER(hn_nvs_sent_none, NULL);
 
+static const uint32_t		hn_nvs_version[] = {
+	HN_NVS_VERSION_5,
+	HN_NVS_VERSION_4,
+	HN_NVS_VERSION_2,
+	HN_NVS_VERSION_1
+};
+
 uint32_t
 hn_chim_alloc(struct hn_softc *sc)
 {
@@ -163,17 +161,22 @@ hn_nvs_req_send(struct hn_softc *sc, voi
  *     Hyper-V extensible switch and the synthetic data path.
  */
 static int 
-hv_nv_init_rx_buffer_with_net_vsp(struct hn_softc *sc, int rxbuf_size)
+hv_nv_init_rx_buffer_with_net_vsp(struct hn_softc *sc)
 {
 	struct vmbus_xact *xact = NULL;
 	struct hn_nvs_rxbuf_conn *conn;
 	const struct hn_nvs_rxbuf_connresp *resp;
 	size_t resp_len;
 	uint32_t status;
-	int error;
+	int error, rxbuf_size;
 
-	KASSERT(rxbuf_size <= NETVSC_RECEIVE_BUFFER_SIZE,
-	    ("invalid rxbuf size %d", rxbuf_size));
+	/*
+	 * Limit RXBUF size for old NVS.
+	 */
+	if (sc->hn_nvs_ver <= HN_NVS_VERSION_2)
+		rxbuf_size = NETVSC_RECEIVE_BUFFER_SIZE_LEGACY;
+	else
+		rxbuf_size = NETVSC_RECEIVE_BUFFER_SIZE;
 
 	/*
 	 * Connect the RXBUF GPADL to the primary channel.
@@ -428,7 +431,7 @@ hv_nv_destroy_send_buffer(struct hn_soft
 }
 
 static int
-hv_nv_negotiate_nvsp_protocol(struct hn_softc *sc, uint32_t nvs_ver)
+hn_nvs_doinit(struct hn_softc *sc, uint32_t nvs_ver)
 {
 	struct vmbus_xact *xact;
 	struct hn_nvs_init *init;
@@ -489,57 +492,55 @@ hv_nv_send_ndis_config(struct hn_softc *
 	return (error);
 }
 
-/*
- * Net VSC connect to VSP
- */
 static int
-hv_nv_connect_to_vsp(struct hn_softc *sc)
+hn_nvs_init(struct hn_softc *sc)
 {
-	uint32_t protocol_list[] = { NVSP_PROTOCOL_VERSION_1,
-	    NVSP_PROTOCOL_VERSION_2,
-	    NVSP_PROTOCOL_VERSION_4,
-	    NVSP_PROTOCOL_VERSION_5 };
 	int i;
-	int protocol_number = nitems(protocol_list);
-	int ret = 0;
-	device_t dev = sc->hn_dev;
-	struct ifnet *ifp = sc->hn_ifp;
-	struct hn_nvs_ndis_init ndis;
-	int rxbuf_size;
 
-	/*
-	 * Negotiate the NVSP version.  Try the latest NVSP first.
-	 */
-	for (i = protocol_number - 1; i >= 0; i--) {
-		if (hv_nv_negotiate_nvsp_protocol(sc, protocol_list[i]) == 0) {
-			sc->hn_nvs_ver = protocol_list[i];
+	for (i = 0; i < nitems(hn_nvs_version); ++i) {
+		int error;
+
+		error = hn_nvs_doinit(sc, hn_nvs_version[i]);
+		if (!error) {
+			sc->hn_nvs_ver = hn_nvs_version[i];
+
+			/* Set NDIS version according to NVS version. */
 			sc->hn_ndis_ver = HN_NDIS_VERSION_6_30;
-			if (sc->hn_nvs_ver <= NVSP_PROTOCOL_VERSION_4)
+			if (sc->hn_nvs_ver <= HN_NVS_VERSION_4)
 				sc->hn_ndis_ver = HN_NDIS_VERSION_6_1;
+
 			if (bootverbose) {
 				if_printf(sc->hn_ifp, "NVS version 0x%x, "
-				    "NDIS version %u.%u\n",
-				    sc->hn_nvs_ver,
+				    "NDIS version %u.%u\n", sc->hn_nvs_ver,
 				    HN_NDIS_VERSION_MAJOR(sc->hn_ndis_ver),
 				    HN_NDIS_VERSION_MINOR(sc->hn_ndis_ver));
 			}
-			break;
+			return (0);
 		}
 	}
+	if_printf(sc->hn_ifp, "no NVS available\n");
+	return (ENXIO);
+}
 
-	if (i < 0) {
-		if (bootverbose)
-			device_printf(dev, "failed to negotiate a valid "
-			    "protocol.\n");
-		return (EPROTO);
-	}
+/*
+ * Net VSC connect to VSP
+ */
+static int
+hv_nv_connect_to_vsp(struct hn_softc *sc, int mtu)
+{
+	int ret = 0;
+	struct hn_nvs_ndis_init ndis;
+
+	ret = hn_nvs_init(sc);
+	if (ret != 0)
+		return (ret);
 
 	/*
 	 * Set the MTU if supported by this NVSP protocol version
 	 * This needs to be right after the NVSP init message per Haiyang
 	 */
-	if (sc->hn_nvs_ver >= NVSP_PROTOCOL_VERSION_2)
-		ret = hv_nv_send_ndis_config(sc, ifp->if_mtu);
+	if (sc->hn_nvs_ver >= HN_NVS_VERSION_2)
+		ret = hv_nv_send_ndis_config(sc, mtu);
 
 	/*
 	 * Initialize NDIS.
@@ -557,13 +558,7 @@ hv_nv_connect_to_vsp(struct hn_softc *sc
 		goto cleanup;
 	}
 
-	/* Post the big receive buffer to NetVSP */
-	if (sc->hn_nvs_ver <= NVSP_PROTOCOL_VERSION_2)
-		rxbuf_size = NETVSC_RECEIVE_BUFFER_SIZE_LEGACY;
-	else
-		rxbuf_size = NETVSC_RECEIVE_BUFFER_SIZE;
-
-	ret = hv_nv_init_rx_buffer_with_net_vsp(sc, rxbuf_size);
+	ret = hv_nv_init_rx_buffer_with_net_vsp(sc);
 	if (ret == 0)
 		ret = hv_nv_init_send_buffer_with_net_vsp(sc);
 
@@ -581,54 +576,19 @@ hv_nv_disconnect_from_vsp(struct hn_soft
 	hv_nv_destroy_send_buffer(sc);
 }
 
-void
-hv_nv_subchan_attach(struct vmbus_channel *chan, struct hn_rx_ring *rxr)
-{
-	KASSERT(rxr->hn_rx_idx == vmbus_chan_subidx(chan),
-	    ("chan%u subidx %u, rxr%d mismatch",
-	     vmbus_chan_id(chan), vmbus_chan_subidx(chan), rxr->hn_rx_idx));
-	vmbus_chan_open(chan, NETVSC_DEVICE_RING_BUFFER_SIZE,
-	    NETVSC_DEVICE_RING_BUFFER_SIZE, NULL, 0,
-	    hv_nv_on_channel_callback, rxr);
-}
-
 /*
  * Net VSC on device add
  * 
  * Callback when the device belonging to this driver is added
  */
 int
-hv_nv_on_device_add(struct hn_softc *sc, struct hn_rx_ring *rxr)
+hv_nv_on_device_add(struct hn_softc *sc, int mtu)
 {
-	struct vmbus_channel *chan = sc->hn_prichan;
-	int ret = 0;
-
-	/*
-	 * Open the channel
-	 */
-	KASSERT(rxr->hn_rx_idx == vmbus_chan_subidx(chan),
-	    ("chan%u subidx %u, rxr%d mismatch",
-	     vmbus_chan_id(chan), vmbus_chan_subidx(chan), rxr->hn_rx_idx));
-	ret = vmbus_chan_open(chan,
-	    NETVSC_DEVICE_RING_BUFFER_SIZE, NETVSC_DEVICE_RING_BUFFER_SIZE,
-	    NULL, 0, hv_nv_on_channel_callback, rxr);
-	if (ret != 0)
-		goto cleanup;
 
 	/*
 	 * Connect with the NetVsp
 	 */
-	ret = hv_nv_connect_to_vsp(sc);
-	if (ret != 0)
-		goto close;
-
-	return (0);
-
-close:
-	/* Now, we can close the channel safely */
-	vmbus_chan_close(chan);
-cleanup:
-	return (ret);
+	return (hv_nv_connect_to_vsp(sc, mtu));
 }
 
 /*
@@ -684,25 +644,6 @@ hn_chim_free(struct hn_softc *sc, uint32
 }
 
 /*
- * Net VSC on send completion
- */
-static void
-hv_nv_on_send_completion(struct hn_softc *sc, struct vmbus_channel *chan,
-    const struct vmbus_chanpkt_hdr *pkt)
-{
-	struct hn_send_ctx *sndc;
-
-	sndc = (struct hn_send_ctx *)(uintptr_t)pkt->cph_xactid;
-	sndc->hn_cb(sndc, sc, chan, VMBUS_CHANPKT_CONST_DATA(pkt),
-	    VMBUS_CHANPKT_DATALEN(pkt));
-	/*
-	 * NOTE:
-	 * 'sndc' CAN NOT be accessed anymore, since it can be freed by
-	 * its callback.
-	 */
-}
-
-/*
  * Net VSC on send
  * Sends a packet on the specified Hyper-V device.
  * Returns 0 on success, non-zero on failure.
@@ -729,190 +670,3 @@ hv_nv_on_send(struct vmbus_channel *chan
 
 	return (ret);
 }
-
-/*
- * Net VSC on receive
- *
- * In the FreeBSD Hyper-V virtual world, this function deals exclusively
- * with virtual addresses.
- */
-static void
-hv_nv_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr,
-    struct vmbus_channel *chan, const struct vmbus_chanpkt_hdr *pkthdr)
-{
-	const struct vmbus_chanpkt_rxbuf *pkt;
-	const struct hn_nvs_hdr *nvs_hdr;
-	int count, i, hlen;
-
-	if (__predict_false(VMBUS_CHANPKT_DATALEN(pkthdr) < sizeof(*nvs_hdr))) {
-		if_printf(rxr->hn_ifp, "invalid nvs RNDIS\n");
-		return;
-	}
-	nvs_hdr = VMBUS_CHANPKT_CONST_DATA(pkthdr);
-
-	/* Make sure that this is a RNDIS message. */
-	if (__predict_false(nvs_hdr->nvs_type != HN_NVS_TYPE_RNDIS)) {
-		if_printf(rxr->hn_ifp, "nvs type %u, not RNDIS\n",
-		    nvs_hdr->nvs_type);
-		return;
-	}
-
-	hlen = VMBUS_CHANPKT_GETLEN(pkthdr->cph_hlen);
-	if (__predict_false(hlen < sizeof(*pkt))) {
-		if_printf(rxr->hn_ifp, "invalid rxbuf chanpkt\n");
-		return;
-	}
-	pkt = (const struct vmbus_chanpkt_rxbuf *)pkthdr;
-
-	if (__predict_false(pkt->cp_rxbuf_id != HN_NVS_RXBUF_SIG)) {
-		if_printf(rxr->hn_ifp, "invalid rxbuf_id 0x%08x\n",
-		    pkt->cp_rxbuf_id);
-		return;
-	}
-
-	count = pkt->cp_rxbuf_cnt;
-	if (__predict_false(hlen <
-	    __offsetof(struct vmbus_chanpkt_rxbuf, cp_rxbuf[count]))) {
-		if_printf(rxr->hn_ifp, "invalid rxbuf_cnt %d\n", count);
-		return;
-	}
-
-	/* Each range represents 1 RNDIS pkt that contains 1 Ethernet frame */
-	for (i = 0; i < count; ++i) {
-		int ofs, len;
-
-		ofs = pkt->cp_rxbuf[i].rb_ofs;
-		len = pkt->cp_rxbuf[i].rb_len;
-		if (__predict_false(ofs + len > NETVSC_RECEIVE_BUFFER_SIZE)) {
-			if_printf(rxr->hn_ifp, "%dth RNDIS msg overflow rxbuf, "
-			    "ofs %d, len %d\n", i, ofs, len);
-			continue;
-		}
-		hv_rf_on_receive(sc, rxr, rxr->hn_rxbuf + ofs, len);
-	}
-	
-	/*
-	 * Moved completion call back here so that all received 
-	 * messages (not just data messages) will trigger a response
-	 * message back to the host.
-	 */
-	hv_nv_on_receive_completion(chan, pkt->cp_hdr.cph_xactid);
-}
-
-/*
- * Net VSC on receive completion
- *
- * Send a receive completion packet to RNDIS device (ie NetVsp)
- */
-static void
-hv_nv_on_receive_completion(struct vmbus_channel *chan, uint64_t tid)
-{
-	struct hn_nvs_rndis_ack ack;
-	int retries = 0;
-	int ret = 0;
-	
-	ack.nvs_type = HN_NVS_TYPE_RNDIS_ACK;
-	ack.nvs_status = HN_NVS_STATUS_OK;
-
-retry_send_cmplt:
-	/* Send the completion */
-	ret = vmbus_chan_send(chan, VMBUS_CHANPKT_TYPE_COMP,
-	    VMBUS_CHANPKT_FLAG_NONE, &ack, sizeof(ack), tid);
-	if (ret == 0) {
-		/* success */
-		/* no-op */
-	} else if (ret == EAGAIN) {
-		/* no more room... wait a bit and attempt to retry 3 times */
-		retries++;
-
-		if (retries < 4) {
-			DELAY(100);
-			goto retry_send_cmplt;
-		}
-	}
-}
-
-static void
-hn_proc_notify(struct hn_softc *sc, const struct vmbus_chanpkt_hdr *pkt)
-{
-	const struct hn_nvs_hdr *hdr;
-
-	if (VMBUS_CHANPKT_DATALEN(pkt) < sizeof(*hdr)) {
-		if_printf(sc->hn_ifp, "invalid nvs notify\n");
-		return;
-	}
-	hdr = VMBUS_CHANPKT_CONST_DATA(pkt);
-
-	if (hdr->nvs_type == HN_NVS_TYPE_TXTBL_NOTE) {
-		/* Useless; ignore */
-		return;
-	}
-	if_printf(sc->hn_ifp, "got notify, nvs type %u\n", hdr->nvs_type);
-}
-
-/*
- * Net VSC on channel callback
- */
-static void
-hv_nv_on_channel_callback(struct vmbus_channel *chan, void *xrxr)
-{
-	struct hn_rx_ring *rxr = xrxr;
-	struct hn_softc *sc = rxr->hn_ifp->if_softc;
-	void *buffer;
-	int bufferlen = NETVSC_PACKET_SIZE;
-
-	buffer = rxr->hn_rdbuf;
-	do {
-		struct vmbus_chanpkt_hdr *pkt = buffer;
-		uint32_t bytes_rxed;
-		int ret;
-
-		bytes_rxed = bufferlen;
-		ret = vmbus_chan_recv_pkt(chan, pkt, &bytes_rxed);
-		if (ret == 0) {
-			if (bytes_rxed > 0) {
-				switch (pkt->cph_type) {
-				case VMBUS_CHANPKT_TYPE_COMP:
-					hv_nv_on_send_completion(sc, chan, pkt);
-					break;
-				case VMBUS_CHANPKT_TYPE_RXBUF:
-					hv_nv_on_receive(sc, rxr, chan, pkt);
-					break;
-				case VMBUS_CHANPKT_TYPE_INBAND:
-					hn_proc_notify(sc, pkt);
-					break;
-				default:
-					if_printf(rxr->hn_ifp,
-					    "unknown chan pkt %u\n",
-					    pkt->cph_type);
-					break;
-				}
-			}
-		} else if (ret == ENOBUFS) {
-			/* Handle large packet */
-			if (bufferlen > NETVSC_PACKET_SIZE) {
-				free(buffer, M_NETVSC);
-				buffer = NULL;
-			}
-
-			/* alloc new buffer */
-			buffer = malloc(bytes_rxed, M_NETVSC, M_NOWAIT);
-			if (buffer == NULL) {
-				if_printf(rxr->hn_ifp,
-				    "hv_cb malloc buffer failed, len=%u\n",
-				    bytes_rxed);
-				bufferlen = 0;
-				break;
-			}
-			bufferlen = bytes_rxed;
-		} else {
-			/* No more packets */
-			break;
-		}
-	} while (1);
-
-	if (bufferlen > NETVSC_PACKET_SIZE)
-		free(buffer, M_NETVSC);
-
-	hv_rf_channel_rollup(rxr, rxr->hn_txr);
-}

Modified: stable/11/sys/dev/hyperv/netvsc/hv_net_vsc.h
==============================================================================
--- stable/11/sys/dev/hyperv/netvsc/hv_net_vsc.h	Mon Oct 17 08:17:06 2016	(r307501)
+++ stable/11/sys/dev/hyperv/netvsc/hv_net_vsc.h	Mon Oct 17 08:20:17 2016	(r307502)
@@ -68,13 +68,6 @@
 
 MALLOC_DECLARE(M_NETVSC);
 
-#define NVSP_INVALID_PROTOCOL_VERSION           (0xFFFFFFFF)
-
-#define NVSP_PROTOCOL_VERSION_1                 2
-#define NVSP_PROTOCOL_VERSION_2                 0x30002
-#define NVSP_PROTOCOL_VERSION_4                 0x40000
-#define NVSP_PROTOCOL_VERSION_5                 0x50000
-
 /*
  * The following arguably belongs in a separate header file
  */
@@ -268,12 +261,10 @@ extern int hv_promisc_mode;
 struct hn_send_ctx;
 
 void netvsc_linkstatus_callback(struct hn_softc *sc, uint32_t status);
-int hv_nv_on_device_add(struct hn_softc *sc, struct hn_rx_ring *rxr);
+int hv_nv_on_device_add(struct hn_softc *sc, int mtu);
 int hv_nv_on_device_remove(struct hn_softc *sc);
 int hv_nv_on_send(struct vmbus_channel *chan, uint32_t rndis_mtype,
 	struct hn_send_ctx *sndc, struct vmbus_gpa *gpa, int gpa_cnt);
-void hv_nv_subchan_attach(struct vmbus_channel *chan,
-    struct hn_rx_ring *rxr);
 
 #endif  /* __HV_NET_VSC_H__ */
 

Modified: stable/11/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c
==============================================================================
--- stable/11/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c	Mon Oct 17 08:17:06 2016	(r307501)
+++ stable/11/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c	Mon Oct 17 08:20:17 2016	(r307502)
@@ -344,9 +344,18 @@ static int hn_encap(struct hn_tx_ring *,
 static int hn_create_rx_data(struct hn_softc *sc, int);
 static void hn_destroy_rx_data(struct hn_softc *sc);
 static void hn_set_chim_size(struct hn_softc *, int);
-static void hn_channel_attach(struct hn_softc *, struct vmbus_channel *);
-static void hn_subchan_attach(struct hn_softc *, struct vmbus_channel *);
-static void hn_subchan_setup(struct hn_softc *);
+static int hn_chan_attach(struct hn_softc *, struct vmbus_channel *);
+static int hn_attach_subchans(struct hn_softc *);
+static void hn_chan_callback(struct vmbus_channel *chan, void *xrxr);
+
+static void hn_nvs_handle_notify(struct hn_softc *sc,
+		const struct vmbus_chanpkt_hdr *pkt);
+static void hn_nvs_handle_comp(struct hn_softc *sc, struct vmbus_channel *chan,
+		const struct vmbus_chanpkt_hdr *pkt);
+static void hn_nvs_handle_rxbuf(struct hn_softc *sc, struct hn_rx_ring *rxr,
+		struct vmbus_channel *chan,
+		const struct vmbus_chanpkt_hdr *pkthdr);
+static void hn_nvs_ack_rxbuf(struct vmbus_channel *chan, uint64_t tid);
 
 static int hn_transmit(struct ifnet *, struct mbuf *);
 static void hn_xmit_qflush(struct ifnet *);
@@ -512,12 +521,13 @@ netvsc_attach(device_t dev)
 	/*
 	 * Associate the first TX/RX ring w/ the primary channel.
 	 */
-	hn_channel_attach(sc, sc->hn_prichan);
+	error = hn_chan_attach(sc, sc->hn_prichan);
+	if (error)
+		goto failed;
 
 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
 	ifp->if_ioctl = hn_ioctl;
 	ifp->if_init = hn_ifinit;
-	/* needed by hv_rf_on_device_add() code */
 	ifp->if_mtu = ETHERMTU;
 	if (hn_use_if_start) {
 		int qdepth = hn_get_txswq_depth(&sc->hn_tx_ring[0]);
@@ -554,8 +564,7 @@ netvsc_attach(device_t dev)
 	if (sc->hn_xact == NULL)
 		goto failed;
 
-	error = hv_rf_on_device_add(sc, &device_info, &ring_cnt,
-	    &sc->hn_rx_ring[0]);
+	error = hv_rf_on_device_add(sc, &device_info, &ring_cnt, ETHERMTU);
 	if (error)
 		goto failed;
 	KASSERT(ring_cnt > 0 && ring_cnt <= sc->hn_rx_ring_inuse,
@@ -572,8 +581,11 @@ netvsc_attach(device_t dev)
 	device_printf(dev, "%d TX ring, %d RX ring\n",
 	    sc->hn_tx_ring_inuse, sc->hn_rx_ring_inuse);
 
-	if (sc->hn_rx_ring_inuse > 1)
-		hn_subchan_setup(sc);
+	if (sc->hn_rx_ring_inuse > 1) {
+		error = hn_attach_subchans(sc);
+		if (error)
+			goto failed;
+	}
 
 #if __FreeBSD_version >= 1100099
 	if (sc->hn_rx_ring_inuse > 1) {
@@ -1566,9 +1578,13 @@ hn_ioctl(struct ifnet *ifp, u_long cmd, 
 		/* Wait for subchannels to be destroyed */
 		vmbus_subchan_drain(sc->hn_prichan);
 
+		sc->hn_rx_ring[0].hn_rx_flags &= ~HN_RX_FLAG_ATTACHED;
+		sc->hn_tx_ring[0].hn_tx_flags &= ~HN_TX_FLAG_ATTACHED;
+		hn_chan_attach(sc, sc->hn_prichan); /* XXX check error */
+
 		ring_cnt = sc->hn_rx_ring_inuse;
 		error = hv_rf_on_device_add(sc, &device_info, &ring_cnt,
-		    &sc->hn_rx_ring[0]);
+		    ifr->ifr_mtu);
 		if (error) {
 			NV_LOCK(sc);
 			sc->temp_unusable = FALSE;
@@ -1594,7 +1610,7 @@ hn_ioctl(struct ifnet *ifp, u_long cmd, 
 				sc->hn_tx_ring[r].hn_tx_flags &=
 				    ~HN_TX_FLAG_ATTACHED;
 			}
-			hn_subchan_setup(sc);
+			hn_attach_subchans(sc); /* XXX check error */
 		}
 
 		if (sc->hn_tx_ring[0].hn_chim_size > sc->hn_chim_szmax)
@@ -2948,14 +2964,18 @@ hn_xmit_txeof_taskfunc(void *xtxr, int p
 	mtx_unlock(&txr->hn_tx_lock);
 }
 
-static void
-hn_channel_attach(struct hn_softc *sc, struct vmbus_channel *chan)
+static int
+hn_chan_attach(struct hn_softc *sc, struct vmbus_channel *chan)
 {
 	struct hn_rx_ring *rxr;
-	int idx;
+	struct hn_tx_ring *txr = NULL;
+	int idx, error;
 
 	idx = vmbus_chan_subidx(chan);
 
+	/*
+	 * Link this channel to RX/TX ring.
+	 */
 	KASSERT(idx >= 0 && idx < sc->hn_rx_ring_inuse,
 	    ("invalid channel index %d, should > 0 && < %d",
 	     idx, sc->hn_rx_ring_inuse));
@@ -2965,60 +2985,260 @@ hn_channel_attach(struct hn_softc *sc, s
 	rxr->hn_rx_flags |= HN_RX_FLAG_ATTACHED;
 
 	if (bootverbose) {
-		if_printf(sc->hn_ifp, "link RX ring %d to channel%u\n",
+		if_printf(sc->hn_ifp, "link RX ring %d to chan%u\n",
 		    idx, vmbus_chan_id(chan));
 	}
 
 	if (idx < sc->hn_tx_ring_inuse) {
-		struct hn_tx_ring *txr = &sc->hn_tx_ring[idx];
-
+		txr = &sc->hn_tx_ring[idx];
 		KASSERT((txr->hn_tx_flags & HN_TX_FLAG_ATTACHED) == 0,
 		    ("TX ring %d already attached", idx));
 		txr->hn_tx_flags |= HN_TX_FLAG_ATTACHED;
 
 		txr->hn_chan = chan;
 		if (bootverbose) {
-			if_printf(sc->hn_ifp, "link TX ring %d to channel%u\n",
+			if_printf(sc->hn_ifp, "link TX ring %d to chan%u\n",
 			    idx, vmbus_chan_id(chan));
 		}
 	}
 
-	/* Bind channel to a proper CPU */
+	/* Bind this channel to a proper CPU. */
 	vmbus_chan_cpu_set(chan, (sc->hn_cpu + idx) % mp_ncpus);
-}
-
-static void
-hn_subchan_attach(struct hn_softc *sc, struct vmbus_channel *chan)
-{
 
-	KASSERT(!vmbus_chan_is_primary(chan),
-	    ("subchannel callback on primary channel"));
-	hn_channel_attach(sc, chan);
+	/* Open this channel */
+	error = vmbus_chan_open(chan, NETVSC_DEVICE_RING_BUFFER_SIZE,
+	    NETVSC_DEVICE_RING_BUFFER_SIZE, NULL, 0, hn_chan_callback, rxr);
+	if (error) {
+		if_printf(sc->hn_ifp, "open chan%u failed: %d\n",
+		    vmbus_chan_id(chan), error);
+		rxr->hn_rx_flags &= ~HN_RX_FLAG_ATTACHED;
+		if (txr != NULL)
+			txr->hn_tx_flags &= ~HN_TX_FLAG_ATTACHED;
+	}
+	return (error);
 }
 
-static void
-hn_subchan_setup(struct hn_softc *sc)
+static int
+hn_attach_subchans(struct hn_softc *sc)
 {
 	struct vmbus_channel **subchans;
 	int subchan_cnt = sc->hn_rx_ring_inuse - 1;
-	int i;
+	int i, error = 0;
 
 	/* Wait for sub-channels setup to complete. */
 	subchans = vmbus_subchan_get(sc->hn_prichan, subchan_cnt);
 
 	/* Attach the sub-channels. */
 	for (i = 0; i < subchan_cnt; ++i) {
-		struct vmbus_channel *subchan = subchans[i];
-
-		/* NOTE: Calling order is critical. */
-		hn_subchan_attach(sc, subchan);
-		hv_nv_subchan_attach(subchan,
-		    &sc->hn_rx_ring[vmbus_chan_subidx(subchan)]);
+		error = hn_chan_attach(sc, subchans[i]);
+		if (error)
+			break;
 	}
 
 	/* Release the sub-channels */
 	vmbus_subchan_rel(subchans, subchan_cnt);
-	if_printf(sc->hn_ifp, "%d sub-channels setup done\n", subchan_cnt);
+
+	if (error) {
+		if_printf(sc->hn_ifp, "sub-channels attach failed: %d\n", error);
+	} else {
+		if (bootverbose) {
+			if_printf(sc->hn_ifp, "%d sub-channels attached\n",
+			    subchan_cnt);
+		}
+	}
+	return (error);
+}
+
+static void
+hn_nvs_handle_notify(struct hn_softc *sc, const struct vmbus_chanpkt_hdr *pkt)
+{
+	const struct hn_nvs_hdr *hdr;
+
+	if (VMBUS_CHANPKT_DATALEN(pkt) < sizeof(*hdr)) {
+		if_printf(sc->hn_ifp, "invalid nvs notify\n");
+		return;
+	}
+	hdr = VMBUS_CHANPKT_CONST_DATA(pkt);
+
+	if (hdr->nvs_type == HN_NVS_TYPE_TXTBL_NOTE) {
+		/* Useless; ignore */
+		return;
+	}
+	if_printf(sc->hn_ifp, "got notify, nvs type %u\n", hdr->nvs_type);
+}
+
+static void
+hn_nvs_handle_comp(struct hn_softc *sc, struct vmbus_channel *chan,
+    const struct vmbus_chanpkt_hdr *pkt)
+{
+	struct hn_send_ctx *sndc;
+
+	sndc = (struct hn_send_ctx *)(uintptr_t)pkt->cph_xactid;
+	sndc->hn_cb(sndc, sc, chan, VMBUS_CHANPKT_CONST_DATA(pkt),
+	    VMBUS_CHANPKT_DATALEN(pkt));
+	/*
+	 * NOTE:
+	 * 'sndc' CAN NOT be accessed anymore, since it can be freed by
+	 * its callback.
+	 */
+}
+
+static void
+hn_nvs_handle_rxbuf(struct hn_softc *sc, struct hn_rx_ring *rxr,
+    struct vmbus_channel *chan, const struct vmbus_chanpkt_hdr *pkthdr)
+{
+	const struct vmbus_chanpkt_rxbuf *pkt;
+	const struct hn_nvs_hdr *nvs_hdr;
+	int count, i, hlen;
+
+	if (__predict_false(VMBUS_CHANPKT_DATALEN(pkthdr) < sizeof(*nvs_hdr))) {
+		if_printf(rxr->hn_ifp, "invalid nvs RNDIS\n");
+		return;
+	}
+	nvs_hdr = VMBUS_CHANPKT_CONST_DATA(pkthdr);
+
+	/* Make sure that this is a RNDIS message. */
+	if (__predict_false(nvs_hdr->nvs_type != HN_NVS_TYPE_RNDIS)) {
+		if_printf(rxr->hn_ifp, "nvs type %u, not RNDIS\n",
+		    nvs_hdr->nvs_type);
+		return;
+	}
+
+	hlen = VMBUS_CHANPKT_GETLEN(pkthdr->cph_hlen);
+	if (__predict_false(hlen < sizeof(*pkt))) {
+		if_printf(rxr->hn_ifp, "invalid rxbuf chanpkt\n");
+		return;
+	}
+	pkt = (const struct vmbus_chanpkt_rxbuf *)pkthdr;
+
+	if (__predict_false(pkt->cp_rxbuf_id != HN_NVS_RXBUF_SIG)) {
+		if_printf(rxr->hn_ifp, "invalid rxbuf_id 0x%08x\n",
+		    pkt->cp_rxbuf_id);
+		return;
+	}
+
+	count = pkt->cp_rxbuf_cnt;
+	if (__predict_false(hlen <
+	    __offsetof(struct vmbus_chanpkt_rxbuf, cp_rxbuf[count]))) {
+		if_printf(rxr->hn_ifp, "invalid rxbuf_cnt %d\n", count);
+		return;
+	}
+
+	/* Each range represents 1 RNDIS pkt that contains 1 Ethernet frame */
+	for (i = 0; i < count; ++i) {
+		int ofs, len;
+
+		ofs = pkt->cp_rxbuf[i].rb_ofs;
+		len = pkt->cp_rxbuf[i].rb_len;
+		if (__predict_false(ofs + len > NETVSC_RECEIVE_BUFFER_SIZE)) {
+			if_printf(rxr->hn_ifp, "%dth RNDIS msg overflow rxbuf, "
+			    "ofs %d, len %d\n", i, ofs, len);
+			continue;
+		}
+		hv_rf_on_receive(sc, rxr, rxr->hn_rxbuf + ofs, len);
+	}
+	
+	/*
+	 * Moved completion call back here so that all received 
+	 * messages (not just data messages) will trigger a response
+	 * message back to the host.
+	 */
+	hn_nvs_ack_rxbuf(chan, pkt->cp_hdr.cph_xactid);
+}
+
+/*
+ * Net VSC on receive completion
+ *
+ * Send a receive completion packet to RNDIS device (ie NetVsp)
+ */
+static void
+hn_nvs_ack_rxbuf(struct vmbus_channel *chan, uint64_t tid)
+{
+	struct hn_nvs_rndis_ack ack;
+	int retries = 0;
+	int ret = 0;
+	
+	ack.nvs_type = HN_NVS_TYPE_RNDIS_ACK;
+	ack.nvs_status = HN_NVS_STATUS_OK;
+
+retry_send_cmplt:
+	/* Send the completion */
+	ret = vmbus_chan_send(chan, VMBUS_CHANPKT_TYPE_COMP,
+	    VMBUS_CHANPKT_FLAG_NONE, &ack, sizeof(ack), tid);
+	if (ret == 0) {
+		/* success */
+		/* no-op */
+	} else if (ret == EAGAIN) {
+		/* no more room... wait a bit and attempt to retry 3 times */
+		retries++;
+
+		if (retries < 4) {
+			DELAY(100);
+			goto retry_send_cmplt;
+		}
+	}
+}
+
+static void
+hn_chan_callback(struct vmbus_channel *chan, void *xrxr)
+{
+	struct hn_rx_ring *rxr = xrxr;
+	struct hn_softc *sc = rxr->hn_ifp->if_softc;
+	void *buffer;
+	int bufferlen = NETVSC_PACKET_SIZE;
+
+	buffer = rxr->hn_rdbuf;
+	do {
+		struct vmbus_chanpkt_hdr *pkt = buffer;
+		uint32_t bytes_rxed;
+		int ret;
+
+		bytes_rxed = bufferlen;
+		ret = vmbus_chan_recv_pkt(chan, pkt, &bytes_rxed);
+		if (ret == 0) {
+			switch (pkt->cph_type) {
+			case VMBUS_CHANPKT_TYPE_COMP:
+				hn_nvs_handle_comp(sc, chan, pkt);
+				break;
+			case VMBUS_CHANPKT_TYPE_RXBUF:
+				hn_nvs_handle_rxbuf(sc, rxr, chan, pkt);
+				break;
+			case VMBUS_CHANPKT_TYPE_INBAND:
+				hn_nvs_handle_notify(sc, pkt);
+				break;
+			default:
+				if_printf(rxr->hn_ifp,
+				    "unknown chan pkt %u\n",
+				    pkt->cph_type);
+				break;
+			}
+		} else if (ret == ENOBUFS) {
+			/* Handle large packet */
+			if (bufferlen > NETVSC_PACKET_SIZE) {
+				free(buffer, M_NETVSC);
+				buffer = NULL;
+			}
+
+			/* alloc new buffer */
+			buffer = malloc(bytes_rxed, M_NETVSC, M_NOWAIT);
+			if (buffer == NULL) {
+				if_printf(rxr->hn_ifp,
+				    "hv_cb malloc buffer failed, len=%u\n",
+				    bytes_rxed);
+				bufferlen = 0;
+				break;
+			}
+			bufferlen = bytes_rxed;
+		} else {
+			/* No more packets */
+			break;
+		}
+	} while (1);
+
+	if (bufferlen > NETVSC_PACKET_SIZE)
+		free(buffer, M_NETVSC);
+
+	hv_rf_channel_rollup(rxr, rxr->hn_txr);
 }
 
 static void

Modified: stable/11/sys/dev/hyperv/netvsc/hv_rndis_filter.c
==============================================================================
--- stable/11/sys/dev/hyperv/netvsc/hv_rndis_filter.c	Mon Oct 17 08:17:06 2016	(r307501)
+++ stable/11/sys/dev/hyperv/netvsc/hv_rndis_filter.c	Mon Oct 17 08:20:17 2016	(r307502)
@@ -1012,7 +1012,7 @@ hv_rf_halt_device(struct hn_softc *sc)
  */
 int
 hv_rf_on_device_add(struct hn_softc *sc, void *additl_info,
-    int *nchan0, struct hn_rx_ring *rxr)
+    int *nchan0, int mtu)
 {
 	int ret;
 	netvsc_device_info *dev_info = (netvsc_device_info *)additl_info;
@@ -1031,7 +1031,7 @@ hv_rf_on_device_add(struct hn_softc *sc,
 	 * (hv_rf_on_receive()) before this call is completed.
 	 * Note:  Earlier code used a function pointer here.
 	 */
-	ret = hv_nv_on_device_add(sc, rxr);
+	ret = hv_nv_on_device_add(sc, mtu);
 	if (ret != 0)
 		return (ret);
 

Modified: stable/11/sys/dev/hyperv/netvsc/hv_rndis_filter.h
==============================================================================
--- stable/11/sys/dev/hyperv/netvsc/hv_rndis_filter.h	Mon Oct 17 08:17:06 2016	(r307501)
+++ stable/11/sys/dev/hyperv/netvsc/hv_rndis_filter.h	Mon Oct 17 08:20:17 2016	(r307502)
@@ -44,7 +44,7 @@ void hv_rf_on_receive(struct hn_softc *s
     const void *data, int dlen);
 void hv_rf_channel_rollup(struct hn_rx_ring *rxr, struct hn_tx_ring *txr);
 int hv_rf_on_device_add(struct hn_softc *sc, void *additl_info, int *nchan,
-    struct hn_rx_ring *rxr);
+    int mtu);
 int hv_rf_on_device_remove(struct hn_softc *sc);
 int hv_rf_on_open(struct hn_softc *sc);
 int hv_rf_on_close(struct hn_softc *sc);

Modified: stable/11/sys/dev/hyperv/netvsc/if_hnreg.h
==============================================================================
--- stable/11/sys/dev/hyperv/netvsc/if_hnreg.h	Mon Oct 17 08:17:06 2016	(r307501)
+++ stable/11/sys/dev/hyperv/netvsc/if_hnreg.h	Mon Oct 17 08:20:17 2016	(r307502)
@@ -40,6 +40,14 @@
 #define HN_NDIS_VERSION_MAJOR(ver)	(((ver) & 0xffff0000) >> 16)
 #define HN_NDIS_VERSION_MINOR(ver)	((ver) & 0xffff)
 
+/*
+ * NVS versions.
+ */
+#define HN_NVS_VERSION_1		0x00002
+#define HN_NVS_VERSION_2		0x30002
+#define HN_NVS_VERSION_4		0x40000
+#define HN_NVS_VERSION_5		0x50000
+
 #define HN_NVS_RXBUF_SIG		0xcafe
 #define HN_NVS_CHIM_SIG			0xface
 



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