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

> Previously we slept for a minimum of 200ms after each cleaning run, even
> if we did clean the majority of entries. This originally came from the
> requirement that we need to take the whole conntrack lock to cleanup
> connections and are therefor limited in the progress we can still make
> when inserting.
>
> However with the now implemented locks per zone and the batching this is
> no longer needed. In addition before we could do partion zone cleanings
> we always cleaned a full zone. This meant that a single high load zone
> would still be cleaned quite fast.
>
> Signed-off-by: Felix Huettner <[email protected]>
> ---
>
> Notes:
>     v4->v5:
>       * added news and coverage counter
>       * fixed busylooping if n_conn_limit < 640

Is it?  I think only if n_conn_limit == 640

>  NEWS            |  8 ++++++++
>  lib/conntrack.c | 24 ++++++++++++++++++++----
>  2 files changed, 28 insertions(+), 4 deletions(-)
>
> diff --git a/NEWS b/NEWS
> index de1a030ad..4d6d05791 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -1,5 +1,13 @@
>  Post-v4.0.0
>  --------------------
> +   - Userspace datapath:
> +     * The conntrack cleanup thread will now continue cleaning if more than 
> 10%
> +       of all checked entries have been deleted in the past iteration. This
> +       will ensure that old entries are removed faster in high churn
> +       situations. The coverage counter conntrack_clean_fast_wakeup has been
> +       added for such events.
> +     * Improved multi-thread multi-zone scalability of the userspace
> +       connection tracking.
>  
>  
>  v4.0.0 - 17 Aug 2026
> diff --git a/lib/conntrack.c b/lib/conntrack.c
> index 0c91d5dc1..0f48eb649 100644
> --- a/lib/conntrack.c
> +++ b/lib/conntrack.c
> @@ -57,6 +57,7 @@ COVERAGE_DEFINE(conntrack_zone_full);
>  COVERAGE_DEFINE(conntrack_remove);
>  COVERAGE_DEFINE(conntrack_insert);
>  COVERAGE_DEFINE(conntrack_maybe_not_found);
> +COVERAGE_DEFINE(conntrack_clean_fast_wakeup);
>  
>  struct conn_lookup_ctx {
>      struct conn_key key;
> @@ -1534,7 +1535,8 @@ ct_sweep_buf(struct conntrack *ct, uint16_t zone,
>  
>  static bool
>  ct_sweep_zone(struct conntrack *ct, uint16_t zone, long long now,
> -              size_t *cleaned_count, size_t *conn_count, size_t limit,
> +              size_t *cleaned_count, size_t *conn_count,
> +              long long *min_expiration, size_t limit,
>                struct cmap_position **current_position)
>      OVS_NO_THREAD_SAFETY_ANALYSIS
>  {
> @@ -1586,6 +1588,8 @@ ct_sweep_zone(struct conntrack *ct, uint16_t zone, long 
> long now,
>          if (now >= expiration) {
>              conn_buf[n_conn_buf++] = conn;
>              (*cleaned_count)++;
> +        } else {
> +            *min_expiration = MIN(*min_expiration, expiration);
>          }
>  
>          if (n_conn_buf == CONN_SWEEP_BUF_SIZE) {
> @@ -1608,6 +1612,7 @@ static long long
>  conntrack_clean(struct conntrack *ct, long long now)
>  {
>      long long next_wakeup = now + conntrack_get_sweep_interval(ct);
> +    long long min_expiration = LLONG_MAX;
>      unsigned int n_conn_limit, i;
>      size_t clean_end, count = 0;
>      size_t total_cleaned = 0;
> @@ -1621,7 +1626,8 @@ conntrack_clean(struct conntrack *ct, long long now)
>          size_t cleaned = 0;
>  
>          zone_finished = ct_sweep_zone(ct, ct->current_clean_zone, now, 
> &cleaned,
> -                                      &zone_count, clean_end - count,
> +                                      &zone_count, &min_expiration,
> +                                      clean_end - count,
>                                        &ct->current_clean_position);
>          total_cleaned += cleaned;
>  
> @@ -1641,6 +1647,17 @@ conntrack_clean(struct conntrack *ct, long long now)
>               " entries in %lld msec", total_cleaned, count,
>               time_msec() - now);
>  
> +    /* If we did clean more than 10% of our connection limit we assume that
> +     * if we would continue we would also find a lot of connections to clean.
> +     * In this case we want to rather continue immediately to ensure we get
> +     * the connections removed in a high load situation. */
> +    if (total_cleaned > (clean_end / 10)) {
> +        COVERAGE_INC(conntrack_clean_fast_wakeup);
> +        next_wakeup = 0;

If n_conn_limit <= 640, clean_end is calculated as a number < 10, which
makes this calculation total_cleaned > 0, so it will just go into
fast_wakeup code, right?

I think we need to do something like:

  if (total_cleaned > (MAX(clean_end, 10) / 10))

Just my (back of envelope) thought:

 +--------------+-----------+----------+
 | n_conn_limit | clean_end | / 10 val |
 +--------------+-----------+----------+
 |  30000000    |  468750   | 46875    |
 |       640    |      10   |     1    |
 |       639    |       9   |     0    |
 ...
 |        64    |       1   |     0    |
 |         1    |       0   |     0    |

Everything under 640 drops into the total_cleaned > clean_end
calculation (unless nothing was cleaned).  Hope I got the math
correct here.

> +    } else {
> +        next_wakeup = MIN(next_wakeup, min_expiration);
> +    }
> +
>      return next_wakeup;
>  }
>  
> @@ -1648,7 +1665,6 @@ conntrack_clean(struct conntrack *ct, long long now)
>   *
>   * We must call conntrack_clean() periodically.  conntrack_clean() return
>   * value gives an hint on when the next cleanup must be done. */
> -#define CT_CLEAN_MIN_INTERVAL_MS 200
>  
>  static void *
>  clean_thread_main(void *f_)
> @@ -1662,7 +1678,7 @@ clean_thread_main(void *f_)
>          next_wake = conntrack_clean(ct, now);
>  
>          if (next_wake < now) {
> -            poll_timer_wait_until(now + CT_CLEAN_MIN_INTERVAL_MS);
> +            poll_immediate_wake();
>          } else {
>              poll_timer_wait_until(next_wake);
>          }
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to