Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/virtio/network/if_vtnet.c
Show First 20 Lines • Show All 187 Lines • ▼ Show 20 Lines | |||||
static void vtnet_stop_rendezvous(struct vtnet_softc *); | static void vtnet_stop_rendezvous(struct vtnet_softc *); | ||||
static void vtnet_stop(struct vtnet_softc *); | static void vtnet_stop(struct vtnet_softc *); | ||||
static int vtnet_virtio_reinit(struct vtnet_softc *); | static int vtnet_virtio_reinit(struct vtnet_softc *); | ||||
static void vtnet_init_rx_filters(struct vtnet_softc *); | static void vtnet_init_rx_filters(struct vtnet_softc *); | ||||
static int vtnet_init_rx_queues(struct vtnet_softc *); | static int vtnet_init_rx_queues(struct vtnet_softc *); | ||||
static int vtnet_init_tx_queues(struct vtnet_softc *); | static int vtnet_init_tx_queues(struct vtnet_softc *); | ||||
static int vtnet_init_rxtx_queues(struct vtnet_softc *); | static int vtnet_init_rxtx_queues(struct vtnet_softc *); | ||||
static void vtnet_set_active_vq_pairs(struct vtnet_softc *); | static void vtnet_set_active_vq_pairs(struct vtnet_softc *); | ||||
static void vtnet_update_rx_offloads(struct vtnet_softc *); | |||||
static int vtnet_reinit(struct vtnet_softc *); | static int vtnet_reinit(struct vtnet_softc *); | ||||
static void vtnet_init_locked(struct vtnet_softc *, int); | static void vtnet_init_locked(struct vtnet_softc *, int); | ||||
static void vtnet_init(void *); | static void vtnet_init(void *); | ||||
static void vtnet_free_ctrl_vq(struct vtnet_softc *); | static void vtnet_free_ctrl_vq(struct vtnet_softc *); | ||||
static void vtnet_exec_ctrl_cmd(struct vtnet_softc *, void *, | static void vtnet_exec_ctrl_cmd(struct vtnet_softc *, void *, | ||||
struct sglist *, int, int); | struct sglist *, int, int); | ||||
static int vtnet_ctrl_mac_cmd(struct vtnet_softc *, uint8_t *); | static int vtnet_ctrl_mac_cmd(struct vtnet_softc *, uint8_t *); | ||||
static int vtnet_ctrl_guest_offloads(struct vtnet_softc *, uint64_t); | |||||
static int vtnet_ctrl_mq_cmd(struct vtnet_softc *, uint16_t); | static int vtnet_ctrl_mq_cmd(struct vtnet_softc *, uint16_t); | ||||
static int vtnet_ctrl_rx_cmd(struct vtnet_softc *, uint8_t, int); | static int vtnet_ctrl_rx_cmd(struct vtnet_softc *, uint8_t, int); | ||||
static int vtnet_set_promisc(struct vtnet_softc *, int); | static int vtnet_set_promisc(struct vtnet_softc *, int); | ||||
static int vtnet_set_allmulti(struct vtnet_softc *, int); | static int vtnet_set_allmulti(struct vtnet_softc *, int); | ||||
static void vtnet_rx_filter(struct vtnet_softc *); | static void vtnet_rx_filter(struct vtnet_softc *); | ||||
static void vtnet_rx_filter_mac(struct vtnet_softc *); | static void vtnet_rx_filter_mac(struct vtnet_softc *); | ||||
static int vtnet_exec_vlan_filter(struct vtnet_softc *, int, uint16_t); | static int vtnet_exec_vlan_filter(struct vtnet_softc *, int, uint16_t); | ||||
static void vtnet_rx_filter_vlan(struct vtnet_softc *); | static void vtnet_rx_filter_vlan(struct vtnet_softc *); | ||||
▲ Show 20 Lines • Show All 825 Lines • ▼ Show 20 Lines | if (virtio_with_feature(dev, VIRTIO_NET_F_CSUM)) { | ||||
if (gso || virtio_with_feature(dev, VIRTIO_NET_F_HOST_ECN)) | if (gso || virtio_with_feature(dev, VIRTIO_NET_F_HOST_ECN)) | ||||
sc->vtnet_flags |= VTNET_FLAG_TSO_ECN; | sc->vtnet_flags |= VTNET_FLAG_TSO_ECN; | ||||
if (ifp->if_capabilities & (IFCAP_TSO4 | IFCAP_TSO6)) | if (ifp->if_capabilities & (IFCAP_TSO4 | IFCAP_TSO6)) | ||||
ifp->if_capabilities |= IFCAP_VLAN_HWTSO; | ifp->if_capabilities |= IFCAP_VLAN_HWTSO; | ||||
} | } | ||||
if (virtio_with_feature(dev, VIRTIO_NET_F_GUEST_CSUM)) { | if (virtio_with_feature(dev, VIRTIO_NET_F_GUEST_CSUM)) { | ||||
ifp->if_capabilities |= IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6; | ifp->if_capabilities |= IFCAP_RXCSUM; | ||||
#ifdef notyet | |||||
/* BMV: Rx checksums not distinguished between IPv4 and IPv6. */ | |||||
ifp->if_capabilities |= IFCAP_RXCSUM_IPV6; | |||||
#endif | |||||
if (vtnet_tunable_int(sc, "fixup_needs_csum", | if (vtnet_tunable_int(sc, "fixup_needs_csum", | ||||
vtnet_fixup_needs_csum) != 0) | vtnet_fixup_needs_csum) != 0) | ||||
sc->vtnet_flags |= VTNET_FLAG_FIXUP_NEEDS_CSUM; | sc->vtnet_flags |= VTNET_FLAG_FIXUP_NEEDS_CSUM; | ||||
if (virtio_with_feature(dev, VIRTIO_NET_F_GUEST_TSO4) || | if (virtio_with_feature(dev, VIRTIO_NET_F_GUEST_TSO4) || | ||||
virtio_with_feature(dev, VIRTIO_NET_F_GUEST_TSO6) || | virtio_with_feature(dev, VIRTIO_NET_F_GUEST_TSO6) || | ||||
virtio_with_feature(dev, VIRTIO_NET_F_GUEST_ECN)) | virtio_with_feature(dev, VIRTIO_NET_F_GUEST_ECN)) | ||||
▲ Show 20 Lines • Show All 153 Lines • ▼ Show 20 Lines | vtnet_ioctl_multi(struct vtnet_softc *sc) | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
vtnet_ioctl_ifcap(struct vtnet_softc *sc, struct ifreq *ifr) | vtnet_ioctl_ifcap(struct vtnet_softc *sc, struct ifreq *ifr) | ||||
{ | { | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
int mask, reinit; | int mask, reinit, update; | ||||
ifp = sc->vtnet_ifp; | ifp = sc->vtnet_ifp; | ||||
mask = (ifr->ifr_reqcap & ifp->if_capabilities) ^ ifp->if_capenable; | mask = (ifr->ifr_reqcap & ifp->if_capabilities) ^ ifp->if_capenable; | ||||
reinit = update = 0; | |||||
VTNET_CORE_LOCK_ASSERT(sc); | VTNET_CORE_LOCK_ASSERT(sc); | ||||
if (mask & IFCAP_TXCSUM) | if (mask & IFCAP_TXCSUM) | ||||
ifp->if_capenable ^= IFCAP_TXCSUM; | ifp->if_capenable ^= IFCAP_TXCSUM; | ||||
if (mask & IFCAP_TXCSUM_IPV6) | if (mask & IFCAP_TXCSUM_IPV6) | ||||
ifp->if_capenable ^= IFCAP_TXCSUM_IPV6; | ifp->if_capenable ^= IFCAP_TXCSUM_IPV6; | ||||
if (mask & IFCAP_TSO4) | if (mask & IFCAP_TSO4) | ||||
ifp->if_capenable ^= IFCAP_TSO4; | ifp->if_capenable ^= IFCAP_TSO4; | ||||
if (mask & IFCAP_TSO6) | if (mask & IFCAP_TSO6) | ||||
ifp->if_capenable ^= IFCAP_TSO6; | ifp->if_capenable ^= IFCAP_TSO6; | ||||
if (mask & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6 | IFCAP_LRO | | if (mask & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6 | IFCAP_LRO)) { | ||||
IFCAP_VLAN_HWFILTER)) { | /* | ||||
/* These Rx features require us to renegotiate. */ | * These Rx features require the negotiated features to | ||||
* be updated. Avoid a full reinit if possible. | |||||
*/ | |||||
if (sc->vtnet_features & VIRTIO_NET_F_CTRL_GUEST_OFFLOADS) | |||||
update = 1; | |||||
else | |||||
reinit = 1; | reinit = 1; | ||||
if (mask & IFCAP_RXCSUM) | if (mask & IFCAP_RXCSUM) | ||||
ifp->if_capenable ^= IFCAP_RXCSUM; | ifp->if_capenable ^= IFCAP_RXCSUM; | ||||
if (mask & IFCAP_RXCSUM_IPV6) | if (mask & IFCAP_RXCSUM_IPV6) | ||||
ifp->if_capenable ^= IFCAP_RXCSUM_IPV6; | ifp->if_capenable ^= IFCAP_RXCSUM_IPV6; | ||||
if (mask & IFCAP_LRO) | if (mask & IFCAP_LRO) | ||||
ifp->if_capenable ^= IFCAP_LRO; | ifp->if_capenable ^= IFCAP_LRO; | ||||
/* | |||||
* VirtIO does not distinguish between IPv4 and IPv6 checksums | |||||
* so treat them as a pair. Guest TSO (LRO) requires receive | |||||
* checksums. | |||||
*/ | |||||
if (ifp->if_capenable & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6)) { | |||||
ifp->if_capenable |= IFCAP_RXCSUM; | |||||
#ifdef notyet | |||||
ifp->if_capenable |= IFCAP_RXCSUM_IPV6; | |||||
#endif | |||||
} else | |||||
ifp->if_capenable &= | |||||
~(IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6 | IFCAP_LRO); | |||||
} | |||||
if (mask & IFCAP_VLAN_HWFILTER) { | |||||
/* These Rx features require renegotiation. */ | |||||
reinit = 1; | |||||
if (mask & IFCAP_VLAN_HWFILTER) | if (mask & IFCAP_VLAN_HWFILTER) | ||||
ifp->if_capenable ^= IFCAP_VLAN_HWFILTER; | ifp->if_capenable ^= IFCAP_VLAN_HWFILTER; | ||||
} else | } | ||||
reinit = 0; | |||||
if (mask & IFCAP_VLAN_HWTSO) | if (mask & IFCAP_VLAN_HWTSO) | ||||
ifp->if_capenable ^= IFCAP_VLAN_HWTSO; | ifp->if_capenable ^= IFCAP_VLAN_HWTSO; | ||||
if (mask & IFCAP_VLAN_HWTAGGING) | if (mask & IFCAP_VLAN_HWTAGGING) | ||||
ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; | ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING; | ||||
if (reinit && (ifp->if_drv_flags & IFF_DRV_RUNNING)) { | if (ifp->if_drv_flags & IFF_DRV_RUNNING) { | ||||
if (reinit) { | |||||
ifp->if_drv_flags &= ~IFF_DRV_RUNNING; | ifp->if_drv_flags &= ~IFF_DRV_RUNNING; | ||||
vtnet_init_locked(sc, 0); | vtnet_init_locked(sc, 0); | ||||
} else if (update) | |||||
vtnet_update_rx_offloads(sc); | |||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
vtnet_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) | vtnet_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 1,747 Lines • ▼ Show 20 Lines | vtnet_virtio_reinit(struct vtnet_softc *sc) | ||||
features = sc->vtnet_negotiated_features; | features = sc->vtnet_negotiated_features; | ||||
/* | /* | ||||
* Re-negotiate with the host, removing any disabled receive | * Re-negotiate with the host, removing any disabled receive | ||||
* features. Transmit features are disabled only on our side | * features. Transmit features are disabled only on our side | ||||
* via if_capenable and if_hwassist. | * via if_capenable and if_hwassist. | ||||
*/ | */ | ||||
if (ifp->if_capabilities & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6)) { | if ((ifp->if_capenable & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6)) == 0) | ||||
/* | features &= ~(VIRTIO_NET_F_GUEST_CSUM | VTNET_LRO_FEATURES); | ||||
* VirtIO does not distinguish between the IPv4 and IPv6 | |||||
* checksums so require both. Guest TSO (LRO) requires | |||||
* Rx checksums. | |||||
*/ | |||||
if ((ifp->if_capenable & | |||||
(IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6)) == 0) { | |||||
features &= ~VIRTIO_NET_F_GUEST_CSUM; | |||||
features &= ~VTNET_LRO_FEATURES; | |||||
} | |||||
} | |||||
if (ifp->if_capabilities & IFCAP_LRO) { | |||||
if ((ifp->if_capenable & IFCAP_LRO) == 0) | if ((ifp->if_capenable & IFCAP_LRO) == 0) | ||||
features &= ~VTNET_LRO_FEATURES; | features &= ~VTNET_LRO_FEATURES; | ||||
} | |||||
if (ifp->if_capabilities & IFCAP_VLAN_HWFILTER) { | |||||
if ((ifp->if_capenable & IFCAP_VLAN_HWFILTER) == 0) | if ((ifp->if_capenable & IFCAP_VLAN_HWFILTER) == 0) | ||||
features &= ~VIRTIO_NET_F_CTRL_VLAN; | features &= ~VIRTIO_NET_F_CTRL_VLAN; | ||||
} | |||||
error = virtio_reinit(dev, features); | error = virtio_reinit(dev, features); | ||||
if (error) { | if (error) { | ||||
device_printf(dev, "virtio reinit error %d\n", error); | device_printf(dev, "virtio reinit error %d\n", error); | ||||
return (error); | return (error); | ||||
} | } | ||||
sc->vtnet_features = features; | sc->vtnet_features = features; | ||||
▲ Show 20 Lines • Show All 111 Lines • ▼ Show 20 Lines | if (vtnet_ctrl_mq_cmd(sc, npairs) != 0) { | ||||
device_printf(dev, | device_printf(dev, | ||||
"cannot set active queue pairs to %d\n", npairs); | "cannot set active queue pairs to %d\n", npairs); | ||||
npairs = 1; | npairs = 1; | ||||
} | } | ||||
sc->vtnet_act_vq_pairs = npairs; | sc->vtnet_act_vq_pairs = npairs; | ||||
} | } | ||||
static void | |||||
vtnet_update_rx_offloads(struct vtnet_softc *sc) | |||||
{ | |||||
struct ifnet *ifp; | |||||
uint64_t features; | |||||
int error; | |||||
ifp = sc->vtnet_ifp; | |||||
features = sc->vtnet_features; | |||||
VTNET_CORE_LOCK_ASSERT(sc); | |||||
if (ifp->if_capabilities & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6)) { | |||||
if (ifp->if_capenable & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6)) | |||||
features |= VIRTIO_NET_F_GUEST_CSUM; | |||||
else | |||||
features &= ~VIRTIO_NET_F_GUEST_CSUM; | |||||
} | |||||
if (ifp->if_capabilities & IFCAP_LRO) { | |||||
if (ifp->if_capenable & IFCAP_LRO) | |||||
features |= VTNET_LRO_FEATURES; | |||||
else | |||||
features &= ~VTNET_LRO_FEATURES; | |||||
} | |||||
error = vtnet_ctrl_guest_offloads(sc, | |||||
features & (VIRTIO_NET_F_GUEST_CSUM | VIRTIO_NET_F_GUEST_TSO4 | | |||||
VIRTIO_NET_F_GUEST_TSO6 | VIRTIO_NET_F_GUEST_ECN | | |||||
VIRTIO_NET_F_GUEST_UFO)); | |||||
if (error) { | |||||
device_printf(sc->vtnet_dev, | |||||
"%s: cannot update Rx features\n", __func__); | |||||
if (ifp->if_drv_flags & IFF_DRV_RUNNING) { | |||||
ifp->if_drv_flags &= ~IFF_DRV_RUNNING; | |||||
vtnet_init_locked(sc, 0); | |||||
} | |||||
} else | |||||
sc->vtnet_features = features; | |||||
} | |||||
static int | static int | ||||
vtnet_reinit(struct vtnet_softc *sc) | vtnet_reinit(struct vtnet_softc *sc) | ||||
{ | { | ||||
device_t dev; | device_t dev; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
int error; | int error; | ||||
dev = sc->vtnet_dev; | dev = sc->vtnet_dev; | ||||
▲ Show 20 Lines • Show All 135 Lines • ▼ Show 20 Lines | vtnet_ctrl_mac_cmd(struct vtnet_softc *sc, uint8_t *hwaddr) | ||||
s.hdr.class = VIRTIO_NET_CTRL_MAC; | s.hdr.class = VIRTIO_NET_CTRL_MAC; | ||||
s.hdr.cmd = VIRTIO_NET_CTRL_MAC_ADDR_SET; | s.hdr.cmd = VIRTIO_NET_CTRL_MAC_ADDR_SET; | ||||
bcopy(hwaddr, &s.addr[0], ETHER_ADDR_LEN); | bcopy(hwaddr, &s.addr[0], ETHER_ADDR_LEN); | ||||
s.ack = VIRTIO_NET_ERR; | s.ack = VIRTIO_NET_ERR; | ||||
sglist_init(&sg, nitems(segs), segs); | sglist_init(&sg, nitems(segs), segs); | ||||
error |= sglist_append(&sg, &s.hdr, sizeof(struct virtio_net_ctrl_hdr)); | error |= sglist_append(&sg, &s.hdr, sizeof(struct virtio_net_ctrl_hdr)); | ||||
error |= sglist_append(&sg, &s.addr[0], ETHER_ADDR_LEN); | error |= sglist_append(&sg, &s.addr[0], ETHER_ADDR_LEN); | ||||
error |= sglist_append(&sg, &s.ack, sizeof(uint8_t)); | |||||
MPASS(error == 0 && sg.sg_nseg == nitems(segs)); | |||||
if (error == 0) | |||||
vtnet_exec_ctrl_cmd(sc, &s.ack, &sg, sg.sg_nseg - 1, 1); | |||||
return (s.ack == VIRTIO_NET_OK ? 0 : EIO); | |||||
} | |||||
static int | |||||
vtnet_ctrl_guest_offloads(struct vtnet_softc *sc, uint64_t offloads) | |||||
{ | |||||
struct sglist_seg segs[3]; | |||||
struct sglist sg; | |||||
struct { | |||||
struct virtio_net_ctrl_hdr hdr __aligned(2); | |||||
uint8_t pad1; | |||||
uint64_t offloads __aligned(8); | |||||
uint8_t pad2; | |||||
uint8_t ack; | |||||
} s; | |||||
int error; | |||||
error = 0; | |||||
MPASS(sc->vtnet_features & VIRTIO_NET_F_CTRL_GUEST_OFFLOADS); | |||||
s.hdr.class = VIRTIO_NET_CTRL_GUEST_OFFLOADS; | |||||
s.hdr.cmd = VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET; | |||||
s.offloads = vtnet_gtoh64(sc, offloads); | |||||
s.ack = VIRTIO_NET_ERR; | |||||
sglist_init(&sg, nitems(segs), segs); | |||||
error |= sglist_append(&sg, &s.hdr, sizeof(struct virtio_net_ctrl_hdr)); | |||||
error |= sglist_append(&sg, &s.offloads, sizeof(uint64_t)); | |||||
error |= sglist_append(&sg, &s.ack, sizeof(uint8_t)); | error |= sglist_append(&sg, &s.ack, sizeof(uint8_t)); | ||||
MPASS(error == 0 && sg.sg_nseg == nitems(segs)); | MPASS(error == 0 && sg.sg_nseg == nitems(segs)); | ||||
if (error == 0) | if (error == 0) | ||||
vtnet_exec_ctrl_cmd(sc, &s.ack, &sg, sg.sg_nseg - 1, 1); | vtnet_exec_ctrl_cmd(sc, &s.ack, &sg, sg.sg_nseg - 1, 1); | ||||
return (s.ack == VIRTIO_NET_OK ? 0 : EIO); | return (s.ack == VIRTIO_NET_OK ? 0 : EIO); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 817 Lines • Show Last 20 Lines |