diff mbox series

[ovs-dev,v1,02/23] netdev-offload-dpdk: Use per-netdev offload metadata

Message ID 6c66b5bb17c5be666b555b1ee47968b4e15dff2a.1612968146.git.grive@u256.net
State New
Headers show
Series dpif-netdev: Parallel offload processing | expand

Commit Message

Gaƫtan Rivet Feb. 10, 2021, 2:57 p.m. UTC
Add a per-netdev offload data field as part of netdev hw_info structure.
Use this field in netdev-offload-dpdk to map offload metadata (ufid to
rte_flow). Use flow API deinit ops to destroy the per-netdev metadata
when deallocating a netdev. Use RCU primitives to ensure coherency
during port deletion.

Signed-off-by: Gaetan Rivet <grive@u256.net>
Reviewed-by: Eli Britstein <elibr@nvidia.com>
---
 lib/netdev-offload-dpdk.c | 131 ++++++++++++++++++++++++++++++++------
 lib/netdev-offload.h      |   2 +
 2 files changed, 114 insertions(+), 19 deletions(-)
diff mbox series

Patch

diff --git a/lib/netdev-offload-dpdk.c b/lib/netdev-offload-dpdk.c
index f2413f5be..47e56af23 100644
--- a/lib/netdev-offload-dpdk.c
+++ b/lib/netdev-offload-dpdk.c
@@ -26,6 +26,7 @@ 
 #include "netdev-provider.h"
 #include "openvswitch/match.h"
 #include "openvswitch/vlog.h"
+#include "ovs-rcu.h"
 #include "packets.h"
 #include "uuid.h"
 
@@ -52,7 +53,6 @@  static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(100, 5);
 /*
  * A mapping from ufid to dpdk rte_flow.
  */
-static struct cmap ufid_to_rte_flow = CMAP_INITIALIZER;
 
 struct ufid_to_rte_flow_data {
     struct cmap_node node;
@@ -63,14 +63,81 @@  struct ufid_to_rte_flow_data {
     struct dpif_flow_stats stats;
 };
 
+struct netdev_offload_dpdk_data {
+    struct cmap ufid_to_rte_flow;
+};
+
+static int
+offload_data_init(struct netdev *netdev)
+{
+    struct netdev_offload_dpdk_data *data;
+
+    data = xzalloc(sizeof *data);
+    cmap_init(&data->ufid_to_rte_flow);
+
+    ovsrcu_set(&netdev->hw_info.offload_data, (void *) data);
+
+    return 0;
+}
+
+static void
+offload_data_destroy__(struct netdev_offload_dpdk_data *data)
+{
+    free(data);
+}
+
+static void
+offload_data_destroy(struct netdev *netdev)
+{
+    struct netdev_offload_dpdk_data *data;
+    struct ufid_to_rte_flow_data *node;
+
+    data = (struct netdev_offload_dpdk_data *)
+        ovsrcu_get(void *, &netdev->hw_info.offload_data);
+    if (data == NULL) {
+        return;
+    }
+
+    if (!cmap_is_empty(&data->ufid_to_rte_flow)) {
+        VLOG_ERR("Incomplete flush: %s contains rte_flow elements",
+                 netdev_get_name(netdev));
+    }
+
+    CMAP_FOR_EACH (node, node, &data->ufid_to_rte_flow) {
+        ovsrcu_postpone(free, node);
+    }
+
+    cmap_destroy(&data->ufid_to_rte_flow);
+    ovsrcu_postpone(offload_data_destroy__, data);
+
+    ovsrcu_set(&netdev->hw_info.offload_data, NULL);
+}
+
+static struct cmap *
+offload_data_map(struct netdev *netdev)
+{
+    struct netdev_offload_dpdk_data *data;
+
+    data = (struct netdev_offload_dpdk_data *)
+        ovsrcu_get(void *, &netdev->hw_info.offload_data);
+
+    return data ? &data->ufid_to_rte_flow : NULL;
+}
+
 /* Find rte_flow with @ufid. */
 static struct ufid_to_rte_flow_data *
-ufid_to_rte_flow_data_find(const ovs_u128 *ufid, bool warn)
+ufid_to_rte_flow_data_find(struct netdev *netdev,
+                           const ovs_u128 *ufid, bool warn)
 {
     size_t hash = hash_bytes(ufid, sizeof *ufid, 0);
     struct ufid_to_rte_flow_data *data;
+    struct cmap *map = offload_data_map(netdev);
+
+    if (!map) {
+        return NULL;
+    }
 
-    CMAP_FOR_EACH_WITH_HASH (data, node, hash, &ufid_to_rte_flow) {
+    CMAP_FOR_EACH_WITH_HASH (data, node, hash, map) {
         if (ovs_u128_equals(*ufid, data->ufid)) {
             return data;
         }
@@ -85,12 +152,19 @@  ufid_to_rte_flow_data_find(const ovs_u128 *ufid, bool warn)
 }
 
 static inline struct ufid_to_rte_flow_data *
-ufid_to_rte_flow_associate(const ovs_u128 *ufid, struct netdev *netdev,
+ufid_to_rte_flow_associate(struct netdev *netdev, const ovs_u128 *ufid,
                            struct rte_flow *rte_flow, bool actions_offloaded)
 {
     size_t hash = hash_bytes(ufid, sizeof *ufid, 0);
-    struct ufid_to_rte_flow_data *data = xzalloc(sizeof *data);
+    struct cmap *map = offload_data_map(netdev);
     struct ufid_to_rte_flow_data *data_prev;
+    struct ufid_to_rte_flow_data *data;
+
+    if (!map) {
+        return NULL;
+    }
+
+    data = xzalloc(sizeof *data);
 
     /*
      * We should not simply overwrite an existing rte flow.
@@ -98,7 +172,7 @@  ufid_to_rte_flow_associate(const ovs_u128 *ufid, struct netdev *netdev,
      * Thus, if following assert triggers, something is wrong:
      * the rte_flow is not destroyed.
      */
-    data_prev = ufid_to_rte_flow_data_find(ufid, false);
+    data_prev = ufid_to_rte_flow_data_find(netdev, ufid, false);
     if (data_prev) {
         ovs_assert(data_prev->rte_flow == NULL);
     }
@@ -108,8 +182,7 @@  ufid_to_rte_flow_associate(const ovs_u128 *ufid, struct netdev *netdev,
     data->rte_flow = rte_flow;
     data->actions_offloaded = actions_offloaded;
 
-    cmap_insert(&ufid_to_rte_flow,
-                CONST_CAST(struct cmap_node *, &data->node), hash);
+    cmap_insert(map, CONST_CAST(struct cmap_node *, &data->node), hash);
     return data;
 }
 
@@ -117,9 +190,13 @@  static inline void
 ufid_to_rte_flow_disassociate(struct ufid_to_rte_flow_data *data)
 {
     size_t hash = hash_bytes(&data->ufid, sizeof data->ufid, 0);
+    struct cmap *map = offload_data_map(data->netdev);
+
+    if (!map) {
+        return;
+    }
 
-    cmap_remove(&ufid_to_rte_flow,
-                CONST_CAST(struct cmap_node *, &data->node), hash);
+    cmap_remove(map, CONST_CAST(struct cmap_node *, &data->node), hash);
     netdev_close(data->netdev);
     ovsrcu_postpone(free, data);
 }
@@ -1421,7 +1498,7 @@  netdev_offload_dpdk_add_flow(struct netdev *netdev,
     if (!flow) {
         goto out;
     }
-    flows_data = ufid_to_rte_flow_associate(ufid, netdev, flow,
+    flows_data = ufid_to_rte_flow_associate(netdev, ufid, flow,
                                             actions_offloaded);
     VLOG_DBG("%s: installed flow %p by ufid "UUID_FMT,
              netdev_get_name(netdev), flow, UUID_ARGS((struct uuid *)ufid));
@@ -1478,7 +1555,7 @@  netdev_offload_dpdk_flow_put(struct netdev *netdev, struct match *match,
      * Here destroy the old rte flow first before adding a new one.
      * Keep the stats for the newly created rule.
      */
-    rte_flow_data = ufid_to_rte_flow_data_find(ufid, false);
+    rte_flow_data = ufid_to_rte_flow_data_find(netdev, ufid, false);
     if (rte_flow_data && rte_flow_data->rte_flow) {
         old_stats = rte_flow_data->stats;
         modification = true;
@@ -1509,7 +1586,7 @@  netdev_offload_dpdk_flow_del(struct netdev *netdev OVS_UNUSED,
 {
     struct ufid_to_rte_flow_data *rte_flow_data;
 
-    rte_flow_data = ufid_to_rte_flow_data_find(ufid, true);
+    rte_flow_data = ufid_to_rte_flow_data_find(netdev, ufid, true);
     if (!rte_flow_data || !rte_flow_data->rte_flow) {
         return -1;
     }
@@ -1523,7 +1600,21 @@  netdev_offload_dpdk_flow_del(struct netdev *netdev OVS_UNUSED,
 static int
 netdev_offload_dpdk_init_flow_api(struct netdev *netdev)
 {
-    return netdev_dpdk_flow_api_supported(netdev) ? 0 : EOPNOTSUPP;
+    int ret = EOPNOTSUPP;
+
+    if (netdev_dpdk_flow_api_supported(netdev)) {
+        ret = offload_data_init(netdev);
+    }
+
+    return ret;
+}
+
+static void
+netdev_offload_dpdk_deinit_flow_api(struct netdev *netdev)
+{
+    if (netdev_dpdk_flow_api_supported(netdev)) {
+        offload_data_destroy(netdev);
+    }
 }
 
 static int
@@ -1540,7 +1631,7 @@  netdev_offload_dpdk_flow_get(struct netdev *netdev,
     struct rte_flow_error error;
     int ret = 0;
 
-    rte_flow_data = ufid_to_rte_flow_data_find(ufid, false);
+    rte_flow_data = ufid_to_rte_flow_data_find(netdev, ufid, false);
     if (!rte_flow_data || !rte_flow_data->rte_flow) {
         ret = -1;
         goto out;
@@ -1575,13 +1666,14 @@  out:
 static int
 netdev_offload_dpdk_flow_flush(struct netdev *netdev)
 {
+    struct cmap *map = offload_data_map(netdev);
     struct ufid_to_rte_flow_data *data;
 
-    CMAP_FOR_EACH (data, node, &ufid_to_rte_flow) {
-        if (data->netdev != netdev) {
-            continue;
-        }
+    if (!map) {
+        return -1;
+    }
 
+    CMAP_FOR_EACH (data, node, map) {
         netdev_offload_dpdk_flow_destroy(data);
     }
 
@@ -1593,6 +1685,7 @@  const struct netdev_flow_api netdev_offload_dpdk = {
     .flow_put = netdev_offload_dpdk_flow_put,
     .flow_del = netdev_offload_dpdk_flow_del,
     .init_flow_api = netdev_offload_dpdk_init_flow_api,
+    .deinit_flow_api = netdev_offload_dpdk_deinit_flow_api,
     .flow_get = netdev_offload_dpdk_flow_get,
     .flow_flush = netdev_offload_dpdk_flow_flush,
 };
diff --git a/lib/netdev-offload.h b/lib/netdev-offload.h
index 18b48790f..d820e23ed 100644
--- a/lib/netdev-offload.h
+++ b/lib/netdev-offload.h
@@ -20,6 +20,7 @@ 
 
 #include "openvswitch/netdev.h"
 #include "openvswitch/types.h"
+#include "ovs-rcu.h"
 #include "packets.h"
 #include "flow.h"
 
@@ -45,6 +46,7 @@  struct netdev_hw_info {
     bool oor;		/* Out of Offload Resources ? */
     int offload_count;  /* Pending (non-offloaded) flow count */
     int pending_count;  /* Offloaded flow count */
+    OVSRCU_TYPE(void *) offload_data; /* Offload metadata. */
 };
 
 enum hw_info_type {