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