Index: sys/dev/hyperv/netvsc/hv_net_vsc.h =================================================================== --- sys/dev/hyperv/netvsc/hv_net_vsc.h +++ sys/dev/hyperv/netvsc/hv_net_vsc.h @@ -227,16 +227,9 @@ unsigned long bitsmap_words; unsigned long *send_section_bitsmap; - /* Receive buffer allocated by us but managed by NetVSP */ - void *rx_buf; - uint32_t rx_buf_size; - uint32_t rx_buf_gpadl_handle; - uint32_t rx_section_count; - /* Holds rndis device info */ void *extension; - struct hyperv_dma rxbuf_dma; struct hyperv_dma txbuf_dma; } netvsc_dev; @@ -286,6 +279,7 @@ struct ifnet *hn_ifp; struct hn_tx_ring *hn_txr; void *hn_rdbuf; + uint8_t *hn_rxbuf; /* shadow sc->hn_rxbuf */ int hn_rx_idx; /* Trust csum verification on host side */ @@ -395,8 +389,15 @@ struct sysctl_oid *hn_rx_sysctl_tree; struct vmbus_xact_ctx *hn_xact; uint32_t hn_nvs_ver; + + uint32_t hn_flags; + void *hn_rxbuf; + uint32_t hn_rxbuf_gpadl; + struct hyperv_dma hn_rxbuf_dma; } hn_softc_t; +#define HN_FLAG_RXBUF_CONNECTED 0x0001 + /* * Externs */ Index: sys/dev/hyperv/netvsc/hv_net_vsc.c =================================================================== --- sys/dev/hyperv/netvsc/hv_net_vsc.c +++ sys/dev/hyperv/netvsc/hv_net_vsc.c @@ -59,9 +59,9 @@ 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 *); +static int hv_nv_init_rx_buffer_with_net_vsp(struct hn_softc *, int); static int hv_nv_destroy_send_buffer(netvsc_dev *net_dev); -static int hv_nv_destroy_rx_buffer(netvsc_dev *net_dev); +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(netvsc_dev *net_dev, struct vmbus_channel *, const struct vmbus_chanpkt_hdr *pkt); @@ -146,29 +146,18 @@ * Hyper-V extensible switch and the synthetic data path. */ static int -hv_nv_init_rx_buffer_with_net_vsp(struct hn_softc *sc) +hv_nv_init_rx_buffer_with_net_vsp(struct hn_softc *sc, int rxbuf_size) { struct vmbus_xact *xact; struct hn_nvs_rxbuf_conn *conn; const struct hn_nvs_rxbuf_connresp *resp; size_t resp_len; struct hn_send_ctx sndc; - netvsc_dev *net_dev; uint32_t status; int error; - net_dev = hv_nv_get_outbound_net_device(sc); - if (!net_dev) { - return (ENODEV); - } - - net_dev->rx_buf = hyperv_dmamem_alloc(bus_get_dma_tag(sc->hn_dev), - PAGE_SIZE, 0, net_dev->rx_buf_size, &net_dev->rxbuf_dma, - BUS_DMA_WAITOK | BUS_DMA_ZERO); - if (net_dev->rx_buf == NULL) { - device_printf(sc->hn_dev, "allocate rxbuf failed\n"); - return (ENOMEM); - } + KASSERT(rxbuf_size <= NETVSC_RECEIVE_BUFFER_SIZE, + ("invalid rxbuf size %d", rxbuf_size)); /* * Connect the RXBUF GPADL to the primary channel. @@ -178,8 +167,7 @@ * just share this RXBUF. */ error = vmbus_chan_gpadl_connect(sc->hn_prichan, - net_dev->rxbuf_dma.hv_paddr, net_dev->rx_buf_size, - &net_dev->rx_buf_gpadl_handle); + sc->hn_rxbuf_dma.hv_paddr, rxbuf_size, &sc->hn_rxbuf_gpadl); if (error) { if_printf(sc->hn_ifp, "rxbuf gpadl connect failed: %d\n", error); @@ -199,7 +187,7 @@ conn = vmbus_xact_req_data(xact); conn->nvs_type = HN_NVS_TYPE_RXBUF_CONN; - conn->nvs_gpadl = net_dev->rx_buf_gpadl_handle; + conn->nvs_gpadl = sc->hn_rxbuf_gpadl; conn->nvs_sig = HN_NVS_RXBUF_SIG; hn_send_ctx_init_simple(&sndc, hn_nvs_sent_xact, xact); @@ -239,12 +227,12 @@ error = EIO; goto cleanup; } - net_dev->rx_section_count = 1; + sc->hn_flags |= HN_FLAG_RXBUF_CONNECTED; return (0); cleanup: - hv_nv_destroy_rx_buffer(net_dev); + hv_nv_destroy_rx_buffer(sc); return (error); } @@ -376,11 +364,11 @@ * Net VSC destroy receive buffer */ static int -hv_nv_destroy_rx_buffer(netvsc_dev *net_dev) +hv_nv_destroy_rx_buffer(struct hn_softc *sc) { int ret = 0; - if (net_dev->rx_section_count) { + if (sc->hn_flags & HN_FLAG_RXBUF_CONNECTED) { struct hn_nvs_rxbuf_disconn disconn; /* @@ -391,37 +379,30 @@ disconn.nvs_sig = HN_NVS_RXBUF_SIG; /* NOTE: No response. */ - ret = hn_nvs_send(net_dev->sc->hn_prichan, + ret = hn_nvs_send(sc->hn_prichan, VMBUS_CHANPKT_FLAG_NONE, &disconn, sizeof(disconn), &hn_send_ctx_none); if (ret != 0) { - if_printf(net_dev->sc->hn_ifp, + if_printf(sc->hn_ifp, "send rxbuf disconn failed: %d\n", ret); return (ret); } - net_dev->rx_section_count = 0; + sc->hn_flags &= ~HN_FLAG_RXBUF_CONNECTED; } - /* Tear down the gpadl on the vsp end */ - if (net_dev->rx_buf_gpadl_handle) { - ret = vmbus_chan_gpadl_disconnect(net_dev->sc->hn_prichan, - net_dev->rx_buf_gpadl_handle); + if (sc->hn_rxbuf_gpadl != 0) { /* - * If we failed here, we might as well return and have a leak - * rather than continue and a bugchk + * Disconnect RXBUF from primary channel. */ + ret = vmbus_chan_gpadl_disconnect(sc->hn_prichan, + sc->hn_rxbuf_gpadl); if (ret != 0) { + if_printf(sc->hn_ifp, + "rxbuf disconn failed: %d\n", ret); return (ret); } - net_dev->rx_buf_gpadl_handle = 0; + sc->hn_rxbuf_gpadl = 0; } - - if (net_dev->rx_buf) { - /* Free up the receive buffer */ - hyperv_dmamem_free(&net_dev->rxbuf_dma, net_dev->rx_buf); - net_dev->rx_buf = NULL; - } - return (ret); } @@ -583,6 +564,7 @@ device_t dev = sc->hn_dev; struct ifnet *ifp = sc->hn_ifp; struct hn_nvs_ndis_init ndis; + int rxbuf_size; net_dev = hv_nv_get_outbound_net_device(sc); @@ -637,12 +619,12 @@ /* Post the big receive buffer to NetVSP */ if (sc->hn_nvs_ver <= NVSP_PROTOCOL_VERSION_2) - net_dev->rx_buf_size = NETVSC_RECEIVE_BUFFER_SIZE_LEGACY; + rxbuf_size = NETVSC_RECEIVE_BUFFER_SIZE_LEGACY; else - net_dev->rx_buf_size = NETVSC_RECEIVE_BUFFER_SIZE; + rxbuf_size = NETVSC_RECEIVE_BUFFER_SIZE; net_dev->send_buf_size = NETVSC_SEND_BUFFER_SIZE; - ret = hv_nv_init_rx_buffer_with_net_vsp(sc); + ret = hv_nv_init_rx_buffer_with_net_vsp(sc, rxbuf_size); if (ret == 0) ret = hv_nv_init_send_buffer_with_net_vsp(sc); @@ -654,10 +636,10 @@ * Net VSC disconnect from VSP */ static void -hv_nv_disconnect_from_vsp(netvsc_dev *net_dev) +hv_nv_disconnect_from_vsp(struct hn_softc *sc) { - hv_nv_destroy_rx_buffer(net_dev); - hv_nv_destroy_send_buffer(net_dev); + hv_nv_destroy_rx_buffer(sc); + hv_nv_destroy_send_buffer(sc->net_dev); } void @@ -731,17 +713,14 @@ int hv_nv_on_device_remove(struct hn_softc *sc, boolean_t destroy_channel) { - netvsc_dev *net_dev = sc->net_dev;; - hv_nv_disconnect_from_vsp(net_dev); - - /* At this point, no one should be accessing net_dev except in here */ + hv_nv_disconnect_from_vsp(sc); /* Now, we can close the channel safely */ vmbus_chan_close(sc->hn_prichan); - free(net_dev, M_NETVSC); + free(sc->net_dev, M_NETVSC); return (0); } @@ -865,7 +844,7 @@ /* Each range represents 1 RNDIS pkt that contains 1 Ethernet frame */ for (i = 0; i < count; i++) { hv_rf_on_receive(net_dev, rxr, - (const uint8_t *)net_dev->rx_buf + pkt->cp_rxbuf[i].rb_ofs, + rxr->hn_rxbuf + pkt->cp_rxbuf[i].rb_ofs, pkt->cp_rxbuf[i].rb_len); } Index: sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c =================================================================== --- sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c +++ sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c @@ -342,7 +342,7 @@ static void hn_start_txeof_taskfunc(void *, int); static void hn_stop_tx_tasks(struct hn_softc *); static int hn_encap(struct hn_tx_ring *, struct hn_txdesc *, struct mbuf **); -static void hn_create_rx_data(struct hn_softc *sc, int); +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_tx_chimney_size(struct hn_softc *, int); static void hn_channel_attach(struct hn_softc *, struct vmbus_channel *); @@ -504,7 +504,9 @@ error = hn_create_tx_data(sc, tx_ring_cnt); if (error) goto failed; - hn_create_rx_data(sc, ring_cnt); + error = hn_create_rx_data(sc, ring_cnt); + if (error) + goto failed; /* * Associate the first TX/RX ring w/ the primary channel. @@ -2176,7 +2178,7 @@ return ip->ip_p; } -static void +static int hn_create_rx_data(struct hn_softc *sc, int ring_cnt) { struct sysctl_oid_list *child; @@ -2189,6 +2191,22 @@ #endif int i; + /* + * Create RXBUF for reception. + * + * NOTE: + * - It is shared by all channels. + * - A large enough buffer is allocated, certain version of NVSes + * may further limit the usable space. + */ + sc->hn_rxbuf = hyperv_dmamem_alloc(bus_get_dma_tag(dev), + PAGE_SIZE, 0, NETVSC_RECEIVE_BUFFER_SIZE, &sc->hn_rxbuf_dma, + BUS_DMA_WAITOK | BUS_DMA_ZERO); + if (sc->hn_rxbuf == NULL) { + device_printf(sc->hn_dev, "allocate rxbuf failed\n"); + return (ENOMEM); + } + sc->hn_rx_ring_cnt = ring_cnt; sc->hn_rx_ring_inuse = sc->hn_rx_ring_cnt; @@ -2225,6 +2243,7 @@ rxr->hn_txr = &sc->hn_tx_ring[i]; rxr->hn_rdbuf = malloc(NETVSC_PACKET_SIZE, M_NETVSC, M_WAITOK); rxr->hn_rx_idx = i; + rxr->hn_rxbuf = sc->hn_rxbuf; /* * Initialize LRO. @@ -2331,6 +2350,8 @@ CTLFLAG_RD, &sc->hn_rx_ring_cnt, 0, "# created RX rings"); SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rx_ring_inuse", CTLFLAG_RD, &sc->hn_rx_ring_inuse, 0, "# used RX rings"); + + return (0); } static void @@ -2354,6 +2375,11 @@ sc->hn_rx_ring_cnt = 0; sc->hn_rx_ring_inuse = 0; + + if (sc->hn_rxbuf != NULL) { + hyperv_dmamem_free(&sc->hn_rxbuf_dma, sc->hn_rxbuf); + sc->hn_rxbuf = NULL; + } } static int