diff mbox series

[ovs-dev,v25,3/8] netdev-offload-tc: Introduce group ID management API

Message ID 20230329072059.78569-4-cmi@nvidia.com
State Changes Requested
Headers show
Series Add offload support for sFlow | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test success github build: passed
ovsrobot/intel-ovs-compilation success test: success

Commit Message

Chris Mi March 29, 2023, 7:20 a.m. UTC
When offloading sample action to TC, userspace creates a unique ID
to map sample action and tunnel info and passes this ID to kernel
instead of the sample info. Kernel will send this ID and sampled
packet to userspace. Using the ID, userspace can recover the sample
info and send sampled packet to the right sample monitoring host.

Signed-off-by: Chris Mi <cmi@nvidia.com>
Reviewed-by: Roi Dayan <roid@nvidia.com>
---
 lib/netdev-offload-tc.c | 118 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 118 insertions(+)
diff mbox series

Patch

diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c
index 247c1ff8b..6ac40b663 100644
--- a/lib/netdev-offload-tc.c
+++ b/lib/netdev-offload-tc.c
@@ -41,6 +41,7 @@ 
 #include "unaligned.h"
 #include "util.h"
 #include "dpif-provider.h"
+#include "cmap.h"
 
 VLOG_DEFINE_THIS_MODULE(netdev_offload_tc);
 
@@ -103,6 +104,107 @@  static void parse_tc_flower_to_stats(struct tc_flower *flower,
 static int get_ufid_adjust_stats(const ovs_u128 *ufid,
                                  struct dpif_flow_stats *stats);
 
+/* When offloading sample action to TC, userspace creates a unique ID
+ * to map sample action and tunnel info and passes this ID to kernel
+ * instead of the sample info. Kernel will send this ID and sampled
+ * packet to userspace. Using the ID, userspace can recover the sample
+ * info and send sampled packet to the right sample monitoring host. */
+struct offload_sample {
+    uint16_t type;           /* enum user_action_cookie_type. */
+    struct nlattr *action;   /* Sample action. Used in flow_get. */
+    struct nlattr *userdata; /* Struct user_action_cookie. */
+    struct nlattr *actions;  /* All actions to get output tunnel. */
+    struct flow_tnl *tunnel; /* Input tunnel. */
+};
+
+/* This maps a sample group ID to struct offload_sample. */
+struct sgid_node {
+    struct cmap_node id_node;
+    uint32_t id;
+    struct offload_sample sample;
+};
+
+static struct ovs_mutex sgid_lock = OVS_MUTEX_INITIALIZER;
+static struct cmap sgid_map = CMAP_INITIALIZER;
+static struct id_pool *sample_group_ids OVS_GUARDED_BY(sgid_lock);
+
+static void
+sgid_node_free(struct sgid_node *node)
+{
+    if (node) {
+        free(node->sample.tunnel);
+        free(node->sample.action);
+        free(node->sample.actions);
+        free(node->sample.userdata);
+        free(node);
+    }
+}
+
+static struct sgid_node *
+sgid_find(uint32_t id)
+{
+    const struct cmap_node *node = cmap_find(&sgid_map, id);
+
+    return node ? CONTAINER_OF(node, struct sgid_node, id_node) : NULL;
+}
+
+static void
+offload_sample_clone(struct offload_sample *new,
+                     const struct offload_sample *old)
+{
+    new->type = old->type;
+    new->action = xmemdup(old->action, old->action->nla_len);
+    new->actions = old->actions
+                   ? xmemdup(old->actions, old->actions->nla_len)
+                   : NULL;
+    new->userdata = xmemdup(old->userdata, old->userdata->nla_len);
+    new->tunnel = old->tunnel
+                  ? xmemdup(old->tunnel, sizeof *old->tunnel)
+                  : NULL;
+}
+
+/* Allocate a unique group id for the given set of flow metadata. The id
+ * space is 2^^32 - 1. 0 is reserved. */
+static uint32_t
+sgid_alloc(const struct offload_sample *sample)
+{
+    struct sgid_node *node = xzalloc(sizeof *node);
+
+    offload_sample_clone(&node->sample, sample);
+    ovs_mutex_lock(&sgid_lock);
+    if (!id_pool_alloc_id(sample_group_ids, &node->id)) {
+        VLOG_ERR("Can't find a free sample group ID");
+        sgid_node_free(node);
+        ovs_mutex_unlock(&sgid_lock);
+        return 0;
+    }
+    cmap_insert(&sgid_map, &node->id_node, node->id);
+    ovs_mutex_unlock(&sgid_lock);
+
+    return node ? node->id : 0;
+}
+
+static void
+sgid_free(uint32_t id)
+{
+    struct sgid_node *node;
+
+    if (!id) {
+        return;
+    }
+
+    ovs_mutex_lock(&sgid_lock);
+    node = sgid_find(id);
+    if (node) {
+        id_pool_free_id(sample_group_ids, node->id);
+        cmap_remove(&sgid_map, &node->id_node, node->id);
+        ovsrcu_postpone(sgid_node_free, node);
+    } else {
+        VLOG_ERR("Freeing nonexistent sample group ID: %"PRIu32, id);
+    }
+    ovs_mutex_unlock(&sgid_lock);
+}
+
 static bool
 is_internal_port(const char *type)
 {
@@ -192,12 +294,17 @@  static struct netlink_field set_flower_map[][4] = {
 
 static struct ovs_mutex ufid_lock = OVS_MUTEX_INITIALIZER;
 
+#define MAX_TC_SAMPLES_PER_FLOW 1
+
 /**
  * struct ufid_tc_data - data entry for ufid-tc hashmaps.
  * @ufid_to_tc_node: Element in @ufid_to_tc hash table by ufid key.
  * @tc_to_ufid_node: Element in @tc_to_ufid hash table by tcf_id key.
  * @ufid: ufid assigned to the flow
  * @id: tc filter id (tcf_id)
+ * @sample_group_id: mapping id for sample actions. Currently only support
+                     sFlow. Use array to support other sample types in the
+                     future.
  * @netdev: netdev associated with the tc rule
  * @adjust_stats: When flow gets updated with new actions, we need to adjust
  *                the reported stats to include previous values as the hardware
@@ -208,6 +315,7 @@  struct ufid_tc_data {
     struct hmap_node tc_to_ufid_node;
     ovs_u128 ufid;
     struct tcf_id id;
+    uint32_t sample_group_id[MAX_TC_SAMPLES_PER_FLOW];
     struct netdev *netdev;
     struct dpif_flow_stats adjust_stats;
 };
@@ -231,6 +339,8 @@  del_ufid_tc_mapping_unlocked(const ovs_u128 *ufid)
     hmap_remove(&ufid_to_tc, &data->ufid_to_tc_node);
     hmap_remove(&tc_to_ufid, &data->tc_to_ufid_node);
     netdev_close(data->netdev);
+    sgid_free(data->sample_group_id[0]);
+    data->sample_group_id[0] = 0;
     free(data);
 }
 
@@ -2128,6 +2238,11 @@  netdev_tc_parse_nl_actions(struct netdev *netdev, struct tc_flower *flower,
             if (err) {
                 return err;
             }
+        } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_SAMPLE) {
+            struct offload_sample sample;
+
+            memset(&sample, 0, sizeof sample);
+            sgid_alloc(&sample);
         } else {
             VLOG_DBG_RL(&rl, "unsupported put action type: %d",
                         nl_attr_type(nla));
@@ -2871,6 +2986,9 @@  netdev_tc_init_flow_api(struct netdev *netdev)
         tc_cleanup_policer_actions(meter_police_ids, METER_POLICE_IDS_BASE,
                                    METER_POLICE_IDS_MAX);
         ovs_mutex_unlock(&meter_police_ids_mutex);
+        ovs_mutex_lock(&sgid_lock);
+        sample_group_ids = id_pool_create(1, UINT32_MAX - 1);
+        ovs_mutex_unlock(&sgid_lock);
 
         ovsthread_once_done(&once);
     }