On 25 Feb 2026, at 11:42, Eli Britstein wrote:
> On 23/02/2026 13:09, Eelco Chaudron wrote:
>> External email: Use caution opening links or attachments
>>
>>
>> This patch introduces a new API to the offload provider framework that
>> allows hardware offload implementations to control UDP tunnel source port
>> selection during tunnel encapsulation.
>>
>> Background and Motivation
>> ==========================
>>
>> UDP-based tunnels (VXLAN, Geneve, etc.) use the UDP source port as an
>> entropy field to enable ECMP load balancing across multiple paths in the
>> network. The source port is typically calculated by hashing packet header
>> fields (5-tuple or inner packet headers) to distribute flows across
>> different paths.
>>
>> However, hardware offload implementations may require different approaches
>> to source port calculation:
>>
>> 1. Hardware NICs may use different hash functions or hash inputs than
>> the software datapath, which can lead to inconsistent flow distribution
>> when mixing hardware and software paths.
>>
>> 2. Some hardware may support enhanced entropy mechanisms (e.g., using
>> additional packet fields or hardware-specific hash engines) that provide
>> better load distribution than the default software implementation.
>>
>> Design
>> ======
>>
>> This patch adds a new optional callback to the dpif_offload_class:
>>
>> bool (*netdev_udp_tnl_get_src_port)(const struct dpif_offload *,
>> const struct netdev *ingress_netdev,
>> const struct dp_packet *packet,
>> ovs_be16 *src_port);
>
> I already have a local version of doca offloads supporting vxlan offload.
>
> I applied this patch and ported the actual implementation for doca. Here are
> my findings:
>
> - There wasn't a need for dpif_offload * argument. I wonder if maybe it's
> safe to omit it?
I assume this is uniform across the API, so for now I'd like to keep it as is.
I guess we can always change it if it becomes noticeable or starts impacting
performance.
> - In the implementation, need to parse the packet in order to set fields in a
> doca struct. For that there is a call to parse_tcp_flags(packet). the const
> interfere with that. I worked around it using CONST_CAST. maybe we can omit
> the const? alternatives?
I'll remove the const in the next patch.
> Other than that, it works.
>
> Also, small nit below.
Thanks for the review, I'll sent out a v2.
//Eelco
>>
>> The callback is invoked during tunnel push operations when hardware offload
>> is enabled and the original ingress port is known. It receives:
>> - ingress_netdev: The original ingress port where the packet was received
>> - packet: The inner packet to be encapsulated
>>
>> If the provider implements this callback and returns true, the returned
>> src_port value is used. Otherwise, OVS falls back to the standard hash-based
>> source port calculation.
>>
>> NOTE: This patch needs to be applied on top of the patch series below:
>> https://patchwork.ozlabs.org/project/openvswitch/list/?series=491639
>>
>> Signed-off-by: Eelco Chaudron <[email protected]>
>> ---
>> lib/dpif-netdev.c | 18 ++++-
>> lib/dpif-offload-dummy.c | 133 +++++++++++++++++++++++++++++++++-
>> lib/dpif-offload-provider.h | 13 ++++
>> lib/dpif-offload.c | 19 +++++
>> lib/dpif-offload.h | 4 +-
>> lib/netdev-native-tnl.c | 11 ++-
>> lib/netdev-native-tnl.h | 5 ++
>> lib/netdev-provider.h | 9 ++-
>> lib/netdev.c | 4 +-
>> lib/netdev.h | 1 +
>> tests/dpif-netdev.at | 101 +++++++++++++++++++++++---
>> utilities/checkpatch_dict.txt | 1 +
>> 12 files changed, 298 insertions(+), 21 deletions(-)
>>
>> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
>> index 64531a02c0..9384edcd96 100644
>> --- a/lib/dpif-netdev.c
>> +++ b/lib/dpif-netdev.c
>> @@ -8374,8 +8374,9 @@ push_tnl_action(const struct dp_netdev_pmd_thread *pmd,
>> const struct nlattr *attr,
>> struct dp_packet_batch *batch)
>> {
>> - struct tx_port *tun_port;
>> + const struct netdev *ingress_netdev = NULL;
>> const struct ovs_action_push_tnl *data;
>> + struct tx_port *tun_port;
>> int err;
>>
>> data = nl_attr_get(attr);
>> @@ -8385,7 +8386,20 @@ push_tnl_action(const struct dp_netdev_pmd_thread
>> *pmd,
>> err = -EINVAL;
>> goto error;
>> }
>> - err = netdev_push_header(tun_port->port->netdev, batch, data);
>> +
>> + if (dpif_offload_enabled() && !dp_packet_batch_is_empty(batch)) {
>> + /* To avoid multiple port lookups per batch, assume that all packets
>> + * in the batch originate from the same flow and therefore share the
>> + * same original input port. */
>> + struct tx_port *in_port = pmd_send_port_cache_lookup(
>> + pmd,
>> batch->packets[0]->md.orig_in_port);
>> + if (in_port) {
>> + ingress_netdev = in_port->port->netdev;
>> + }
>> + }
>> +
>> + err = netdev_push_header(tun_port->port->netdev, ingress_netdev, batch,
>> + data);
>> if (!err) {
>> return 0;
>> }
>> diff --git a/lib/dpif-offload-dummy.c b/lib/dpif-offload-dummy.c
>> index 3dd9eff14b..4c199c3aec 100644
>> --- a/lib/dpif-offload-dummy.c
>> +++ b/lib/dpif-offload-dummy.c
>> @@ -24,6 +24,7 @@
>> #include "dummy.h"
>> #include "id-fpool.h"
>> #include "netdev-provider.h"
>> +#include "netdev-native-tnl.h"
> move line up
Thanks, I guess I'll never learn my ABC ;)
[...]
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev