svn commit: r307502 - stable/11/sys/dev/hyperv/netvsc
Sepherosa Ziehau
sephe at FreeBSD.org
Mon Oct 17 08:20:19 UTC 2016
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
More information about the svn-src-stable
mailing list