On Tue, Jan 24, 2023 at 08:21:28PM +0100, Adrián Moreno wrote: > From: Adrian Moreno <amore...@redhat.com> > > IPFIX templates have to be sent for each Observation Domain ID. > Currently, a timer is kept at each dpif_ipfix_exporter to send them. > This works fine for per-bridge sampling where there is only one > Observation Domain ID per exporter. However, this is does not work for > per-flow sampling where more than one Observation Domain IDs can be > specified by the controller. In this case, ovs-vswitchd will only send > template information for one (arbitrary) DomainID. > > Fix per-flow sampling by using an hmap to keep a timer for each > Observation Domain ID. > > Signed-off-by: Adrian Moreno <amore...@redhat.com> > > --- > - v2: > - Fixed a potential race condition in unit test. > - v1: > - Added unit test. > - Drop domain_id from "struct dpif_ipfix_domain" since the hashmap > already uses it as index. > - Added OVS_REQUES(mutex) to dpif_ipfix_exporter_find_domain. > --- > ofproto/ofproto-dpif-ipfix.c | 127 ++++++++++++++++++++++++++++------- > tests/system-traffic.at | 52 ++++++++++++++ > 2 files changed, 156 insertions(+), 23 deletions(-) > > diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c > index 742eed399..fa71fda0f 100644 > --- a/ofproto/ofproto-dpif-ipfix.c > +++ b/ofproto/ofproto-dpif-ipfix.c > @@ -124,11 +124,18 @@ struct dpif_ipfix_port { > uint32_t ifindex; > }; > > +struct dpif_ipfix_domain { > + struct hmap_node hmap_node; /* In struct dpif_ipfix_exporter's domains. > */ > + time_t last_template_set_time; > +}; > + > struct dpif_ipfix_exporter { > uint32_t exporter_id; /* Exporting Process identifier */ > struct collectors *collectors; > uint32_t seq_number; > - time_t last_template_set_time; > + struct hmap domains; /* Contains struct dpif_ipfix_domain indexed by > + observation domain id. */ > + time_t last_stats_sent_time; > struct hmap cache_flow_key_map; /* ipfix_flow_cache_entry. */ > struct ovs_list cache_flow_start_timestamp_list; /* > ipfix_flow_cache_entry. */ > uint32_t cache_active_timeout; /* In seconds. */
I'm not sure if it is important, but this change bumps the cache_flow_key_map from the 1st to 2nd cacheline (on x86_64). This can be avoided by moving seq_number above collectors, as there is a 32-bit hole there. Possibly there is further scope for making this structure more cache-fiendly. But again, I'm not sure if it is important. Before: $ pahole ofproto/libofproto_la-ofproto-dpif-ipfix.o ... struct dpif_ipfix_exporter { uint32_t exporter_id; /* 0 4 */ /* XXX 4 bytes hole, try to pack */ struct collectors * collectors; /* 8 8 */ uint32_t seq_number; /* 16 4 */ /* XXX 4 bytes hole, try to pack */ time_t last_template_set_time; /* 24 8 */ struct hmap cache_flow_key_map; /* 32 32 */ /* --- cacheline 1 boundary (64 bytes) --- */ struct ovs_list cache_flow_start_timestamp_list; /* 64 16 */ uint32_t cache_active_timeout; /* 80 4 */ uint32_t cache_max_flows; /* 84 4 */ char * virtual_obs_id; /* 88 8 */ uint8_t virtual_obs_len; /* 96 1 */ /* XXX 7 bytes hole, try to pack */ ofproto_ipfix_stats ofproto_stats; /* 104 88 */ /* --- cacheline 3 boundary (192 bytes) --- */ struct dpif_ipfix_global_stats ipfix_global_stats; /* 192 152 */ /* size: 344, cachelines: 6, members: 12 */ /* sum members: 329, holes: 3, sum holes: 15 */ /* last cacheline: 24 bytes */ } ... After: $ pahole ofproto/libofproto_la-ofproto-dpif-ipfix.o ... struct dpif_ipfix_exporter { uint32_t exporter_id; /* 0 4 */ /* XXX 4 bytes hole, try to pack */ struct collectors * collectors; /* 8 8 */ uint32_t seq_number; /* 16 4 */ /* XXX 4 bytes hole, try to pack */ struct hmap domains; /* 24 32 */ time_t last_stats_sent_time; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ struct hmap cache_flow_key_map; /* 64 32 */ struct ovs_list cache_flow_start_timestamp_list; /* 96 16 */ uint32_t cache_active_timeout; /* 112 4 */ uint32_t cache_max_flows; /* 116 4 */ char * virtual_obs_id; /* 120 8 */ /* --- cacheline 2 boundary (128 bytes) --- */ uint8_t virtual_obs_len; /* 128 1 */ /* XXX 7 bytes hole, try to pack */ ofproto_ipfix_stats ofproto_stats; /* 136 88 */ /* --- cacheline 3 boundary (192 bytes) was 32 bytes ago --- */ struct dpif_ipfix_global_stats ipfix_global_stats; /* 224 152 */ /* size: 376, cachelines: 6, members: 13 */ /* sum members: 361, holes: 3, sum holes: 15 */ /* last cacheline: 56 bytes */ }; ... Proposal: $ git diff diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c index fa71fda0f276..1701d91e8a72 100644 --- a/ofproto/ofproto-dpif-ipfix.c +++ b/ofproto/ofproto-dpif-ipfix.c @@ -131,8 +131,8 @@ struct dpif_ipfix_domain { struct dpif_ipfix_exporter { uint32_t exporter_id; /* Exporting Process identifier */ - struct collectors *collectors; uint32_t seq_number; + struct collectors *collectors; struct hmap domains; /* Contains struct dpif_ipfix_domain indexed by observation domain id. */ time_t last_stats_sent_time; $ pahole ofproto/libofproto_la-ofproto-dpif-ipfix.o ... struct dpif_ipfix_exporter { uint32_t exporter_id; /* 0 4 */ uint32_t seq_number; /* 4 4 */ struct collectors * collectors; /* 8 8 */ struct hmap domains; /* 16 32 */ time_t last_stats_sent_time; /* 48 8 */ struct hmap cache_flow_key_map; /* 56 32 */ /* --- cacheline 1 boundary (64 bytes) was 24 bytes ago --- */ struct ovs_list cache_flow_start_timestamp_list; /* 88 16 */ uint32_t cache_active_timeout; /* 104 4 */ uint32_t cache_max_flows; /* 108 4 */ char * virtual_obs_id; /* 112 8 */ uint8_t virtual_obs_len; /* 120 1 */ /* XXX 7 bytes hole, try to pack */ /* --- cacheline 2 boundary (128 bytes) --- */ ofproto_ipfix_stats ofproto_stats; /* 128 88 */ /* --- cacheline 3 boundary (192 bytes) was 24 bytes ago --- */ struct dpif_ipfix_global_stats ipfix_global_stats; /* 216 152 */ /* size: 368, cachelines: 6, members: 13 */ /* sum members: 361, holes: 1, sum holes: 7 */ /* last cacheline: 48 bytes */ }; ... > diff --git a/tests/system-traffic.at b/tests/system-traffic.at > index 08c78ff57..1421ab0ab 100644 > --- a/tests/system-traffic.at > +++ b/tests/system-traffic.at > @@ -7488,3 +7488,55 @@ OVS_WAIT_UNTIL([cat p2.pcap | grep -E "0x0050: *0000 > *0000 *5002 *2000 *b85e *00 > > OVS_TRAFFIC_VSWITCHD_STOP > AT_CLEANUP > + > +AT_SETUP([ipfix - muliple domains]) > +AT_SKIP_IF([test $HAVE_TCPDUMP = no]) > +OVS_TRAFFIC_VSWITCHD_START() > + > +ADD_NAMESPACES(at_ns0, at_ns1) > + > +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24") > +ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24") > + > +dnl Create a dummy IPFIX collector just in localhost so that port seems open. > +OVS_DAEMONIZE([nc -l -u 127.0.0.1 5555 > /dev/null], [nc.pid]) > + > +OVS_DAEMONIZE([tcpdump -n -i lo udp port 5555 -w ipfix.pcap], [tcpdump.pid]) > +sleep 1 > + > +dnl Configure a Flow Sample Collector Set with id = 1. > +AT_CHECK([ovs-vsctl -- --id=@br get Bridge br0 \ > + -- --id=@i create IPFIX targets=\"127.0.0.1:5555\" \ > + -- create Flow_Sample_Collector_Set bridge=@br id=1 > ipfix=@i], > + [ignore], [ignore]) > + > +dnl Push flows that will allow communication between the network namespaces > +dnl and sample all ICMP traffic with multiple observation domain IDs. > +AT_DATA([flows.txt], [dnl > +table=0,priority=100,in_port=ovs-p0,ip,icmp > actions=sample(probability=65535,collector_set_id=1,obs_domain_id=1),NORMAL > +table=0,priority=100,in_port=ovs-p1,ip,icmp > actions=sample(probability=65535,collector_set_id=1,obs_domain_id=2),NORMAL > +table=0,priority=0,actions=NORMAL > +]) > +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) > + > +NS_CHECK_EXEC([at_ns0], [ping -q -c 3 -i 0.3 -w 2 10.1.1.2 | FORMAT_PING], > [0], [dnl > +3 packets transmitted, 3 received, 0% packet loss, time 0ms > +]) > + > +sleep 1 > +kill $(cat tcpdump.pid) > +wait $(cat tcpdump.pid) 2> /dev/null > + > +dnl Check templates (Flow ID = 0x02) where sent to ObservationDomain 1. > +AT_CHECK([test $(tcpdump -r ipfix.pcap --count "udp port 5555 and > udp[[20:4]] = 0x00000001 and udp[[24:2]] = 0x02" 2>/dev/null | tr -d > "packets") -gt 0]) > +dnl Check that exactly 3 samples (Flow ID != 0x02) where sent to > ObservationDomain 1. > +AT_CHECK([test $(tcpdump -r ipfix.pcap --count "udp port 5555 and > udp[[20:4]] = 0x00000001 and udp[[24:2]] != 0x02" 2>/dev/null | tr -d > "packets") -eq 3]) > + > +dnl Check templates (Flow ID = 0x02) where sent to ObservationDomain 2. > +AT_CHECK([test $(tcpdump -r ipfix.pcap --count "udp port 5555 and > udp[[20:4]] = 0x00000002 and udp[[24:2]] = 0x02" 2>/dev/null | tr -d > "packets") -gt 0]) > +dnl Check that exactly 3 samples (Flow ID != 0x02) where sent to > ObservationDomain 2. > +AT_CHECK([test $(tcpdump -r ipfix.pcap --count "udp port 5555 and > udp[[20:4]] = 0x00000002 and udp[[24:2]] != 0x02" 2>/dev/null | tr -d > "packets") -eq 3]) > + > +OVS_TRAFFIC_VSWITCHD_STOP > +AT_CLEANUP > + nit: git complains, I think it is about the trailing blank line above. $ git am a.patch Applying: ofproto-ipfix: use per-domain template timeouts .git/rebase-apply/patch:366: new blank line at EOF. + warning: 1 line adds whitespace errors. _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev