This patch introduces a new API to the offload provider framework that
allows hardware offload implementations to control hash value calculation
for the OVS_ACTION_ATTR_HASH action.
Background and Motivation
=========================
The OVS hash action (OVS_ACTION_ATTR_HASH) is used to compute a hash value
from packet header fields, primarily for load balancing across multiple
paths using the select group action. The hash value is stored in the
packet's metadata and used by subsequent actions to distribute flows
across multiple output ports.
However, hardware offload implementations may require different approaches
to hash calculation:
1. Hardware NICs may use different hash functions or hash inputs than
the software datapath, which can lead to inconsistent load distribution
when mixing hardware and software paths.
2. Some hardware may support enhanced hashing mechanisms (e.g., using
symmetric hashing for bidirectional flows 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_get_dp_hash)(const struct dpif_offload *,
const struct netdev *ingress_netdev,
struct dp_packet *,
const struct ovs_action_hash *, uint32_t *hash);
The callback is invoked during hash action execution 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 packet to be hashed
- hash_action: The hash action parameters including algorithm and basis
If the provider implements this callback and returns true, the returned
hash value is used. Otherwise, OVS falls back to the standard hash
calculation.
Signed-off-by: Eelco Chaudron <[email protected]>
---
v3 changes:
- Changed the dummy dp-hash to a simple addition.
- Reverted to using the dp_execute_action callback to do the actual work.
Trying to make it fit into the DP-agnostic code of odp_execute_actions()
did not make sense and became a mess.
---
lib/dpif-netdev.c | 102 ++++++++++++++++++++++++++++++++----
lib/dpif-offload-dummy.c | 56 ++++++++++++++++++++
lib/dpif-offload-provider.h | 12 +++++
lib/dpif-offload.c | 19 +++++++
lib/dpif-offload.h | 4 ++
lib/dpif.c | 7 ++-
lib/odp-execute.c | 71 +++++++++++++++----------
lib/odp-execute.h | 11 ++--
tests/dpif-netdev.at | 47 +++++++++++++++++
9 files changed, 286 insertions(+), 43 deletions(-)
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 4151ea056..3d6c9cb03 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -8005,7 +8005,28 @@ dp_execute_lb_output_action(struct dp_netdev_pmd_thread
*pmd,
}
}
-static void
+static bool
+dp_hw_offload_hash(struct dp_netdev_pmd_thread *pmd,
+ const struct ovs_action_hash *hash_act,
+ struct dp_packet *packet, odp_port_t *cached_in_odpp,
+ struct netdev **cached_in_netdev)
+{
+ if (*cached_in_odpp != packet->md.orig_in_port || !*cached_in_netdev) {
+ struct tx_port *in_port = pmd_send_port_cache_lookup(
+ pmd, packet->md.orig_in_port);
+
+ if (!in_port) {
+ return false;
+ }
+ *cached_in_odpp = packet->md.orig_in_port;
+ *cached_in_netdev = in_port->port->netdev;
+ }
+
+ return dpif_offload_netdev_get_dp_hash(*cached_in_netdev, packet, hash_act,
+ &packet->md.dp_hash);
+}
+
+static bool
dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
const struct nlattr *a, bool should_steal)
OVS_NO_THREAD_SAFETY_ANALYSIS
@@ -8022,12 +8043,12 @@ dp_execute_cb(void *aux_, struct dp_packet_batch
*packets_,
case OVS_ACTION_ATTR_OUTPUT:
dp_execute_output_action(pmd, packets_, should_steal,
nl_attr_get_odp_port(a));
- return;
+ return true;
case OVS_ACTION_ATTR_LB_OUTPUT:
dp_execute_lb_output_action(pmd, packets_, should_steal,
nl_attr_get_u32(a));
- return;
+ return true;
case OVS_ACTION_ATTR_TUNNEL_PUSH:
if (should_steal) {
@@ -8043,7 +8064,7 @@ dp_execute_cb(void *aux_, struct dp_packet_batch
*packets_,
COVERAGE_ADD(datapath_drop_tunnel_push_error,
packet_count);
}
- return;
+ return true;
case OVS_ACTION_ATTR_TUNNEL_POP:
if (*depth < MAX_RECIRC_DEPTH) {
@@ -8074,7 +8095,7 @@ dp_execute_cb(void *aux_, struct dp_packet_batch
*packets_,
if (!should_steal) {
dp_packet_batch_destroy(&tnl_pkt);
}
- return;
+ return true;
}
struct dp_packet *packet;
@@ -8089,7 +8110,7 @@ dp_execute_cb(void *aux_, struct dp_packet_batch
*packets_,
if (!should_steal) {
dp_packet_batch_destroy(&tnl_pkt);
}
- return;
+ return true;
}
COVERAGE_ADD(datapath_drop_invalid_tnl_port,
dp_packet_batch_size(packets_));
@@ -8139,7 +8160,7 @@ dp_execute_cb(void *aux_, struct dp_packet_batch
*packets_,
ofpbuf_uninit(&actions);
fat_rwlock_unlock(&dp->upcall_rwlock);
- return;
+ return true;
}
COVERAGE_ADD(datapath_drop_lock_error,
dp_packet_batch_size(packets_));
@@ -8166,7 +8187,7 @@ dp_execute_cb(void *aux_, struct dp_packet_batch
*packets_,
if (!should_steal) {
dp_packet_batch_destroy(&recirc_pkts);
}
- return;
+ return true;
}
COVERAGE_ADD(datapath_drop_recirc_error,
@@ -8318,6 +8339,69 @@ dp_execute_cb(void *aux_, struct dp_packet_batch
*packets_,
pmd->ctx.now / 1000);
break;
+ case OVS_ACTION_ATTR_HASH: {
+ const struct ovs_action_hash *hash_act = nl_attr_get(a);
+ struct dp_packet *packet;
+ struct netdev *cached_in_netdev = NULL;
+ odp_port_t cached_in_odpp = ODPP_NONE;
+
+ if (!dpif_offload_enabled()) {
+ return false;
+ }
+
+ /* This is a hardware offload-enhanced version of similar code
+ * executed for OVS_ACTION_ATTR_HASH in odp_execute_actions(). */
+ switch (hash_act->hash_alg) {
+ case OVS_HASH_ALG_L4: {
+ struct flow flow;
+ uint32_t hash;
+
+ DP_PACKET_BATCH_FOR_EACH (i, packet, packets_) {
+ /* RSS hash can be used here instead of 5tuple for
+ * performance reasons. */
+ if (dp_hw_offload_hash(pmd, hash_act, packet,
+ &cached_in_odpp,
+ &cached_in_netdev)) {
+ continue;
+ }
+
+ if (dp_packet_rss_valid(packet)) {
+ hash = dp_packet_get_rss_hash(packet);
+ hash = hash_int(hash, hash_act->hash_basis);
+ } else {
+ flow_extract(packet, &flow);
+ hash = flow_hash_5tuple(&flow, hash_act->hash_basis);
+ }
+ packet->md.dp_hash = hash;
+ }
+ break;
+ }
+ case OVS_HASH_ALG_SYM_L4: {
+ struct flow flow;
+ uint32_t hash;
+
+ DP_PACKET_BATCH_FOR_EACH (i, packet, packets_) {
+ if (dp_hw_offload_hash(pmd, hash_act, packet,
+ &cached_in_odpp,
+ &cached_in_netdev)) {
+ continue;
+ }
+
+ flow_extract(packet, &flow);
+ hash = flow_hash_symmetric_l3l4(&flow,
+ hash_act->hash_basis,
+ false);
+ packet->md.dp_hash = hash;
+ }
+ break;
+ }
+ default:
+ /* Assert on unknown hash algorithm. */
+ OVS_NOT_REACHED();
+ }
+ break;
+ }
+
case OVS_ACTION_ATTR_PUSH_VLAN:
case OVS_ACTION_ATTR_POP_VLAN:
case OVS_ACTION_ATTR_PUSH_MPLS:
@@ -8325,7 +8409,6 @@ dp_execute_cb(void *aux_, struct dp_packet_batch
*packets_,
case OVS_ACTION_ATTR_SET:
case OVS_ACTION_ATTR_SET_MASKED:
case OVS_ACTION_ATTR_SAMPLE:
- case OVS_ACTION_ATTR_HASH:
case OVS_ACTION_ATTR_UNSPEC:
case OVS_ACTION_ATTR_TRUNC:
case OVS_ACTION_ATTR_PUSH_ETH:
@@ -8344,6 +8427,7 @@ dp_execute_cb(void *aux_, struct dp_packet_batch
*packets_,
}
dp_packet_delete_batch(packets_, should_steal);
+ return true;
}
static void
diff --git a/lib/dpif-offload-dummy.c b/lib/dpif-offload-dummy.c
index 878276a94..0004e484f 100644
--- a/lib/dpif-offload-dummy.c
+++ b/lib/dpif-offload-dummy.c
@@ -640,6 +640,61 @@ dummy_offload_udp_tnl_get_src_port(
return true;
}
+static bool
+get_l4_sym_hash(struct flow *flow, uint32_t seed, uint32_t *hash_)
+{
+ struct ds ds = DS_EMPTY_INITIALIZER;
+ uint32_t hash = seed;
+
+ if (!dl_type_is_ip_any(flow->dl_type)) {
+ return false;
+ }
+
+ hash += flow->nw_proto;
+ if (!(flow->nw_frag & FLOW_NW_FRAG_MASK)
+ && (flow->nw_proto == IPPROTO_TCP
+ || flow->nw_proto == IPPROTO_SCTP
+ || flow->nw_proto == IPPROTO_UDP)) {
+ hash += ntohs(flow->tp_src) + ntohs(flow->tp_dst);
+ }
+
+ ds_put_format(&ds, "l4_sym_hash: %8.8x for packet: ", hash);
+ flow_format(&ds, flow, NULL);
+ VLOG_DBG("%s", ds_cstr(&ds));
+ ds_destroy(&ds);
+
+ *hash_ = hash;
+ return true;
+}
+
+static bool
+dummy_offload_get_dp_hash(const struct dpif_offload *offload OVS_UNUSED,
+ const struct netdev *ingress_netdev OVS_UNUSED,
+ struct dp_packet *packet,
+ const struct ovs_action_hash *hash_act,
+ uint32_t *hash)
+{
+ switch ((enum ovs_hash_alg) hash_act->hash_alg) {
+ case OVS_HASH_ALG_L4:
+ case OVS_HASH_ALG_SYM_L4: {
+ /* For our implementation we will use a simple symmetric L4 hash. */
+ struct flow flow;
+
+ flow_extract(packet, &flow);
+ if (!get_l4_sym_hash(&flow, hash_act->hash_basis, hash)) {
+ return false;
+ }
+ break;
+ }
+
+ case __OVS_HASH_MAX:
+ default:
+ OVS_NOT_REACHED();
+ }
+
+ return true;
+}
+
static bool
dummy_offload_are_all_actions_supported(const struct dpif_offload *offload_,
odp_port_t in_odp,
@@ -1162,6 +1217,7 @@ dummy_netdev_hw_offload_run(struct netdev *netdev)
.get_netdev = dummy_offload_get_netdev, \
.netdev_hw_post_process = dummy_offload_hw_post_process, \
.netdev_udp_tnl_get_src_port = dummy_offload_udp_tnl_get_src_port, \
+ .netdev_get_dp_hash = dummy_offload_get_dp_hash, \
.netdev_flow_put = dummy_flow_put, \
.netdev_flow_del = dummy_flow_del, \
.netdev_flow_stats = dummy_flow_stats, \
diff --git a/lib/dpif-offload-provider.h b/lib/dpif-offload-provider.h
index 444b13138..5dec51173 100644
--- a/lib/dpif-offload-provider.h
+++ b/lib/dpif-offload-provider.h
@@ -288,6 +288,18 @@ struct dpif_offload_class {
struct dp_packet *packet,
ovs_be16 *src_port);
+ /* Allows the offload provider to override the default dp-hash calculation.
+ * Called during packet processing to determine the hash value for datapath
+ * operations (e.g., load balancing, packet distribution).
+ *
+ * If implemented, should return true and set 'hash' to the desired hash
+ * value. If not implemented or if default behavior is desired, should
+ * return false to use the standard hash calculation. */
+ bool (*netdev_get_dp_hash)(const struct dpif_offload *,
+ const struct netdev *ingress_netdev,
+ struct dp_packet *,
+ const struct ovs_action_hash *, uint32_t *hash);
+
/* Add or modify the specified flow directly in the offload datapath.
* The actual implementation may choose to handle the offload
* asynchronously by returning EINPROGRESS and invoking the supplied
diff --git a/lib/dpif-offload.c b/lib/dpif-offload.c
index 04dabc42c..bc33d2ab8 100644
--- a/lib/dpif-offload.c
+++ b/lib/dpif-offload.c
@@ -1491,6 +1491,25 @@ dpif_offload_netdev_udp_tnl_get_src_port(const struct
netdev *ingress_netdev,
packet, src_port);
}
+bool
+dpif_offload_netdev_get_dp_hash(const struct netdev *ingress_netdev,
+ struct dp_packet *packet,
+ const struct ovs_action_hash *hash_action,
+ uint32_t *hash)
+{
+ const struct dpif_offload *offload;
+
+ offload = ovsrcu_get(const struct dpif_offload *,
+ &ingress_netdev->dpif_offload);
+
+ if (OVS_UNLIKELY(!offload) || !offload->class->netdev_get_dp_hash) {
+ return false;
+ }
+
+ return offload->class->netdev_get_dp_hash(offload, ingress_netdev, packet,
+ hash_action, hash);
+}
+
void
dpif_offload_datapath_register_flow_unreference_cb(
struct dpif *dpif, dpif_offload_flow_unreference_cb *cb)
diff --git a/lib/dpif-offload.h b/lib/dpif-offload.h
index bf7643320..5b377de28 100644
--- a/lib/dpif-offload.h
+++ b/lib/dpif-offload.h
@@ -116,6 +116,10 @@ int dpif_offload_netdev_hw_post_process(struct netdev *,
unsigned pmd_id,
bool dpif_offload_netdev_udp_tnl_get_src_port(const struct netdev *,
struct dp_packet *,
ovs_be16 *src_port);
+bool dpif_offload_netdev_get_dp_hash(const struct netdev *,
+ struct dp_packet *,
+ const struct ovs_action_hash *,
+ uint32_t *hash);
/* Callback invoked when a hardware flow offload operation (put/del) completes.
diff --git a/lib/dpif.c b/lib/dpif.c
index a62da6483..348848fa8 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -1182,7 +1182,7 @@ struct dpif_execute_helper_aux {
/* This is called for actions that need the context of the datapath to be
* meaningful. */
-static void
+static bool
dpif_execute_helper_cb(void *aux_, struct dp_packet_batch *packets_,
const struct nlattr *action, bool should_steal)
{
@@ -1264,6 +1264,10 @@ dpif_execute_helper_cb(void *aux_, struct
dp_packet_batch *packets_,
}
case OVS_ACTION_ATTR_HASH:
+ /* For this action, no dp-specific handling is needed; fall back to
+ * the default software handling. */
+ return false;
+
case OVS_ACTION_ATTR_PUSH_VLAN:
case OVS_ACTION_ATTR_POP_VLAN:
case OVS_ACTION_ATTR_PUSH_MPLS:
@@ -1286,6 +1290,7 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet_batch
*packets_,
OVS_NOT_REACHED();
}
dp_packet_delete_batch(packets_, should_steal);
+ return true;
}
/* Executes 'execute' by performing most of the actions in userspace and
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 618fb5ac8..641325db3 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -850,7 +850,7 @@ odp_execute_check_pkt_len(void *dp, struct dp_packet
*packet, bool steal,
}
static bool
-requires_datapath_assistance(const struct nlattr *a)
+might_require_datapath_assistance(const struct nlattr *a)
{
enum ovs_action_attr type = nl_attr_type(a);
@@ -867,11 +867,16 @@ requires_datapath_assistance(const struct nlattr *a)
case OVS_ACTION_ATTR_PSAMPLE:
return true;
+ /* OVS_ACTION_ATTR_HASH is in general datapath-agnostic: it has a
+ * software implementation, but the datapath may override it (e.g. to
+ * use a hardware offload specific hash). */
+ case OVS_ACTION_ATTR_HASH:
+ return true;
+
case OVS_ACTION_ATTR_SET:
case OVS_ACTION_ATTR_SET_MASKED:
case OVS_ACTION_ATTR_PUSH_VLAN:
case OVS_ACTION_ATTR_POP_VLAN:
- case OVS_ACTION_ATTR_HASH:
case OVS_ACTION_ATTR_PUSH_MPLS:
case OVS_ACTION_ATTR_POP_MPLS:
case OVS_ACTION_ATTR_TRUNC:
@@ -921,15 +926,22 @@ requires_datapath_assistance(const struct nlattr *a)
* the packets in 'batch'. If 'steal' is true, possibly modifies and
* definitely free the packets in 'batch', otherwise leaves 'batch' unchanged.
*
- * Some actions (e.g. output actions) can only be executed by a datapath. This
- * function implements those actions by passing the action and the packets to
- * 'dp_execute_action' (along with 'dp'). If 'dp_execute_action' is passed a
- * true 'steal' parameter then it must definitely free the packets passed into
- * it. The packet can be modified whether 'steal' is false or true. If a
- * packet is removed from the batch, then the fate of the packet is determined
- * by the code that does this removal, irrespective of the value of 'steal'.
- * Otherwise, if the packet is not removed from the batch and 'steal' is false
- * then the packet could either be cloned or not. */
+ * For actions that might require datapath-specific handling (as determined by
+ * might_require_datapath_assistance()), 'dp_execute_action' is invoked first,
+ * if non-NULL. If the callback returns true, the action is considered fully
+ * handled and execution continues with the next action. If the callback
+ * returns false, this function falls through to its built-in software
+ * implementation of the action.
+ *
+ * If 'dp_execute_action' returns true and was passed a true 'steal' parameter,
+ * it must have freed the packets. If it returns false, it must not have freed
+ * the packets, as this function will fall through to its built-in software
+ * implementation and eventually free them itself. The packet can be modified
+ * whether 'steal' is false or true. If a packet is removed from the batch,
+ * then the fate of the packet is determined by the code that does this
+ * removal, irrespective of the value of 'steal'. Otherwise, if the packet is
+ * not removed from the batch and 'steal' is false then the packet could either
+ * be cloned or not. */
void
odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
const struct nlattr *actions, size_t actions_len,
@@ -944,24 +956,27 @@ odp_execute_actions(void *dp, struct dp_packet_batch
*batch, bool steal,
enum ovs_action_attr attr_type = (enum ovs_action_attr) type;
bool last_action = (left <= NLA_ALIGN(a->nla_len));
- if (requires_datapath_assistance(a)) {
- if (dp_execute_action) {
- /* Allow 'dp_execute_action' to steal the packet data if we do
- * not need it any more. */
- bool should_steal = steal && last_action;
-
- dp_execute_action(dp, batch, a, should_steal);
-
- if (last_action || dp_packet_batch_is_empty(batch)) {
- /* We do not need to free the packets.
- * Either dp_execute_actions() has stolen them
- * or the batch is freed due to errors. In either
- * case we do not need to execute further actions.
- */
- return;
- }
+ if (dp_execute_action && might_require_datapath_assistance(a)) {
+ /* Allow 'dp_execute_action' to steal the packet data if we do
+ * not need it any more. */
+ bool should_steal = steal && last_action;
+ bool handled;
+
+ handled = dp_execute_action(dp, batch, a, should_steal);
+
+ if (handled && (last_action || dp_packet_batch_is_empty(batch))) {
+ /* We do not need to free the packets. Either
+ * dp_execute_actions() has stolen them or the batch is freed
+ * due to errors. In either case we do not need to execute
+ * further actions. */
+ return;
+ }
+
+ if (handled) {
+ continue;
}
- continue;
+ /* If not handled by the datapath for some specific reason,
+ * fall through to the non-datapath-assisted handling. */
}
switch (attr_type) {
diff --git a/lib/odp-execute.h b/lib/odp-execute.h
index 7a54fa6ec..2ac6d6bbf 100644
--- a/lib/odp-execute.h
+++ b/lib/odp-execute.h
@@ -26,13 +26,14 @@
struct nlattr;
struct dp_packet_batch;
-typedef void (*odp_execute_cb)(void *dp, struct dp_packet_batch *batch,
+typedef bool (*odp_execute_cb)(void *dp, struct dp_packet_batch *batch,
const struct nlattr *action, bool should_steal);
-/* Actions that need to be executed in the context of a datapath are handed
- * to 'dp_execute_action', if non-NULL. Currently this is called only for
- * actions OVS_ACTION_ATTR_OUTPUT and OVS_ACTION_ATTR_USERSPACE so
- * 'dp_execute_action' needs to handle only these. */
+/* Executes all of the 'actions_len' bytes of datapath actions in 'actions' on
+ * the packets in 'batch'. Actions that might require datapath-specific
+ * handling are first offered to 'dp_execute_action' (if non-NULL); if the
+ * callback returns false the action is handled by the built-in software
+ * implementation instead. */
void odp_execute_actions(void *dp, struct dp_packet_batch *batch,
bool steal,
const struct nlattr *actions, size_t actions_len,
diff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at
index 14f238e62..336a74062 100644
--- a/tests/dpif-netdev.at
+++ b/tests/dpif-netdev.at
@@ -658,6 +658,53 @@
arp,in_port=ANY,dl_vlan=11,dl_vlan_pcp=7,vlan_tci1=0x0000,dl_src=00:06:07:08:09:
DPIF_NETDEV_FLOW_HW_OFFLOAD_OFFSETS_VID_ARP([dummy])
DPIF_NETDEV_FLOW_HW_OFFLOAD_OFFSETS_VID_ARP([dummy-pmd])
+AT_SETUP([dpif-netdev - partial hw offload - dp_hash - dummy-pmd])
+OVS_VSWITCHD_START(
+ [add-port br0 p1 -- \
+ add-port br0 p2 -- \
+ set interface p1 type=dummy-pmd ofport_request=1 options:ifindex=1100 -- \
+ set interface p2 type=dummy-pmd ofport_request=2 options:ifindex=1200 \
+ options:tx_pcap=p2.pcap -- \
+ set bridge br0 datapath-type=dummy other-config:datapath-id=1234 \
+ fail-mode=secure], [], [], [--dummy-numa="0"])
+AT_CHECK([ovs-appctl vlog/set dpif_offload_dummy:file:dbg])
+
+AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:hw-offload=true])
+OVS_WAIT_UNTIL([grep "Flow HW offload is enabled" ovs-vswitchd.log])
+
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-group br0 \
+ 'group_id=123,type=select,bucket=output:p2,output:p2'])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 'ip actions=group:123'])
+
+packet="in_port(1),"\
+"eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:1),eth_type(0x0800),"\
+"ipv4(src=192.168.1.1,dst=192.168.1.100,proto=6,tos=0,ttl=128,frag=no),"\
+"tcp(src=1000,dst=1000)"
+
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet])
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 $packet])
+
+AT_CHECK([ovs-appctl dpctl/dump-flows -m | strip_hw_offload], [0], [dnl
+recirc_id(0),in_port(p1),packet_type(ns=0,id=0),eth(),eth_type(0x0800),ipv4(),tcp(src=1000/0,dst=1000/0),
packets:1, bytes:118, used:0.0s, offloaded:partial, dp:ovs,
actions:hash(sym_l4(0)),recirc(0x1)
+recirc_id(0x1),dp_hash(0x7d6/0xf),in_port(p1),packet_type(ns=0,id=0),eth(),eth_type(0x0800),ipv4(),tcp(src=1000/0,dst=1000/0),
packets:1, bytes:118, used:0.0s, offloaded:yes, dp:dummy, actions:p2
+])
+
+OVS_WAIT_UNTIL([grep "l4_sym_hash: 000007d6 for packet: tcp,in_port=1," \
+ ovs-vswitchd.log])
+
+AT_CHECK(
+ [ovs-appctl --format json dpif/offload/show \
+ | sed 's/.*"p1":{\([[^}]]*\)}.*/\1/; s/,/\n/g; s/"//g' \
+ | sed -n '/^rx_offload_/p' | sort], [0], [dnl
+rx_offload_full:0
+rx_offload_miss:1
+rx_offload_partial:1
+rx_offload_pipe_abort:0
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
AT_SETUP([dpif-netdev - full hw offload - dummy-pmd])
OVS_VSWITCHD_START(
[add-port br0 p1 -- \
--
2.54.0
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev