Offload the sample action if it contains psample information by creating a tc "sample" action with the user cookie inside the action's cookie.
Avoid using the "sample" action's cookie to store the ufid. Signed-off-by: Adrian Moreno <[email protected]> --- include/linux/automake.mk | 5 +- include/linux/pkt_cls.h | 2 + include/linux/tc_act/tc_sample.h | 26 ++++++++++ lib/netdev-offload-tc.c | 67 +++++++++++++++++++++++++ lib/tc.c | 84 ++++++++++++++++++++++++++++++-- lib/tc.h | 7 +++ utilities/ovs-psample.c | 8 +-- 7 files changed, 190 insertions(+), 9 deletions(-) create mode 100644 include/linux/tc_act/tc_sample.h diff --git a/include/linux/automake.mk b/include/linux/automake.mk index ac306b53c..c2a270152 100644 --- a/include/linux/automake.mk +++ b/include/linux/automake.mk @@ -5,9 +5,10 @@ noinst_HEADERS += \ include/linux/pkt_cls.h \ include/linux/psample.h \ include/linux/gen_stats.h \ + include/linux/tc_act/tc_ct.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/tc_act/tc_vlan.h diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h index fb4a7ecea..c566ac1b5 100644 --- a/include/linux/pkt_cls.h +++ b/include/linux/pkt_cls.h @@ -8,6 +8,8 @@ #include <linux/types.h> #include <linux/pkt_sched.h> +#define TC_COOKIE_MAX_SIZE 16 + /* Action attributes */ enum { TCA_ACT_UNSPEC, diff --git a/include/linux/tc_act/tc_sample.h b/include/linux/tc_act/tc_sample.h new file mode 100644 index 000000000..398f32761 --- /dev/null +++ b/include/linux/tc_act/tc_sample.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_SAMPLE_H +#define __LINUX_TC_SAMPLE_H + +#include <linux/types.h> +#include <linux/pkt_cls.h> +#include <linux/if_ether.h> + +struct tc_sample { + tc_gen; +}; + +enum { + TCA_SAMPLE_UNSPEC, + TCA_SAMPLE_TM, + TCA_SAMPLE_PARMS, + TCA_SAMPLE_RATE, + TCA_SAMPLE_TRUNC_SIZE, + TCA_SAMPLE_PSAMPLE_GROUP, + TCA_SAMPLE_PAD, + __TCA_SAMPLE_MAX +}; +#define TCA_SAMPLE_MAX (__TCA_SAMPLE_MAX - 1) + +#endif + diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c index 36e814bee..0e7273431 100644 --- a/lib/netdev-offload-tc.c +++ b/lib/netdev-offload-tc.c @@ -1038,6 +1038,21 @@ parse_tc_flower_to_actions__(struct tc_flower *flower, struct ofpbuf *buf, nl_msg_end_nested(buf, offset); } break; + case TC_ACT_SAMPLE: { + size_t offset; + + offset = nl_msg_start_nested(buf, OVS_ACTION_ATTR_SAMPLE); + nl_msg_put_u32(buf, OVS_SAMPLE_ATTR_PROBABILITY, + UINT32_MAX / action->sample.rate); + nl_msg_put_u32(buf, OVS_SAMPLE_ATTR_PSAMPLE_GROUP, + action->sample.group_id); + nl_msg_put_unspec(buf, OVS_SAMPLE_ATTR_PSAMPLE_COOKIE, + &action->sample.cookie[0], + action->sample.cookie_len); + + nl_msg_end_nested(buf, offset); + } + break; } if (action->jump_action && action->type != TC_ACT_POLICE_MTU) { @@ -2054,6 +2069,53 @@ parse_check_pkt_len_action(struct netdev *netdev, struct tc_flower *flower, return 0; } +static int +parse_sample_action(struct tc_flower *flower, const struct nlattr *nl_act, + struct tc_action *action) +{ + /* Only offloadable if it's psample only. Use the policy to enforce it by + * making psample arguments mandatory and omitting actions. */ + static const struct nl_policy ovs_sample_policy[] = { + [OVS_SAMPLE_ATTR_PROBABILITY] = { .type = NL_A_U32 }, + [OVS_SAMPLE_ATTR_PSAMPLE_GROUP] = { .type = NL_A_U32, }, + [OVS_SAMPLE_ATTR_PSAMPLE_COOKIE] = { .type = NL_A_UNSPEC, + .optional = true, + .max_len = TC_COOKIE_MAX_SIZE } + }; + struct nlattr *a[ARRAY_SIZE(ovs_sample_policy)]; + uint32_t probability; + + if (!nl_parse_nested(nl_act, ovs_sample_policy, a, ARRAY_SIZE(a))) { + return EOPNOTSUPP; + } + + action->type = TC_ACT_SAMPLE; + /* OVS probability and TC sampling rate have different semantics. + * The former represents the number of sampled packets out of UINT32_MAX + * while the other represents the ratio between observed and sampled + * packets. */ + probability = nl_attr_get_u32(a[OVS_SAMPLE_ATTR_PROBABILITY]); + if (!probability) { + return EINVAL; + } + action->sample.rate = UINT32_MAX / probability; + + action->sample.group_id = + nl_attr_get_u32(a[OVS_SAMPLE_ATTR_PSAMPLE_GROUP]); + + if (a[OVS_SAMPLE_ATTR_PSAMPLE_COOKIE]) { + action->sample.cookie_len = + nl_attr_get_size(a[OVS_SAMPLE_ATTR_PSAMPLE_COOKIE]); + + memcpy(&action->sample.cookie[0], + nl_attr_get(a[OVS_SAMPLE_ATTR_PSAMPLE_COOKIE]), + action->sample.cookie_len); + } + + flower->action_count++; + return 0; +} + static int netdev_tc_parse_nl_actions(struct netdev *netdev, struct tc_flower *flower, struct offload_info *info, @@ -2195,6 +2257,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) { + err = parse_sample_action(flower, nla, action); + if (err) { + return err; + } } else { VLOG_DBG_RL(&rl, "unsupported put action type: %d", nl_attr_type(nla)); diff --git a/lib/tc.c b/lib/tc.c index 7176991c3..08d23064d 100644 --- a/lib/tc.c +++ b/lib/tc.c @@ -22,15 +22,16 @@ #include <linux/if_ether.h> #include <linux/if_packet.h> #include <linux/rtnetlink.h> +#include <linux/tc_act/tc_ct.h> #include <linux/tc_act/tc_csum.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> @@ -1563,6 +1564,42 @@ 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, const struct nlattr *cookie, + 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]); + action->sample.cookie_len = nl_attr_get_size(cookie); + memcpy(&action->sample.cookie[0], nl_attr_get(cookie), + MIN(action->sample.cookie_len, TC_COOKIE_MAX_SIZE)); + + return 0; +} + static const struct nl_policy mirred_policy[] = { [TCA_MIRRED_PARMS] = { .type = NL_A_UNSPEC, .min_len = sizeof(struct tc_mirred), @@ -2078,6 +2115,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, act_cookie, flower); } else { VLOG_ERR_RL(&error_rl, "unknown tc action kind: %s", act_kind); err = EINVAL; @@ -2087,7 +2126,8 @@ nl_parse_single_action(struct nlattr *action, struct tc_flower *flower, return err; } - if (act_cookie) { + /* TC_ACT_SAMPLE uses the cookie to store action-specific metadata. */ + if (act_cookie && strcmp(act_kind, "sample")) { flower->flow_cookie.data = nl_attr_get(act_cookie); flower->flow_cookie.len = nl_attr_get_size(act_cookie); } @@ -2901,6 +2941,29 @@ 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, struct tc_action *action, + 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, action->sample.rate); + nl_msg_put_u32(request, TCA_SAMPLE_PSAMPLE_GROUP, + action->sample.group_id); + } + nl_msg_end_nested(request, offset); + if (action->sample.cookie_len) { + nl_msg_put_unspec(request, TCA_ACT_COOKIE, &action->sample.cookie[0], + action->sample.cookie_len); + } +} + static inline void nl_msg_put_act_cookie(struct ofpbuf *request, struct tc_cookie *ck) { if (ck->len) { @@ -3220,6 +3283,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++; @@ -3506,13 +3570,27 @@ 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++); + /* TC_ACT_SAMPLE uses the action cookie so intentionally + * skipping flow action configuration. */ + nl_msg_put_act_sample(request, action, action_pc); + nl_msg_end_nested(request, act_offset); + break; + } } prev_action_pc = action_pc; } } - if (!flower->action_count) { + /* A flow with only a TC_ACT_SAMPLE action is possible. It is sampling the + * packet as it gets dropped. But given TC_ACT_SAMPLE uses the cookie to + * store action-specific data, add the explicit drop action to store the + * flow cookie. */ + if (!flower->action_count || + (flower->action_count == 1 && + flower->actions[0].type == TC_ACT_SAMPLE)) { act_offset = nl_msg_start_nested(request, act_index++); nl_msg_put_act_gact(request, 0); nl_msg_put_act_cookie(request, &flower->flow_cookie); diff --git a/lib/tc.h b/lib/tc.h index 40ea3d816..e6ad6950e 100644 --- a/lib/tc.h +++ b/lib/tc.h @@ -201,6 +201,7 @@ enum tc_action_type { TC_ACT_CT, TC_ACT_POLICE, TC_ACT_POLICE_MTU, + TC_ACT_SAMPLE, }; enum nat_type { @@ -296,6 +297,12 @@ struct tc_action { uint32_t result_jump; uint16_t mtu; } police; + struct { + uint32_t rate; + uint32_t group_id; + uint16_t cookie_len; + uint8_t cookie[TC_COOKIE_MAX_SIZE]; + } sample; }; enum tc_action_type type; diff --git a/utilities/ovs-psample.c b/utilities/ovs-psample.c index 9127d065c..51c72cc30 100644 --- a/utilities/ovs-psample.c +++ b/utilities/ovs-psample.c @@ -35,7 +35,7 @@ #include "openvswitch/uuid.h" #include "openvswitch/vlog.h" -VLOG_DEFINE_THIS_MODULE(ovs_sample); +VLOG_DEFINE_THIS_MODULE(ovs_psample); /* -g, --group: Group id filter option */ static uint32_t group_id = 0; @@ -206,7 +206,7 @@ parse_psample(struct ofpbuf *buf, struct sample *sample) { struct nlmsghdr *nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg); struct genlmsghdr *genl = ofpbuf_try_pull(&b, sizeof *genl); struct nlattr *attr; - const char *cookie; + const uint32_t *cookie; struct nlattr *a[ARRAY_SIZE(psample_packet_policy)]; if (!nlmsg || !genl @@ -227,8 +227,8 @@ parse_psample(struct ofpbuf *buf, struct sample *sample) { if (attr && nl_attr_get_size(attr) == 8) { cookie = nl_attr_get(attr); sample->has_cookie = true; - sample->obs_domain_id = (uint32_t) *(&cookie[0]); - sample->obs_point_id = (uint32_t) *(&cookie[4]); + sample->obs_domain_id = cookie[0]; + sample->obs_point_id = cookie[1]; } return 0; } -- 2.44.0 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
