On 6/19/23 07:05, Chris Mi wrote: > Create a unique group ID to map the sFlow info when offloading sample > action to TC. When showing the offloaded datapath flows, translate the > group ID from TC sample action to sFlow info using the mapping. > > Signed-off-by: Chris Mi <c...@nvidia.com> > Reviewed-by: Roi Dayan <r...@nvidia.com> > --- > lib/netdev-offload-tc.c | 288 ++++++++++++++++++++++++++++++++++++---- > lib/tc.c | 63 ++++++++- > lib/tc.h | 6 + > 3 files changed, 331 insertions(+), 26 deletions(-)
This patch needs a NEWS entry. > > diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c > index 8d571aca8..71ec8ef1f 100644 > --- a/lib/netdev-offload-tc.c > +++ b/lib/netdev-offload-tc.c > @@ -95,6 +95,7 @@ static int police_idx_lookup(uint32_t police_idx, uint32_t > *meter_id); > static int netdev_tc_parse_nl_actions(struct netdev *netdev, > struct tc_flower *flower, > struct offload_info *info, > + const struct flow_tnl *tnl, > const struct nlattr *actions, > size_t actions_len, > bool *recirc_act, bool more_actions, > @@ -136,6 +137,12 @@ 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 bool > +psample_supported(void) > +{ > + return psample_sock != NULL; > +} > + > static void > sgid_node_free(struct sgid_node *node) > { > @@ -236,6 +243,30 @@ sgid_free(uint32_t id) > } > } > > +static void free_all_flower_sgids(struct tc_flower *flower) > +{ > + const struct tc_action *action = flower->actions; > + > + for (int i = 0; i < flower->action_count; i++, action++) { > + if (action->type == TC_ACT_SAMPLE) { > + sgid_free(action->sample.group_id); > + } > + } > +} > + > +static unsigned int get_flower_sgid_count(struct tc_flower *flower) > +{ > + const struct tc_action *action = flower->actions; > + unsigned int count = 0; > + > + for (int i = 0; i < flower->action_count; i++, action++) { > + if (action->type == TC_ACT_SAMPLE && action->sample.group_id) { > + count++; > + } > + } > + return count; > +} > + > static bool > is_internal_port(const char *type) > { > @@ -370,7 +401,12 @@ 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]); > + for (int i = 0; i < MAX_TC_SAMPLES_PER_FLOW; i++) { > + if (!data->sample_group_id[i]) { > + break; > + } > + sgid_free(data->sample_group_id[i]); > + } > free(data); > } > > @@ -426,10 +462,12 @@ del_filter_and_ufid_mapping(struct tcf_id *id, const > ovs_u128 *ufid, > /* Add ufid entry to ufid_to_tc hashmap. */ > static void > add_ufid_tc_mapping(struct netdev *netdev, const ovs_u128 *ufid, > - struct tcf_id *id, struct dpif_flow_stats *stats) > + struct tcf_id *id, struct dpif_flow_stats *stats, > + struct tc_flower *flower) > { > struct ufid_tc_data *new_data = xzalloc(sizeof *new_data); > size_t ufid_hash = hash_bytes(ufid, sizeof *ufid, 0); > + const struct tc_action *action = flower->actions; > size_t tc_hash; > > tc_hash = hash_int(hash_int(id->prio, id->handle), id->ifindex); > @@ -442,6 +480,15 @@ add_ufid_tc_mapping(struct netdev *netdev, const > ovs_u128 *ufid, > new_data->adjust_stats = *stats; > } > > + for (int i = 0, si = 0; i < flower->action_count; i++, action++) { > + if (action->type == TC_ACT_SAMPLE && action->sample.group_id) { > + new_data->sample_group_id[si++] = action->sample.group_id; > + if (si >= MAX_TC_SAMPLES_PER_FLOW) { > + break; > + } > + } > + } > + > ovs_mutex_lock(&ufid_lock); > hmap_insert(&ufid_to_tc, &new_data->ufid_to_tc_node, ufid_hash); > hmap_insert(&tc_to_ufid, &new_data->tc_to_ufid_node, tc_hash); > @@ -908,6 +955,19 @@ parse_tc_flower_to_actions__(struct tc_flower *flower, > struct ofpbuf *buf, > action = &flower->actions[i]; > > switch (action->type) { > + case TC_ACT_SAMPLE: { > + const struct sgid_node *node; > + > + node = sgid_find(action->sample.group_id); > + if (!node) { > + VLOG_WARN("Can't find sample group ID data for ID: %u", > + action->sample.group_id); > + return ENOENT; > + } > + nl_msg_put(buf, node->sample.action, > + node->sample.action->nla_len); > + } > + break; > case TC_ACT_VLAN_POP: { > nl_msg_put_flag(buf, OVS_ACTION_ATTR_POP_VLAN); > } > @@ -2023,11 +2083,164 @@ parse_match_ct_state_to_flower(struct tc_flower > *flower, struct match *match) > } > } > > +static int > +parse_userspace_attributes(const struct nlattr *actions, > + struct offload_sample *sample) > +{ > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); > + const struct nlattr *nla; > + unsigned int left; > + > + NL_NESTED_FOR_EACH_UNSAFE (nla, left, actions) { > + if (nl_attr_type(nla) == OVS_USERSPACE_ATTR_USERDATA) { > + struct user_action_cookie cookie; > + > + memcpy(&cookie, nl_attr_get_unspec(nla, sizeof cookie), > + sizeof cookie); > + if (cookie.type == USER_ACTION_COOKIE_SFLOW) { > + sample->type = USER_ACTION_COOKIE_SFLOW; > + sample->userdata = CONST_CAST(struct nlattr *, nla); > + return 0; > + } > + } > + } > + > + VLOG_DBG_RL(&rl, "Can't offload userspace action other than sFlow"); > + return EOPNOTSUPP; > +} > + > +static int > +parse_sample_actions_attribute(const struct nlattr *actions, > + struct offload_sample *sample) > +{ > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); > + const struct nlattr *nla; > + unsigned int left; > + int err = EINVAL; > + > + NL_NESTED_FOR_EACH_UNSAFE (nla, left, actions) { > + if (nl_attr_type(nla) == OVS_ACTION_ATTR_USERSPACE) { > + err = parse_userspace_attributes(nla, sample); > + } else { > + /* We can't offload other nested actions. */ > + VLOG_DBG_RL(&rl, "Can only offload OVS_ACTION_ATTR_USERSPACE" > + " attribute"); > + return EINVAL; > + } > + } > + > + if (err) { > + VLOG_ERR_RL(&error_rl, "No OVS_ACTION_ATTR_USERSPACE attribute"); > + } > + return err; > +} > + > +static void > +offload_sample_init(struct offload_sample *sample, > + const struct nlattr *next_actions, > + size_t next_actions_len, > + struct tc_flower *flower, > + const struct flow_tnl *tnl) > +{ > + memset(sample, 0, sizeof *sample); > + if (flower->tunnel) { > + sample->tunnel = CONST_CAST(struct flow_tnl *, tnl); > + } > + > + sample->userspace_actions = xmalloc(next_actions_len + NLA_HDRLEN); > + nullable_memcpy((char *) sample->userspace_actions + NLA_HDRLEN, > + next_actions, next_actions_len); > + sample->userspace_actions->nla_len = next_actions_len + NLA_HDRLEN; > + sample->ifindex = flower->ifindex; > +} > + > +static int > +parse_sample_action(struct tc_flower *flower, struct tc_action *tc_action, > + const struct nlattr *next_actions, size_t > next_actions_len, > + const struct nlattr *sample_action, > + const struct flow_tnl *tnl) > +{ > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); > + struct offload_sample sample; > + const struct nlattr *nla; > + unsigned int left; > + uint32_t rate = 0; > + int ret = EINVAL; > + uint32_t sgid; > + > + offload_sample_init(&sample, next_actions, next_actions_len, flower, > tnl); > + sample.action = CONST_CAST(struct nlattr *, sample_action); > + > + NL_NESTED_FOR_EACH_UNSAFE (nla, left, sample_action) { > + if (nl_attr_type(nla) == OVS_SAMPLE_ATTR_ACTIONS) { > + ret = parse_sample_actions_attribute(nla, &sample); > + } else if (nl_attr_type(nla) == OVS_SAMPLE_ATTR_PROBABILITY) { > + rate = UINT32_MAX / nl_attr_get_u32(nla); > + } else { > + return EINVAL; > + } > + } > + > + /* This check makes sure both attributes above were present and valid. */ > + if (!rate || ret) { > + return EINVAL; > + } > + > + sgid = sgid_alloc(&sample); > + if (!sgid) { > + VLOG_DBG_RL(&rl, "Failed allocating group id for sample action"); > + return ENOENT; > + } > + > + tc_action->type = TC_ACT_SAMPLE; > + tc_action->sample.rate = rate; > + tc_action->sample.group_id = sgid; > + flower->action_count++; > + > + return 0; > +} > + > +static int > +parse_userspace_action(struct tc_flower *flower, struct tc_action *tc_action, > + const struct nlattr *next_actions, > + size_t next_actions_len, > + const struct nlattr *userspace_action, > + const struct flow_tnl *tnl) > +{ > + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); > + struct offload_sample sample; > + uint32_t sgid; > + int err; > + > + offload_sample_init(&sample, next_actions, next_actions_len, flower, > tnl); > + > + /* If sampling rate is 1, there is only a sFlow cookie inside of a > + * userspace action, but no sample attribute. That means we can > + * only offload userspace actions for sFlow. */ > + err = parse_userspace_attributes(userspace_action, &sample); > + if (err) { > + return err; > + } > + sample.action = (struct nlattr *) userspace_action; This should be a CONST_CAST. > + sgid = sgid_alloc(&sample); > + if (!sgid) { > + VLOG_DBG_RL(&rl, "Failed allocating group id for sample action"); > + return ENOENT; > + } > + tc_action->type = TC_ACT_SAMPLE; > + tc_action->sample.group_id = sgid; > + tc_action->sample.rate = 1; > + flower->action_count++; > + > + return 0; > +} > > static int > parse_check_pkt_len_action(struct netdev *netdev, struct tc_flower *flower, > - struct offload_info *info, struct tc_action > *action, > - const struct nlattr *nla, bool last_action, > + struct offload_info *info, > + const struct flow_tnl *tnl, > + struct tc_action *action, const struct nlattr > *nla, > + bool last_action, > struct tc_action **need_jump_update, > bool *recirc_act) > { > @@ -2066,7 +2279,7 @@ parse_check_pkt_len_action(struct netdev *netdev, > struct tc_flower *flower, > * NOTE: The last_action parameter means that there are no more actions > * after the if () then ... else () case. */ > nl_actions = a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER]; > - err = netdev_tc_parse_nl_actions(netdev, flower, info, > + err = netdev_tc_parse_nl_actions(netdev, flower, info, tnl, > nl_attr_get(nl_actions), > nl_attr_get_size(nl_actions), > recirc_act, !last_action, > @@ -2082,7 +2295,7 @@ parse_check_pkt_len_action(struct netdev *netdev, > struct tc_flower *flower, > > /* Parse and add the less than action(s). */ > nl_actions = a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL]; > - err = netdev_tc_parse_nl_actions(netdev, flower, info, > + err = netdev_tc_parse_nl_actions(netdev, flower, info, tnl, > nl_attr_get(nl_actions), > nl_attr_get_size(nl_actions), > recirc_act, !last_action, > @@ -2135,6 +2348,7 @@ parse_check_pkt_len_action(struct netdev *netdev, > struct tc_flower *flower, > static int > netdev_tc_parse_nl_actions(struct netdev *netdev, struct tc_flower *flower, > struct offload_info *info, > + const struct flow_tnl *tnl, > const struct nlattr *actions, size_t actions_len, > bool *recirc_act, bool more_actions, > struct tc_action **need_jump_update) > @@ -2264,7 +2478,8 @@ netdev_tc_parse_nl_actions(struct netdev *netdev, > struct tc_flower *flower, > action->police.index = police_index; > flower->action_count++; > } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_CHECK_PKT_LEN) { > - err = parse_check_pkt_len_action(netdev, flower, info, action, > nla, > + err = parse_check_pkt_len_action(netdev, flower, info, tnl, > + action, nla, > nl_attr_len_pad(nla, > left) >= left > && !more_actions, > @@ -2273,14 +2488,28 @@ 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 if (nl_attr_type(nla) == OVS_ACTION_ATTR_SAMPLE || > + nl_attr_type(nla) == OVS_ACTION_ATTR_USERSPACE) { > + if (!psample_supported()) { > + VLOG_DBG_RL(&rl, "Unsupported put action type: %d, psample > is " > + "not initialized successfully", > nl_attr_type(nla)); > + return EOPNOTSUPP; > + } > + if (get_flower_sgid_count(flower) >= MAX_TC_SAMPLES_PER_FLOW) { > + VLOG_ERR_RL(&error_rl, "Only %u TC_SAMPLE action per " > + "flow is supported", MAX_TC_SAMPLES_PER_FLOW); > + return EOPNOTSUPP; > + } > + if (nl_attr_type(nla) == OVS_ACTION_ATTR_SAMPLE) { > + err = parse_sample_action(flower, action, actions, > actions_len, > + nla, tnl); > + } else { > + err = parse_userspace_action(flower, action, actions, > + actions_len, nla, tnl); > + } > + if (err) { > + return err; > + } > } else { > VLOG_DBG_RL(&rl, "unsupported put action type: %d", > nl_attr_type(nla)); > @@ -2320,6 +2549,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match > *match, > } > > memset(&flower, 0, sizeof flower); > + flower.ifindex = ifindex; > > chain = key->recirc_id; > mask->recirc_id = 0; > @@ -2585,16 +2815,17 @@ netdev_tc_flow_put(struct netdev *netdev, struct > match *match, > } > > /* Parse all (nested) actions. */ > - err = netdev_tc_parse_nl_actions(netdev, &flower, info, > + err = netdev_tc_parse_nl_actions(netdev, &flower, info, tnl, > actions, actions_len, &recirc_act, > false, NULL); > if (err) { > - return err; > + goto error_out; > } > > if ((chain || recirc_act) && !info->recirc_id_shared_with_tc) { > VLOG_DBG_RL(&rl, "flow_put: recirc_id sharing not supported"); > - return EOPNOTSUPP; > + err = EOPNOTSUPP; > + goto error_out; > } > > memset(&adjust_stats, 0, sizeof adjust_stats); > @@ -2608,7 +2839,8 @@ netdev_tc_flow_put(struct netdev *netdev, struct match > *match, > prio = get_prio_for_tc_flower(&flower); > if (prio == 0) { > VLOG_ERR_RL(&rl, "couldn't get tc prio: %s", ovs_strerror(ENOSPC)); > - return ENOSPC; > + err = ENOSPC; > + goto error_out; > } > > flower.act_cookie.data = ufid; > @@ -2617,14 +2849,20 @@ netdev_tc_flow_put(struct netdev *netdev, struct > match *match, > block_id = get_block_id_from_netdev(netdev); > id = tc_make_tcf_id_chain(ifindex, block_id, chain, prio, hook); > err = tc_replace_flower(&id, &flower); > - if (!err) { > - if (stats) { > - memset(stats, 0, sizeof *stats); > - netdev_tc_adjust_stats(stats, &adjust_stats); > - } > - add_ufid_tc_mapping(netdev, ufid, &id, &adjust_stats); > + if (err) { > + goto error_out; > } > > + if (stats) { > + memset(stats, 0, sizeof *stats); > + netdev_tc_adjust_stats(stats, &adjust_stats); > + } > + > + add_ufid_tc_mapping(netdev, ufid, &id, &adjust_stats, &flower); > + return 0; > + > +error_out: > + free_all_flower_sgids(&flower); > return err; > } > > diff --git a/lib/tc.c b/lib/tc.c > index 270dc95ce..361914525 100644 > --- a/lib/tc.c > +++ b/lib/tc.c > @@ -23,14 +23,15 @@ > #include <linux/if_packet.h> > #include <linux/rtnetlink.h> > #include <linux/tc_act/tc_csum.h> > +#include <linux/tc_act/tc_ct.h> > #include <linux/tc_act/tc_gact.h> > #include <linux/tc_act/tc_mirred.h> > #include <linux/tc_act/tc_mpls.h> > #include <linux/tc_act/tc_pedit.h> > +#include <linux/tc_act/tc_sample.h> > #include <linux/tc_act/tc_skbedit.h> > #include <linux/tc_act/tc_tunnel_key.h> > #include <linux/tc_act/tc_vlan.h> > -#include <linux/tc_act/tc_ct.h> > #include <linux/gen_stats.h> > #include <net/if.h> > #include <unistd.h> > @@ -1487,6 +1488,38 @@ nl_parse_act_police(const struct nlattr *options, > struct tc_flower *flower) > return 0; > } > > +static const struct nl_policy sample_policy[] = { > + [TCA_SAMPLE_PARMS] = { .type = NL_A_UNSPEC, > + .min_len = sizeof(struct tc_sample), > + .optional = false, }, > + [TCA_SAMPLE_PSAMPLE_GROUP] = { .type = NL_A_U32, > + .optional = false, }, > + [TCA_SAMPLE_RATE] = { .type = NL_A_U32, > + .optional = false, }, > +}; > + > +static int > +nl_parse_act_sample(struct nlattr *options, struct tc_flower *flower) > +{ > + struct nlattr *sample_attrs[ARRAY_SIZE(sample_policy)]; > + struct tc_action *action; > + > + if (!nl_parse_nested(options, sample_policy, sample_attrs, > + ARRAY_SIZE(sample_policy))) { > + VLOG_ERR_RL(&error_rl, "Failed to parse sample action options"); > + return EPROTO; > + } > + > + action = &flower->actions[flower->action_count++]; > + action->type = TC_ACT_SAMPLE; > + action->sample.group_id = > + nl_attr_get_u32(sample_attrs[TCA_SAMPLE_PSAMPLE_GROUP]); > + action->sample.rate = > + nl_attr_get_u32(sample_attrs[TCA_SAMPLE_RATE]); > + > + return 0; > +} > + > static const struct nl_policy mirred_policy[] = { > [TCA_MIRRED_PARMS] = { .type = NL_A_UNSPEC, > .min_len = sizeof(struct tc_mirred), > @@ -2002,6 +2035,8 @@ nl_parse_single_action(struct nlattr *action, struct > tc_flower *flower, > nl_parse_act_ct(act_options, flower); > } else if (!strcmp(act_kind, "police")) { > nl_parse_act_police(act_options, flower); > + } else if (!strcmp(act_kind, "sample")) { > + nl_parse_act_sample(act_options, flower); > } else { > VLOG_ERR_RL(&error_rl, "unknown tc action kind: %s", act_kind); > err = EINVAL; > @@ -2804,6 +2839,24 @@ nl_msg_put_act_mirred(struct ofpbuf *request, int > ifindex, int action, > nl_msg_end_nested(request, offset); > } > > +static void > +nl_msg_put_act_sample(struct ofpbuf *request, uint32_t rate, uint32_t > group_id, > + uint32_t action_pc) > +{ > + size_t offset; > + > + nl_msg_put_string(request, TCA_ACT_KIND, "sample"); > + offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED); > + { > + struct tc_sample parm = { .action = action_pc }; > + > + nl_msg_put_unspec(request, TCA_SAMPLE_PARMS, &parm, sizeof parm); > + nl_msg_put_u32(request, TCA_SAMPLE_RATE, rate); > + nl_msg_put_u32(request, TCA_SAMPLE_PSAMPLE_GROUP, group_id); > + } > + nl_msg_end_nested(request, offset); > +} > + > static inline void > nl_msg_put_act_cookie(struct ofpbuf *request, struct tc_cookie *ck) { > if (ck->len) { > @@ -3116,6 +3169,7 @@ get_action_index_for_tc_actions(struct tc_flower > *flower, uint16_t act_index, > case TC_ACT_MPLS_SET: > case TC_ACT_GOTO: > case TC_ACT_CT: > + case TC_ACT_SAMPLE: > /* Increase act_index by one if we are sure this type of action > * will only add one tc action in the kernel. */ > act_index++; > @@ -3323,6 +3377,13 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct > tc_flower *flower) > nl_msg_end_nested(request, act_offset); > } > break; > + case TC_ACT_SAMPLE: { > + act_offset = nl_msg_start_nested(request, act_index++); > + nl_msg_put_act_sample(request, action->sample.rate, > + action->sample.group_id, action_pc); > + nl_msg_end_nested(request, act_offset); > + } > + break; > case TC_ACT_OUTPUT: { > if (!released && flower->tunnel) { > nl_msg_put_flower_acts_release(request, act_index++); > diff --git a/lib/tc.h b/lib/tc.h > index cdd3b4f60..5f6e15d5c 100644 > --- a/lib/tc.h > +++ b/lib/tc.h > @@ -192,6 +192,7 @@ enum tc_action_type { > TC_ACT_CT, > TC_ACT_POLICE, > TC_ACT_POLICE_MTU, > + TC_ACT_SAMPLE, > }; > > enum nat_type { > @@ -283,6 +284,10 @@ struct tc_action { > uint32_t result_jump; > uint16_t mtu; > } police; > + struct { > + uint32_t rate; > + uint32_t group_id; > + } sample; > }; > > enum tc_action_type type; > @@ -380,6 +385,7 @@ struct tc_flower { > enum tc_offloaded_state offloaded_state; > /* Used to force skip_hw when probing tc features. */ > enum tc_offload_policy tc_policy; > + uint16_t ifindex; > }; > > int tc_replace_flower(struct tcf_id *id, struct tc_flower *flower); No need for variable names. _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev