On Wed, Jul 10, 2024 at 02:18:35PM GMT, Ilya Maximets wrote: > On 7/9/24 16:03, Eelco Chaudron wrote: > > > > > > On 9 Jul 2024, at 15:52, Adrián Moreno wrote: > > > >> On Tue, Jul 09, 2024 at 11:45:51AM GMT, Eelco Chaudron wrote: > >>> On 7 Jul 2024, at 22:08, Adrian Moreno wrote: > >>> > >>>> Use the newly added psample action to implement OpenFlow sample() actions > >>>> with local sampling configuration if possible. > >>>> > >>>> A bit of refactoring in compose_sample_actions arguments helps make it a > >>>> bit more readable. > >>> > >>> Some comments below. > >>> > >>> Cheers, > >>> > >>> Eelco > >>> > >>>> Signed-off-by: Adrian Moreno <amore...@redhat.com> > >>>> --- > >>>> ofproto/ofproto-dpif-lsample.c | 16 +++ > >>>> ofproto/ofproto-dpif-lsample.h | 5 + > >>>> ofproto/ofproto-dpif-xlate.c | 251 +++++++++++++++++++++++---------- > >>>> ofproto/ofproto-dpif-xlate.h | 5 +- > >>>> ofproto/ofproto-dpif.c | 2 +- > >>>> tests/ofproto-dpif.at | 146 +++++++++++++++++++ > >>>> 6 files changed, 345 insertions(+), 80 deletions(-) > >>>> > >>>> diff --git a/ofproto/ofproto-dpif-lsample.c > >>>> b/ofproto/ofproto-dpif-lsample.c > >>>> index d675a116f..534ad96f0 100644 > >>>> --- a/ofproto/ofproto-dpif-lsample.c > >>>> +++ b/ofproto/ofproto-dpif-lsample.c > >>>> @@ -140,6 +140,22 @@ dpif_lsample_set_options(struct dpif_lsample > >>>> *lsample, > >>>> return changed; > >>>> } > >>>> > >>>> +/* Returns the group_id for a given collector_set_id, if it exists. */ > >>>> +bool > >>>> +dpif_lsample_get_group_id(struct dpif_lsample *ps, uint32_t > >>>> collector_set_id, > >>>> + uint32_t *group_id) > >>>> +{ > >>>> + struct lsample_exporter_node *node; > >>>> + bool found = false; > >>>> + > >>>> + node = dpif_lsample_find_exporter_node(ps, collector_set_id); > >>>> + if (node) { > >>>> + found = true; > >>>> + *group_id = node->exporter.options.group_id; > >>>> + } > >>>> + return found; > >>>> +} > >>>> + > >>>> struct dpif_lsample * > >>>> dpif_lsample_create(void) > >>>> { > >>>> diff --git a/ofproto/ofproto-dpif-lsample.h > >>>> b/ofproto/ofproto-dpif-lsample.h > >>>> index bee36c9c5..9c1026551 100644 > >>>> --- a/ofproto/ofproto-dpif-lsample.h > >>>> +++ b/ofproto/ofproto-dpif-lsample.h > >>>> @@ -18,6 +18,7 @@ > >>>> #define OFPROTO_DPIF_LSAMPLE_H 1 > >>>> > >>>> #include <stdbool.h> > >>>> +#include <stdint.h> > >>>> #include <stdlib.h> > >>>> > >>>> struct dpif_lsample; > >>>> @@ -33,4 +34,8 @@ bool dpif_lsample_set_options(struct dpif_lsample *, > >>>> const struct ofproto_lsample_options *, > >>>> size_t n_opts); > >>>> > >>>> +bool dpif_lsample_get_group_id(struct dpif_lsample *, > >>>> + uint32_t collector_set_id, > >>>> + uint32_t *group_id); > >>>> + > >>>> #endif /* OFPROTO_DPIF_LSAMPLE_H */ > >>>> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c > >>>> index 7c4950895..5e8113d5e 100644 > >>>> --- a/ofproto/ofproto-dpif-xlate.c > >>>> +++ b/ofproto/ofproto-dpif-xlate.c > >>>> @@ -47,6 +47,7 @@ > >>>> #include "ofproto/ofproto-dpif-ipfix.h" > >>>> #include "ofproto/ofproto-dpif-mirror.h" > >>>> #include "ofproto/ofproto-dpif-monitor.h" > >>>> +#include "ofproto/ofproto-dpif-lsample.h" > >>> > >>> Add in alphabetical order? > >> > >> Ack. > >> > >>> > >>>> #include "ofproto/ofproto-dpif-sflow.h" > >>>> #include "ofproto/ofproto-dpif-trace.h" > >>>> #include "ofproto/ofproto-dpif-xlate-cache.h" > >>>> @@ -117,6 +118,7 @@ struct xbridge { > >>>> struct dpif_sflow *sflow; /* SFlow handle, or null. */ > >>>> struct dpif_ipfix *ipfix; /* Ipfix handle, or null. */ > >>>> struct netflow *netflow; /* Netflow handle, or null. */ > >>>> + struct dpif_lsample *lsample; /* Local sample handle, or null. */ > >>> > >>> I would move above netflow, as you also do the actual init/unref before > >>> netflow. > >> > >> > >> Ack. > >> > >> > >>> > >>>> struct stp *stp; /* STP or null if disabled. */ > >>>> struct rstp *rstp; /* RSTP or null if disabled. */ > >>>> > >>>> @@ -686,6 +688,7 @@ static void xlate_xbridge_set(struct xbridge *, > >>>> struct dpif *, > >>>> const struct mbridge *, > >>>> const struct dpif_sflow *, > >>>> const struct dpif_ipfix *, > >>>> + const struct dpif_lsample *, > >>>> const struct netflow *, > >>>> bool forward_bpdu, bool has_in_band, > >>>> const struct dpif_backer_support *, > >>>> @@ -1069,6 +1072,7 @@ xlate_xbridge_set(struct xbridge *xbridge, > >>>> const struct mbridge *mbridge, > >>>> const struct dpif_sflow *sflow, > >>>> const struct dpif_ipfix *ipfix, > >>>> + const struct dpif_lsample *lsample, > >>>> const struct netflow *netflow, > >>>> bool forward_bpdu, bool has_in_band, > >>>> const struct dpif_backer_support *support, > >>>> @@ -1099,6 +1103,11 @@ xlate_xbridge_set(struct xbridge *xbridge, > >>>> xbridge->ipfix = dpif_ipfix_ref(ipfix); > >>>> } > >>>> > >>>> + if (xbridge->lsample != lsample) { > >>>> + dpif_lsample_unref(xbridge->lsample); > >>>> + xbridge->lsample = dpif_lsample_ref(lsample); > >>>> + } > >>>> + > >>>> if (xbridge->stp != stp) { > >>>> stp_unref(xbridge->stp); > >>>> xbridge->stp = stp_ref(stp); > >>>> @@ -1213,9 +1222,10 @@ xlate_xbridge_copy(struct xbridge *xbridge) > >>>> xlate_xbridge_set(new_xbridge, > >>>> xbridge->dpif, xbridge->ml, xbridge->stp, > >>>> xbridge->rstp, xbridge->ms, xbridge->mbridge, > >>>> - xbridge->sflow, xbridge->ipfix, xbridge->netflow, > >>>> - xbridge->forward_bpdu, xbridge->has_in_band, > >>>> - &xbridge->support, xbridge->addr); > >>>> + xbridge->sflow, xbridge->ipfix, xbridge->lsample, > >>>> + xbridge->netflow, xbridge->forward_bpdu, > >>>> + xbridge->has_in_band, &xbridge->support, > >>>> + xbridge->addr); > >>>> LIST_FOR_EACH (xbundle, list_node, &xbridge->xbundles) { > >>>> xlate_xbundle_copy(new_xbridge, xbundle); > >>>> } > >>>> @@ -1372,6 +1382,7 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, > >>>> const char *name, > >>>> const struct mbridge *mbridge, > >>>> const struct dpif_sflow *sflow, > >>>> const struct dpif_ipfix *ipfix, > >>>> + const struct dpif_lsample *lsample, > >>>> const struct netflow *netflow, > >>>> bool forward_bpdu, bool has_in_band, > >>>> const struct dpif_backer_support *support) > >>>> @@ -1396,7 +1407,7 @@ xlate_ofproto_set(struct ofproto_dpif *ofproto, > >>>> const char *name, > >>>> old_addr = xbridge->addr; > >>>> > >>>> xlate_xbridge_set(xbridge, dpif, ml, stp, rstp, ms, mbridge, sflow, > >>>> ipfix, > >>>> - netflow, forward_bpdu, has_in_band, support, > >>>> + lsample, netflow, forward_bpdu, has_in_band, > >>>> support, > >>>> xbridge_addr); > >>>> > >>>> if (xbridge_addr != old_addr) { > >>>> @@ -1428,6 +1439,7 @@ xlate_xbridge_remove(struct xlate_cfg *xcfg, > >>>> struct xbridge *xbridge) > >>>> mbridge_unref(xbridge->mbridge); > >>>> dpif_sflow_unref(xbridge->sflow); > >>>> dpif_ipfix_unref(xbridge->ipfix); > >>>> + dpif_lsample_unref(xbridge->lsample); > >>>> netflow_unref(xbridge->netflow); > >>>> stp_unref(xbridge->stp); > >>>> rstp_unref(xbridge->rstp); > >>>> @@ -3357,58 +3369,92 @@ xlate_normal(struct xlate_ctx *ctx) > >>>> } > >>>> } > >>>> > >>>> -/* Appends a "sample" action for sFlow or IPFIX to 'ctx->odp_actions'. > >>>> The > >>>> - * 'probability' is the number of packets out of UINT32_MAX to sample. > >>>> The > >>>> - * 'cookie' is passed back in the callback for each sampled packet. > >>>> - * 'tunnel_out_port', if not ODPP_NONE, is added as the > >>>> - * OVS_USERSPACE_ATTR_EGRESS_TUN_PORT attribute. If 'include_actions', > >>>> - * an OVS_USERSPACE_ATTR_ACTIONS attribute is added. If > >>>> - * 'emit_set_tunnel', sample(sampling_port=1) would translate into > >>>> - * datapath sample action set(tunnel(...)), sample(...) and it is used > >>>> - * for sampling egress tunnel information. > >>>> - */ > >>>> +/* Psample-related arguments for compose_sample_action. */ > >>>> +struct sample_psample_args { > >>>> + uint32_t group_id; /* Group to be used in psample. > >>>> */ > >>>> + struct ofpbuf cookie; /* Cookie to be used in > >>>> psample. */ > >>>> +}; > >>>> + > >>>> +/* Userspace-related arguments for compose_sample_action. */ > >>>> +struct sample_userspace_args { > >>>> + struct user_action_cookie cookie; /* Data passed back in the > >>>> upcall > >>>> + * for each sampled packet. */ > >>>> + odp_port_t tunnel_out_port; /* If not ODPP_NONE, it is > >>>> added in > >>>> + * > >>>> OVS_USERSPACE_ATTR_EGRESS_TUN_PORT > >>>> + * attribute. */ > >>>> + bool include_actions; /* Whether > >>>> OVS_USERSPACE_ATTR_ACTIONS > >>>> + * is to be set. */ > >>>> + > >>>> +}; > >>>> + > >>>> +/* Arguments for compose_sample_action. */ > >>>> +struct compose_sample_args { > >>>> + uint32_t probability; /* Number of packets out > >>>> of > >>>> + * UINT32_MAX to sample. > >>>> */ > >>>> + struct sample_userspace_args *userspace; /* Optional, > >>>> + * arguments for > >>>> userspace. */ > >>>> + struct sample_psample_args *psample; /* Optional, > >>>> + * arguments for psample. > >>>> */ > >>>> +}; > >>>> + > >>>> +/* Composes sample action according to 'args'. */ > >>>> static size_t > >>>> compose_sample_action(struct xlate_ctx *ctx, > >>>> - const uint32_t probability, > >>>> - const struct user_action_cookie *cookie, > >>>> - const odp_port_t tunnel_out_port, > >>>> - bool include_actions) > >>>> + const struct compose_sample_args *args) > >>>> { > >>>> - if (probability == 0) { > >>>> + if (args->probability == 0) { > >>>> /* No need to generate sampling or the inner action. */ > >>>> return 0; > >>>> } > >>>> > >>>> + /* At least one of userspace or psample config must be provided. */ > >>>> + ovs_assert(args->userspace || args->psample); > >>>> + > >>>> /* If the slow path meter is configured by the controller, > >>>> * insert a meter action before the user space action. */ > >>>> struct ofproto *ofproto = &ctx->xin->ofproto->up; > >>>> uint32_t meter_id = ofproto->slowpath_meter_id; > >>>> + size_t cookie_offset = 0; > >>>> + > >>>> + /* The meter action is only used to throttle userspace actions. > >>>> + * If they are not needed and the sampling rate is 100%, avoid > >>>> generating > >>>> + * a sample action. */ > >>>> + bool is_sample = (args->probability < UINT32_MAX || > >>>> + (args->userspace && meter_id != UINT32_MAX)); > >>>> > >>> > >>> This new line should be removed due to the way things got merged. > >> > >> Ack > >> > >>> > >>>> - /* When meter action is not required, avoid generate sample action > >>>> - * for 100% sampling rate. */ > >>>> - bool is_sample = probability < UINT32_MAX || meter_id != UINT32_MAX; > >>>> size_t sample_offset = 0, actions_offset = 0; > >>>> if (is_sample) { > >>>> sample_offset = nl_msg_start_nested(ctx->odp_actions, > >>>> OVS_ACTION_ATTR_SAMPLE); > >>>> nl_msg_put_u32(ctx->odp_actions, OVS_SAMPLE_ATTR_PROBABILITY, > >>>> - probability); > >>>> + args->probability); > >>>> actions_offset = nl_msg_start_nested(ctx->odp_actions, > >>>> OVS_SAMPLE_ATTR_ACTIONS); > >>>> } > >>>> > >>>> - if (meter_id != UINT32_MAX) { > >>>> - nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_METER, > >>>> meter_id); > >>>> + if (args->psample) { > >>>> + odp_put_psample_action(ctx->odp_actions, > >>>> + args->psample->group_id, > >>>> + args->psample->cookie.data, > >>>> + args->psample->cookie.size); > >>>> + } > >>>> + > >>>> + if (args->userspace) { > >>>> + if (meter_id != UINT32_MAX) { > >>>> + nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_METER, > >>>> meter_id); > >>>> + } > >>>> + > >>>> + odp_port_t odp_port = ofp_port_to_odp_port( > >>>> + ctx->xbridge, ctx->xin->flow.in_port.ofp_port); > >>>> + uint32_t pid = dpif_port_get_pid(ctx->xbridge->dpif, odp_port); > >>>> + int res = odp_put_userspace_action(pid, > >>>> &args->userspace->cookie, > >>>> + sizeof > >>>> args->userspace->cookie, > >>>> + > >>>> args->userspace->tunnel_out_port, > >>>> + > >>>> args->userspace->include_actions, > >>>> + ctx->odp_actions, > >>>> &cookie_offset); > >>>> + ovs_assert(res == 0); > >>>> } > >>>> > >>>> - odp_port_t odp_port = ofp_port_to_odp_port( > >>>> - ctx->xbridge, ctx->xin->flow.in_port.ofp_port); > >>>> - uint32_t pid = dpif_port_get_pid(ctx->xbridge->dpif, odp_port); > >>>> - size_t cookie_offset; > >>>> - int res = odp_put_userspace_action(pid, cookie, sizeof *cookie, > >>>> - tunnel_out_port, include_actions, > >>>> - ctx->odp_actions, > >>>> &cookie_offset); > >>>> - ovs_assert(res == 0); > >>>> if (is_sample) { > >>>> nl_msg_end_nested(ctx->odp_actions, actions_offset); > >>>> nl_msg_end_nested(ctx->odp_actions, sample_offset); > >>>> @@ -3428,19 +3474,23 @@ static size_t > >>>> compose_sflow_action(struct xlate_ctx *ctx) > >>>> { > >>>> struct dpif_sflow *sflow = ctx->xbridge->sflow; > >>>> + struct sample_userspace_args userspace = {}; > >>>> + struct compose_sample_args args = {}; > >>>> + > >>>> if (!sflow || ctx->xin->flow.in_port.ofp_port == OFPP_NONE) { > >>>> return 0; > >>>> } > >>>> > >>>> - struct user_action_cookie cookie; > >>>> + userspace.cookie.type = USER_ACTION_COOKIE_SFLOW; > >>>> + userspace.cookie.ofp_in_port = ctx->xin->flow.in_port.ofp_port; > >>>> + userspace.cookie.ofproto_uuid = ctx->xbridge->ofproto->uuid; > >>>> + userspace.tunnel_out_port = ODPP_NONE; > >>>> + userspace.include_actions = true; > >>>> > >>>> - memset(&cookie, 0, sizeof cookie); > >>>> - cookie.type = USER_ACTION_COOKIE_SFLOW; > >>>> - cookie.ofp_in_port = ctx->xin->flow.in_port.ofp_port; > >>>> - cookie.ofproto_uuid = ctx->xbridge->ofproto->uuid; > >>>> + args.probability = dpif_sflow_get_probability(sflow); > >>>> + args.userspace = &userspace; > >>>> > >>>> - return compose_sample_action(ctx, dpif_sflow_get_probability(sflow), > >>>> - &cookie, ODPP_NONE, true); > >>>> + return compose_sample_action(ctx, &args); > >>>> } > >>>> > >>>> /* If flow IPFIX is enabled, make sure IPFIX flow sample action > >>>> @@ -3451,7 +3501,10 @@ static void > >>>> compose_ipfix_action(struct xlate_ctx *ctx, odp_port_t output_odp_port) > >>>> { > >>>> struct dpif_ipfix *ipfix = ctx->xbridge->ipfix; > >>>> - odp_port_t tunnel_out_port = ODPP_NONE; > >>>> + struct sample_userspace_args userspace = {0}; > >>>> + struct compose_sample_args args = {0}; > >>> > >>> Should both not be {}; same as the previous function? > >>> > >> > >> Funny you mention it. "{}" is actually not part of the C standard until > >> C23 gets published (which has not yet happened IIUC). It is an extension > >> introduced by C++ but pretty much supported by most C compilers. "{0}" > >> is the pure-C way of it. > >> > >> I also dislike the inconsistency. > >> So I guess the correct way would be to write "{0}" in both cases. > >> > >> More on this: > >> https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2900.htm > > > > Well, we used to have compiler problem with doing {0} as some compilers > > only initialized the first element or dump warnings, for some previous > > patches we went with {}. I’m assuming our compilers are all up to date now, > > as we get no warning from the robot, so we should be ok. Ilya any feedback > > on this? > > I think, we should keep the memset in all cases. There are holes in > these structures and we're using memcmp for action list comparisons. >
Ack. Will keep the memset for the userspace cookie. Thanks > Best regards, Ilya Maximets. > _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev