Felix Huettner via dev <[email protected]> writes:

> If a single zone creates a lot of churn we would have otherwise blocked
> the clean thread for quite a while.
>
> The approach with cmap_position might have the drawback that we
> potentially skip some connections that have been inserted in the
> meantime, but cmap_cursor can not be used as we have a rcu quiesce
> period in between iterations.
>
> Signed-off-by: Felix Huettner <[email protected]>
> ---
>
> Notes:
>     v4->v5: fix conntrack_clean jumping to next zone if the current one was
>             only partially cleaned
>
>  lib/conntrack-private.h | 11 +++++--
>  lib/conntrack.c         | 64 +++++++++++++++++++++++++++++------------
>  2 files changed, 53 insertions(+), 22 deletions(-)
>
> diff --git a/lib/conntrack-private.h b/lib/conntrack-private.h
> index 213b23fce..1a28ea680 100644
> --- a/lib/conntrack-private.h
> +++ b/lib/conntrack-private.h
> @@ -211,9 +211,13 @@ struct conntrack {
>      atomic_int64_t default_zone_limit;
>  
>      uint32_t hash_basis; /* Salt for hashing a connection key. */
> +
> +    /* Background cleanup thread. */
>      pthread_t clean_thread; /* Periodically cleans up connection tracker. */
>      struct latch clean_thread_exit; /* To destroy the 'clean_thread'. */
> -    unsigned int next_clean_zone; /* Next zone where the clean should run. */
> +    uint16_t current_clean_zone; /* Current zone where the clean should run. 
> */
> +    /* The position in the cmap of the current zone. */
> +    struct cmap_position *current_clean_position;
>  
>      /* Counting connections. */
>      atomic_count n_conn; /* Number of connections currently tracked. */
> @@ -235,8 +239,9 @@ struct conntrack {
>  
>  /* Lock acquisition order:
>   *    1. 'conn->lock'
> - *    2. 'ct_lock'
> - *    3. 'resources_lock'
> + *    2. 'zone_lock'
> + *    3. 'ct_lock'
> + *    4. 'resources_lock'
>   */
>  
>  extern struct ct_l4_proto ct_proto_tcp;
> diff --git a/lib/conntrack.c b/lib/conntrack.c
> index 3e38e46ec..f2adb82a3 100644
> --- a/lib/conntrack.c
> +++ b/lib/conntrack.c
> @@ -529,6 +529,10 @@ conntrack_destroy(struct conntrack *ct)
>      ovs_mutex_unlock(&ct->resources_lock);
>      ovs_mutex_destroy(&ct->resources_lock);
>  
> +    if (ct->current_clean_position) {
> +        free(ct->current_clean_position);
> +    }
> +
>      ipf_destroy(ct->ipf);
>      free(ct);
>  }
> @@ -1493,20 +1497,36 @@ conntrack_get_sweep_interval(struct conntrack *ct)
>      return ms;
>  }
>  
> -static size_t
> +static bool
>  ct_sweep_zone(struct conntrack *ct, uint16_t zone, long long now,
> -              size_t *cleaned_count)
> +              size_t *cleaned_count, size_t *conn_count, size_t limit,
> +              struct cmap_position **current_position)
>      OVS_NO_THREAD_SAFETY_ANALYSIS
>  {
>      struct conn_key_node *keyn;
>      struct conntrack_zone *cz;
> -    unsigned int conn_count = 0;
> -    unsigned int cleaned = 0;
> +    unsigned int conn_handled = 0;
>      struct conn *conn;
> +    struct cmap_node *node;
>      long long expiration;
>  
>      cz = zone_lookup(ct, zone);
> -    CMAP_FOR_EACH (keyn, cm_node, &cz->conns) {
> +    if (atomic_count_get(&cz->count) == 0) {
> +        *conn_count = 0;

I think we should free and NULL the current position here.  Otherwise,
when the early bail happens it might be stale.

> +        return true;
> +    }
> +
> +    if (!*current_position) {
> +        *current_position = xzalloc(sizeof(**current_position));
> +    }
> +
> +    while ((node = cmap_next_position(&cz->conns, *current_position))) {
> +        keyn = OBJECT_CONTAINING(node, keyn, cm_node);
> +        if (conn_handled > limit) {
> +            *conn_count = conn_handled;
> +            return false;
> +        }
> +
>          if (keyn->dir != CT_DIR_FWD) {
>              continue;
>          }
> @@ -1515,13 +1535,16 @@ ct_sweep_zone(struct conntrack *ct, uint16_t zone, 
> long long now,
>          expiration = conn_expiration(conn);
>          if (now >= expiration) {
>              conn_clean(ct, conn);
> -            cleaned++;
> +            (*cleaned_count)++;
>          }
>  
> -        conn_count++;
> +        conn_handled++;
>      }
> -    *cleaned_count = cleaned;
> -    return conn_count;
> +
> +    free(*current_position);
> +    *current_position = NULL;
> +    *conn_count = conn_handled;
> +    return true;
>  }
>  
>  /* Cleans up old connection entries from 'ct'.  Returns the time
> @@ -1534,29 +1557,32 @@ conntrack_clean(struct conntrack *ct, long long now)
>      unsigned int n_conn_limit, i;
>      size_t clean_end, count = 0;

 * count at this point is '0'

>      size_t total_cleaned = 0;
> -    uint16_t current_zone = ct->next_clean_zone;
> +    bool zone_finished;
>  
>      atomic_read_relaxed(&ct->n_conn_limit, &n_conn_limit);
>      clean_end = n_conn_limit / 64;
>  
>      for (i = 0; i < ARRAY_SIZE(ct->zones); i++) {
> +        size_t zone_count = 0;
>          size_t cleaned = 0;
>  
> +        zone_finished = ct_sweep_zone(ct, ct->current_clean_zone, now, 
> &cleaned,
> +                                      &zone_count, clean_end - count,

Given you moved the point where we accumulate vs. the point where we
check, I have a spidey sense that there *could* be an underflow here
someday.  Might be better to either try pinning:

    size_t clean_budget =
       clean_end - count < clean_end ? clean_end - count : 0

or keep the (count > clean_end) check happening before the sweep.

> +                                      &ct->current_clean_position);
> +        total_cleaned += cleaned;
> +
> +        if (zone_finished) {
> +            /* This will overflow and thereby allow us to iterate through all
> +             * zones. */
> +            ct->current_clean_zone++;
> +        }
> +
>          if (count > clean_end) {

Count check happens.  Without an increment anywhere I can find.

>              next_wakeup = 0;
>              break;
>          }
> -
> -        count += ct_sweep_zone(ct, current_zone, now, &cleaned);
> -        total_cleaned += cleaned;
> -
> -        /* This will overflow and thereby allow us to iterate through all
> -         * zones. */
> -        current_zone++;

With no more increment, I guess optimizer throws away the check
anyway. :)

>      }
>  
> -    ct->next_clean_zone = current_zone + 1;
> -
>      VLOG_DBG("conntrack cleaned %"PRIuSIZE" entries out of %"PRIuSIZE
>               " entries in %lld msec", total_cleaned, count,
>               time_msec() - now);
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to