Index: sys/dev/virtio/network/if_vtnet.c =================================================================== --- sys/dev/virtio/network/if_vtnet.c +++ sys/dev/virtio/network/if_vtnet.c @@ -71,6 +71,7 @@ #include #include #include +#include #include #include @@ -110,6 +111,7 @@ static int vtnet_alloc_rx_filters(struct vtnet_softc *); static void vtnet_free_rx_filters(struct vtnet_softc *); static int vtnet_alloc_virtqueues(struct vtnet_softc *); +static int vtnet_alloc_interface(struct vtnet_softc *); static int vtnet_setup_interface(struct vtnet_softc *); static int vtnet_ioctl_mtu(struct vtnet_softc *, int); static int vtnet_ioctl_ifflags(struct vtnet_softc *); @@ -434,6 +436,12 @@ VTNET_CORE_LOCK_INIT(sc); callout_init_mtx(&sc->vtnet_tick_ch, VTNET_CORE_MTX(sc), 0); + error = vtnet_alloc_interface(sc); + if (error) { + device_printf(dev, "cannot allocate interface\n"); + goto fail; + } + vtnet_setup_sysctl(sc); vtnet_setup_features(sc); @@ -775,6 +783,12 @@ if (rxq->vtnrx_sg == NULL) return (ENOMEM); +#if defined(INET) || defined(INET6) + if (tcp_lro_init(&rxq->vtnrx_lro) != 0) + return (ENOMEM); + rxq->vtnrx_lro.ifp = sc->vtnet_ifp; +#endif + NET_TASK_INIT(&rxq->vtnrx_intrtask, 0, vtnet_rxq_tq_intr, rxq); rxq->vtnrx_tq = taskqueue_create(rxq->vtnrx_name, M_NOWAIT, taskqueue_thread_enqueue, &rxq->vtnrx_tq); @@ -853,6 +867,10 @@ rxq->vtnrx_sc = NULL; rxq->vtnrx_id = -1; +#if defined(INET) || defined(INET6) + tcp_lro_free(&rxq->vtnrx_lro); +#endif + if (rxq->vtnrx_sg != NULL) { sglist_free(rxq->vtnrx_sg); rxq->vtnrx_sg = NULL; @@ -992,22 +1010,34 @@ } static int -vtnet_setup_interface(struct vtnet_softc *sc) +vtnet_alloc_interface(struct vtnet_softc *sc) { device_t dev; - struct pfil_head_args pa; struct ifnet *ifp; dev = sc->vtnet_dev; - ifp = sc->vtnet_ifp = if_alloc(IFT_ETHER); - if (ifp == NULL) { - device_printf(dev, "cannot allocate ifnet structure\n"); - return (ENOSPC); - } + ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) + return (ENOMEM); - if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + sc->vtnet_ifp = ifp; ifp->if_softc = sc; + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + + return (0); +} + +static int +vtnet_setup_interface(struct vtnet_softc *sc) +{ + device_t dev; + struct pfil_head_args pa; + struct ifnet *ifp; + + dev = sc->vtnet_dev; + ifp = sc->vtnet_ifp; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_KNOWSEPOCH; ifp->if_baudrate = IF_Gbps(10); @@ -1906,9 +1936,14 @@ rxq->vtnrx_stats.vrxs_ipackets++; rxq->vtnrx_stats.vrxs_ibytes += m->m_pkthdr.len; - VTNET_RXQ_UNLOCK(rxq); +#if defined(INET) || defined(INET6) + if (ifp->if_capenable & IFCAP_LRO && rxq->vtnrx_lro.lro_cnt != 0) { + if (tcp_lro_rx(&rxq->vtnrx_lro, m, 0) == 0) + return; + } +#endif + (*ifp->if_input)(ifp, m); - VTNET_RXQ_LOCK(rxq); } static int @@ -2013,14 +2048,14 @@ } vtnet_rxq_input(rxq, m, &lhdr); - - /* Must recheck after dropping the Rx lock. */ - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) - break; } - if (deq > 0) + if (deq > 0) { +#if defined(INET) || defined(INET6) + tcp_lro_flush_all(&rxq->vtnrx_lro); +#endif virtqueue_notify(vq); + } return (count > 0 ? 0 : EAGAIN); } Index: sys/dev/virtio/network/if_vtnetvar.h =================================================================== --- sys/dev/virtio/network/if_vtnetvar.h +++ sys/dev/virtio/network/if_vtnetvar.h @@ -79,6 +79,7 @@ struct vtnet_rxq_stats vtnrx_stats; struct taskqueue *vtnrx_tq; struct task vtnrx_intrtask; + struct lro_ctrl vtnrx_lro; #ifdef DEV_NETMAP uint32_t vtnrx_nm_refill; struct virtio_net_hdr_mrg_rxbuf vtnrx_shrhdr; @@ -325,8 +326,7 @@ /* * The VIRTIO_NET_F_GUEST_TSO[46] features permit the host to send us - * frames larger than 1514 bytes. We do not yet support software LRO - * via tcp_lro_rx(). + * frames larger than 1514 bytes. */ #define VTNET_LRO_FEATURES (VIRTIO_NET_F_GUEST_TSO4 | \ VIRTIO_NET_F_GUEST_TSO6 | VIRTIO_NET_F_GUEST_ECN)