Hi David,

On 9/20/22 17:50, David Marchand wrote:
I did not finish reviewing the rest, but I have an additional comment
on the virtio/vhost features negotiations.


On Wed, Jul 13, 2022 at 8:45 PM Mike Pattrick <[email protected]> wrote:
diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
index 811c62a87..a69bab829 100644
--- a/lib/netdev-dpdk.c
+++ b/lib/netdev-dpdk.c

[snip]

@@ -3938,6 +3980,37 @@ new_device(int vid)
                  dev->vhost_reconfigured = true;
              }

+            uint64_t features;
+            if (rte_vhost_get_negotiated_features(vid, &features)) {
+                VLOG_INFO("Error checking guest features for "
+                          "vHost Device '%s'", dev->vhost_id);
+            } else {
+                if (features & (1ULL << VIRTIO_NET_F_GUEST_CSUM)) {
+                    dev->hw_ol_features |= NETDEV_TX_TCP_CKSUM_OFFLOAD;
+                    dev->hw_ol_features |= NETDEV_TX_UDP_CKSUM_OFFLOAD;
+                    dev->hw_ol_features |= NETDEV_TX_SCTP_CKSUM_OFFLOAD;
+                }
+
+                if (userspace_tso_enabled()) {
+                    if (features & (1ULL << VIRTIO_NET_F_GUEST_TSO4)
+                        && features & (1ULL << VIRTIO_NET_F_GUEST_TSO6)) {
+
+                        dev->hw_ol_features |= NETDEV_TX_TSO_OFFLOAD;
+                        VLOG_DBG("%s: TSO enabled on vhost port",
+                                 netdev_get_name(&dev->up));
+                    } else {
+                        VLOG_WARN("%s: Tx TSO offload is not supported.",
+                                  netdev_get_name(&dev->up));
+                    }
+                }
+            }
+
+            /* There is no support in virtio net to offload IPv4 csum,
+             * but the vhost library handles IPv4 csum offloading fine. */
+            dev->hw_ol_features |= NETDEV_TX_IPV4_CKSUM_OFFLOAD;
+
+            netdev_dpdk_update_netdev_flags(dev);
+
              ovsrcu_index_set(&dev->vid, vid);
              exists = true;

@@ -4001,6 +4074,14 @@ destroy_device(int vid)
                     dev->up.n_rxq * sizeof *dev->vhost_rxq_enabled);
              netdev_dpdk_txq_map_clear(dev);

+            /* Clear offload capabilities before next new_device. */
+            dev->hw_ol_features &= ~NETDEV_TX_IPV4_CKSUM_OFFLOAD;
+            dev->hw_ol_features &= ~NETDEV_TX_TCP_CKSUM_OFFLOAD;
+            dev->hw_ol_features &= ~NETDEV_TX_UDP_CKSUM_OFFLOAD;
+            dev->hw_ol_features &= ~NETDEV_TX_SCTP_CKSUM_OFFLOAD;
+            dev->hw_ol_features &= ~NETDEV_TX_TSO_OFFLOAD;
+            netdev_dpdk_update_netdev_flags(dev);
+
              netdev_change_seq_changed(&dev->up);
              ovs_mutex_unlock(&dev->mutex);
              exists = true;
@@ -4938,22 +5019,7 @@ netdev_dpdk_reconfigure(struct netdev *netdev)
      }

      err = dpdk_eth_dev_init(dev);
-
-    if (dev->hw_ol_features & NETDEV_TX_IPV4_CKSUM_OFFLOAD) {
-        netdev->ol_flags |= NETDEV_TX_OFFLOAD_IPV4_CKSUM;
-    } else {
-        netdev->ol_flags &= ~NETDEV_TX_OFFLOAD_IPV4_CKSUM;
-    }
-
-    if (dev->hw_ol_features & NETDEV_TX_TSO_OFFLOAD) {
-        netdev->ol_flags |= NETDEV_TX_OFFLOAD_TCP_TSO;
-        netdev->ol_flags |= NETDEV_TX_OFFLOAD_TCP_CKSUM;
-        netdev->ol_flags |= NETDEV_TX_OFFLOAD_UDP_CKSUM;
-        netdev->ol_flags |= NETDEV_TX_OFFLOAD_IPV4_CKSUM;
-        if (dev->hw_ol_features & NETDEV_TX_SCTP_CHECKSUM_OFFLOAD) {
-            netdev->ol_flags |= NETDEV_TX_OFFLOAD_SCTP_CKSUM;
-        }
-    }
+    netdev_dpdk_update_netdev_flags(dev);

      /* If both requested and actual hwaddr were previously
       * unset (initialized to 0), then first device init above
@@ -4995,11 +5061,6 @@ dpdk_vhost_reconfigure_helper(struct netdev_dpdk *dev)
          dev->tx_q[0].map = 0;
      }

-    if (userspace_tso_enabled()) {
-        dev->hw_ol_features |= NETDEV_TX_TSO_OFFLOAD;
-        VLOG_DBG("%s: TSO enabled on vhost port", netdev_get_name(&dev->up));
-    }
-
      netdev_dpdk_remap_txqs(dev);

      if (netdev_dpdk_get_vid(dev) >= 0) {
@@ -5020,6 +5081,8 @@ dpdk_vhost_reconfigure_helper(struct netdev_dpdk *dev)
          }
      }

+    netdev_dpdk_update_netdev_flags(dev);
+
      return 0;
  }

@@ -5042,7 +5105,7 @@ netdev_dpdk_vhost_client_reconfigure(struct netdev 
*netdev)
      struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);
      int err;
      uint64_t vhost_flags = 0;
-    uint64_t vhost_unsup_flags;
+    uint64_t vhost_unsup_flags = 0;

      ovs_mutex_lock(&dev->mutex);

@@ -5094,19 +5157,17 @@ netdev_dpdk_vhost_client_reconfigure(struct netdev 
*netdev)
              goto unlock;
          }

+        netdev->ol_flags |= NETDEV_TX_OFFLOAD_TCP_CKSUM;
+        netdev->ol_flags |= NETDEV_TX_OFFLOAD_UDP_CKSUM;
+        netdev->ol_flags |= NETDEV_TX_OFFLOAD_SCTP_CKSUM;
+        netdev->ol_flags |= NETDEV_TX_OFFLOAD_IPV4_CKSUM;

We don't need ol_flags updates here.
Before new_device, we don't know what the guest supports, so no need
to report some tx capability to OVS upper layers.
ol_flags are set once and for all during new_device.


+
          if (userspace_tso_enabled()) {
              netdev->ol_flags |= NETDEV_TX_OFFLOAD_TCP_TSO;

Idem.


-            netdev->ol_flags |= NETDEV_TX_OFFLOAD_TCP_CKSUM;
-            netdev->ol_flags |= NETDEV_TX_OFFLOAD_UDP_CKSUM;
-            netdev->ol_flags |= NETDEV_TX_OFFLOAD_SCTP_CKSUM;
-            netdev->ol_flags |= NETDEV_TX_OFFLOAD_IPV4_CKSUM;
              vhost_unsup_flags = 1ULL << VIRTIO_NET_F_HOST_ECN
                                  | 1ULL << VIRTIO_NET_F_HOST_UFO;
-        } else {
-            /* This disables checksum offloading and all the features
-             * that depends on it (TSO, UFO, ECN) according to virtio
-             * specification. */
-            vhost_unsup_flags = 1ULL << VIRTIO_NET_F_CSUM;

So far, VIRTIO_NET_F_CSUM served as a barrier, preventing a guest from
negotiating TSO virtio features bits (HOST_TSO4, HOST_TSO6 or
HOST_UFO).

*In theory*, if we go with this patch and just remove
VIRTIO_NET_F_CSUM in the non tso case, I would have expected a (newly
started) guest see those HOST_TSO* features as available, since OVS is
not disabling them via rte_vhost_driver_disable_features().

*But in practice*, on my RHEL8, a guest only negotiates 0x17020a783
features, iow:
VIRTIO_NET_F_CSUM
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
VIRTIO_NET_F_HOST_ECN
VIRTIO_NET_F_MRG_RXBUF
VIRTIO_NET_F_GUEST_ANNOUNCE
VIRTIO_RING_F_INDIRECT_DESC
VIRTIO_RING_F_EVENT_IDX
! VHOST_USER_F_PROTOCOL_FEATURES
VIRTIO_F_VERSION_1

After scratching my head for some time, I understand that this
behavior comes from the vhost library filtering features based on
vhost-user flags passed at register:
https://git.dpdk.org/dpdk-stable/tree/lib/vhost/socket.c?h=21.11#n917

Since OVS did not enable extbuf support (which is under tso check),
the vhost library implictly disables those HOST_TSO* virtio features.

This is a bit hard to understand unless you know the internals of the
vhost library.
I suggest updating this patch to help future people looking into this code.

Yes, it is not straightforward to understand, I thought the existing logging in rte_vhost_driver_register() would be enough:

VHOST_LOG_CONFIG(path, INFO, "Linear buffers requested without external buffers,\n"); VHOST_LOG_CONFIG(path, INFO, "disabling host segmentation offloading support\n");

But having an explicit comment here would help to get it without having
to run it.


One solution would be to add comments in the code explaining why a
guest can't see HOST_TSO* features.
But I recommend OVS explicitly disables those HOST_TSO* and HOST_UFO
features (something like diff below).


+            VLOG_DBG("%s: TSO enabled on vhost port",
+                     netdev_get_name(&dev->up));
          }

          err = rte_vhost_driver_disable_features(dev->vhost_id,

I have been running with the diff below on top of this patch (inline
patch might get broken because of gmail.. sorry).

diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
index 069107d48..d3c9be045 100644
--- a/lib/netdev-dpdk.c
+++ b/lib/netdev-dpdk.c
@@ -4007,6 +4007,7 @@ new_device(int vid)
          ovs_mutex_lock(&dev->mutex);
          if (nullable_string_is_equal(ifname, dev->vhost_id)) {
              uint32_t qp_num = rte_vhost_get_vring_num(vid) / VIRTIO_QNUM;
+            uint64_t features;

              /* Get NUMA information */
              newnode = rte_vhost_get_numa_node(vid);
@@ -4031,7 +4032,6 @@ new_device(int vid)
                  dev->vhost_reconfigured = true;
              }

-            uint64_t features;
              if (rte_vhost_get_negotiated_features(vid, &features)) {
                  VLOG_INFO("Error checking guest features for "
                            "vHost Device '%s'", dev->vhost_id);
@@ -5155,8 +5155,6 @@ netdev_dpdk_vhost_client_reconfigure(struct
netdev *netdev)
  {
      struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);
      int err;
-    uint64_t vhost_flags = 0;
-    uint64_t vhost_unsup_flags = 0;

      ovs_mutex_lock(&dev->mutex);

@@ -5166,6 +5164,9 @@ netdev_dpdk_vhost_client_reconfigure(struct
netdev *netdev)
       *  2. A path has been specified.
       */
      if (!(dev->vhost_driver_flags & RTE_VHOST_USER_CLIENT) && dev->vhost_id) {
+        uint64_t virtio_unsup_features;
+        uint64_t vhost_flags = 0;
+
          /* Register client-mode device. */
          vhost_flags |= RTE_VHOST_USER_CLIENT;

@@ -5208,21 +5209,21 @@ netdev_dpdk_vhost_client_reconfigure(struct
netdev *netdev)
              goto unlock;
          }

-        netdev->ol_flags |= NETDEV_TX_OFFLOAD_TCP_CKSUM;
-        netdev->ol_flags |= NETDEV_TX_OFFLOAD_UDP_CKSUM;
-        netdev->ol_flags |= NETDEV_TX_OFFLOAD_SCTP_CKSUM;
-        netdev->ol_flags |= NETDEV_TX_OFFLOAD_IPV4_CKSUM;
-
          if (userspace_tso_enabled()) {
-            netdev->ol_flags |= NETDEV_TX_OFFLOAD_TCP_TSO;
-            vhost_unsup_flags = 1ULL << VIRTIO_NET_F_HOST_ECN
-                                | 1ULL << VIRTIO_NET_F_HOST_UFO;
-            VLOG_DBG("%s: TSO enabled on vhost port",
-                     netdev_get_name(&dev->up));
+            virtio_unsup_features = 1ULL << VIRTIO_NET_F_HOST_ECN
+                                   | 1ULL << VIRTIO_NET_F_HOST_UFO;
+        } else {
+            /* Advertise checksum offloading to the guest, but explicitly
+             * disable TSO and friends.
+             * NOTE: we can't disable HOST_ECN which may have been wrongly
+             * negotiated by a running guest. */
+            virtio_unsup_features = 1ULL << VIRTIO_NET_F_HOST_TSO4
+                                   | 1ULL << VIRTIO_NET_F_HOST_TSO6
+                                   | 1ULL << VIRTIO_NET_F_HOST_UFO;
          }

          err = rte_vhost_driver_disable_features(dev->vhost_id,
-                                                vhost_unsup_flags);
+                                                virtio_unsup_features);
          if (err) {
              VLOG_ERR("rte_vhost_driver_disable_features failed for "
                       "vhost user client port: %s\n", dev->up.name);



I think above patch makes sense.

Thanks,
Maxime

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to