diff mbox series

[ovs-dev,1/2] ofproto-ipfix: use per-domain template timeouts

Message ID 20221018120214.1106988-2-amorenoz@redhat.com
State Superseded
Headers show
Series ofproto-ipfix: use per-domain template timeouts | expand

Checks

Context Check Description
ovsrobot/apply-robot warning apply and check: warning
ovsrobot/github-robot-_Build_and_Test success github build: passed
ovsrobot/intel-ovs-compilation success test: success

Commit Message

Adrian Moreno Oct. 18, 2022, 12:02 p.m. UTC
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 <amorenoz@redhat.com>
---
 ofproto/ofproto-dpif-ipfix.c | 130 ++++++++++++++++++++++++++++-------
 1 file changed, 107 insertions(+), 23 deletions(-)

Comments

0-day Robot Oct. 18, 2022, 12:18 p.m. UTC | #1
Bleep bloop.  Greetings Adrian Moreno, I am a robot and I have tried out your patch.
Thanks for your contribution.

I encountered some error that I wasn't expecting.  See the details below.


checkpatch:
WARNING: Comment with 'xxx' marker
#273 FILE: ofproto/ofproto-dpif-ipfix.c:2928:
        /* XXX: Make frequency of the (Options) Template and Exporter Process

Lines checked: 321, Warnings: 1, Errors: 0


Please check this out.  If you feel there has been an error, please email aconole@redhat.com

Thanks,
0-day Robot
Mike Pattrick Jan. 13, 2023, 5:55 a.m. UTC | #2
On Tue, Oct 18, 2022 at 8:02 AM Adrian Moreno <amorenoz@redhat.com> wrote:
>
> 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 <amorenoz@redhat.com>
> ---
>  ofproto/ofproto-dpif-ipfix.c | 130 ++++++++++++++++++++++++++++-------
>  1 file changed, 107 insertions(+), 23 deletions(-)
>
> diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c
> index 742eed399..98a3f9b71 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.*/
> +    uint32_t obs_domain_id;
> +    time_t last_template_set_time;

Just a thought, we end up setting hmap_node.hash to obs_domain_id, do
we need to hold on to that variable? It should be unique, could we
ditch obs_domain_id and just use HMAP_FOR_EACH_WITH_HASH below
instead?

> +};
> +
>  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. */
> +    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. */
> @@ -617,6 +624,9 @@ static void get_export_time_now(uint64_t *, uint32_t *);
>
>  static void dpif_ipfix_cache_expire_now(struct dpif_ipfix_exporter *, bool);
>
> +static void dpif_ipfix_exporter_del_domain(struct dpif_ipfix_exporter * ,
> +                                           struct dpif_ipfix_domain * );
> +
>  static bool
>  ofproto_ipfix_bridge_exporter_options_equal(
>      const struct ofproto_ipfix_bridge_exporter_options *a,
> @@ -697,13 +707,14 @@ dpif_ipfix_exporter_init(struct dpif_ipfix_exporter *exporter)
>      exporter->exporter_id = ++exporter_total_count;
>      exporter->collectors = NULL;
>      exporter->seq_number = 1;
> -    exporter->last_template_set_time = 0;
> +    exporter->last_stats_sent_time = 0;
>      hmap_init(&exporter->cache_flow_key_map);
>      ovs_list_init(&exporter->cache_flow_start_timestamp_list);
>      exporter->cache_active_timeout = 0;
>      exporter->cache_max_flows = 0;
>      exporter->virtual_obs_id = NULL;
>      exporter->virtual_obs_len = 0;
> +    hmap_init(&exporter->domains);
>
>      memset(&exporter->ipfix_global_stats, 0,
>             sizeof(struct dpif_ipfix_global_stats));
> @@ -711,6 +722,7 @@ dpif_ipfix_exporter_init(struct dpif_ipfix_exporter *exporter)
>
>  static void
>  dpif_ipfix_exporter_clear(struct dpif_ipfix_exporter *exporter)
> +    OVS_REQUIRES(mutex)
>  {
>      /* Flush the cache with flow end reason "forced end." */
>      dpif_ipfix_cache_expire_now(exporter, true);
> @@ -719,22 +731,29 @@ dpif_ipfix_exporter_clear(struct dpif_ipfix_exporter *exporter)
>      exporter->exporter_id = 0;
>      exporter->collectors = NULL;
>      exporter->seq_number = 1;
> -    exporter->last_template_set_time = 0;
> +    exporter->last_stats_sent_time = 0;
>      exporter->cache_active_timeout = 0;
>      exporter->cache_max_flows = 0;
>      free(exporter->virtual_obs_id);
>      exporter->virtual_obs_id = NULL;
>      exporter->virtual_obs_len = 0;
>
> +    struct dpif_ipfix_domain *dom;
> +    HMAP_FOR_EACH_SAFE (dom, hmap_node, &exporter->domains) {
> +        dpif_ipfix_exporter_del_domain(exporter, dom);
> +    }
> +
>      memset(&exporter->ipfix_global_stats, 0,
>             sizeof(struct dpif_ipfix_global_stats));
>  }
>
>  static void
>  dpif_ipfix_exporter_destroy(struct dpif_ipfix_exporter *exporter)
> +    OVS_REQUIRES(mutex)
>  {
>      dpif_ipfix_exporter_clear(exporter);
>      hmap_destroy(&exporter->cache_flow_key_map);
> +    hmap_destroy(&exporter->domains);
>  }
>
>  static bool
> @@ -742,7 +761,7 @@ dpif_ipfix_exporter_set_options(struct dpif_ipfix_exporter *exporter,
>                                  const struct sset *targets,
>                                  const uint32_t cache_active_timeout,
>                                  const uint32_t cache_max_flows,
> -                                const char *virtual_obs_id)
> +                                const char *virtual_obs_id) OVS_REQUIRES(mutex)
>  {
>      size_t virtual_obs_len;
>      collectors_destroy(exporter->collectors);
> @@ -769,6 +788,40 @@ dpif_ipfix_exporter_set_options(struct dpif_ipfix_exporter *exporter,
>      return true;
>  }
>
> +static struct dpif_ipfix_domain *
> +dpif_ipfix_exporter_find_domain(const struct dpif_ipfix_exporter *exporter,
> +                                uint32_t domain_id)

I believe this function would also call for an OVS_REQUIRES(mutex)

> +{
> +    struct dpif_ipfix_domain *dom;
> +    HMAP_FOR_EACH_IN_BUCKET (dom, hmap_node, hash_int(domain_id, 0),
> +                             &exporter->domains) {
> +        if (dom->obs_domain_id == domain_id) {
> +            return dom;
> +        }
> +    }
> +    return NULL;
> +}
> +
> +static struct dpif_ipfix_domain *
> +dpif_ipfix_exporter_insert_domain(struct dpif_ipfix_exporter *exporter,
> +                                  const uint32_t domain_id) OVS_REQUIRES(mutex)
> +{
> +    struct dpif_ipfix_domain *dom = xmalloc(sizeof *dom);
> +    dom->obs_domain_id = domain_id;
> +    dom->last_template_set_time = 0;
> +    hmap_insert(&exporter->domains, &dom->hmap_node, hash_int(domain_id, 0));
> +    return dom;
> +}
> +
> +static void
> +dpif_ipfix_exporter_del_domain(struct dpif_ipfix_exporter *exporter,
> +                               struct dpif_ipfix_domain *dom)
> +    OVS_REQUIRES(mutex)
> +{
> +    hmap_remove(&exporter->domains, &dom->hmap_node);
> +    free(dom);
> +}
> +
>  static struct dpif_ipfix_port *
>  dpif_ipfix_find_port(const struct dpif_ipfix *di,
>                       odp_port_t odp_port) OVS_REQUIRES(mutex)
> @@ -909,6 +962,7 @@ dpif_ipfix_bridge_exporter_init(struct dpif_ipfix_bridge_exporter *exporter)
>
>  static void
>  dpif_ipfix_bridge_exporter_clear(struct dpif_ipfix_bridge_exporter *exporter)
> +    OVS_REQUIRES(mutex)
>  {
>      dpif_ipfix_exporter_clear(&exporter->exporter);
>      ofproto_ipfix_bridge_exporter_options_destroy(exporter->options);
> @@ -918,6 +972,7 @@ dpif_ipfix_bridge_exporter_clear(struct dpif_ipfix_bridge_exporter *exporter)
>
>  static void
>  dpif_ipfix_bridge_exporter_destroy(struct dpif_ipfix_bridge_exporter *exporter)
> +    OVS_REQUIRES(mutex)
>  {
>      dpif_ipfix_bridge_exporter_clear(exporter);
>      dpif_ipfix_exporter_destroy(&exporter->exporter);
> @@ -927,7 +982,7 @@ static void
>  dpif_ipfix_bridge_exporter_set_options(
>      struct dpif_ipfix_bridge_exporter *exporter,
>      const struct ofproto_ipfix_bridge_exporter_options *options,
> -    bool *options_changed)
> +    bool *options_changed) OVS_REQUIRES(mutex)
>  {
>      if (!options || sset_is_empty(&options->targets)) {
>          /* No point in doing any work if there are no targets. */
> @@ -1003,6 +1058,7 @@ dpif_ipfix_flow_exporter_init(struct dpif_ipfix_flow_exporter *exporter)
>
>  static void
>  dpif_ipfix_flow_exporter_clear(struct dpif_ipfix_flow_exporter *exporter)
> +    OVS_REQUIRES(mutex)
>  {
>      dpif_ipfix_exporter_clear(&exporter->exporter);
>      ofproto_ipfix_flow_exporter_options_destroy(exporter->options);
> @@ -1011,6 +1067,7 @@ dpif_ipfix_flow_exporter_clear(struct dpif_ipfix_flow_exporter *exporter)
>
>  static void
>  dpif_ipfix_flow_exporter_destroy(struct dpif_ipfix_flow_exporter *exporter)
> +    OVS_REQUIRES(mutex)
>  {
>      dpif_ipfix_flow_exporter_clear(exporter);
>      dpif_ipfix_exporter_destroy(&exporter->exporter);
> @@ -1020,7 +1077,7 @@ static bool
>  dpif_ipfix_flow_exporter_set_options(
>      struct dpif_ipfix_flow_exporter *exporter,
>      const struct ofproto_ipfix_flow_exporter_options *options,
> -    bool *options_changed)
> +    bool *options_changed) OVS_REQUIRES(mutex)
>  {
>      if (sset_is_empty(&options->targets)) {
>          /* No point in doing any work if there are no targets. */
> @@ -1071,6 +1128,7 @@ dpif_ipfix_flow_exporter_set_options(
>  static void
>  remove_flow_exporter(struct dpif_ipfix *di,
>                       struct dpif_ipfix_flow_exporter_map_node *node)
> +                     OVS_REQUIRES(mutex)
>  {
>      hmap_remove(&di->flow_exporter_map, &node->node);
>      dpif_ipfix_flow_exporter_destroy(&node->exporter);
> @@ -2000,6 +2058,7 @@ static void
>  ipfix_cache_update(struct dpif_ipfix_exporter *exporter,
>                     struct ipfix_flow_cache_entry *entry,
>                     enum ipfix_sampled_packet_type sampled_pkt_type)
> +                   OVS_REQUIRES(mutex)
>  {
>      struct ipfix_flow_cache_entry *old_entry;
>      size_t current_flows = 0;
> @@ -2811,14 +2870,36 @@ dpif_ipfix_flow_sample(struct dpif_ipfix *di, const struct dp_packet *packet,
>      ovs_mutex_unlock(&mutex);
>  }
>
> +static bool
> +dpif_ipfix_should_send_template(struct dpif_ipfix_exporter *exporter,
> +                                const uint32_t observation_domain_id,
> +                                const uint32_t export_time_sec)
> +    OVS_REQUIRES(mutex)
> +{
> +    struct dpif_ipfix_domain *domain;
> +    domain = dpif_ipfix_exporter_find_domain(exporter,
> +                                             observation_domain_id);
> +    if (!domain) {
> +        /* First time we see this obs_domain_id. */
> +        domain = dpif_ipfix_exporter_insert_domain(exporter,
> +                                                   observation_domain_id);
> +    }
> +
> +    if ((domain->last_template_set_time + IPFIX_TEMPLATE_INTERVAL)
> +        <= export_time_sec) {
> +        domain->last_template_set_time = export_time_sec;
> +        return true;
> +    }
> +    return false;
> +}
> +
>  static void
>  dpif_ipfix_cache_expire(struct dpif_ipfix_exporter *exporter,
>                          bool forced_end, const uint64_t export_time_usec,
> -                        const uint32_t export_time_sec)
> +                        const uint32_t export_time_sec) OVS_REQUIRES(mutex)
>  {
>      struct ipfix_flow_cache_entry *entry;
>      uint64_t max_flow_start_timestamp_usec;
> -    bool template_msg_sent = false;
>      enum ipfix_flow_end_reason flow_end_reason;
>
>      if (ovs_list_is_empty(&exporter->cache_flow_start_timestamp_list)) {
> @@ -2844,25 +2925,28 @@ dpif_ipfix_cache_expire(struct dpif_ipfix_exporter *exporter,
>              break;
>          }
>
> -        ovs_list_remove(&entry->cache_flow_start_timestamp_list_node);
> -        hmap_remove(&exporter->cache_flow_key_map,
> -                    &entry->flow_key_map_node);
> +        /* XXX: Make frequency of the (Options) Template and Exporter Process
> +         * Statistics transmission configurable.
> +         * Cf. IETF RFC 5101 Section 4.3. and 10.3.6. */
> +        if ((exporter->last_stats_sent_time + IPFIX_TEMPLATE_INTERVAL)
> +             <= export_time_sec) {
> +            exporter->last_stats_sent_time = export_time_sec;
> +            ipfix_send_exporter_data_msg(exporter, export_time_sec);
> +        }
>
> -         /* XXX: Make frequency of the (Options) Template and Exporter Process
> -          * Statistics transmission configurable.
> -          * Cf. IETF RFC 5101 Section 4.3. and 10.3.6. */
> -        if (!template_msg_sent
> -            && (exporter->last_template_set_time + IPFIX_TEMPLATE_INTERVAL)
> -                <= export_time_sec) {
> +        if (dpif_ipfix_should_send_template(exporter,
> +                                            entry->flow_key.obs_domain_id,
> +                                            export_time_sec)) {
> +            VLOG_DBG("Sending templates for ObservationDomainID %"PRIu32,
> +                     entry->flow_key.obs_domain_id);
>              ipfix_send_template_msgs(exporter, export_time_sec,
>                                       entry->flow_key.obs_domain_id);
> -            exporter->last_template_set_time = export_time_sec;
> -            template_msg_sent = true;
> -
> -            /* Send Exporter Process Statistics. */
> -            ipfix_send_exporter_data_msg(exporter, export_time_sec);
>          }
>
> +        ovs_list_remove(&entry->cache_flow_start_timestamp_list_node);
> +        hmap_remove(&exporter->cache_flow_key_map,
> +                    &entry->flow_key_map_node);
> +
>          /* XXX: Group multiple data records for the same obs domain id
>           * into the same message. */
>          ipfix_send_data_msg(exporter, export_time_sec, entry, flow_end_reason);
> @@ -2883,7 +2967,7 @@ get_export_time_now(uint64_t *export_time_usec, uint32_t *export_time_sec)
>
>  static void
>  dpif_ipfix_cache_expire_now(struct dpif_ipfix_exporter *exporter,
> -                            bool forced_end)
> +                            bool forced_end) OVS_REQUIRES(mutex)
>  {
>      uint64_t export_time_usec;
>      uint32_t export_time_sec;
> --
> 2.37.3
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
Adrian Moreno Jan. 13, 2023, 10:07 a.m. UTC | #3
Thanks for the review Mike,

I'll update the patch.

On 1/13/23 06:55, Mike Pattrick wrote:
> On Tue, Oct 18, 2022 at 8:02 AM Adrian Moreno <amorenoz@redhat.com> wrote:
>>
>> 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 <amorenoz@redhat.com>
>> ---
>>   ofproto/ofproto-dpif-ipfix.c | 130 ++++++++++++++++++++++++++++-------
>>   1 file changed, 107 insertions(+), 23 deletions(-)
>>
>> diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c
>> index 742eed399..98a3f9b71 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.*/
>> +    uint32_t obs_domain_id;
>> +    time_t last_template_set_time;
> 
> Just a thought, we end up setting hmap_node.hash to obs_domain_id, do
> we need to hold on to that variable? It should be unique, could we
> ditch obs_domain_id and just use HMAP_FOR_EACH_WITH_HASH below
> instead?
> 

Good point! I think you're right. I'll give it a try.

>> +};
>> +
>>   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. */
>> +    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. */
>> @@ -617,6 +624,9 @@ static void get_export_time_now(uint64_t *, uint32_t *);
>>
>>   static void dpif_ipfix_cache_expire_now(struct dpif_ipfix_exporter *, bool);
>>
>> +static void dpif_ipfix_exporter_del_domain(struct dpif_ipfix_exporter * ,
>> +                                           struct dpif_ipfix_domain * );
>> +
>>   static bool
>>   ofproto_ipfix_bridge_exporter_options_equal(
>>       const struct ofproto_ipfix_bridge_exporter_options *a,
>> @@ -697,13 +707,14 @@ dpif_ipfix_exporter_init(struct dpif_ipfix_exporter *exporter)
>>       exporter->exporter_id = ++exporter_total_count;
>>       exporter->collectors = NULL;
>>       exporter->seq_number = 1;
>> -    exporter->last_template_set_time = 0;
>> +    exporter->last_stats_sent_time = 0;
>>       hmap_init(&exporter->cache_flow_key_map);
>>       ovs_list_init(&exporter->cache_flow_start_timestamp_list);
>>       exporter->cache_active_timeout = 0;
>>       exporter->cache_max_flows = 0;
>>       exporter->virtual_obs_id = NULL;
>>       exporter->virtual_obs_len = 0;
>> +    hmap_init(&exporter->domains);
>>
>>       memset(&exporter->ipfix_global_stats, 0,
>>              sizeof(struct dpif_ipfix_global_stats));
>> @@ -711,6 +722,7 @@ dpif_ipfix_exporter_init(struct dpif_ipfix_exporter *exporter)
>>
>>   static void
>>   dpif_ipfix_exporter_clear(struct dpif_ipfix_exporter *exporter)
>> +    OVS_REQUIRES(mutex)
>>   {
>>       /* Flush the cache with flow end reason "forced end." */
>>       dpif_ipfix_cache_expire_now(exporter, true);
>> @@ -719,22 +731,29 @@ dpif_ipfix_exporter_clear(struct dpif_ipfix_exporter *exporter)
>>       exporter->exporter_id = 0;
>>       exporter->collectors = NULL;
>>       exporter->seq_number = 1;
>> -    exporter->last_template_set_time = 0;
>> +    exporter->last_stats_sent_time = 0;
>>       exporter->cache_active_timeout = 0;
>>       exporter->cache_max_flows = 0;
>>       free(exporter->virtual_obs_id);
>>       exporter->virtual_obs_id = NULL;
>>       exporter->virtual_obs_len = 0;
>>
>> +    struct dpif_ipfix_domain *dom;
>> +    HMAP_FOR_EACH_SAFE (dom, hmap_node, &exporter->domains) {
>> +        dpif_ipfix_exporter_del_domain(exporter, dom);
>> +    }
>> +
>>       memset(&exporter->ipfix_global_stats, 0,
>>              sizeof(struct dpif_ipfix_global_stats));
>>   }
>>
>>   static void
>>   dpif_ipfix_exporter_destroy(struct dpif_ipfix_exporter *exporter)
>> +    OVS_REQUIRES(mutex)
>>   {
>>       dpif_ipfix_exporter_clear(exporter);
>>       hmap_destroy(&exporter->cache_flow_key_map);
>> +    hmap_destroy(&exporter->domains);
>>   }
>>
>>   static bool
>> @@ -742,7 +761,7 @@ dpif_ipfix_exporter_set_options(struct dpif_ipfix_exporter *exporter,
>>                                   const struct sset *targets,
>>                                   const uint32_t cache_active_timeout,
>>                                   const uint32_t cache_max_flows,
>> -                                const char *virtual_obs_id)
>> +                                const char *virtual_obs_id) OVS_REQUIRES(mutex)
>>   {
>>       size_t virtual_obs_len;
>>       collectors_destroy(exporter->collectors);
>> @@ -769,6 +788,40 @@ dpif_ipfix_exporter_set_options(struct dpif_ipfix_exporter *exporter,
>>       return true;
>>   }
>>
>> +static struct dpif_ipfix_domain *
>> +dpif_ipfix_exporter_find_domain(const struct dpif_ipfix_exporter *exporter,
>> +                                uint32_t domain_id)
> 
> I believe this function would also call for an OVS_REQUIRES(mutex)
> 

Yes. Good catch. Thanks.

>> +{
>> +    struct dpif_ipfix_domain *dom;
>> +    HMAP_FOR_EACH_IN_BUCKET (dom, hmap_node, hash_int(domain_id, 0),
>> +                             &exporter->domains) {
>> +        if (dom->obs_domain_id == domain_id) {
>> +            return dom;
>> +        }
>> +    }
>> +    return NULL;
>> +}
>> +
>> +static struct dpif_ipfix_domain *
>> +dpif_ipfix_exporter_insert_domain(struct dpif_ipfix_exporter *exporter,
>> +                                  const uint32_t domain_id) OVS_REQUIRES(mutex)
>> +{
>> +    struct dpif_ipfix_domain *dom = xmalloc(sizeof *dom);
>> +    dom->obs_domain_id = domain_id;
>> +    dom->last_template_set_time = 0;
>> +    hmap_insert(&exporter->domains, &dom->hmap_node, hash_int(domain_id, 0));
>> +    return dom;
>> +}
>> +
>> +static void
>> +dpif_ipfix_exporter_del_domain(struct dpif_ipfix_exporter *exporter,
>> +                               struct dpif_ipfix_domain *dom)
>> +    OVS_REQUIRES(mutex)
>> +{
>> +    hmap_remove(&exporter->domains, &dom->hmap_node);
>> +    free(dom);
>> +}
>> +
>>   static struct dpif_ipfix_port *
>>   dpif_ipfix_find_port(const struct dpif_ipfix *di,
>>                        odp_port_t odp_port) OVS_REQUIRES(mutex)
>> @@ -909,6 +962,7 @@ dpif_ipfix_bridge_exporter_init(struct dpif_ipfix_bridge_exporter *exporter)
>>
>>   static void
>>   dpif_ipfix_bridge_exporter_clear(struct dpif_ipfix_bridge_exporter *exporter)
>> +    OVS_REQUIRES(mutex)
>>   {
>>       dpif_ipfix_exporter_clear(&exporter->exporter);
>>       ofproto_ipfix_bridge_exporter_options_destroy(exporter->options);
>> @@ -918,6 +972,7 @@ dpif_ipfix_bridge_exporter_clear(struct dpif_ipfix_bridge_exporter *exporter)
>>
>>   static void
>>   dpif_ipfix_bridge_exporter_destroy(struct dpif_ipfix_bridge_exporter *exporter)
>> +    OVS_REQUIRES(mutex)
>>   {
>>       dpif_ipfix_bridge_exporter_clear(exporter);
>>       dpif_ipfix_exporter_destroy(&exporter->exporter);
>> @@ -927,7 +982,7 @@ static void
>>   dpif_ipfix_bridge_exporter_set_options(
>>       struct dpif_ipfix_bridge_exporter *exporter,
>>       const struct ofproto_ipfix_bridge_exporter_options *options,
>> -    bool *options_changed)
>> +    bool *options_changed) OVS_REQUIRES(mutex)
>>   {
>>       if (!options || sset_is_empty(&options->targets)) {
>>           /* No point in doing any work if there are no targets. */
>> @@ -1003,6 +1058,7 @@ dpif_ipfix_flow_exporter_init(struct dpif_ipfix_flow_exporter *exporter)
>>
>>   static void
>>   dpif_ipfix_flow_exporter_clear(struct dpif_ipfix_flow_exporter *exporter)
>> +    OVS_REQUIRES(mutex)
>>   {
>>       dpif_ipfix_exporter_clear(&exporter->exporter);
>>       ofproto_ipfix_flow_exporter_options_destroy(exporter->options);
>> @@ -1011,6 +1067,7 @@ dpif_ipfix_flow_exporter_clear(struct dpif_ipfix_flow_exporter *exporter)
>>
>>   static void
>>   dpif_ipfix_flow_exporter_destroy(struct dpif_ipfix_flow_exporter *exporter)
>> +    OVS_REQUIRES(mutex)
>>   {
>>       dpif_ipfix_flow_exporter_clear(exporter);
>>       dpif_ipfix_exporter_destroy(&exporter->exporter);
>> @@ -1020,7 +1077,7 @@ static bool
>>   dpif_ipfix_flow_exporter_set_options(
>>       struct dpif_ipfix_flow_exporter *exporter,
>>       const struct ofproto_ipfix_flow_exporter_options *options,
>> -    bool *options_changed)
>> +    bool *options_changed) OVS_REQUIRES(mutex)
>>   {
>>       if (sset_is_empty(&options->targets)) {
>>           /* No point in doing any work if there are no targets. */
>> @@ -1071,6 +1128,7 @@ dpif_ipfix_flow_exporter_set_options(
>>   static void
>>   remove_flow_exporter(struct dpif_ipfix *di,
>>                        struct dpif_ipfix_flow_exporter_map_node *node)
>> +                     OVS_REQUIRES(mutex)
>>   {
>>       hmap_remove(&di->flow_exporter_map, &node->node);
>>       dpif_ipfix_flow_exporter_destroy(&node->exporter);
>> @@ -2000,6 +2058,7 @@ static void
>>   ipfix_cache_update(struct dpif_ipfix_exporter *exporter,
>>                      struct ipfix_flow_cache_entry *entry,
>>                      enum ipfix_sampled_packet_type sampled_pkt_type)
>> +                   OVS_REQUIRES(mutex)
>>   {
>>       struct ipfix_flow_cache_entry *old_entry;
>>       size_t current_flows = 0;
>> @@ -2811,14 +2870,36 @@ dpif_ipfix_flow_sample(struct dpif_ipfix *di, const struct dp_packet *packet,
>>       ovs_mutex_unlock(&mutex);
>>   }
>>
>> +static bool
>> +dpif_ipfix_should_send_template(struct dpif_ipfix_exporter *exporter,
>> +                                const uint32_t observation_domain_id,
>> +                                const uint32_t export_time_sec)
>> +    OVS_REQUIRES(mutex)
>> +{
>> +    struct dpif_ipfix_domain *domain;
>> +    domain = dpif_ipfix_exporter_find_domain(exporter,
>> +                                             observation_domain_id);
>> +    if (!domain) {
>> +        /* First time we see this obs_domain_id. */
>> +        domain = dpif_ipfix_exporter_insert_domain(exporter,
>> +                                                   observation_domain_id);
>> +    }
>> +
>> +    if ((domain->last_template_set_time + IPFIX_TEMPLATE_INTERVAL)
>> +        <= export_time_sec) {
>> +        domain->last_template_set_time = export_time_sec;
>> +        return true;
>> +    }
>> +    return false;
>> +}
>> +
>>   static void
>>   dpif_ipfix_cache_expire(struct dpif_ipfix_exporter *exporter,
>>                           bool forced_end, const uint64_t export_time_usec,
>> -                        const uint32_t export_time_sec)
>> +                        const uint32_t export_time_sec) OVS_REQUIRES(mutex)
>>   {
>>       struct ipfix_flow_cache_entry *entry;
>>       uint64_t max_flow_start_timestamp_usec;
>> -    bool template_msg_sent = false;
>>       enum ipfix_flow_end_reason flow_end_reason;
>>
>>       if (ovs_list_is_empty(&exporter->cache_flow_start_timestamp_list)) {
>> @@ -2844,25 +2925,28 @@ dpif_ipfix_cache_expire(struct dpif_ipfix_exporter *exporter,
>>               break;
>>           }
>>
>> -        ovs_list_remove(&entry->cache_flow_start_timestamp_list_node);
>> -        hmap_remove(&exporter->cache_flow_key_map,
>> -                    &entry->flow_key_map_node);
>> +        /* XXX: Make frequency of the (Options) Template and Exporter Process
>> +         * Statistics transmission configurable.
>> +         * Cf. IETF RFC 5101 Section 4.3. and 10.3.6. */
>> +        if ((exporter->last_stats_sent_time + IPFIX_TEMPLATE_INTERVAL)
>> +             <= export_time_sec) {
>> +            exporter->last_stats_sent_time = export_time_sec;
>> +            ipfix_send_exporter_data_msg(exporter, export_time_sec);
>> +        }
>>
>> -         /* XXX: Make frequency of the (Options) Template and Exporter Process
>> -          * Statistics transmission configurable.
>> -          * Cf. IETF RFC 5101 Section 4.3. and 10.3.6. */
>> -        if (!template_msg_sent
>> -            && (exporter->last_template_set_time + IPFIX_TEMPLATE_INTERVAL)
>> -                <= export_time_sec) {
>> +        if (dpif_ipfix_should_send_template(exporter,
>> +                                            entry->flow_key.obs_domain_id,
>> +                                            export_time_sec)) {
>> +            VLOG_DBG("Sending templates for ObservationDomainID %"PRIu32,
>> +                     entry->flow_key.obs_domain_id);
>>               ipfix_send_template_msgs(exporter, export_time_sec,
>>                                        entry->flow_key.obs_domain_id);
>> -            exporter->last_template_set_time = export_time_sec;
>> -            template_msg_sent = true;
>> -
>> -            /* Send Exporter Process Statistics. */
>> -            ipfix_send_exporter_data_msg(exporter, export_time_sec);
>>           }
>>
>> +        ovs_list_remove(&entry->cache_flow_start_timestamp_list_node);
>> +        hmap_remove(&exporter->cache_flow_key_map,
>> +                    &entry->flow_key_map_node);
>> +
>>           /* XXX: Group multiple data records for the same obs domain id
>>            * into the same message. */
>>           ipfix_send_data_msg(exporter, export_time_sec, entry, flow_end_reason);
>> @@ -2883,7 +2967,7 @@ get_export_time_now(uint64_t *export_time_usec, uint32_t *export_time_sec)
>>
>>   static void
>>   dpif_ipfix_cache_expire_now(struct dpif_ipfix_exporter *exporter,
>> -                            bool forced_end)
>> +                            bool forced_end) OVS_REQUIRES(mutex)
>>   {
>>       uint64_t export_time_usec;
>>       uint32_t export_time_sec;
>> --
>> 2.37.3
>>
>> _______________________________________________
>> dev mailing list
>> dev@openvswitch.org
>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>
>
diff mbox series

Patch

diff --git a/ofproto/ofproto-dpif-ipfix.c b/ofproto/ofproto-dpif-ipfix.c
index 742eed399..98a3f9b71 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.*/
+    uint32_t obs_domain_id;
+    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. */
+    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. */
@@ -617,6 +624,9 @@  static void get_export_time_now(uint64_t *, uint32_t *);
 
 static void dpif_ipfix_cache_expire_now(struct dpif_ipfix_exporter *, bool);
 
+static void dpif_ipfix_exporter_del_domain(struct dpif_ipfix_exporter * ,
+                                           struct dpif_ipfix_domain * );
+
 static bool
 ofproto_ipfix_bridge_exporter_options_equal(
     const struct ofproto_ipfix_bridge_exporter_options *a,
@@ -697,13 +707,14 @@  dpif_ipfix_exporter_init(struct dpif_ipfix_exporter *exporter)
     exporter->exporter_id = ++exporter_total_count;
     exporter->collectors = NULL;
     exporter->seq_number = 1;
-    exporter->last_template_set_time = 0;
+    exporter->last_stats_sent_time = 0;
     hmap_init(&exporter->cache_flow_key_map);
     ovs_list_init(&exporter->cache_flow_start_timestamp_list);
     exporter->cache_active_timeout = 0;
     exporter->cache_max_flows = 0;
     exporter->virtual_obs_id = NULL;
     exporter->virtual_obs_len = 0;
+    hmap_init(&exporter->domains);
 
     memset(&exporter->ipfix_global_stats, 0,
            sizeof(struct dpif_ipfix_global_stats));
@@ -711,6 +722,7 @@  dpif_ipfix_exporter_init(struct dpif_ipfix_exporter *exporter)
 
 static void
 dpif_ipfix_exporter_clear(struct dpif_ipfix_exporter *exporter)
+    OVS_REQUIRES(mutex)
 {
     /* Flush the cache with flow end reason "forced end." */
     dpif_ipfix_cache_expire_now(exporter, true);
@@ -719,22 +731,29 @@  dpif_ipfix_exporter_clear(struct dpif_ipfix_exporter *exporter)
     exporter->exporter_id = 0;
     exporter->collectors = NULL;
     exporter->seq_number = 1;
-    exporter->last_template_set_time = 0;
+    exporter->last_stats_sent_time = 0;
     exporter->cache_active_timeout = 0;
     exporter->cache_max_flows = 0;
     free(exporter->virtual_obs_id);
     exporter->virtual_obs_id = NULL;
     exporter->virtual_obs_len = 0;
 
+    struct dpif_ipfix_domain *dom;
+    HMAP_FOR_EACH_SAFE (dom, hmap_node, &exporter->domains) {
+        dpif_ipfix_exporter_del_domain(exporter, dom);
+    }
+
     memset(&exporter->ipfix_global_stats, 0,
            sizeof(struct dpif_ipfix_global_stats));
 }
 
 static void
 dpif_ipfix_exporter_destroy(struct dpif_ipfix_exporter *exporter)
+    OVS_REQUIRES(mutex)
 {
     dpif_ipfix_exporter_clear(exporter);
     hmap_destroy(&exporter->cache_flow_key_map);
+    hmap_destroy(&exporter->domains);
 }
 
 static bool
@@ -742,7 +761,7 @@  dpif_ipfix_exporter_set_options(struct dpif_ipfix_exporter *exporter,
                                 const struct sset *targets,
                                 const uint32_t cache_active_timeout,
                                 const uint32_t cache_max_flows,
-                                const char *virtual_obs_id)
+                                const char *virtual_obs_id) OVS_REQUIRES(mutex)
 {
     size_t virtual_obs_len;
     collectors_destroy(exporter->collectors);
@@ -769,6 +788,40 @@  dpif_ipfix_exporter_set_options(struct dpif_ipfix_exporter *exporter,
     return true;
 }
 
+static struct dpif_ipfix_domain *
+dpif_ipfix_exporter_find_domain(const struct dpif_ipfix_exporter *exporter,
+                                uint32_t domain_id)
+{
+    struct dpif_ipfix_domain *dom;
+    HMAP_FOR_EACH_IN_BUCKET (dom, hmap_node, hash_int(domain_id, 0),
+                             &exporter->domains) {
+        if (dom->obs_domain_id == domain_id) {
+            return dom;
+        }
+    }
+    return NULL;
+}
+
+static struct dpif_ipfix_domain *
+dpif_ipfix_exporter_insert_domain(struct dpif_ipfix_exporter *exporter,
+                                  const uint32_t domain_id) OVS_REQUIRES(mutex)
+{
+    struct dpif_ipfix_domain *dom = xmalloc(sizeof *dom);
+    dom->obs_domain_id = domain_id;
+    dom->last_template_set_time = 0;
+    hmap_insert(&exporter->domains, &dom->hmap_node, hash_int(domain_id, 0));
+    return dom;
+}
+
+static void
+dpif_ipfix_exporter_del_domain(struct dpif_ipfix_exporter *exporter,
+                               struct dpif_ipfix_domain *dom)
+    OVS_REQUIRES(mutex)
+{
+    hmap_remove(&exporter->domains, &dom->hmap_node);
+    free(dom);
+}
+
 static struct dpif_ipfix_port *
 dpif_ipfix_find_port(const struct dpif_ipfix *di,
                      odp_port_t odp_port) OVS_REQUIRES(mutex)
@@ -909,6 +962,7 @@  dpif_ipfix_bridge_exporter_init(struct dpif_ipfix_bridge_exporter *exporter)
 
 static void
 dpif_ipfix_bridge_exporter_clear(struct dpif_ipfix_bridge_exporter *exporter)
+    OVS_REQUIRES(mutex)
 {
     dpif_ipfix_exporter_clear(&exporter->exporter);
     ofproto_ipfix_bridge_exporter_options_destroy(exporter->options);
@@ -918,6 +972,7 @@  dpif_ipfix_bridge_exporter_clear(struct dpif_ipfix_bridge_exporter *exporter)
 
 static void
 dpif_ipfix_bridge_exporter_destroy(struct dpif_ipfix_bridge_exporter *exporter)
+    OVS_REQUIRES(mutex)
 {
     dpif_ipfix_bridge_exporter_clear(exporter);
     dpif_ipfix_exporter_destroy(&exporter->exporter);
@@ -927,7 +982,7 @@  static void
 dpif_ipfix_bridge_exporter_set_options(
     struct dpif_ipfix_bridge_exporter *exporter,
     const struct ofproto_ipfix_bridge_exporter_options *options,
-    bool *options_changed)
+    bool *options_changed) OVS_REQUIRES(mutex)
 {
     if (!options || sset_is_empty(&options->targets)) {
         /* No point in doing any work if there are no targets. */
@@ -1003,6 +1058,7 @@  dpif_ipfix_flow_exporter_init(struct dpif_ipfix_flow_exporter *exporter)
 
 static void
 dpif_ipfix_flow_exporter_clear(struct dpif_ipfix_flow_exporter *exporter)
+    OVS_REQUIRES(mutex)
 {
     dpif_ipfix_exporter_clear(&exporter->exporter);
     ofproto_ipfix_flow_exporter_options_destroy(exporter->options);
@@ -1011,6 +1067,7 @@  dpif_ipfix_flow_exporter_clear(struct dpif_ipfix_flow_exporter *exporter)
 
 static void
 dpif_ipfix_flow_exporter_destroy(struct dpif_ipfix_flow_exporter *exporter)
+    OVS_REQUIRES(mutex)
 {
     dpif_ipfix_flow_exporter_clear(exporter);
     dpif_ipfix_exporter_destroy(&exporter->exporter);
@@ -1020,7 +1077,7 @@  static bool
 dpif_ipfix_flow_exporter_set_options(
     struct dpif_ipfix_flow_exporter *exporter,
     const struct ofproto_ipfix_flow_exporter_options *options,
-    bool *options_changed)
+    bool *options_changed) OVS_REQUIRES(mutex)
 {
     if (sset_is_empty(&options->targets)) {
         /* No point in doing any work if there are no targets. */
@@ -1071,6 +1128,7 @@  dpif_ipfix_flow_exporter_set_options(
 static void
 remove_flow_exporter(struct dpif_ipfix *di,
                      struct dpif_ipfix_flow_exporter_map_node *node)
+                     OVS_REQUIRES(mutex)
 {
     hmap_remove(&di->flow_exporter_map, &node->node);
     dpif_ipfix_flow_exporter_destroy(&node->exporter);
@@ -2000,6 +2058,7 @@  static void
 ipfix_cache_update(struct dpif_ipfix_exporter *exporter,
                    struct ipfix_flow_cache_entry *entry,
                    enum ipfix_sampled_packet_type sampled_pkt_type)
+                   OVS_REQUIRES(mutex)
 {
     struct ipfix_flow_cache_entry *old_entry;
     size_t current_flows = 0;
@@ -2811,14 +2870,36 @@  dpif_ipfix_flow_sample(struct dpif_ipfix *di, const struct dp_packet *packet,
     ovs_mutex_unlock(&mutex);
 }
 
+static bool
+dpif_ipfix_should_send_template(struct dpif_ipfix_exporter *exporter,
+                                const uint32_t observation_domain_id,
+                                const uint32_t export_time_sec)
+    OVS_REQUIRES(mutex)
+{
+    struct dpif_ipfix_domain *domain;
+    domain = dpif_ipfix_exporter_find_domain(exporter,
+                                             observation_domain_id);
+    if (!domain) {
+        /* First time we see this obs_domain_id. */
+        domain = dpif_ipfix_exporter_insert_domain(exporter,
+                                                   observation_domain_id);
+    }
+
+    if ((domain->last_template_set_time + IPFIX_TEMPLATE_INTERVAL)
+        <= export_time_sec) {
+        domain->last_template_set_time = export_time_sec;
+        return true;
+    }
+    return false;
+}
+
 static void
 dpif_ipfix_cache_expire(struct dpif_ipfix_exporter *exporter,
                         bool forced_end, const uint64_t export_time_usec,
-                        const uint32_t export_time_sec)
+                        const uint32_t export_time_sec) OVS_REQUIRES(mutex)
 {
     struct ipfix_flow_cache_entry *entry;
     uint64_t max_flow_start_timestamp_usec;
-    bool template_msg_sent = false;
     enum ipfix_flow_end_reason flow_end_reason;
 
     if (ovs_list_is_empty(&exporter->cache_flow_start_timestamp_list)) {
@@ -2844,25 +2925,28 @@  dpif_ipfix_cache_expire(struct dpif_ipfix_exporter *exporter,
             break;
         }
 
-        ovs_list_remove(&entry->cache_flow_start_timestamp_list_node);
-        hmap_remove(&exporter->cache_flow_key_map,
-                    &entry->flow_key_map_node);
+        /* XXX: Make frequency of the (Options) Template and Exporter Process
+         * Statistics transmission configurable.
+         * Cf. IETF RFC 5101 Section 4.3. and 10.3.6. */
+        if ((exporter->last_stats_sent_time + IPFIX_TEMPLATE_INTERVAL)
+             <= export_time_sec) {
+            exporter->last_stats_sent_time = export_time_sec;
+            ipfix_send_exporter_data_msg(exporter, export_time_sec);
+        }
 
-         /* XXX: Make frequency of the (Options) Template and Exporter Process
-          * Statistics transmission configurable.
-          * Cf. IETF RFC 5101 Section 4.3. and 10.3.6. */
-        if (!template_msg_sent
-            && (exporter->last_template_set_time + IPFIX_TEMPLATE_INTERVAL)
-                <= export_time_sec) {
+        if (dpif_ipfix_should_send_template(exporter,
+                                            entry->flow_key.obs_domain_id,
+                                            export_time_sec)) {
+            VLOG_DBG("Sending templates for ObservationDomainID %"PRIu32,
+                     entry->flow_key.obs_domain_id);
             ipfix_send_template_msgs(exporter, export_time_sec,
                                      entry->flow_key.obs_domain_id);
-            exporter->last_template_set_time = export_time_sec;
-            template_msg_sent = true;
-
-            /* Send Exporter Process Statistics. */
-            ipfix_send_exporter_data_msg(exporter, export_time_sec);
         }
 
+        ovs_list_remove(&entry->cache_flow_start_timestamp_list_node);
+        hmap_remove(&exporter->cache_flow_key_map,
+                    &entry->flow_key_map_node);
+
         /* XXX: Group multiple data records for the same obs domain id
          * into the same message. */
         ipfix_send_data_msg(exporter, export_time_sec, entry, flow_end_reason);
@@ -2883,7 +2967,7 @@  get_export_time_now(uint64_t *export_time_usec, uint32_t *export_time_sec)
 
 static void
 dpif_ipfix_cache_expire_now(struct dpif_ipfix_exporter *exporter,
-                            bool forced_end)
+                            bool forced_end) OVS_REQUIRES(mutex)
 {
     uint64_t export_time_usec;
     uint32_t export_time_sec;