aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBryan Venteicher <bryanv@FreeBSD.org>2021-01-19 04:55:25 +0000
committerBryan Venteicher <bryanv@FreeBSD.org>2021-01-19 04:55:25 +0000
commite36a6b1b1f390be3191d51c11bfcf1afbad55c41 (patch)
treed322b2e27435cae35b3c238c7a5d7ed5933f6c62
parentdc9029d863dcc48efebb6a31a25553a7459132aa (diff)
downloadsrc-e36a6b1b1f390be3191d51c11bfcf1afbad55c41.tar.gz
src-e36a6b1b1f390be3191d51c11bfcf1afbad55c41.zip
if_vtnet: Add support for CTRL_GUEST_OFFLOADS feature
This allows the Rx checksum and LRO to be modified without a full reinit of the device. Remove IFCAP_RXCSUM_IPV6 from the interface capabilities since in VirtIO Rx checksums are just enabled or disabled for all protocols. Properly update IFCAP_LRO if LRO is becomes disabled when Rx checksums are disabled. Reviewed by: grehan (mentor) Differential Revision: https://reviews.freebsd.org/D27916
-rw-r--r--sys/dev/virtio/network/if_vtnet.c157
-rw-r--r--sys/dev/virtio/network/if_vtnetvar.h1
2 files changed, 127 insertions, 31 deletions
diff --git a/sys/dev/virtio/network/if_vtnet.c b/sys/dev/virtio/network/if_vtnet.c
index 876dc1e152c4..642c513ff0b3 100644
--- a/sys/dev/virtio/network/if_vtnet.c
+++ b/sys/dev/virtio/network/if_vtnet.c
@@ -193,6 +193,7 @@ static int vtnet_init_rx_queues(struct vtnet_softc *);
static int vtnet_init_tx_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_update_rx_offloads(struct vtnet_softc *);
static int vtnet_reinit(struct vtnet_softc *);
static void vtnet_init_locked(struct vtnet_softc *, int);
static void vtnet_init(void *);
@@ -201,6 +202,7 @@ static void vtnet_free_ctrl_vq(struct vtnet_softc *);
static void vtnet_exec_ctrl_cmd(struct vtnet_softc *, void *,
struct sglist *, int, int);
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_rx_cmd(struct vtnet_softc *, uint8_t, int);
static int vtnet_set_promisc(struct vtnet_softc *, int);
@@ -1045,7 +1047,11 @@ vtnet_setup_interface(struct vtnet_softc *sc)
}
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",
vtnet_fixup_needs_csum) != 0)
@@ -1215,10 +1221,11 @@ static int
vtnet_ioctl_ifcap(struct vtnet_softc *sc, struct ifreq *ifr)
{
struct ifnet *ifp;
- int mask, reinit;
+ int mask, reinit, update;
ifp = sc->vtnet_ifp;
mask = (ifr->ifr_reqcap & ifp->if_capabilities) ^ ifp->if_capenable;
+ reinit = update = 0;
VTNET_CORE_LOCK_ASSERT(sc);
@@ -1231,10 +1238,15 @@ vtnet_ioctl_ifcap(struct vtnet_softc *sc, struct ifreq *ifr)
if (mask & IFCAP_TSO6)
ifp->if_capenable ^= IFCAP_TSO6;
- if (mask & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6 | IFCAP_LRO |
- IFCAP_VLAN_HWFILTER)) {
- /* These Rx features require us to renegotiate. */
- reinit = 1;
+ if (mask & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6 | IFCAP_LRO)) {
+ /*
+ * 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;
if (mask & IFCAP_RXCSUM)
ifp->if_capenable ^= IFCAP_RXCSUM;
@@ -1242,19 +1254,41 @@ vtnet_ioctl_ifcap(struct vtnet_softc *sc, struct ifreq *ifr)
ifp->if_capenable ^= IFCAP_RXCSUM_IPV6;
if (mask & 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)
ifp->if_capenable ^= IFCAP_VLAN_HWFILTER;
- } else
- reinit = 0;
+ }
if (mask & IFCAP_VLAN_HWTSO)
ifp->if_capenable ^= IFCAP_VLAN_HWTSO;
if (mask & IFCAP_VLAN_HWTAGGING)
ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
- if (reinit && (ifp->if_drv_flags & IFF_DRV_RUNNING)) {
- ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
- vtnet_init_locked(sc, 0);
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ if (reinit) {
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ vtnet_init_locked(sc, 0);
+ } else if (update)
+ vtnet_update_rx_offloads(sc);
}
return (0);
@@ -3023,28 +3057,14 @@ vtnet_virtio_reinit(struct vtnet_softc *sc)
* via if_capenable and if_hwassist.
*/
- if (ifp->if_capabilities & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6)) {
- /*
- * 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_capenable & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6)) == 0)
+ features &= ~(VIRTIO_NET_F_GUEST_CSUM | VTNET_LRO_FEATURES);
- if (ifp->if_capabilities & IFCAP_LRO) {
- if ((ifp->if_capenable & IFCAP_LRO) == 0)
- features &= ~VTNET_LRO_FEATURES;
- }
+ if ((ifp->if_capenable & IFCAP_LRO) == 0)
+ features &= ~VTNET_LRO_FEATURES;
- if (ifp->if_capabilities & IFCAP_VLAN_HWFILTER) {
- if ((ifp->if_capenable & IFCAP_VLAN_HWFILTER) == 0)
- features &= ~VIRTIO_NET_F_CTRL_VLAN;
- }
+ if ((ifp->if_capenable & IFCAP_VLAN_HWFILTER) == 0)
+ features &= ~VIRTIO_NET_F_CTRL_VLAN;
error = virtio_reinit(dev, features);
if (error) {
@@ -3172,6 +3192,47 @@ vtnet_set_active_vq_pairs(struct vtnet_softc *sc)
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
vtnet_reinit(struct vtnet_softc *sc)
{
@@ -3338,6 +3399,40 @@ vtnet_ctrl_mac_cmd(struct vtnet_softc *sc, uint8_t *hwaddr)
}
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));
+ 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_mq_cmd(struct vtnet_softc *sc, uint16_t npairs)
{
struct sglist_seg segs[3];
diff --git a/sys/dev/virtio/network/if_vtnetvar.h b/sys/dev/virtio/network/if_vtnetvar.h
index 0fd1238b2dcb..1f94325604fe 100644
--- a/sys/dev/virtio/network/if_vtnetvar.h
+++ b/sys/dev/virtio/network/if_vtnetvar.h
@@ -293,6 +293,7 @@ CTASSERT(sizeof(struct vtnet_mac_filter) <= PAGE_SIZE);
#define VTNET_COMMON_FEATURES \
(VIRTIO_NET_F_MAC | \
VIRTIO_NET_F_STATUS | \
+ VIRTIO_NET_F_CTRL_GUEST_OFFLOADS | \
VIRTIO_NET_F_MTU | \
VIRTIO_NET_F_CTRL_VQ | \
VIRTIO_NET_F_CTRL_RX | \