Eelco Chaudron <[email protected]> writes:

> On 18 May 2026, at 19:13, Aaron Conole wrote:
>
>> This allows configuring hardware offload on/off.  The ct_sweep
>> expiration is always trying to fill the ops batch anyway, so it
>> doesn't need an actual check for enabled / disabled.  Wrapping that
>> code in a check may also be harmful in the event that offload is
>> disabled with offloaded connections.
>>
>> Signed-off-by: Aaron Conole <[email protected]>
>
> Hi Aaron,
>
> See some comments below.
>
> //Eelco
>
>> ---
>>  lib/conntrack.c    | 25 +++++++++++++----------
>>  lib/ct-offload.c   | 51 +++++++++++++++++++++++++++++++++++++++++++++-
>>  lib/ct-offload.h   |  8 ++++++++
>>  lib/dpif-offload.c | 13 ++++++++++++
>>  lib/dpif-offload.h |  1 +
>>  vswitchd/bridge.c  |  4 ++++
>>  6 files changed, 90 insertions(+), 12 deletions(-)
>>
>> diff --git a/lib/conntrack.c b/lib/conntrack.c
>> index 6ebae26411..83dac97b89 100644
>> --- a/lib/conntrack.c
>> +++ b/lib/conntrack.c
>> @@ -534,12 +534,14 @@ conn_clean(struct conntrack *ct, struct conn *conn)
>>          atomic_count_dec(&zl->czl.count);
>>      }
>>
>> -    struct ct_offload_ctx offload_ctx = {
>> -        .conn           = conn,
>> -        .netdev_in      = NULL,
>> -        .input_port_id  = ODPP_NONE,
>> -    };
>> -    ct_offload_conn_del(&offload_ctx);
>> +    if (ct_offload_conn_is_offloaded(conn)) {
>> +        struct ct_offload_ctx offload_ctx = {
>> +            .conn           = conn,
>> +            .netdev_in      = NULL,
>> +            .input_port_id  = ODPP_NONE,
>> +        };
>> +        ct_offload_conn_del(&offload_ctx);
>> +    }
>>
>>      ovsrcu_postpone(delete_conn, conn);
>>      atomic_count_dec(&ct->n_conn);
>> @@ -1392,7 +1394,7 @@ process_one(struct conntrack *ct, struct dp_packet 
>> *pkt,
>>          }
>>          ovs_mutex_unlock(&ct->ct_lock);
>>
>> -        if (conn) {
>> +        if (conn && ct_offload_enabled()) {
>>              struct ct_offload_ctx offload_ctx = {
>>                  .conn           = conn,
>>                  .netdev_in      = NULL,
>> @@ -1404,6 +1406,7 @@ process_one(struct conntrack *ct, struct dp_packet 
>> *pkt,
>>
>>      if (!create_new_conn && conn && ctx->reply &&
>>          (pkt->md.ct_state & CS_ESTABLISHED) &&
>> +        ct_offload_enabled() &&
>>          ct_offload_conn_is_offloaded(conn) &&
>>          !ct_offload_conn_is_established(conn)) {
>>          /* Notify offload providers that the connection is established.
>> @@ -1568,7 +1571,6 @@ ct_sweep(struct conntrack *ct, struct rculist *list, 
>> long long now,
>>      size_t cleaned = 0;
>>      size_t count = 0;
>>
>> -
>>      ct_offload_op_batch_init(&batch);
>>
>>      RCULIST_FOR_EACH (conn, node, list) {
>> @@ -1582,7 +1584,6 @@ ct_sweep(struct conntrack *ct, struct rculist *list, 
>> long long now,
>>                      .netdev_in      = NULL,
>>                      .input_port_id  = ODPP_NONE,
>>                  };
>> -
>>                  ct_offload_op_batch_add(&batch, CT_OFFLOAD_OP_UPD,
>>                                          &offload_ctx);
>>              }
>> @@ -1590,12 +1591,14 @@ ct_sweep(struct conntrack *ct, struct rculist *list, 
>> long long now,
>>          count++;
>>      }
>>
>> -    /* Run the batch. */
>> +    /* Run the batch: providers that can supply a hw last-used timestamp
>> +     * return error==0, allowing us to extend the expiration.  A non-zero
>> +     * error (typically ENODATA) means the connection has no hw activity
>> +     * and should be expired normally. */
>>      ct_offload_op_batch_submit(&batch);
>>
>>      CT_OFFLOAD_BATCH_OP_FOR_EACH (idx, op, &batch) {
>>          struct conn *c = CONST_CAST(struct conn *, op->ctx.conn);
>> -
>>          if (op->error) {
>>              conn_clean(ct, c);
>>              cleaned++;
>> diff --git a/lib/ct-offload.c b/lib/ct-offload.c
>> index 979b5b13db..1b4d230b80 100644
>> --- a/lib/ct-offload.c
>> +++ b/lib/ct-offload.c
>> @@ -20,8 +20,10 @@
>>  #include "conntrack.h"
>>  #include "conntrack-private.h"
>>  #include "ct-offload.h"
>> +#include "dpif-offload.h"
>>  #include "ovs-thread.h"
>>  #include "util.h"
>> +#include "vswitch-idl.h"
>>
>>  #include "openvswitch/list.h"
>>  #include "openvswitch/vlog.h"
>> @@ -51,6 +53,12 @@ static struct ovs_list  ct_offload_classes
>>      OVS_GUARDED_BY(ct_offload_mutex)
>>      = OVS_LIST_INITIALIZER(&ct_offload_classes);
>>
>> +/* Built-in CT offload provider classes.  Only those whose name matches a
>> + * registered dpif offload class will be activated by 
>> ct_offload_module_init().
>> + */
>> +static const struct ct_offload_class *base_ct_offload_classes[] = {
>> +};
>> +
>>
>>  /* ct_offload_register() - register a CT offload provider class.
>>   *
>> @@ -140,11 +148,52 @@ ct_offload_alloc_private_slot(void)
>>
>>  /* ct_offload_module_init() - register built-in CT offload providers.
>>   *
>> - * Must be called once before any connections are created. */
>> + * Only registers providers whose name matches a currently-registered dpif
>> + * offload class, so CT offload is automatically tied to the active hardware
>> + * offload provider.  Safe to call multiple times; subsequent calls are
>> + * no-ops (duplicate registration is detected and skipped). */
>>  void
>>  ct_offload_module_init(void)
>>  {
>>      ct_offload_alloc_private_slot();
>> +
>> +    for (size_t i = 0; i < ARRAY_SIZE(base_ct_offload_classes); i++) {
>> +        const struct ct_offload_class *class = base_ct_offload_classes[i];
>> +
>> +        if (dpif_offload_class_is_registered(class->name)) {
>> +            ct_offload_register(class);
>> +        }
>> +    }
>> +}
>> +
>> +/* ct_offload_enabled() - returns true when hardware offload is active.
>> + *
>> + * Delegates to dpif_offload_enabled() so CT offload shares the same global
>> + * enable/disable knob as datapath hardware offload. */
>> +bool
>> +ct_offload_enabled(void)
>> +{
>> +    return dpif_offload_enabled();
>> +}
>> +
>> +/* ct_offload_set_global_cfg() - configure CT offload from OVSDB.
>> + *
>> + * Must be called alongside dpif_offload_set_global_cfg() so that CT offload
>> + * providers are registered once hardware offload has been enabled and the
>> + * appropriate dpif offload classes are known. */
>> +void
>> +ct_offload_set_global_cfg(const struct ovsrec_open_vswitch *cfg OVS_UNUSED)
>> +{
>> +    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
>> +
>> +    if (!dpif_offload_enabled()) {
>> +        return;
>> +    }
>> +
>> +    if (ovsthread_once_start(&once)) {
>> +        ct_offload_module_init();
>> +        ovsthread_once_done(&once);
>> +    }
>>  }
>
> This whole part is a bit unclear to me.  The offload classes
> exist, but they might not be in use, meaning not tied to a
> dpif-offload-provider.  They get dynamically created on dpif
> creation.  It looks like the conntrack infrastructure is
> global, not per dpif (this might need to change, but I guess
> not part of this effort).
>
> For example, a ct offload provider could be registered and
> initialized even if the corresponding dpif type is not in
> use by any bridge.
>
> I am mainly confused how the specific offload provider
> instance gets linked to the offload provider's specific
> data.  I assumed the dpif-offload-provider would register
> the ct-offload at init time (and destruct on destroy).

I think the dpif-offload-provider should have this responsibility.  The
reason for implementing it this way is to avoid

  dpif -> ct -> dpif

cascaded calls.

> Maybe this will become more clear in subsequent patches.
>
>>
>>  /* ct_offload_conn_add_() - notify all eligible providers of a new 
>> connection.
>> diff --git a/lib/ct-offload.h b/lib/ct-offload.h
>> index fcb3170fa1..fe4ecd33b8 100644
>> --- a/lib/ct-offload.h
>> +++ b/lib/ct-offload.h
>> @@ -21,6 +21,7 @@
>>
>>  struct conn;
>>  struct netdev;
>> +struct ovsrec_open_vswitch;
>>
>>  /* Context for offload as part of the callbacks that all connection
>>   * offload APIs receive.
>> @@ -91,6 +92,13 @@ void ct_offload_unregister(const struct ct_offload_class 
>> *);
>>  void ct_offload_alloc_private_slot(void);
>>  /* Module initialization (register built-in providers). */
>>  void ct_offload_module_init(void);
>> +/* Global configuration: call alongside dpif_offload_set_global_cfg() to
>> + * enable CT offload when hardware offload is active. */
>> +void ct_offload_set_global_cfg(const struct ovsrec_open_vswitch *);
>> +
>> +/* Returns true when CT offload is enabled (delegates to 
>> dpif_offload_enabled).
>> + */
>> +bool ct_offload_enabled(void);
>>
>>  /* Per-connection offload API that dispatches to all registered providers. 
>> */
>>  int       ct_offload_conn_add(const struct ct_offload_ctx *);
>> diff --git a/lib/dpif-offload.c b/lib/dpif-offload.c
>> index bb2feced9e..30cc4fd271 100644
>> --- a/lib/dpif-offload.c
>> +++ b/lib/dpif-offload.c
>> @@ -516,6 +516,19 @@ dpif_offload_enabled(void)
>>      return enabled;
>>  }
>>
>> +/* dpif_offload_class_is_registered() - returns true if a dpif offload class
>> + * with the given name has been successfully registered. */
>> +bool
>> +dpif_offload_class_is_registered(const char *name)
>> +{
>> +    bool found;
>> +
>> +    ovs_mutex_lock(&dpif_offload_mutex);
>> +    found = shash_find(&dpif_offload_classes, name) != NULL;
>> +    ovs_mutex_unlock(&dpif_offload_mutex);
>> +    return found;
>> +}
>> +
>>  bool
>>  dpif_offload_rebalance_policy_enabled(void)
>>  {
>> diff --git a/lib/dpif-offload.h b/lib/dpif-offload.h
>> index 7fad3ebee3..d95e4b9463 100644
>> --- a/lib/dpif-offload.h
>> +++ b/lib/dpif-offload.h
>> @@ -45,6 +45,7 @@ enum dpif_offload_impl_type {
>>  void dpif_offload_set_global_cfg(const struct ovsrec_open_vswitch *);
>>  bool dpif_offload_enabled(void);
>>  bool dpif_offload_rebalance_policy_enabled(void);
>> +bool dpif_offload_class_is_registered(const char *name);
>>
>>  
>>  /* Per dpif specific functions. */
>> diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
>> index 7a68e19ac3..91ffe76648 100644
>> --- a/vswitchd/bridge.c
>> +++ b/vswitchd/bridge.c
>> @@ -28,6 +28,7 @@
>>  #include "daemon.h"
>>  #include "dirs.h"
>>  #include "dpif.h"
>> +#include "ct-offload.h"
>
> Alphabetical order?
>
>>  #include "dpif-offload.h"
>>  #include "dpdk.h"
>>  #include "hash.h"
>> @@ -543,6 +544,8 @@ bridge_init(const char *remote)
>>  void
>>  bridge_exit(bool delete_datapath)
>>  {
>> +    ct_offload_flush();
>
> Calling this low-level operation from bridge_exit() seems
> like a layer violation.  Should this not be called as part
> of the dpif or conntrack teardown instead?

Yes, I don't remember why I put it here, but it may have been the
result of some earlier version where I had some kind of memory leak.

> conntrack_destroy() already cleans all connections via
> conn_clean() which calls ct_offload_conn_del() for each
> offloaded connection.
>
>>      if_notifier_manual_set_cb(NULL);
>>      if_notifier_destroy(ifnotifier);
>>      seq_destroy(ifaces_changed);
>> @@ -3396,6 +3399,7 @@ bridge_run(void)
>>
>>      if (cfg && ovsdb_idl_get_seqno(idl) != idl_seqno) {
>>          dpif_offload_set_global_cfg(cfg);
>> +        ct_offload_set_global_cfg(cfg);
>
> Same layering concern as ct_offload_flush() above.  Should
> dpif_offload_set_global_cfg() call this internally rather
> than having bridge.c know about ct-offload?

That makes sense to me.

>>      }
>>
>>      if (cfg) {
>> -- 
>> 2.53.0

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

Reply via email to