{"id":2221007,"url":"http://patchwork.ozlabs.org/api/1.1/patches/2221007/?format=json","web_url":"http://patchwork.ozlabs.org/project/openvswitch/patch/20260408170613.587902-6-aconole@redhat.com/","project":{"id":47,"url":"http://patchwork.ozlabs.org/api/1.1/projects/47/?format=json","name":"Open vSwitch","link_name":"openvswitch","list_id":"ovs-dev.openvswitch.org","list_email":"ovs-dev@openvswitch.org","web_url":"http://openvswitch.org/","scm_url":"git@github.com:openvswitch/ovs.git","webscm_url":"https://github.com/openvswitch/ovs"},"msgid":"<20260408170613.587902-6-aconole@redhat.com>","date":"2026-04-08T17:06:01","name":"[ovs-dev,RFC,05/12] ct-offload: Add a new interface as an offload provider.","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"29b041e6d9ea4493d33af3316e5254dc774d095b","submitter":{"id":67184,"url":"http://patchwork.ozlabs.org/api/1.1/people/67184/?format=json","name":"Aaron Conole","email":"aconole@redhat.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/openvswitch/patch/20260408170613.587902-6-aconole@redhat.com/mbox/","series":[{"id":499163,"url":"http://patchwork.ozlabs.org/api/1.1/series/499163/?format=json","web_url":"http://patchwork.ozlabs.org/project/openvswitch/list/?series=499163","date":"2026-04-08T17:05:56","name":"ct-offload: Introduce a conntrack offload infrastructure.","version":1,"mbox":"http://patchwork.ozlabs.org/series/499163/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2221007/comments/","check":"success","checks":"http://patchwork.ozlabs.org/api/patches/2221007/checks/","tags":{},"headers":{"Return-Path":"<ovs-dev-bounces@openvswitch.org>","X-Original-To":["incoming@patchwork.ozlabs.org","dev@openvswitch.org"],"Delivered-To":["patchwork-incoming@legolas.ozlabs.org","ovs-dev@lists.linuxfoundation.org"],"Authentication-Results":["legolas.ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=i0sf5DiW;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org\n (client-ip=2605:bc80:3010::133; helo=smtp2.osuosl.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org)","smtp2.osuosl.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key)\n header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=i0sf5DiW","smtp1.osuosl.org; dmarc=pass (p=quarantine dis=none)\n header.from=redhat.com","smtp1.osuosl.org;\n dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com\n header.a=rsa-sha256 header.s=mimecast20190719 header.b=i0sf5DiW"],"Received":["from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4frTxt4XzVz1xv0\n\tfor <incoming@patchwork.ozlabs.org>; Thu, 09 Apr 2026 03:06:58 +1000 (AEST)","from localhost (localhost [127.0.0.1])\n\tby smtp2.osuosl.org (Postfix) with ESMTP id 441FE40817;\n\tWed,  8 Apr 2026 17:06:57 +0000 (UTC)","from smtp2.osuosl.org ([127.0.0.1])\n by localhost (smtp2.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id x6q5vM9UP41Z; Wed,  8 Apr 2026 17:06:56 +0000 (UTC)","from lists.linuxfoundation.org (lf-lists.osuosl.org\n [IPv6:2605:bc80:3010:104::8cd3:938])\n\tby smtp2.osuosl.org (Postfix) with ESMTPS id 1BA7A40818;\n\tWed,  8 Apr 2026 17:06:56 +0000 (UTC)","from lf-lists.osuosl.org (localhost [127.0.0.1])\n\tby lists.linuxfoundation.org (Postfix) with ESMTP id EFB65C054A;\n\tWed,  8 Apr 2026 17:06:55 +0000 (UTC)","from smtp1.osuosl.org (smtp1.osuosl.org [140.211.166.138])\n by lists.linuxfoundation.org (Postfix) with ESMTP id 1B5F2C054A\n for <dev@openvswitch.org>; Wed,  8 Apr 2026 17:06:54 +0000 (UTC)","from localhost (localhost [127.0.0.1])\n by smtp1.osuosl.org (Postfix) with ESMTP id 96C10826C0\n for <dev@openvswitch.org>; Wed,  8 Apr 2026 17:06:36 +0000 (UTC)","from smtp1.osuosl.org ([127.0.0.1])\n by localhost (smtp1.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id WgrOwOyGn_Xq for <dev@openvswitch.org>;\n Wed,  8 Apr 2026 17:06:33 +0000 (UTC)","from us-smtp-delivery-124.mimecast.com\n (us-smtp-delivery-124.mimecast.com [170.10.129.124])\n by smtp1.osuosl.org (Postfix) with ESMTPS id 0044A82656\n for <dev@openvswitch.org>; Wed,  8 Apr 2026 17:06:32 +0000 (UTC)","from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com\n (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by\n relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3,\n cipher=TLS_AES_256_GCM_SHA384) id us-mta-425-Qu5TMTdLM76d9drApvikSw-1; Wed,\n 08 Apr 2026 13:06:28 -0400","from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com\n (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest\n SHA256)\n (No client certificate requested)\n by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS\n id 7CE28195608F; Wed,  8 Apr 2026 17:06:27 +0000 (UTC)","from RHTRH0061144.redhat.com (unknown [10.22.89.172])\n by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP\n id BE0A530001BB; Wed,  8 Apr 2026 17:06:25 +0000 (UTC)"],"X-Virus-Scanned":["amavis at osuosl.org","amavis at osuosl.org"],"X-Comment":"SPF check N/A for local connections -\n client-ip=2605:bc80:3010:104::8cd3:938; helo=lists.linuxfoundation.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=<UNKNOWN> ","DKIM-Filter":["OpenDKIM Filter v2.11.0 smtp2.osuosl.org 1BA7A40818","OpenDKIM Filter v2.11.0 smtp1.osuosl.org 0044A82656"],"Received-SPF":"Pass (mailfrom) identity=mailfrom; client-ip=170.10.129.124;\n helo=us-smtp-delivery-124.mimecast.com; envelope-from=aconole@redhat.com;\n receiver=<UNKNOWN>","DMARC-Filter":"OpenDMARC Filter v1.4.2 smtp1.osuosl.org 0044A82656","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n s=mimecast20190719; t=1775667991;\n h=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n to:to:cc:cc:mime-version:mime-version:content-type:content-type:\n content-transfer-encoding:content-transfer-encoding:\n in-reply-to:in-reply-to:references:references;\n bh=PBRrfpMJPqrYCC6af7jFsEFTfg7JHg4h1hjzGfoVX6Y=;\n b=i0sf5DiW4oJDg2wuIpefMglOvpqNkDfkizKrUi2Gv4p3VXYQwRQeVioAkEHyqPwjF8Fv0a\n W//4DHRAKELw+PZaV93cAefJttWiyQgLjuJopYnjteOheWpaLVi1BspjZRMStYngbtE3IZ\n UEcwYxbnWeQJg6tWNuIqP/PxH9LeGUs=","X-MC-Unique":"Qu5TMTdLM76d9drApvikSw-1","X-Mimecast-MFC-AGG-ID":"Qu5TMTdLM76d9drApvikSw_1775667987","To":"dev@openvswitch.org","Date":"Wed,  8 Apr 2026 13:06:01 -0400","Message-ID":"<20260408170613.587902-6-aconole@redhat.com>","In-Reply-To":"<20260408170613.587902-1-aconole@redhat.com>","References":"<20260408170613.587902-1-aconole@redhat.com>","MIME-Version":"1.0","X-Scanned-By":"MIMEDefang 3.4.1 on 10.30.177.4","X-Mimecast-Spam-Score":"0","X-Mimecast-MFC-PROC-ID":"-pmcfD26oibaG6dOTNk9qhy_wrn746CYZU7yZz3Q3Rw_1775667987","X-Mimecast-Originator":"redhat.com","Subject":"[ovs-dev] [RFC 05/12] ct-offload: Add a new interface as an offload\n provider.","X-BeenThere":"ovs-dev@openvswitch.org","X-Mailman-Version":"2.1.30","Precedence":"list","List-Id":"<ovs-dev.openvswitch.org>","List-Unsubscribe":"<https://mail.openvswitch.org/mailman/options/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=unsubscribe>","List-Archive":"<http://mail.openvswitch.org/pipermail/ovs-dev/>","List-Post":"<mailto:ovs-dev@openvswitch.org>","List-Help":"<mailto:ovs-dev-request@openvswitch.org?subject=help>","List-Subscribe":"<https://mail.openvswitch.org/mailman/listinfo/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=subscribe>","From":"Aaron Conole via dev <ovs-dev@openvswitch.org>","Reply-To":"Aaron Conole <aconole@redhat.com>","Cc":"Eli Britstein <elibr@nvidia.com>, Florian Westphal <fwestpha@redhat.com>,\n Flavio Leitner <fbl@redhat.com>","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"ovs-dev-bounces@openvswitch.org","Sender":"\"dev\" <ovs-dev-bounces@openvswitch.org>"},"content":"This adds the basic primitives, initialization, and operations that\nconntrack offload providers will need to implement in order to\noffer a path to offloading.\n\nSigned-off-by: Aaron Conole <aconole@redhat.com>\n---\n lib/automake.mk  |   2 +\n lib/ct-offload.c | 257 +++++++++++++++++++++++++++++++++++++++++++++++\n lib/ct-offload.h |  97 ++++++++++++++++++\n 3 files changed, 356 insertions(+)\n create mode 100644 lib/ct-offload.c\n create mode 100644 lib/ct-offload.h","diff":"diff --git a/lib/automake.mk b/lib/automake.mk\nindex 027dd986ba..f11e3de27c 100644\n--- a/lib/automake.mk\n+++ b/lib/automake.mk\n@@ -97,6 +97,8 @@ lib_libopenvswitch_la_SOURCES = \\\n \tlib/conntrack-other.c \\\n \tlib/conntrack.c \\\n \tlib/conntrack.h \\\n+\tlib/ct-offload.c \\\n+\tlib/ct-offload.h \\\n \tlib/cooperative-multitasking.c \\\n \tlib/cooperative-multitasking.h \\\n \tlib/cooperative-multitasking-private.h \\\ndiff --git a/lib/ct-offload.c b/lib/ct-offload.c\nnew file mode 100644\nindex 0000000000..3bd6200e37\n--- /dev/null\n+++ b/lib/ct-offload.c\n@@ -0,0 +1,257 @@\n+/*\n+ * Copyright (c) 2026 Red Hat, Inc.\n+ *\n+ * Licensed under the Apache License, Version 2.0 (the \"License\");\n+ * you may not use this file except in compliance with the License.\n+ * You may obtain a copy of the License at:\n+ *\n+ *     http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+\n+#include <config.h>\n+#include <errno.h>\n+\n+#include \"ct-offload.h\"\n+#include \"ovs-thread.h\"\n+#include \"util.h\"\n+\n+#include \"openvswitch/list.h\"\n+#include \"openvswitch/vlog.h\"\n+\n+VLOG_DEFINE_THIS_MODULE(ct_offload);\n+\n+/* Node in the registered-provider list. */\n+struct ct_offload_class_node {\n+    const struct ct_offload_class *class;\n+    struct ovs_list               list_node;\n+};\n+\n+/* Global list of registered CT offload classes and a mutex to protect it.\n+ * Providers are expected to be registered at module init time and\n+ * unregistered only at module teardown, so contention is minimal. */\n+static struct ovs_mutex ct_offload_mutex = OVS_MUTEX_INITIALIZER;\n+static struct ovs_list  ct_offload_classes\n+    OVS_GUARDED_BY(ct_offload_mutex)\n+    = OVS_LIST_INITIALIZER(&ct_offload_classes);\n+\n+\n+/* ct_offload_register() - register a CT offload provider class.\n+ *\n+ * Calls class->init() if provided.  Returns 0 on success or a positive\n+ * errno value on failure.  Attempting to register the same class twice\n+ * returns EEXIST. */\n+int\n+ct_offload_register(const struct ct_offload_class *class)\n+{\n+    struct ct_offload_class_node *node;\n+    int error = 0;\n+\n+    ovs_assert(class);\n+    ovs_assert(class->name);\n+\n+    ovs_mutex_lock(&ct_offload_mutex);\n+\n+    /* Detect duplicate registrations. */\n+    LIST_FOR_EACH (node, list_node, &ct_offload_classes) {\n+        if (!strcmp(node->class->name, class->name)) {\n+            VLOG_WARN(\"attempted to register duplicate ct offload class: %s\",\n+                      class->name);\n+            error = EEXIST;\n+            goto out;\n+        }\n+    }\n+\n+    error = class->init ? class->init() : 0;\n+    if (error) {\n+        VLOG_WARN(\"failed to initialize ct offload class %s: %s\",\n+                  class->name, ovs_strerror(error));\n+        goto out;\n+    }\n+\n+    node = xmalloc(sizeof *node);\n+    node->class = class;\n+    ovs_list_push_back(&ct_offload_classes, &node->list_node);\n+    VLOG_DBG(\"registered ct offload class: %s\", class->name);\n+\n+out:\n+    ovs_mutex_unlock(&ct_offload_mutex);\n+    return error;\n+}\n+\n+/* ct_offload_unregister() - unregister a previously registered class.\n+ *\n+ * Safe to call even if the class was never registered (no-op in that\n+ * case). */\n+void\n+ct_offload_unregister(const struct ct_offload_class *class)\n+{\n+    struct ct_offload_class_node *node;\n+\n+    ovs_assert(class);\n+\n+    ovs_mutex_lock(&ct_offload_mutex);\n+    LIST_FOR_EACH (node, list_node, &ct_offload_classes) {\n+        if (node->class == class) {\n+            ovs_list_remove(&node->list_node);\n+            free(node);\n+            VLOG_DBG(\"unregistered ct offload class: %s\", class->name);\n+            goto out;\n+        }\n+    }\n+    VLOG_WARN(\"attempted to unregister unknown ct offload class: %s\",\n+              class->name);\n+\n+out:\n+    ovs_mutex_unlock(&ct_offload_mutex);\n+}\n+\n+/* ct_offload_module_init() - register built-in CT offload providers.\n+ *\n+ * Must be called once before any connections are created. */\n+void\n+ct_offload_module_init(void)\n+{\n+    /* No built-in providers yet; third parties call ct_offload_register()\n+     * directly from their own module-init routines. */\n+}\n+\n+/* ct_offload_conn_add() - notify all eligible providers of a new connection.\n+ *\n+ * Iterates over registered providers and calls conn_add() on each one that\n+ * reports can_offload() == true for this context.  Returns the first non-zero\n+ * error encountered, but continues notifying remaining providers.  This allows\n+ * the underlying hardware conntrack details across providers function. */\n+int\n+ct_offload_conn_add(const struct ct_offload_ctx *ctx)\n+{\n+    struct ct_offload_class_node *node;\n+    int ret = 0;\n+\n+    ovs_mutex_lock(&ct_offload_mutex);\n+    LIST_FOR_EACH (node, list_node, &ct_offload_classes) {\n+        const struct ct_offload_class *class = node->class;\n+\n+        if (class->can_offload && !class->can_offload(ctx)) {\n+            continue;\n+        }\n+        if (class->conn_add) {\n+            int error = class->conn_add(ctx);\n+\n+            if (error && !ret) {\n+                ret = error;\n+            }\n+        }\n+    }\n+    ovs_mutex_unlock(&ct_offload_mutex);\n+\n+    return ret;\n+}\n+\n+/* ct_offload_conn_del() - notify all providers that a connection was removed.\n+ *\n+ * Called unconditionally on all providers so that each can clean up any\n+ * state it may have installed. */\n+void\n+ct_offload_conn_del(const struct ct_offload_ctx *ctx)\n+{\n+    struct ct_offload_class_node *node;\n+\n+    ovs_mutex_lock(&ct_offload_mutex);\n+    LIST_FOR_EACH (node, list_node, &ct_offload_classes) {\n+        const struct ct_offload_class *class = node->class;\n+\n+        if (class->conn_del) {\n+            class->conn_del(ctx);\n+        }\n+    }\n+    ovs_mutex_unlock(&ct_offload_mutex);\n+}\n+\n+void\n+ct_offload_conn_established(const struct ct_offload_ctx *ctx)\n+{\n+    struct ct_offload_class_node *node;\n+\n+    ovs_mutex_lock(&ct_offload_mutex);\n+    LIST_FOR_EACH (node, list_node, &ct_offload_classes) {\n+        const struct ct_offload_class *class = node->class;\n+\n+        if (class->conn_established) {\n+            class->conn_established(ctx);\n+        }\n+    }\n+    ovs_mutex_unlock(&ct_offload_mutex);\n+}\n+\n+/* ct_offload_conn_update() - query the hardware last-used timestamp.\n+ *\n+ * Iterates over providers and returns the first non-zero timestamp returned\n+ * by a provider's conn_update() callback.  Returns 0 if no provider\n+ * supplies a timestamp. */\n+long long\n+ct_offload_conn_update(const struct ct_offload_ctx *ctx)\n+{\n+    struct ct_offload_class_node *node;\n+    long long last_used = 0;\n+\n+    ovs_mutex_lock(&ct_offload_mutex);\n+    LIST_FOR_EACH (node, list_node, &ct_offload_classes) {\n+        const struct ct_offload_class *class = node->class;\n+\n+        if (class->conn_update) {\n+            long long ts = class->conn_update(ctx);\n+\n+            if (ts) {\n+                last_used = ts;\n+                break;\n+            }\n+        }\n+    }\n+    ovs_mutex_unlock(&ct_offload_mutex);\n+\n+    return last_used;\n+}\n+\n+/* ct_offload_can_offload() - returns true if any provider can offload ctx. */\n+bool\n+ct_offload_can_offload(const struct ct_offload_ctx *ctx)\n+{\n+    struct ct_offload_class_node *node;\n+    bool result = false;\n+\n+    ovs_mutex_lock(&ct_offload_mutex);\n+    LIST_FOR_EACH (node, list_node, &ct_offload_classes) {\n+        const struct ct_offload_class *class = node->class;\n+\n+        if (class->can_offload && class->can_offload(ctx)) {\n+            result = true;\n+            break;\n+        }\n+    }\n+    ovs_mutex_unlock(&ct_offload_mutex);\n+\n+    return result;\n+}\n+\n+/* ct_offload_flush() - flush all offloaded connections from every provider. */\n+void\n+ct_offload_flush(void)\n+{\n+    struct ct_offload_class_node *node;\n+\n+    ovs_mutex_lock(&ct_offload_mutex);\n+    LIST_FOR_EACH (node, list_node, &ct_offload_classes) {\n+        const struct ct_offload_class *class = node->class;\n+\n+        if (class->flush) {\n+            class->flush();\n+        }\n+    }\n+    ovs_mutex_unlock(&ct_offload_mutex);\n+}\ndiff --git a/lib/ct-offload.h b/lib/ct-offload.h\nnew file mode 100644\nindex 0000000000..824b94a5c1\n--- /dev/null\n+++ b/lib/ct-offload.h\n@@ -0,0 +1,97 @@\n+/*\n+ * Copyright (c) 2026 Red Hat, Inc.\n+ *\n+ * Licensed under the Apache License, Version 2.0 (the \"License\");\n+ * you may not use this file except in compliance with the License.\n+ * You may obtain a copy of the License at:\n+ *\n+ *     http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+\n+#ifndef CT_OFFLOAD_H\n+#define CT_OFFLOAD_H\n+\n+#include \"openvswitch/types.h\"\n+\n+struct conn;\n+struct netdev;\n+\n+/* Context for offload as part of the callbacks that all connection\n+ * offload APIs receive.\n+ */\n+struct ct_offload_ctx {\n+    const struct conn *conn;        /* Connection object being offloaded. */\n+    struct netdev *netdev_in;       /* Input netdev. */\n+    odp_port_t input_port_id;       /* ODP port number. */\n+};\n+\n+enum ct_offload_op_type {\n+    CT_OFFLOAD_OP_ADD,              /* Add operation. */\n+    CT_OFFLOAD_OP_DEL,              /* Del operation. */\n+    CT_OFFLOAD_OP_UPD,              /* Update operation. */\n+    CT_OFFLOAD_OP_POLICY,           /* Policy check operation. */\n+    CT_OFFLOAD_OP_FLUSH,            /* Flush. */\n+    CT_OFFLOAD_OP_EST,              /* Established - notify that a connection\n+                                     * has a reply seen. */\n+};\n+\n+struct ct_offload_op {\n+    enum ct_offload_op_type type;\n+    struct ct_offload_ctx   ctx;\n+    int                     error;\n+};\n+\n+/* Batched set of offload contexts and operations.*/\n+struct ct_offload_op_batch {\n+    struct ct_offload_op *ops;\n+    size_t                n_ops;\n+    size_t                allocated;\n+};\n+\n+\n+/* CT offload class describes a conntrack offload provider implementation. */\n+struct ct_offload_class {\n+    const char *name;\n+\n+    /* Initialization routine for the provider. */\n+    int (*init)(void);\n+\n+    /* Per-connection operation callbacks get called for individual operations\n+     * on the fast path or when batching is not in use. */\n+    int  (*conn_add)(const struct ct_offload_ctx *);\n+    void (*conn_del)(const struct ct_offload_ctx *);\n+    /* Populate the last-used timestamp for the connection.  Returns the\n+     * last-used time in milliseconds since epoch, or 0 on failure. */\n+    long long (*conn_update)(const struct ct_offload_ctx *);\n+    /* Called exactly once when the first reply-direction packet is seen\n+     * for an offloaded connection. */\n+    void (*conn_established)(const struct ct_offload_ctx *);\n+    /* Check whether this provider can offload a connection. */\n+    bool (*can_offload)(const struct ct_offload_ctx *);\n+    /* Flush all offloaded connections. */\n+    void (*flush)(void);\n+};\n+\n+/* Register/unregister a provider.  Must be called at module init, before\n+ * any connections are created. */\n+int  ct_offload_register(const struct ct_offload_class *);\n+void ct_offload_unregister(const struct ct_offload_class *);\n+\n+/* Module initialization (register built-in providers). */\n+void ct_offload_module_init(void);\n+\n+/* Per-connection offload API that dispatches to all registered providers. */\n+int       ct_offload_conn_add(const struct ct_offload_ctx *);\n+void      ct_offload_conn_del(const struct ct_offload_ctx *);\n+long long ct_offload_conn_update(const struct ct_offload_ctx *);\n+void      ct_offload_conn_established(const struct ct_offload_ctx *);\n+bool      ct_offload_can_offload(const struct ct_offload_ctx *);\n+void      ct_offload_flush(void);\n+\n+#endif /* CT_OFFLOAD_H */\n","prefixes":["ovs-dev","RFC","05/12"]}