diff mbox series

[ovs-dev,RFC,05/12] ct-offload: Add a new interface as an offload provider.

Message ID 20260408170613.587902-6-aconole@redhat.com
State New
Headers show
Series ct-offload: Introduce a conntrack offload infrastructure. | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test success github build: passed

Commit Message

Aaron Conole April 8, 2026, 5:06 p.m. UTC
This adds the basic primitives, initialization, and operations that
conntrack offload providers will need to implement in order to
offer a path to offloading.

Signed-off-by: Aaron Conole <aconole@redhat.com>
---
 lib/automake.mk  |   2 +
 lib/ct-offload.c | 257 +++++++++++++++++++++++++++++++++++++++++++++++
 lib/ct-offload.h |  97 ++++++++++++++++++
 3 files changed, 356 insertions(+)
 create mode 100644 lib/ct-offload.c
 create mode 100644 lib/ct-offload.h
diff mbox series

Patch

diff --git a/lib/automake.mk b/lib/automake.mk
index 027dd986ba..f11e3de27c 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -97,6 +97,8 @@  lib_libopenvswitch_la_SOURCES = \
 	lib/conntrack-other.c \
 	lib/conntrack.c \
 	lib/conntrack.h \
+	lib/ct-offload.c \
+	lib/ct-offload.h \
 	lib/cooperative-multitasking.c \
 	lib/cooperative-multitasking.h \
 	lib/cooperative-multitasking-private.h \
diff --git a/lib/ct-offload.c b/lib/ct-offload.c
new file mode 100644
index 0000000000..3bd6200e37
--- /dev/null
+++ b/lib/ct-offload.c
@@ -0,0 +1,257 @@ 
+/*
+ * Copyright (c) 2026 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+#include <errno.h>
+
+#include "ct-offload.h"
+#include "ovs-thread.h"
+#include "util.h"
+
+#include "openvswitch/list.h"
+#include "openvswitch/vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(ct_offload);
+
+/* Node in the registered-provider list. */
+struct ct_offload_class_node {
+    const struct ct_offload_class *class;
+    struct ovs_list               list_node;
+};
+
+/* Global list of registered CT offload classes and a mutex to protect it.
+ * Providers are expected to be registered at module init time and
+ * unregistered only at module teardown, so contention is minimal. */
+static struct ovs_mutex ct_offload_mutex = OVS_MUTEX_INITIALIZER;
+static struct ovs_list  ct_offload_classes
+    OVS_GUARDED_BY(ct_offload_mutex)
+    = OVS_LIST_INITIALIZER(&ct_offload_classes);
+
+
+/* ct_offload_register() - register a CT offload provider class.
+ *
+ * Calls class->init() if provided.  Returns 0 on success or a positive
+ * errno value on failure.  Attempting to register the same class twice
+ * returns EEXIST. */
+int
+ct_offload_register(const struct ct_offload_class *class)
+{
+    struct ct_offload_class_node *node;
+    int error = 0;
+
+    ovs_assert(class);
+    ovs_assert(class->name);
+
+    ovs_mutex_lock(&ct_offload_mutex);
+
+    /* Detect duplicate registrations. */
+    LIST_FOR_EACH (node, list_node, &ct_offload_classes) {
+        if (!strcmp(node->class->name, class->name)) {
+            VLOG_WARN("attempted to register duplicate ct offload class: %s",
+                      class->name);
+            error = EEXIST;
+            goto out;
+        }
+    }
+
+    error = class->init ? class->init() : 0;
+    if (error) {
+        VLOG_WARN("failed to initialize ct offload class %s: %s",
+                  class->name, ovs_strerror(error));
+        goto out;
+    }
+
+    node = xmalloc(sizeof *node);
+    node->class = class;
+    ovs_list_push_back(&ct_offload_classes, &node->list_node);
+    VLOG_DBG("registered ct offload class: %s", class->name);
+
+out:
+    ovs_mutex_unlock(&ct_offload_mutex);
+    return error;
+}
+
+/* ct_offload_unregister() - unregister a previously registered class.
+ *
+ * Safe to call even if the class was never registered (no-op in that
+ * case). */
+void
+ct_offload_unregister(const struct ct_offload_class *class)
+{
+    struct ct_offload_class_node *node;
+
+    ovs_assert(class);
+
+    ovs_mutex_lock(&ct_offload_mutex);
+    LIST_FOR_EACH (node, list_node, &ct_offload_classes) {
+        if (node->class == class) {
+            ovs_list_remove(&node->list_node);
+            free(node);
+            VLOG_DBG("unregistered ct offload class: %s", class->name);
+            goto out;
+        }
+    }
+    VLOG_WARN("attempted to unregister unknown ct offload class: %s",
+              class->name);
+
+out:
+    ovs_mutex_unlock(&ct_offload_mutex);
+}
+
+/* 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)
+{
+    /* No built-in providers yet; third parties call ct_offload_register()
+     * directly from their own module-init routines. */
+}
+
+/* ct_offload_conn_add() - notify all eligible providers of a new connection.
+ *
+ * Iterates over registered providers and calls conn_add() on each one that
+ * reports can_offload() == true for this context.  Returns the first non-zero
+ * error encountered, but continues notifying remaining providers.  This allows
+ * the underlying hardware conntrack details across providers function. */
+int
+ct_offload_conn_add(const struct ct_offload_ctx *ctx)
+{
+    struct ct_offload_class_node *node;
+    int ret = 0;
+
+    ovs_mutex_lock(&ct_offload_mutex);
+    LIST_FOR_EACH (node, list_node, &ct_offload_classes) {
+        const struct ct_offload_class *class = node->class;
+
+        if (class->can_offload && !class->can_offload(ctx)) {
+            continue;
+        }
+        if (class->conn_add) {
+            int error = class->conn_add(ctx);
+
+            if (error && !ret) {
+                ret = error;
+            }
+        }
+    }
+    ovs_mutex_unlock(&ct_offload_mutex);
+
+    return ret;
+}
+
+/* ct_offload_conn_del() - notify all providers that a connection was removed.
+ *
+ * Called unconditionally on all providers so that each can clean up any
+ * state it may have installed. */
+void
+ct_offload_conn_del(const struct ct_offload_ctx *ctx)
+{
+    struct ct_offload_class_node *node;
+
+    ovs_mutex_lock(&ct_offload_mutex);
+    LIST_FOR_EACH (node, list_node, &ct_offload_classes) {
+        const struct ct_offload_class *class = node->class;
+
+        if (class->conn_del) {
+            class->conn_del(ctx);
+        }
+    }
+    ovs_mutex_unlock(&ct_offload_mutex);
+}
+
+void
+ct_offload_conn_established(const struct ct_offload_ctx *ctx)
+{
+    struct ct_offload_class_node *node;
+
+    ovs_mutex_lock(&ct_offload_mutex);
+    LIST_FOR_EACH (node, list_node, &ct_offload_classes) {
+        const struct ct_offload_class *class = node->class;
+
+        if (class->conn_established) {
+            class->conn_established(ctx);
+        }
+    }
+    ovs_mutex_unlock(&ct_offload_mutex);
+}
+
+/* ct_offload_conn_update() - query the hardware last-used timestamp.
+ *
+ * Iterates over providers and returns the first non-zero timestamp returned
+ * by a provider's conn_update() callback.  Returns 0 if no provider
+ * supplies a timestamp. */
+long long
+ct_offload_conn_update(const struct ct_offload_ctx *ctx)
+{
+    struct ct_offload_class_node *node;
+    long long last_used = 0;
+
+    ovs_mutex_lock(&ct_offload_mutex);
+    LIST_FOR_EACH (node, list_node, &ct_offload_classes) {
+        const struct ct_offload_class *class = node->class;
+
+        if (class->conn_update) {
+            long long ts = class->conn_update(ctx);
+
+            if (ts) {
+                last_used = ts;
+                break;
+            }
+        }
+    }
+    ovs_mutex_unlock(&ct_offload_mutex);
+
+    return last_used;
+}
+
+/* ct_offload_can_offload() - returns true if any provider can offload ctx. */
+bool
+ct_offload_can_offload(const struct ct_offload_ctx *ctx)
+{
+    struct ct_offload_class_node *node;
+    bool result = false;
+
+    ovs_mutex_lock(&ct_offload_mutex);
+    LIST_FOR_EACH (node, list_node, &ct_offload_classes) {
+        const struct ct_offload_class *class = node->class;
+
+        if (class->can_offload && class->can_offload(ctx)) {
+            result = true;
+            break;
+        }
+    }
+    ovs_mutex_unlock(&ct_offload_mutex);
+
+    return result;
+}
+
+/* ct_offload_flush() - flush all offloaded connections from every provider. */
+void
+ct_offload_flush(void)
+{
+    struct ct_offload_class_node *node;
+
+    ovs_mutex_lock(&ct_offload_mutex);
+    LIST_FOR_EACH (node, list_node, &ct_offload_classes) {
+        const struct ct_offload_class *class = node->class;
+
+        if (class->flush) {
+            class->flush();
+        }
+    }
+    ovs_mutex_unlock(&ct_offload_mutex);
+}
diff --git a/lib/ct-offload.h b/lib/ct-offload.h
new file mode 100644
index 0000000000..824b94a5c1
--- /dev/null
+++ b/lib/ct-offload.h
@@ -0,0 +1,97 @@ 
+/*
+ * Copyright (c) 2026 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CT_OFFLOAD_H
+#define CT_OFFLOAD_H
+
+#include "openvswitch/types.h"
+
+struct conn;
+struct netdev;
+
+/* Context for offload as part of the callbacks that all connection
+ * offload APIs receive.
+ */
+struct ct_offload_ctx {
+    const struct conn *conn;        /* Connection object being offloaded. */
+    struct netdev *netdev_in;       /* Input netdev. */
+    odp_port_t input_port_id;       /* ODP port number. */
+};
+
+enum ct_offload_op_type {
+    CT_OFFLOAD_OP_ADD,              /* Add operation. */
+    CT_OFFLOAD_OP_DEL,              /* Del operation. */
+    CT_OFFLOAD_OP_UPD,              /* Update operation. */
+    CT_OFFLOAD_OP_POLICY,           /* Policy check operation. */
+    CT_OFFLOAD_OP_FLUSH,            /* Flush. */
+    CT_OFFLOAD_OP_EST,              /* Established - notify that a connection
+                                     * has a reply seen. */
+};
+
+struct ct_offload_op {
+    enum ct_offload_op_type type;
+    struct ct_offload_ctx   ctx;
+    int                     error;
+};
+
+/* Batched set of offload contexts and operations.*/
+struct ct_offload_op_batch {
+    struct ct_offload_op *ops;
+    size_t                n_ops;
+    size_t                allocated;
+};
+
+
+/* CT offload class describes a conntrack offload provider implementation. */
+struct ct_offload_class {
+    const char *name;
+
+    /* Initialization routine for the provider. */
+    int (*init)(void);
+
+    /* Per-connection operation callbacks get called for individual operations
+     * on the fast path or when batching is not in use. */
+    int  (*conn_add)(const struct ct_offload_ctx *);
+    void (*conn_del)(const struct ct_offload_ctx *);
+    /* Populate the last-used timestamp for the connection.  Returns the
+     * last-used time in milliseconds since epoch, or 0 on failure. */
+    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 *);
+    /* Check whether this provider can offload a connection. */
+    bool (*can_offload)(const struct ct_offload_ctx *);
+    /* Flush all offloaded connections. */
+    void (*flush)(void);
+};
+
+/* Register/unregister a provider.  Must be called at module init, before
+ * any connections are created. */
+int  ct_offload_register(const struct ct_offload_class *);
+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 *);
+long long ct_offload_conn_update(const struct ct_offload_ctx *);
+void      ct_offload_conn_established(const struct ct_offload_ctx *);
+bool      ct_offload_can_offload(const struct ct_offload_ctx *);
+void      ct_offload_flush(void);
+
+#endif /* CT_OFFLOAD_H */