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

> We replace the existing rculists used for conntrack sweeping with just
> iterating over all known connections. This allows us to get rid of the
> exp_lists in struct conntrack and thereby also of the usage of ct_lock
> in the connection insert and deletion path.
>
> This means after this commit connection insertion and deletion across
> different zones has no longer any shared lock. Within the same zone
> there is still a single lock.
>
> The exp_lists where not ordered in any way, the only difference compared
> to iterating over all connections was that they only contained
> connections in the forward direction.
> This means that this patch will trade away some cleaning performance in
> order to gain connection insertion and deletion speeds. We will improve
> the ct cleaning in the following commits.
>
> Signed-off-by: Felix Huettner <[email protected]>
> ---
>
> Notes:
>     v4->v5: fix formatting
>
>  lib/conntrack-private.h | 11 +-----
>  lib/conntrack.c         | 82 ++++++++++++++++-------------------------
>  2 files changed, 32 insertions(+), 61 deletions(-)
>
> diff --git a/lib/conntrack-private.h b/lib/conntrack-private.h
> index 1803ed7d0..213b23fce 100644
> --- a/lib/conntrack-private.h
> +++ b/lib/conntrack-private.h
> @@ -116,8 +116,6 @@ enum ct_timeout {
>      N_CT_TM
>  };
>  
> -#define N_EXP_LISTS 100
> -
>  struct conn_key_node {
>      enum key_dir dir;
>      struct conn_key key;
> @@ -134,9 +132,6 @@ struct conn {
>                              * True as soon as a thread has started freeing
>                              * its memory. */
>  
> -    /* Inserted once by a PMD, then managed by the 'ct_clean' thread. */
> -    struct rculist node;
> -
>      /* Mutable data. */
>      struct ovs_mutex lock; /* Guards all mutable fields. */
>      ovs_u128 label;
> @@ -212,17 +207,13 @@ struct conntrack_zone {
>  struct conntrack {
>      struct ovs_mutex ct_lock; /* Protects the following fields. */
>      struct conntrack_zone zones[UINT16_MAX + 1];
> -    struct rculist exp_lists[N_EXP_LISTS];
>      struct cmap timeout_policies;
>      atomic_int64_t default_zone_limit;
>  
>      uint32_t hash_basis; /* Salt for hashing a connection key. */
>      pthread_t clean_thread; /* Periodically cleans up connection tracker. */
>      struct latch clean_thread_exit; /* To destroy the 'clean_thread'. */
> -    unsigned int next_list; /* Next list where the newly created connection
> -                             * gets inserted. */
> -    unsigned int next_sweep; /* List from which the gc thread will resume
> -                              * the sweeping. */
> +    unsigned int next_clean_zone; /* Next zone where the clean should run. */
>  
>      /* Counting connections. */
>      atomic_count n_conn; /* Number of connections currently tracked. */
> diff --git a/lib/conntrack.c b/lib/conntrack.c
> index 86b18ff2b..3e38e46ec 100644
> --- a/lib/conntrack.c
> +++ b/lib/conntrack.c
> @@ -41,7 +41,6 @@
>  #include "ovs-thread.h"
>  #include "openvswitch/poll-loop.h"
>  #include "random.h"
> -#include "rculist.h"
>  #include "timeval.h"
>  #include "unaligned.h"
>  
> @@ -106,7 +105,6 @@ static enum ct_update_res conn_update(struct conntrack 
> *ct, struct conn *conn,
>                                        long long now);
>  static long long int conn_expiration(const struct conn *);
>  static bool conn_expired(const struct conn *, long long now);
> -static void conn_expire_push_front(struct conntrack *ct, struct conn *conn);
>  static void set_mark(struct dp_packet *, struct conn *,
>                       uint32_t val, uint32_t mask);
>  static void set_label(struct dp_packet *, struct conn *,
> @@ -279,9 +277,6 @@ conntrack_init(void)
>          cz->limit = CONN_LIMIT_NONE;
>          cz->requested_limit = CONN_LIMIT_USE_DEFAULT;
>      }
> -    for (unsigned i = 0; i < ARRAY_SIZE(ct->exp_lists); i++) {
> -        rculist_init(&ct->exp_lists[i]);
> -    }
>      timeout_policy_init(ct);
>      ovs_mutex_unlock(&ct->ct_lock);
>  
> @@ -443,7 +438,6 @@ zone_limit_update(struct conntrack *ct, int32_t zone, 
> uint32_t limit)
>  
>  static void
>  conn_clean__(struct conntrack *ct, struct conn *conn)
> -    OVS_REQUIRES(ct->ct_lock)
>  {
>      struct conntrack_zone *cz;
>      uint16_t fwd_zone;
> @@ -470,7 +464,6 @@ conn_clean__(struct conntrack *ct, struct conn *conn)
>                      &conn->key_node[CT_DIR_REV].cm_node, hash);
>      }
>  
> -    rculist_remove(&conn->node);
>      ovs_mutex_unlock(&cz->zone_lock);
>  }
>  
> @@ -478,16 +471,14 @@ conn_clean__(struct conntrack *ct, struct conn *conn)
>     datastructures. */
>  static void
>  conn_clean(struct conntrack *ct, struct conn *conn)
> -    OVS_EXCLUDED(conn->lock, ct->ct_lock)
> +    OVS_EXCLUDED(conn->lock)
>  {
>      if (atomic_flag_test_and_set(&conn->reclaimed)) {
>          return;
>      }
>  
>      COVERAGE_INC(conntrack_remove);
> -    ovs_mutex_lock(&ct->ct_lock);
>      conn_clean__(ct, conn);
> -    ovs_mutex_unlock(&ct->ct_lock);
>  
>      ovsrcu_postpone(delete_conn, conn);
>      atomic_count_dec(&ct->n_conn);
> @@ -506,18 +497,11 @@ void
>  conntrack_destroy(struct conntrack *ct)
>  {
>      struct conntrack_zone *cz;
> -    struct conn *conn;
>  
>      latch_set(&ct->clean_thread_exit);
>      pthread_join(ct->clean_thread, NULL);
>      latch_destroy(&ct->clean_thread_exit);
>  
> -    for (unsigned i = 0; i < N_EXP_LISTS; i++) {
> -        RCULIST_FOR_EACH (conn, node, &ct->exp_lists[i]) {
> -            conn_clean(ct, conn);
> -        }
> -    }
> -
>      struct timeout_policy *tp;
>      CMAP_FOR_EACH (tp, node, &ct->timeout_policies) {
>          uint32_t hash = hash_int(tp->policy.id, ct->hash_basis);
> @@ -526,7 +510,7 @@ conntrack_destroy(struct conntrack *ct)
>          ovsrcu_postpone(free, tp);
>      }
>  
> -    ovs_mutex_lock(&ct->ct_lock);
> +    conntrack_flush(ct, NULL);
>      for (unsigned i = 0; i < ARRAY_SIZE(ct->zones); i++) {
>          cz = zone_lookup(ct, i);
>          ovs_mutex_lock(&cz->zone_lock);
> @@ -536,7 +520,6 @@ conntrack_destroy(struct conntrack *ct)
>      }
>      cmap_destroy(&ct->timeout_policies);
>  
> -    ovs_mutex_unlock(&ct->ct_lock);
>      ovs_mutex_destroy(&ct->ct_lock);
>  
>      ovs_mutex_lock(&ct->resources_lock);
> @@ -921,7 +904,7 @@ conn_insert(struct conntrack *ct, struct conntrack_zone 
> *cz,
>              const struct nat_action_info_t *nat_action_info,
>              const char *helper, const struct alg_exp_node *alg_exp,
>              enum ct_alg_ctl_type ct_alg_ctl, uint32_t tp_id)
> -    OVS_REQUIRES(ct->ct_lock, cz->zone_lock)
> +    OVS_REQUIRES(cz->zone_lock)
>  {
>      struct conn_key_node *fwd_key_node, *rev_key_node;
>      struct conn *nc = NULL;
> @@ -994,7 +977,6 @@ conn_insert(struct conntrack *ct, struct conntrack_zone 
> *cz,
>  
>      cmap_insert(&cz->conns,
>                  &fwd_key_node->cm_node, ctx->hash);
> -    conn_expire_push_front(ct, nc);
>      atomic_count_inc(&ct->n_conn);
>      atomic_count_inc(&cz->count);
>  
> @@ -1055,20 +1037,17 @@ conn_maybe_not_found(struct conntrack *ct, struct 
> dp_packet *pkt,
>       * We do not use multiple small if's as this confused clangs locking
>       * analysis. */
>      if (commit) {
> -        ovs_mutex_lock(&ct->ct_lock);
>          ovs_mutex_lock(&cz->zone_lock);
>          bool found = conn_lookup_zone(ct, cz, &ctx->key, now, NULL, NULL);
>          if (!found) {
>              if (!pkt_validate_and_set_new_ct_state(pkt, ctx, alg_exp)) {
>                  ovs_mutex_unlock(&cz->zone_lock);
> -                ovs_mutex_unlock(&ct->ct_lock);
>                  return nc;
>              }
>              nc = conn_insert(ct, cz, pkt, ctx, now, nat_action_info,
>                               helper, alg_exp, ct_alg_ctl, tp_id);
>          }
>          ovs_mutex_unlock(&cz->zone_lock);
> -        ovs_mutex_unlock(&ct->ct_lock);
>      } else {
>          bool found = conn_lookup_zone(ct, cz, &ctx->key, now, NULL, NULL);
>          if (!found) {
> @@ -1515,28 +1494,34 @@ conntrack_get_sweep_interval(struct conntrack *ct)
>  }
>  
>  static size_t
> -ct_sweep(struct conntrack *ct, struct rculist *list, long long now,
> -         size_t *cleaned_count)
> +ct_sweep_zone(struct conntrack *ct, uint16_t zone, long long now,
> +              size_t *cleaned_count)
>      OVS_NO_THREAD_SAFETY_ANALYSIS
>  {
> +    struct conn_key_node *keyn;
> +    struct conntrack_zone *cz;
> +    unsigned int conn_count = 0;
> +    unsigned int cleaned = 0;
>      struct conn *conn;
> -    size_t cleaned = 0;
> -    size_t count = 0;
> +    long long expiration;
>  
> -    RCULIST_FOR_EACH (conn, node, list) {
> -        if (conn_expired(conn, now)) {
> +    cz = zone_lookup(ct, zone);
> +    CMAP_FOR_EACH (keyn, cm_node, &cz->conns) {
> +        if (keyn->dir != CT_DIR_FWD) {
> +            continue;
> +        }
> +
> +        conn = CONTAINER_OF(keyn, struct conn, key_node[keyn->dir]);
> +        expiration = conn_expiration(conn);
> +        if (now >= expiration) {
>              conn_clean(ct, conn);
>              cleaned++;
>          }
>  
> -        count++;
> +        conn_count++;
>      }
> -
> -    if (cleaned_count) {
> -        *cleaned_count = cleaned;
> -    }
> -
> -    return count;
> +    *cleaned_count = cleaned;
> +    return conn_count;
>  }
>  
>  /* Cleans up old connection entries from 'ct'.  Returns the time
> @@ -1549,23 +1534,28 @@ conntrack_clean(struct conntrack *ct, long long now)
>      unsigned int n_conn_limit, i;
>      size_t clean_end, count = 0;
>      size_t total_cleaned = 0;
> +    uint16_t current_zone = ct->next_clean_zone;
>  
>      atomic_read_relaxed(&ct->n_conn_limit, &n_conn_limit);
>      clean_end = n_conn_limit / 64;
>  
> -    for (i = ct->next_sweep; i < N_EXP_LISTS; i++) {
> -        size_t cleaned;
> +    for (i = 0; i < ARRAY_SIZE(ct->zones); i++) {
> +        size_t cleaned = 0;
>  
>          if (count > clean_end) {
>              next_wakeup = 0;
>              break;
>          }
>  
> -        count += ct_sweep(ct, &ct->exp_lists[i], now, &cleaned);
> +        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++;
>      }
>  
> -    ct->next_sweep = (i < N_EXP_LISTS) ? i : 0;
> +    ct->next_clean_zone = current_zone + 1;

You do fix this up later from what I can tell, but technically this
skips an unfinished zone in the event of early exit (break above), so if
we're bisecting it will have a weird kind of behavior.  Maybe if you can
fix it here.

>  
>      VLOG_DBG("conntrack cleaned %"PRIuSIZE" entries out of %"PRIuSIZE
>               " entries in %lld msec", total_cleaned, count,
> @@ -2602,16 +2592,6 @@ conn_update(struct conntrack *ct, struct conn *conn, 
> struct dp_packet *pkt,
>      return update_res;
>  }
>  
> -static void
> -conn_expire_push_front(struct conntrack *ct, struct conn *conn)
> -    OVS_REQUIRES(ct->ct_lock)
> -{
> -    unsigned int curr = ct->next_list;
> -
> -    ct->next_list = (ct->next_list + 1) % N_EXP_LISTS;
> -    rculist_push_front(&ct->exp_lists[curr], &conn->node);
> -}
> -
>  static long long int
>  conn_expiration(const struct conn *conn)
>  {
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to