On 06/10/2026 17:30, Eelco Chaudron wrote:
External email: Use caution opening links or attachments


Allow the netdev_hw_post_process() callback to return an optional set of
alternative actions via a new 'alt_actions' parameter.  When set, these
actions are executed instead of the flow's own actions, enabling offload
providers to indicate that a subset of the actions was already performed
in hardware.

Note that batching for packets with alternative actions is not optimal,
as it requires a linear scan over the current batch list to find a
matching (flow, alt_actions) pair.
Thanks Eelco. Few nits below

Signed-off-by: Eelco Chaudron <[email protected]>
---
  lib/dpif-netdev.c           | 101 +++++++++++++++++++++++++++---------
  lib/dpif-offload-dpdk.c     |   5 +-
  lib/dpif-offload-dummy.c    |   8 ++-
  lib/dpif-offload-provider.h |   7 ++-
  lib/dpif-offload.c          |   4 +-
  lib/dpif-offload.h          |   7 +++
  6 files changed, 102 insertions(+), 30 deletions(-)

diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 7165fde12..a500b02a5 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -197,6 +197,7 @@ struct dpcls {
  struct dp_packet_flow_map {
      struct dp_packet *packet;
      struct dp_netdev_flow *flow;
+    struct dp_netdev_actions *alt_actions; /* NULL = use flow's own actions. */
      uint16_t tcp_flags;
  };

@@ -468,6 +469,17 @@ struct dpif_netdev {
      uint64_t last_port_seq;
  };

+/* Verify that struct offload_actions (defined in dpif-offload.h) is
+ * layout-identical to struct dp_netdev_actions.  The two structs are kept
+ * separate to avoid exposing dp_netdev_actions as a public API, but must
+ * remain binary-compatible so that ALIGNED_CAST() between them is safe. */
+BUILD_ASSERT_DECL(sizeof(struct offload_actions) ==
+                  sizeof(struct dp_netdev_actions));
+BUILD_ASSERT_DECL(offsetof(struct offload_actions, size) ==
+                  offsetof(struct dp_netdev_actions, size));
+BUILD_ASSERT_DECL(offsetof(struct offload_actions, actions) ==
+                  offsetof(struct dp_netdev_actions, actions));
+
  static int get_port_by_number(struct dp_netdev *dp, odp_port_t port_no,
                                struct dp_netdev_port **portp)
      OVS_REQ_RDLOCK(dp->port_rwlock);
@@ -7060,6 +7072,7 @@ struct packet_batch_per_flow {
      unsigned int byte_count;
      uint16_t tcp_flags;
      struct dp_netdev_flow *flow;
+    struct dp_netdev_actions *alt_actions; /* NULL = use flow's own actions. */

      struct dp_packet_batch array;
  };
@@ -7076,11 +7089,15 @@ packet_batch_per_flow_update(struct 
packet_batch_per_flow *batch,

  static inline void ALWAYS_INLINE
  packet_batch_per_flow_init(struct packet_batch_per_flow *batch,
-                           struct dp_netdev_flow *flow)
+                           struct dp_netdev_flow *flow,
+                           struct dp_netdev_actions *alt_actions)
  {
-    flow->batch = batch;
+    if (!alt_actions) {
+        flow->batch = batch;
+    }

      batch->flow = flow;
+    batch->alt_actions = alt_actions;
      dp_packet_batch_init(&batch->array);
      batch->byte_count = 0;
      batch->tcp_flags = 0;
@@ -7097,37 +7114,67 @@ packet_batch_per_flow_execute(struct 
packet_batch_per_flow *batch,
                          batch->byte_count,
                          batch->tcp_flags, pmd->ctx.now / 1000);

-    actions = dp_netdev_flow_get_actions(flow);
+    actions = batch->alt_actions ? batch->alt_actions
+                                 : dp_netdev_flow_get_actions(flow);

      dp_netdev_execute_actions(pmd, &batch->array, true, &flow->flow,
                                actions->actions, actions->size);
  }

-static inline void ALWAYS_INLINE
-dp_netdev_queue_batches(struct dp_packet *pkt,
-                        struct dp_netdev_flow *flow, uint16_t tcp_flags,
-                        struct packet_batch_per_flow *batches,
-                        size_t *n_batches)
+static inline struct packet_batch_per_flow * ALWAYS_INLINE
+dp_netdev_get_batch(struct dp_netdev_flow *flow,
+                    struct dp_netdev_actions *alt_actions,
+                    struct packet_batch_per_flow *batches,
+                    size_t *n_batches)
  {
-    struct packet_batch_per_flow *batch = flow->batch;
+    struct packet_batch_per_flow *batch = NULL;
+
+    if (alt_actions == NULL) {
if (!alt_actions)
+        batch = flow->batch;
+    } else {
+        /* Scan for an existing batch with matching (flow, alt_actions). */
+        for (size_t i = 0; i < *n_batches; i++) {
+            if (batches[i].flow == flow &&
+                batches[i].alt_actions == alt_actions) {
+                batch = &batches[i];
+                break;
+            }
+        }
+    }

      if (OVS_UNLIKELY(!batch)) {
          batch = &batches[(*n_batches)++];
-        packet_batch_per_flow_init(batch, flow);
+        packet_batch_per_flow_init(batch, flow, alt_actions);
      }

+    return batch;
+}
+
+static inline void ALWAYS_INLINE
+dp_netdev_queue_batches(struct dp_packet *pkt,
+                        struct dp_netdev_flow *flow,
+                        struct dp_netdev_actions *alt_actions,
+                        uint16_t tcp_flags,
+                        struct packet_batch_per_flow *batches,
+                        size_t *n_batches)
+{
+    struct packet_batch_per_flow *batch;
+
+    batch = dp_netdev_get_batch(flow, alt_actions, batches, n_batches);
      packet_batch_per_flow_update(batch, pkt, tcp_flags);
  }

  static inline void ALWAYS_INLINE
  packet_enqueue_to_flow_map(struct dp_packet *packet,
                             struct dp_netdev_flow *flow,
+                           struct dp_netdev_actions *alt_actions,
                             uint16_t tcp_flags,
                             struct dp_packet_flow_map *flow_map,
                             size_t index)
  {
      struct dp_packet_flow_map *map = &flow_map[index];
      map->flow = flow;
+    map->alt_actions = alt_actions;
      map->packet = packet;
      map->tcp_flags = tcp_flags;
  }
@@ -7181,7 +7228,7 @@ smc_lookup_batch(struct dp_netdev_pmd_thread *pmd,
                      /* Add these packets into the flow map in the same order
                       * as received.
                       */
-                    packet_enqueue_to_flow_map(packet, flow, tcp_flags,
+                    packet_enqueue_to_flow_map(packet, flow, NULL, tcp_flags,
                                                 flow_map, recv_idx);
                      n_smc_hit++;
                      hit = true;
@@ -7234,9 +7281,11 @@ smc_lookup_single(struct dp_netdev_pmd_thread *pmd,
  static inline int ALWAYS_INLINE
  dp_netdev_hw_flow(const struct dp_netdev_pmd_thread *pmd,
                    struct dp_packet *packet,
+                  struct dp_netdev_actions **alt_actions,
                    struct dp_netdev_flow **flow)
  {
      struct dp_netdev_rxq *rxq = pmd->ctx.last_rxq;
+    struct offload_actions *offload_actions = NULL;
move line up
      bool post_process_api_supported;
      void *flow_reference = NULL;
      int err;
@@ -7246,11 +7295,13 @@ dp_netdev_hw_flow(const struct dp_netdev_pmd_thread 
*pmd,

      if (!post_process_api_supported) {
          *flow = NULL;
+        *alt_actions = NULL;
          return 0;
      }

      err = dpif_offload_netdev_hw_post_process(rxq->port->netdev, pmd->core_id,
-                                              packet, &flow_reference);
+                                              packet, &offload_actions,
+                                              &flow_reference);
      if (err && err != EOPNOTSUPP) {
          if (err != ECANCELED) {
              COVERAGE_INC(datapath_drop_hw_post_process);
@@ -7261,6 +7312,7 @@ dp_netdev_hw_flow(const struct dp_netdev_pmd_thread *pmd,
      }

      *flow = flow_reference;
+    *alt_actions = ALIGNED_CAST(struct dp_netdev_actions *, offload_actions);
      return 0;
  }

@@ -7269,6 +7321,7 @@ dp_netdev_hw_flow(const struct dp_netdev_pmd_thread *pmd,
  static inline void ALWAYS_INLINE
  dfc_processing_enqueue_classified_packet(struct dp_packet *packet,
                                           struct dp_netdev_flow *flow,
+                                         struct dp_netdev_actions *alt_actions,
                                           uint16_t tcp_flags,
                                           bool batch_enable,
                                           struct packet_batch_per_flow 
*batches,
@@ -7278,17 +7331,16 @@ dfc_processing_enqueue_classified_packet(struct 
dp_packet *packet,

  {
      if (OVS_LIKELY(batch_enable)) {
-        dp_netdev_queue_batches(packet, flow, tcp_flags, batches,
+        dp_netdev_queue_batches(packet, flow, alt_actions, tcp_flags, batches,
                                  n_batches);
      } else {
          /* Flow batching should be performed only after fast-path
           * processing is also completed for packets with emc miss
           * or else it will result in reordering of packets with
           * same datapath flows. */
-        packet_enqueue_to_flow_map(packet, flow, tcp_flags,
+        packet_enqueue_to_flow_map(packet, flow, alt_actions, tcp_flags,
                                     flow_map, (*map_cnt)++);
      }
-
  }

  /* Try to process all ('cnt') the 'packets' using only the datapath flow cache
@@ -7339,6 +7391,7 @@ dfc_processing(struct dp_netdev_pmd_thread *pmd,
                              cnt);
      int i;
      DP_PACKET_BATCH_REFILL_FOR_EACH (i, cnt, packet, packets_) {
+        struct dp_netdev_actions *alt_actions = NULL;
          struct dp_netdev_flow *flow = NULL;
          uint16_t tcp_flags;

@@ -7360,17 +7413,17 @@ dfc_processing(struct dp_netdev_pmd_thread *pmd,
          }

          if (offload_enabled && recirc_depth == 0) {
-            if (OVS_UNLIKELY(dp_netdev_hw_flow(pmd, packet, &flow))) {
+            if (OVS_UNLIKELY(dp_netdev_hw_flow(pmd, packet, &alt_actions,
+                                               &flow))) {
                  /* Packet restoration failed and it was dropped, do not
-                 * continue processing.
-                 */
+                 * continue processing. */
                  continue;
              }
              if (OVS_LIKELY(flow)) {
                  tcp_flags = parse_tcp_flags(packet, NULL, NULL, NULL);
                  n_phwol_hit++;
                  dfc_processing_enqueue_classified_packet(
-                        packet, flow, tcp_flags, batch_enable,
+                        packet, flow, alt_actions, tcp_flags, batch_enable,
                          batches, n_batches, flow_map, &map_cnt);
                  continue;
              }
@@ -7386,7 +7439,7 @@ dfc_processing(struct dp_netdev_pmd_thread *pmd,
              if (OVS_LIKELY(flow)) {
                  n_simple_hit++;
                  dfc_processing_enqueue_classified_packet(
-                        packet, flow, tcp_flags, batch_enable,
+                        packet, flow, NULL, tcp_flags, batch_enable,
                          batches, n_batches, flow_map, &map_cnt);
                  continue;
              }
@@ -7405,7 +7458,7 @@ dfc_processing(struct dp_netdev_pmd_thread *pmd,
              tcp_flags = miniflow_get_tcp_flags(&key->mf);
              n_emc_hit++;
              dfc_processing_enqueue_classified_packet(
-                    packet, flow, tcp_flags, batch_enable,
+                    packet, flow, NULL, tcp_flags, batch_enable,
                      batches, n_batches, flow_map, &map_cnt);
          } else {
              /* Exact match cache missed. Group missed packets together at
@@ -7625,7 +7678,7 @@ fast_path_processing(struct dp_netdev_pmd_thread *pmd,
           * as received.
           */
          tcp_flags = miniflow_get_tcp_flags(&keys[i]->mf);
-        packet_enqueue_to_flow_map(packet, flow, tcp_flags,
+        packet_enqueue_to_flow_map(packet, flow, NULL, tcp_flags,
                                     flow_map, recv_idx);
      }

@@ -7677,8 +7730,8 @@ dp_netdev_input__(struct dp_netdev_pmd_thread *pmd,
          if (OVS_UNLIKELY(!map->flow)) {
              continue;
          }
-        dp_netdev_queue_batches(map->packet, map->flow, map->tcp_flags,
-                                batches, &n_batches);
+        dp_netdev_queue_batches(map->packet, map->flow, map->alt_actions,
+                                map->tcp_flags, batches, &n_batches);
       }

      /* All the flow batches need to be reset before any call to
diff --git a/lib/dpif-offload-dpdk.c b/lib/dpif-offload-dpdk.c
index 293b3b8e9..0ea16ec8b 100644
--- a/lib/dpif-offload-dpdk.c
+++ b/lib/dpif-offload-dpdk.c
@@ -832,10 +832,13 @@ dpdk_flow_count_by_thread(struct dpdk_offload *offload, 
unsigned int tid)
  static int
  dpdk_offload_hw_post_process(const struct dpif_offload *offload_,
                               struct netdev *netdev, unsigned pmd_id,
-                             struct dp_packet *packet, void **flow_reference)
+                             struct dp_packet *packet,
+                             struct offload_actions **alt_actions,
+                             void **flow_reference)
  {
      struct dpdk_offload *offload = dpdk_offload_cast(offload_);

+    *alt_actions = NULL;
      return dpdk_netdev_hw_miss_packet_recover(offload, netdev, pmd_id, packet,
                                                flow_reference);
  }
diff --git a/lib/dpif-offload-dummy.c b/lib/dpif-offload-dummy.c
index 878276a94..022c48ede 100644
--- a/lib/dpif-offload-dummy.c
+++ b/lib/dpif-offload-dummy.c
@@ -580,7 +580,9 @@ dummy_offload_get_port_by_odp_port(const struct 
dpif_offload *offload_,
  static int
  dummy_offload_hw_post_process(const struct dpif_offload *offload_,
                                struct netdev *netdev, unsigned pmd_id,
-                              struct dp_packet *packet, void **flow_reference_)
+                              struct dp_packet *packet,
+                              struct offload_actions **alt_actions,
+                              void **flow_reference_)
  {
      struct dummy_offloaded_flow *off_flow;
      struct dummy_offload_port *port;
@@ -590,6 +592,7 @@ dummy_offload_hw_post_process(const struct dpif_offload 
*offload_,
      port = dummy_offload_get_port_by_netdev(offload_, netdev);
      if (!port || !dp_packet_has_flow_mark(packet, &flow_mark)) {
          *flow_reference_ = NULL;
+        *alt_actions = NULL;
          return 0;
      }

@@ -607,7 +610,8 @@ dummy_offload_hw_post_process(const struct dpif_offload 
*offload_,
      }
      ovs_mutex_unlock(&port->port_mutex);

-     *flow_reference_ = flow_reference;
+    *flow_reference_ = flow_reference;
+    *alt_actions = NULL;
      return 0;
  }

diff --git a/lib/dpif-offload-provider.h b/lib/dpif-offload-provider.h
index 444b13138..800d52e9c 100644
--- a/lib/dpif-offload-provider.h
+++ b/lib/dpif-offload-provider.h
@@ -269,10 +269,13 @@ struct dpif_offload_class {
       *
       * When zero (0) is returned, the 'flow_reference' pointer may reference
       * the flow_reference passed to the matching flow.  This can be used to
-     * support partial offloads.  The returned pointer must remain valid until
-     * the end of the next RCU grace period. */
+     * support partial offloads.  The 'alt_actions' pointer may also be set to
+     * override the flow's normal actions, for example because a subset of the
omit 'normal'
+     * actions was already executed in hardware.  Any pointers set must remain
+     * valid until the end of the next RCU grace period. */
      int (*netdev_hw_post_process)(const struct dpif_offload *, struct netdev 
*,
                                    unsigned pmd_id, struct dp_packet *,
+                                  struct offload_actions **alt_actions,
                                    void **flow_reference);

      /* Allows the offload provider to override the default UDP tunnel source
diff --git a/lib/dpif-offload.c b/lib/dpif-offload.c
index 04dabc42c..b381eeaa6 100644
--- a/lib/dpif-offload.c
+++ b/lib/dpif-offload.c
@@ -1438,6 +1438,7 @@ dpif_offload_datapath_flow_stats(const char *dpif_name, 
odp_port_t in_port,
  int
  dpif_offload_netdev_hw_post_process(struct netdev *netdev, unsigned pmd_id,
                                      struct dp_packet *packet,
+                                    struct offload_actions **alt_actions,
                                      void **flow_reference)
  {
      const struct dpif_offload *offload;
@@ -1463,7 +1464,8 @@ dpif_offload_netdev_hw_post_process(struct netdev 
*netdev, unsigned pmd_id,
      }

      rc = offload->class->netdev_hw_post_process(offload, netdev, pmd_id,
-                                                packet, flow_reference);
+                                                packet, alt_actions,
+                                                flow_reference);
      if (rc == EOPNOTSUPP) {
          /* API unsupported by the port; avoid subsequent calls. */
          atomic_store_relaxed(&netdev->hw_info.post_process_api_supported,
diff --git a/lib/dpif-offload.h b/lib/dpif-offload.h
index bf7643320..d48fcb1f0 100644
--- a/lib/dpif-offload.h
+++ b/lib/dpif-offload.h
@@ -40,6 +40,12 @@ enum dpif_offload_impl_type {
      DPIF_OFFLOAD_IMPL_FLOWS_PROVIDER_ONLY,
  };

+/* Immutable structure that holds a set of actions. */
+struct offload_actions {
+    unsigned int size;       /* Size of 'actions', in bytes. */
+    struct nlattr actions[]; /* Sequence of OVS_ACTION_ATTR_* attributes. */
+};
+

  /* Global functions. */
  void dpif_offload_set_global_cfg(const struct ovsrec_open_vswitch *);
@@ -112,6 +118,7 @@ bool dpif_offload_netdev_same_offload(const struct netdev *,
                                        const struct netdev *);
  int dpif_offload_netdev_hw_post_process(struct netdev *, unsigned pmd_id,
                                          struct dp_packet *,
+                                        struct offload_actions **alt_actions,
                                          void **flow_reference);
  bool dpif_offload_netdev_udp_tnl_get_src_port(const struct netdev *,
                                                struct dp_packet *,
--
2.55.0
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to