Add a private data slot allocated from the connection's private store
to track offload state per connection.  Three states are defined:
none, added, and established.  The state is set on a successful
conn_add(), cleared on conn_del(), and transitions from added to
established when conn_established() is called for the first time.
Two public helpers, ct_offload_conn_is_offloaded() and
ct_offload_conn_is_established(), expose the current state.

Use ct_offload_conn_is_offloaded() in conntrack-tcp.c to bypass TCP
sequence-number checking for offloaded connections.

Assisted-by: Claude Sonnet 4.6 <[email protected]>
Signed-off-by: Aaron Conole <[email protected]>
---
 lib/conntrack-tcp.c |  9 +++--
 lib/ct-offload.c    | 89 +++++++++++++++++++++++++++++++++++++++++++--
 lib/ct-offload.h    | 29 ++++++++++++---
 3 files changed, 115 insertions(+), 12 deletions(-)

diff --git a/lib/conntrack-tcp.c b/lib/conntrack-tcp.c
index 5ab300b661..c6630223dd 100644
--- a/lib/conntrack-tcp.c
+++ b/lib/conntrack-tcp.c
@@ -44,6 +44,7 @@
 #include "conntrack-tp.h"
 #include "coverage.h"
 #include "ct-dpif.h"
+#include "ct-offload.h"
 #include "dp-packet.h"
 #include "util.h"
 
@@ -133,9 +134,11 @@ tcp_get_wscale(const struct tcp_header *tcp)
 }
 
 static bool
-tcp_bypass_seq_chk(struct conntrack *ct)
+tcp_bypass_seq_chk(struct conntrack *ct, struct conn *conn)
+    OVS_REQUIRES(conn->lock)
 {
-    if (!conntrack_get_tcp_seq_chk(ct)) {
+    if (!conntrack_get_tcp_seq_chk(ct) ||
+        ct_offload_conn_is_offloaded(conn)) {
         COVERAGE_INC(conntrack_tcp_seq_chk_bypass);
         return true;
     }
@@ -290,7 +293,7 @@ tcp_conn_update(struct conntrack *ct, struct conn *conn_,
         /* Acking not more than one window forward */
         && ((tcp_flags & TCP_RST) == 0 || orig_seq == src->seqlo
             || (orig_seq == src->seqlo + 1) || (orig_seq + 1 == src->seqlo)))
-        || tcp_bypass_seq_chk(ct)) {
+        || tcp_bypass_seq_chk(ct, conn_)) {
         /* Require an exact/+1 sequence match on resets when possible */
 
         /* update max window */
diff --git a/lib/ct-offload.c b/lib/ct-offload.c
index 7339b205b2..58afef7203 100644
--- a/lib/ct-offload.c
+++ b/lib/ct-offload.c
@@ -20,6 +20,8 @@
 
 #include <errno.h>
 
+#include "conntrack.h"
+#include "conntrack-private.h"
 #include "ovs-thread.h"
 #include "util.h"
 
@@ -28,6 +30,14 @@
 
 VLOG_DEFINE_THIS_MODULE(ct_offload);
 
+/* Private slot for storing per-connection offload state. */
+static ct_private_id_t ct_offload_private_id = CT_PRIVATE_ID_INVALID;
+
+/* Values stored in the private slot. */
+#define CT_OFFLOAD_STATE_NONE  ((void *)(uintptr_t) 0)  /* Not offloaded. */
+#define CT_OFFLOAD_STATE_ADDED ((void *)(uintptr_t) 1)  /* Offload pending. */
+#define CT_OFFLOAD_STATE_EST   ((void *)(uintptr_t) 2)  /* Established. */
+
 /* Node in the registered-provider list. */
 struct ct_offload_class_node {
     const struct ct_offload_class *class;
@@ -116,16 +126,39 @@ out:
     ovs_rwlock_unlock(&ct_offload_classes_rwlock);
 }
 
+/* ct_offload_alloc_private_slot() - allocate the per-connection private slot.
+ *
+ * Called once at module init.  Logs an error if the slot pool is exhausted. */
+static void
+ct_offload_alloc_private_slot(void)
+{
+    ct_offload_private_id = conn_private_id_alloc(NULL);
+    if (ct_offload_private_id == CT_PRIVATE_ID_INVALID) {
+        VLOG_ERR("failed to allocate private slot for ct offload");
+    }
+}
+
 /* ct_offload_module_init() - register built-in CT offload providers.
  *
  * Must be called once before any connections are created. */
 void
 ct_offload_module_init(void)
 {
+    ct_offload_alloc_private_slot();
     /* No built-in providers yet; third parties call ct_offload_register()
      * directly from their own module-init routines. */
 }
 
+/* ct_offload_init_for_tests() - allocate the internal private slot for tests.
+ *
+ * Must not be called in production code; use ct_offload_module_init()
+ * instead. */
+void
+ct_offload_init_for_tests(void)
+{
+    ct_offload_alloc_private_slot();
+}
+
 /* Internal helpers -- callers must hold ct_offload_classes_rwlock (rdlock).
  *
  * When 'batched' is true the helper skips providers that implement
@@ -140,8 +173,10 @@ ct_offload_module_init(void)
 static int
 ct_offload_conn_add__(const struct ct_offload_ctx *ctx, bool batched)
     OVS_REQ_RDLOCK(ct_offload_classes_rwlock)
+    OVS_REQUIRES(ctx->conn->lock)
 {
     struct ct_offload_class_node *node;
+    bool offloaded = false;
     int ret = 0;
 
     LIST_FOR_EACH (node, list_node, &ct_offload_classes) {
@@ -157,16 +192,24 @@ ct_offload_conn_add__(const struct ct_offload_ctx *ctx, 
bool batched)
 
         int error = class->conn_add(ctx);
 
-        if (error && !ret) {
+        if (!error) {
+            offloaded = true;
+        } else if (!ret) {
             ret = error;
         }
     }
 
+    if (offloaded) {
+        conn_private_set(ctx->conn, ct_offload_private_id,
+                         CT_OFFLOAD_STATE_ADDED);
+    }
+
     return ret;
 }
 
 int
 ct_offload_conn_add(const struct ct_offload_ctx *ctx)
+    OVS_REQUIRES(ctx->conn->lock)
 {
     int ret;
 
@@ -184,6 +227,7 @@ ct_offload_conn_add(const struct ct_offload_ctx *ctx)
 static void
 ct_offload_conn_del__(const struct ct_offload_ctx *ctx, bool batched)
     OVS_REQ_RDLOCK(ct_offload_classes_rwlock)
+    OVS_REQUIRES(ctx->conn->lock)
 {
     struct ct_offload_class_node *node;
 
@@ -196,10 +240,13 @@ ct_offload_conn_del__(const struct ct_offload_ctx *ctx, 
bool batched)
 
         class->conn_del(ctx);
     }
+
+    conn_private_set(ctx->conn, ct_offload_private_id, CT_OFFLOAD_STATE_NONE);
 }
 
 void
 ct_offload_conn_del(const struct ct_offload_ctx *ctx)
+    OVS_REQUIRES(ctx->conn->lock)
 {
     ovs_rwlock_rdlock(&ct_offload_classes_rwlock);
     ct_offload_conn_del__(ctx, false);
@@ -209,8 +256,15 @@ ct_offload_conn_del(const struct ct_offload_ctx *ctx)
 static void
 ct_offload_conn_established__(const struct ct_offload_ctx *ctx, bool batched)
     OVS_REQ_RDLOCK(ct_offload_classes_rwlock)
+    OVS_REQUIRES(ctx->conn->lock)
 {
+    if (conn_private_get(ctx->conn, ct_offload_private_id)
+        != CT_OFFLOAD_STATE_ADDED) {
+        return;
+    }
+
     struct ct_offload_class_node *node;
+    bool established = false;
 
     LIST_FOR_EACH (node, list_node, &ct_offload_classes) {
         const struct ct_offload_class *class = node->class;
@@ -219,20 +273,43 @@ ct_offload_conn_established__(const struct ct_offload_ctx 
*ctx, bool batched)
             continue;
         }
 
-        if (class->conn_established) {
-            class->conn_established(ctx);
+        if (class->conn_established && class->conn_established(ctx)) {
+            established = true;
         }
     }
+
+    if (established) {
+        conn_private_set(ctx->conn, ct_offload_private_id,
+                         CT_OFFLOAD_STATE_EST);
+    }
 }
 
 void
 ct_offload_conn_established(const struct ct_offload_ctx *ctx)
+    OVS_REQUIRES(ctx->conn->lock)
 {
     ovs_rwlock_rdlock(&ct_offload_classes_rwlock);
     ct_offload_conn_established__(ctx, false);
     ovs_rwlock_unlock(&ct_offload_classes_rwlock);
 }
 
+bool
+ct_offload_conn_is_offloaded(const struct conn *conn)
+    OVS_REQUIRES(conn->lock)
+{
+    void *state = conn_private_get(conn, ct_offload_private_id);
+
+    return state == CT_OFFLOAD_STATE_ADDED || state == CT_OFFLOAD_STATE_EST;
+}
+
+bool
+ct_offload_conn_is_established(const struct conn *conn)
+    OVS_REQUIRES(conn->lock)
+{
+    return conn_private_get(conn, ct_offload_private_id)
+           == CT_OFFLOAD_STATE_EST;
+}
+
 /* ct_offload_conn_update__() - query the hardware last-used timestamp.
  *
  * Iterates over providers and returns the first non-zero timestamp returned
@@ -404,11 +481,15 @@ ct_offload_op_batch_submit(struct ct_offload_op_batch 
*batch)
 
         switch (op->type) {
         case CT_OFFLOAD_OP_ADD:
+            ovs_mutex_lock(&op->ctx.conn->lock);
             op->error = ct_offload_conn_add__(&op->ctx, true);
+            ovs_mutex_unlock(&op->ctx.conn->lock);
             break;
 
         case CT_OFFLOAD_OP_DEL:
+            ovs_mutex_lock(&op->ctx.conn->lock);
             ct_offload_conn_del__(&op->ctx, true);
+            ovs_mutex_unlock(&op->ctx.conn->lock);
             op->error = 0;
             break;
 
@@ -429,7 +510,9 @@ ct_offload_op_batch_submit(struct ct_offload_op_batch 
*batch)
             break;
 
         case CT_OFFLOAD_OP_EST:
+            ovs_mutex_lock(&op->ctx.conn->lock);
             ct_offload_conn_established__(&op->ctx, true);
+            ovs_mutex_unlock(&op->ctx.conn->lock);
             op->error = 0;
             break;
 
diff --git a/lib/ct-offload.h b/lib/ct-offload.h
index 754232730c..4971a61e53 100644
--- a/lib/ct-offload.h
+++ b/lib/ct-offload.h
@@ -81,8 +81,9 @@ struct ct_offload_class {
      * the current expiration. */
     long long (*conn_update)(const struct ct_offload_ctx *);
     /* Called exactly once when the first reply-direction packet is seen
-     * for an offloaded connection. */
-    void (*conn_established)(const struct ct_offload_ctx *);
+     * for an offloaded connection.  Returns true if the connection is
+     * confirmed established in hardware, false otherwise. */
+    bool (*conn_established)(const struct ct_offload_ctx *);
     /* Check whether this provider can offload a connection. */
     bool (*can_offload)(const struct ct_offload_ctx *);
     /* Flush all offloaded connections. */
@@ -98,14 +99,30 @@ void ct_offload_unregister(const struct ct_offload_class *);
 /* Module initialization (register built-in providers). */
 void ct_offload_module_init(void);
 
-/* Per-connection offload API that dispatches to all registered providers. */
-int       ct_offload_conn_add(const struct ct_offload_ctx *);
-void      ct_offload_conn_del(const struct ct_offload_ctx *);
+/* Testing entry point: allocates the internal private slot.  Not for
+ * production use; call ct_offload_module_init() instead. */
+void ct_offload_init_for_tests(void);
+
+/* Per-connection offload API that dispatches to all registered providers.
+ * conn_add, conn_del, and conn_established require conn->lock to be held by
+ * the caller; conn_update, can_offload, and flush do not. */
+int       ct_offload_conn_add(const struct ct_offload_ctx *ctx)
+              OVS_REQUIRES(ctx->conn->lock);
+void      ct_offload_conn_del(const struct ct_offload_ctx *ctx)
+              OVS_REQUIRES(ctx->conn->lock);
 long long ct_offload_conn_update(const struct ct_offload_ctx *);
-void      ct_offload_conn_established(const struct ct_offload_ctx *);
+void      ct_offload_conn_established(const struct ct_offload_ctx *ctx)
+              OVS_REQUIRES(ctx->conn->lock);
 bool      ct_offload_can_offload(const struct ct_offload_ctx *);
 void      ct_offload_flush(void);
 
+/* Connection state query helpers.  Both require conn->lock to be held
+ * by the caller. */
+bool ct_offload_conn_is_offloaded(const struct conn *conn)
+    OVS_REQUIRES(conn->lock);
+bool ct_offload_conn_is_established(const struct conn *conn)
+    OVS_REQUIRES(conn->lock);
+
 /* Batch offload API.
  *
  * The default implementation dispatches each operation individually using the
-- 
2.51.0

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to