Message ID | 20230619050557.310690-4-cmi@nvidia.com |
---|---|
State | Changes Requested |
Headers | show |
Series | Add offload support for sFlow | expand |
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 |
On 19 Jun 2023, at 7:05, 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 <cmi@nvidia.com> > Reviewed-by: Roi Dayan <roid@nvidia.com> Thanks for making all the suggested changes. Acked-by: Eelco Chaudron <echaudro@redhat.com> //Eelco
On 6/19/23 07:05, 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 <cmi@nvidia.com> > Reviewed-by: Roi Dayan <roid@nvidia.com> > --- > lib/netdev-offload-tc.c | 156 +++++++++++++++++++++++++++++++++++++--- > lib/util.c | 6 ++ > lib/util.h | 1 + > 3 files changed, 154 insertions(+), 9 deletions(-) > > diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c > index 4f26dd8cc..56726acf8 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,125 @@ 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. */ > + 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 { > + 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. */ Nit: it's either '^' or '**', not '^^'. > +static uint32_t > +sgid_alloc(const struct offload_sample *sample) > +{ > + struct sgid_node *node = xzalloc(sizeof *node); > + > + offload_sample_clone(&node->sample, sample, true); > + > + 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 +312,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 +333,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 +357,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 +2260,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 +3011,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; The prototype doesn't need variables names in this case. > char *xmemdup0(const char *, size_t) MALLOC_LIKE; > char *xstrdup(const char *) MALLOC_LIKE; > char *nullable_xstrdup(const char *) MALLOC_LIKE;
On 6/24/2023 4:17 AM, Ilya Maximets wrote: > On 6/19/23 07:05, 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 <cmi@nvidia.com> >> Reviewed-by: Roi Dayan <roid@nvidia.com> >> --- >> lib/netdev-offload-tc.c | 156 +++++++++++++++++++++++++++++++++++++--- >> lib/util.c | 6 ++ >> lib/util.h | 1 + >> 3 files changed, 154 insertions(+), 9 deletions(-) >> >> diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c >> index 4f26dd8cc..56726acf8 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,125 @@ 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. */ >> + 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 { >> + 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. */ > > Nit: it's either '^' or '**', not '^^'. Done. > >> +static uint32_t >> +sgid_alloc(const struct offload_sample *sample) >> +{ >> + struct sgid_node *node = xzalloc(sizeof *node); >> + >> + offload_sample_clone(&node->sample, sample, true); >> + >> + 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 +312,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 +333,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 +357,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 +2260,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 +3011,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; > > The prototype doesn't need variables names in this case. Done. > >> char *xmemdup0(const char *, size_t) MALLOC_LIKE; >> char *xstrdup(const char *) MALLOC_LIKE; >> char *nullable_xstrdup(const char *) MALLOC_LIKE; >
diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c index 4f26dd8cc..56726acf8 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,125 @@ 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. */ + 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 { + 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, true); + + 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 +312,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 +333,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 +357,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 +2260,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 +3011,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;