@@ -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;
@@ -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;
+ 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;
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,
+ &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) {
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++;
}
- 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);
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 <felix.huettner@digits.schwarz> --- 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(-)