On 1 Jun 2023, at 13:16, Chris Mi wrote:

> 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 <[email protected]>
> Reviewed-by: Roi Dayan <[email protected]>

See 3 comments below.

//Eelco

> ---
>  lib/netdev-offload-tc.c | 160 +++++++++++++++++++++++++++++++++++++---
>  lib/util.c              |   6 ++
>  lib/util.h              |   1 +
>  3 files changed, 158 insertions(+), 9 deletions(-)
>
> diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c
> index 4f26dd8cc..79bc3225a 100644
> --- a/lib/netdev-offload-tc.c
> +++ b/lib/netdev-offload-tc.c
> @@ -19,28 +19,29 @@
>  #include <errno.h>
>  #include <linux/if_ether.h>
>
> +#include "cmap.h"
> +#include "dpif-provider.h"
>  #include "dpif.h"
>  #include "hash.h"
>  #include "id-pool.h"
> -#include "openvswitch/hmap.h"
> -#include "openvswitch/match.h"
> -#include "openvswitch/ofpbuf.h"
> -#include "openvswitch/thread.h"
> -#include "openvswitch/types.h"
> -#include "openvswitch/util.h"
> -#include "openvswitch/vlog.h"
>  #include "netdev-linux.h"
>  #include "netdev-offload-provider.h"
>  #include "netdev-provider.h"
>  #include "netdev-vport.h"
> -#include "netlink.h"
>  #include "netlink-socket.h"
> +#include "netlink.h"
>  #include "odp-netlink.h"
>  #include "odp-util.h"
> +#include "openvswitch/hmap.h"
> +#include "openvswitch/match.h"
> +#include "openvswitch/ofpbuf.h"
> +#include "openvswitch/thread.h"
> +#include "openvswitch/types.h"
> +#include "openvswitch/util.h"
> +#include "openvswitch/vlog.h"
>  #include "tc.h"
>  #include "unaligned.h"
>  #include "util.h"
> -#include "dpif-provider.h"
>
>  VLOG_DEFINE_THIS_MODULE(netdev_offload_tc);
>
> @@ -103,6 +104,129 @@ 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 *userspace_actions;  /* All actions to get output tunnel. 
> */
> +    size_t userspace_actions_len;      /* All actions length without nla
> +                                          header. */

You do not need the len here, also you still copy, see below (and patch 7).

> +    struct flow_tnl *tunnel;           /* Input tunnel. */
> +    uint16_t ifindex;                  /* Input ifindex. */
> +};
> +
> +/* 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;
> +};
> +
> +/* The sgid_map mutex protects the sample_group_ids and the sgid_map for
> + * cmap_insert(), cmap_remove(), or cmap_replace() operations. */
> +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) {
> +        if (node->id) {
> +            ovs_mutex_lock(&sgid_lock);
> +            id_pool_free_id(sample_group_ids, node->id);
> +            ovs_mutex_unlock(&sgid_lock);
> +        }
> +        free(node->sample.tunnel);
> +        free(node->sample.action);
> +        free(node->sample.userspace_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 *dst,
> +                     const struct offload_sample *src,
> +                     bool steal_userspace_actions)
> +{
> +    dst->type = src->type;
> +    dst->action = nullable_xmemdup(src->action, src->action ?
> +                                                src->action->nla_len : 0);
> +    if (steal_userspace_actions) {
> +        dst->userspace_actions = src->userspace_actions;
> +    } else {
> +        uint16_t len = src->userspace_actions_len;
> +
> +        dst->userspace_actions = xmalloc(len + NLA_HDRLEN);
> +        nullable_memcpy((char *) dst->userspace_actions + NLA_HDRLEN,
> +                        src->userspace_actions, len);
> +        dst->userspace_actions->nla_len = len + NLA_HDRLEN;
> +    }

This is wrong, it should simply, be the below:

    if (steal_userspace_actions) {
        dst->userspace_actions = src->userspace_actions;
    } else {
        dst->userspace_actions = src->userspace_actions
            ? xmemdup(src->userspace_actions,
                      src->userspace_actions->nla_len)
            : NULL;
    }

> +    dst->userdata = nullable_xmemdup(src->userdata, src->userdata ?
> +                                                    src->userdata->nla_len :
> +                                                    0);
> +    dst->tunnel = nullable_xmemdup(src->tunnel, sizeof *src->tunnel);
> +    dst->ifindex = src->ifindex;
> +}
> +
> +/* 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, false);

I think it steal should be set to true, as we would like to steel the userspace 
actions, and not make a copy.

> +
> +    ovs_mutex_lock(&sgid_lock);
> +
> +    if (id_pool_alloc_id(sample_group_ids, &node->id)) {
> +        cmap_insert(&sgid_map, &node->id_node, node->id);
> +    } else {
> +        VLOG_ERR("Can't find a free sample group ID");
> +        sgid_node_free(node);
> +        node = NULL;
> +    }
> +
> +    ovs_mutex_unlock(&sgid_lock);
> +
> +    return node ? node->id : 0;
> +}
> +
> +static void
> +sgid_free(uint32_t id)
> +{
> +    struct sgid_node *node;
> +
> +    if (!id) {
> +        return;
> +    }
> +
> +    node = sgid_find(id);
> +    if (node) {
> +        ovs_mutex_lock(&sgid_lock);
> +        cmap_remove(&sgid_map, &node->id_node, node->id);
> +        ovs_mutex_unlock(&sgid_lock);
> +        ovsrcu_postpone(sgid_node_free, node);
> +    } else {
> +        VLOG_ERR("Freeing nonexistent sample group ID: %"PRIu32, id);
> +    }
> +}
> +
>  static bool
>  is_internal_port(const char *type)
>  {
> @@ -192,12 +316,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 supports
> +                     a single sFlow action. Use an array to support multiple
> +                     similar actions or additional 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 +337,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 +361,7 @@ 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]);
>      free(data);
>  }
>
> @@ -2133,6 +2264,14 @@ 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;
> +            uint32_t sgid;
> +
> +            memset(&sample, 0, sizeof sample);
> +            sgid = sgid_alloc(&sample);
> +            sgid_free(sgid);
> +            return EOPNOTSUPP;
>          } else {
>              VLOG_DBG_RL(&rl, "unsupported put action type: %d",
>                          nl_attr_type(nla));
> @@ -2876,6 +3015,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);
>      }
> diff --git a/lib/util.c b/lib/util.c
> index 3fb3a4b40..beac6c813 100644
> --- a/lib/util.c
> +++ b/lib/util.c
> @@ -190,6 +190,12 @@ xmemdup(const void *p_, size_t size)
>      return p;
>  }
>
> +void *
> +nullable_xmemdup(const void *p_, size_t size)
> +{
> +    return p_ == NULL || size == 0 ? NULL : xmemdup(p_, size);
> +}
> +
>  char *
>  xmemdup0(const char *p_, size_t length)
>  {
> diff --git a/lib/util.h b/lib/util.h
> index 62801e85f..815091edd 100644
> --- a/lib/util.h
> +++ b/lib/util.h
> @@ -167,6 +167,7 @@ void *xcalloc(size_t, size_t) MALLOC_LIKE;
>  void *xzalloc(size_t) MALLOC_LIKE;
>  void *xrealloc(void *, size_t);
>  void *xmemdup(const void *, size_t) MALLOC_LIKE;
> +void *nullable_xmemdup(const void *p_, size_t size) MALLOC_LIKE;
>  char *xmemdup0(const char *, size_t) MALLOC_LIKE;
>  char *xstrdup(const char *) MALLOC_LIKE;
>  char *nullable_xstrdup(const char *) MALLOC_LIKE;
> -- 
> 2.26.3

Here is a full diff of the suggested changes, if you sent a v28 with only these 
changes I can ack the patch:

diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c
index 79bc3225a..56726acf8 100644
--- a/lib/netdev-offload-tc.c
+++ b/lib/netdev-offload-tc.c
@@ -114,8 +114,6 @@ struct offload_sample {
     struct nlattr *action;             /* Sample action. Used in flow_get. */
     struct nlattr *userdata;           /* Struct user_action_cookie. */
     struct nlattr *userspace_actions;  /* All actions to get output tunnel. */
-    size_t userspace_actions_len;      /* All actions length without nla
-                                          header. */
     struct flow_tnl *tunnel;           /* Input tunnel. */
     uint16_t ifindex;                  /* Input ifindex. */
 };
@@ -169,12 +167,10 @@ offload_sample_clone(struct offload_sample *dst,
     if (steal_userspace_actions) {
         dst->userspace_actions = src->userspace_actions;
     } else {
-        uint16_t len = src->userspace_actions_len;
-
-        dst->userspace_actions = xmalloc(len + NLA_HDRLEN);
-        nullable_memcpy((char *) dst->userspace_actions + NLA_HDRLEN,
-                        src->userspace_actions, len);
-        dst->userspace_actions->nla_len = len + NLA_HDRLEN;
+        dst->userspace_actions = src->userspace_actions
+            ? xmemdup(src->userspace_actions,
+                      src->userspace_actions->nla_len)
+            : NULL;
     }
     dst->userdata = nullable_xmemdup(src->userdata, src->userdata ?
                                                     src->userdata->nla_len :
@@ -190,7 +186,7 @@ sgid_alloc(const struct offload_sample *sample)
 {
     struct sgid_node *node = xzalloc(sizeof *node);

-    offload_sample_clone(&node->sample, sample, false);
+    offload_sample_clone(&node->sample, sample, true);

     ovs_mutex_lock(&sgid_lock);

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

Reply via email to