Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/virtio/network/if_vtnet.c
Show First 20 Lines • Show All 594 Lines • ▼ Show 20 Lines | vtnet_config_change(device_t dev) | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
vtnet_negotiate_features(struct vtnet_softc *sc) | vtnet_negotiate_features(struct vtnet_softc *sc) | ||||
{ | { | ||||
device_t dev; | device_t dev; | ||||
uint64_t features; | uint64_t features, negotiated_features; | ||||
int no_csum; | int no_csum; | ||||
dev = sc->vtnet_dev; | dev = sc->vtnet_dev; | ||||
features = virtio_bus_is_modern(dev) ? VTNET_MODERN_FEATURES : | features = virtio_bus_is_modern(dev) ? VTNET_MODERN_FEATURES : | ||||
VTNET_LEGACY_FEATURES; | VTNET_LEGACY_FEATURES; | ||||
/* | /* | ||||
* TSO and LRO are only available when their corresponding checksum | * TSO and LRO are only available when their corresponding checksum | ||||
Show All 9 Lines | |||||
#ifndef VTNET_LEGACY_TX | #ifndef VTNET_LEGACY_TX | ||||
if (vtnet_tunable_int(sc, "mq_disable", vtnet_mq_disable)) | if (vtnet_tunable_int(sc, "mq_disable", vtnet_mq_disable)) | ||||
features &= ~VIRTIO_NET_F_MQ; | features &= ~VIRTIO_NET_F_MQ; | ||||
#else | #else | ||||
features &= ~VIRTIO_NET_F_MQ; | features &= ~VIRTIO_NET_F_MQ; | ||||
#endif | #endif | ||||
sc->vtnet_features = virtio_negotiate_features(dev, features); | negotiated_features = virtio_negotiate_features(dev, features); | ||||
if (virtio_with_feature(dev, VTNET_LRO_FEATURES) && | if (virtio_with_feature(dev, VTNET_LRO_FEATURES) && | ||||
virtio_with_feature(dev, VIRTIO_NET_F_MRG_RXBUF) == 0) { | virtio_with_feature(dev, VIRTIO_NET_F_MRG_RXBUF) == 0) { | ||||
/* | /* | ||||
* LRO without mergeable buffers requires special care. This | * LRO without mergeable buffers requires special care. This | ||||
* is not ideal because every receive buffer must be large | * is not ideal because every receive buffer must be large | ||||
* enough to hold the maximum TCP packet, the Ethernet header, | * enough to hold the maximum TCP packet, the Ethernet header, | ||||
* and the header. This requires up to 34 descriptors with | * and the header. This requires up to 34 descriptors with | ||||
* MCLBYTES clusters. If we do not have indirect descriptors, | * MCLBYTES clusters. If we do not have indirect descriptors, | ||||
* LRO is disabled since the virtqueue will not contain very | * LRO is disabled since the virtqueue will not contain very | ||||
* many receive buffers. | * many receive buffers. | ||||
*/ | */ | ||||
if (!virtio_with_feature(dev, VIRTIO_RING_F_INDIRECT_DESC)) { | if (!virtio_with_feature(dev, VIRTIO_RING_F_INDIRECT_DESC)) { | ||||
device_printf(dev, | device_printf(dev, | ||||
"LRO disabled since both mergeable buffers and " | "LRO disabled since both mergeable buffers and " | ||||
"indirect descriptors were not negotiated\n"); | "indirect descriptors were not negotiated\n"); | ||||
features &= ~VTNET_LRO_FEATURES; | features &= ~VTNET_LRO_FEATURES; | ||||
sc->vtnet_features = | negotiated_features = | ||||
virtio_negotiate_features(dev, features); | virtio_negotiate_features(dev, features); | ||||
} else | } else | ||||
sc->vtnet_flags |= VTNET_FLAG_LRO_NOMRG; | sc->vtnet_flags |= VTNET_FLAG_LRO_NOMRG; | ||||
} | } | ||||
sc->vtnet_features = negotiated_features; | |||||
sc->vtnet_negotiated_features = negotiated_features; | |||||
virtio_finalize_features(dev); | virtio_finalize_features(dev); | ||||
} | } | ||||
static void | static void | ||||
vtnet_setup_features(struct vtnet_softc *sc) | vtnet_setup_features(struct vtnet_softc *sc) | ||||
{ | { | ||||
device_t dev; | device_t dev; | ||||
▲ Show 20 Lines • Show All 2,298 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
static int | static int | ||||
vtnet_virtio_reinit(struct vtnet_softc *sc) | vtnet_virtio_reinit(struct vtnet_softc *sc) | ||||
{ | { | ||||
device_t dev; | device_t dev; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
uint64_t features; | uint64_t features; | ||||
int mask, error; | int error; | ||||
dev = sc->vtnet_dev; | dev = sc->vtnet_dev; | ||||
ifp = sc->vtnet_ifp; | ifp = sc->vtnet_ifp; | ||||
features = sc->vtnet_features; | features = sc->vtnet_negotiated_features; | ||||
mask = 0; | |||||
#if defined(INET) | |||||
mask |= IFCAP_RXCSUM; | |||||
#endif | |||||
#if defined (INET6) | |||||
mask |= IFCAP_RXCSUM_IPV6; | |||||
#endif | |||||
/* | /* | ||||
* 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 & mask) { | if (ifp->if_capabilities & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6)) { | ||||
/* | /* | ||||
* We require both IPv4 and IPv6 offloading to be enabled | * VirtIO does not distinguish between the IPv4 and IPv6 | ||||
* in order to negotiated it: VirtIO does not distinguish | * checksums so require both. Guest TSO (LRO) requires | ||||
* between the two. | * Rx checksums. | ||||
*/ | */ | ||||
if ((ifp->if_capenable & mask) != mask) | if ((ifp->if_capenable & | ||||
(IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6)) == 0) { | |||||
features &= ~VIRTIO_NET_F_GUEST_CSUM; | features &= ~VIRTIO_NET_F_GUEST_CSUM; | ||||
features &= ~VTNET_LRO_FEATURES; | |||||
} | } | ||||
} | |||||
if (ifp->if_capabilities & IFCAP_LRO) { | 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_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; | |||||
virtio_reinit_complete(dev); | |||||
return (0); | |||||
} | |||||
static void | static void | ||||
vtnet_init_rx_filters(struct vtnet_softc *sc) | vtnet_init_rx_filters(struct vtnet_softc *sc) | ||||
{ | { | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
ifp = sc->vtnet_ifp; | ifp = sc->vtnet_ifp; | ||||
if (sc->vtnet_flags & VTNET_FLAG_CTRL_RX) { | if (sc->vtnet_flags & VTNET_FLAG_CTRL_RX) { | ||||
/* Restore promiscuous and all-multicast modes. */ | |||||
vtnet_rx_filter(sc); | vtnet_rx_filter(sc); | ||||
/* Restore filtered MAC addresses. */ | |||||
vtnet_rx_filter_mac(sc); | vtnet_rx_filter_mac(sc); | ||||
} | } | ||||
if (ifp->if_capenable & IFCAP_VLAN_HWFILTER) | if (ifp->if_capenable & IFCAP_VLAN_HWFILTER) | ||||
vtnet_rx_filter_vlan(sc); | vtnet_rx_filter_vlan(sc); | ||||
} | } | ||||
static int | static int | ||||
▲ Show 20 Lines • Show All 92 Lines • ▼ Show 20 Lines | vtnet_set_active_vq_pairs(struct vtnet_softc *sc) | ||||
} | } | ||||
sc->vtnet_act_vq_pairs = npairs; | sc->vtnet_act_vq_pairs = npairs; | ||||
} | } | ||||
static int | static int | ||||
vtnet_reinit(struct vtnet_softc *sc) | vtnet_reinit(struct vtnet_softc *sc) | ||||
{ | { | ||||
device_t dev; | |||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
int error; | int error; | ||||
dev = sc->vtnet_dev; | |||||
ifp = sc->vtnet_ifp; | ifp = sc->vtnet_ifp; | ||||
/* Use the current MAC address. */ | |||||
bcopy(IF_LLADDR(ifp), sc->vtnet_hwaddr, ETHER_ADDR_LEN); | bcopy(IF_LLADDR(ifp), sc->vtnet_hwaddr, ETHER_ADDR_LEN); | ||||
vtnet_set_macaddr(sc); | |||||
error = vtnet_virtio_reinit(sc); | |||||
if (error) | |||||
return (error); | |||||
vtnet_set_macaddr(sc); | |||||
vtnet_set_active_vq_pairs(sc); | vtnet_set_active_vq_pairs(sc); | ||||
if (sc->vtnet_flags & VTNET_FLAG_CTRL_VQ) | |||||
vtnet_init_rx_filters(sc); | |||||
ifp->if_hwassist = 0; | ifp->if_hwassist = 0; | ||||
if (ifp->if_capenable & IFCAP_TXCSUM) | if (ifp->if_capenable & IFCAP_TXCSUM) | ||||
ifp->if_hwassist |= VTNET_CSUM_OFFLOAD; | ifp->if_hwassist |= VTNET_CSUM_OFFLOAD; | ||||
if (ifp->if_capenable & IFCAP_TXCSUM_IPV6) | if (ifp->if_capenable & IFCAP_TXCSUM_IPV6) | ||||
ifp->if_hwassist |= VTNET_CSUM_OFFLOAD_IPV6; | ifp->if_hwassist |= VTNET_CSUM_OFFLOAD_IPV6; | ||||
if (ifp->if_capenable & IFCAP_TSO4) | if (ifp->if_capenable & IFCAP_TSO4) | ||||
ifp->if_hwassist |= CSUM_IP_TSO; | ifp->if_hwassist |= CSUM_IP_TSO; | ||||
if (ifp->if_capenable & IFCAP_TSO6) | if (ifp->if_capenable & IFCAP_TSO6) | ||||
ifp->if_hwassist |= CSUM_IP6_TSO; | ifp->if_hwassist |= CSUM_IP6_TSO; | ||||
if (sc->vtnet_flags & VTNET_FLAG_CTRL_VQ) | |||||
vtnet_init_rx_filters(sc); | |||||
error = vtnet_init_rxtx_queues(sc); | error = vtnet_init_rxtx_queues(sc); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
vtnet_enable_interrupts(sc); | |||||
ifp->if_drv_flags |= IFF_DRV_RUNNING; | |||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
vtnet_init_locked(struct vtnet_softc *sc, int init_mode) | vtnet_init_locked(struct vtnet_softc *sc, int init_mode) | ||||
{ | { | ||||
device_t dev; | device_t dev; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
Show All 15 Lines | case VTNET_INIT_NETMAP_ENTER: | ||||
nm_set_native_flags(NA(ifp)); | nm_set_native_flags(NA(ifp)); | ||||
break; | break; | ||||
case VTNET_INIT_NETMAP_EXIT: | case VTNET_INIT_NETMAP_EXIT: | ||||
nm_clear_native_flags(NA(ifp)); | nm_clear_native_flags(NA(ifp)); | ||||
break; | break; | ||||
} | } | ||||
#endif /* DEV_NETMAP */ | #endif /* DEV_NETMAP */ | ||||
/* Reinitialize with the host. */ | if (vtnet_reinit(sc) != 0) { | ||||
if (vtnet_virtio_reinit(sc) != 0) | vtnet_stop(sc); | ||||
goto fail; | return; | ||||
} | |||||
if (vtnet_reinit(sc) != 0) | ifp->if_drv_flags |= IFF_DRV_RUNNING; | ||||
goto fail; | |||||
virtio_reinit_complete(dev); | |||||
vtnet_update_link_status(sc); | vtnet_update_link_status(sc); | ||||
vtnet_enable_interrupts(sc); | |||||
callout_reset(&sc->vtnet_tick_ch, hz, vtnet_tick, sc); | callout_reset(&sc->vtnet_tick_ch, hz, vtnet_tick, sc); | ||||
#ifdef DEV_NETMAP | #ifdef DEV_NETMAP | ||||
/* Re-enable txsync/rxsync. */ | /* Re-enable txsync/rxsync. */ | ||||
netmap_enable_all_rings(ifp); | netmap_enable_all_rings(ifp); | ||||
#endif /* DEV_NETMAP */ | #endif /* DEV_NETMAP */ | ||||
return; | |||||
fail: | |||||
vtnet_stop(sc); | |||||
} | } | ||||
static void | static void | ||||
vtnet_init(void *xsc) | vtnet_init(void *xsc) | ||||
{ | { | ||||
struct vtnet_softc *sc; | struct vtnet_softc *sc; | ||||
sc = xsc; | sc = xsc; | ||||
Show All 18 Lines | |||||
static void | static void | ||||
vtnet_exec_ctrl_cmd(struct vtnet_softc *sc, void *cookie, | vtnet_exec_ctrl_cmd(struct vtnet_softc *sc, void *cookie, | ||||
struct sglist *sg, int readable, int writable) | struct sglist *sg, int readable, int writable) | ||||
{ | { | ||||
struct virtqueue *vq; | struct virtqueue *vq; | ||||
vq = sc->vtnet_ctrl_vq; | vq = sc->vtnet_ctrl_vq; | ||||
VTNET_CORE_LOCK_ASSERT(sc); | |||||
MPASS(sc->vtnet_flags & VTNET_FLAG_CTRL_VQ); | MPASS(sc->vtnet_flags & VTNET_FLAG_CTRL_VQ); | ||||
VTNET_CORE_LOCK_ASSERT(sc); | |||||
if (!virtqueue_empty(vq)) | if (!virtqueue_empty(vq)) | ||||
return; | return; | ||||
/* | /* | ||||
* Poll for the response, but the command is likely completed before | * Poll for the response, but the command is likely completed before | ||||
* returning from the notify. | * returning from the notify. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 442 Lines • ▼ Show 20 Lines | if (sc->vtnet_flags & VTNET_FLAG_MAC) { | ||||
sc->vtnet_hwaddr[0] = 0xB2; | sc->vtnet_hwaddr[0] = 0xB2; | ||||
arc4rand(&sc->vtnet_hwaddr[1], ETHER_ADDR_LEN - 1, 0); | arc4rand(&sc->vtnet_hwaddr[1], ETHER_ADDR_LEN - 1, 0); | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
vtnet_set_macaddr(struct vtnet_softc *sc) | vtnet_set_macaddr(struct vtnet_softc *sc) | ||||
{ | { | ||||
device_t dev; | |||||
int error; | int error; | ||||
dev = sc->vtnet_dev; | |||||
if (sc->vtnet_flags & VTNET_FLAG_CTRL_MAC) { | if (sc->vtnet_flags & VTNET_FLAG_CTRL_MAC) { | ||||
error = vtnet_ctrl_mac_cmd(sc, sc->vtnet_hwaddr); | error = vtnet_ctrl_mac_cmd(sc, sc->vtnet_hwaddr); | ||||
if (error) | if (error) | ||||
if_printf(sc->vtnet_ifp, "unable to set MAC address\n"); | device_printf(dev, "unable to set MAC address\n"); | ||||
return; | return; | ||||
} | } | ||||
/* MAC in config is read-only in modern VirtIO. */ | /* MAC in config is read-only in modern VirtIO. */ | ||||
if (!vtnet_modern(sc) && sc->vtnet_flags & VTNET_FLAG_MAC) { | if (!vtnet_modern(sc) && sc->vtnet_flags & VTNET_FLAG_MAC) { | ||||
for (int i = 0; i < ETHER_ADDR_LEN; i++) { | for (int i = 0; i < ETHER_ADDR_LEN; i++) { | ||||
virtio_write_dev_config_1(sc->vtnet_dev, | virtio_write_dev_config_1(dev, | ||||
offsetof(struct virtio_net_config, mac) + i, | offsetof(struct virtio_net_config, mac) + i, | ||||
sc->vtnet_hwaddr[i]); | sc->vtnet_hwaddr[i]); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
vtnet_attached_set_macaddr(struct vtnet_softc *sc) | vtnet_attached_set_macaddr(struct vtnet_softc *sc) | ||||
▲ Show 20 Lines • Show All 383 Lines • Show Last 20 Lines |