Eelco Chaudron via dev <[email protected]> writes: > 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.
I just realized that if we flip the hw-offload while traffic is running we'd potentially have different hashes. I'm trying to decide if we should document it. Previously, flipping HWOL shouldn't really cause major issues, but I think this affects when OVN pipeline implements some select group logic (or maybe I misunderstand the flows). > 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); ^ Should we actually use an intermediate here and only assign packet->md.dp_hash on true return? Otherwise, a provider that maniplates this and then returns 'false' might leave the hash value in a bad state. > +} > + > +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; > + } > + Should we somehow consolidate this with the odp-execute code? CPD from the PMD suite pointed out that these look the exact same: -- >8 -- +Found a 16 line (79 tokens) duplication in the following files: +Starting at line 8368 of /home/aconole/git/ovs-reviews/lib/dpif-netdev.c +Starting at line 1013 of /home/aconole/git/ovs-reviews/lib/odp-execute.c +Found a 16 line (45 tokens) duplication in the following files: +Starting at line 8390 of /home/aconole/git/ovs-reviews/lib/dpif-netdev.c +Starting at line 1029 of /home/aconole/git/ovs-reviews/lib/odp-execute.c -- 8< -- If these hash implementations diverge we'll definitely see some weird reports. > 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); We technically don't pass any other action objects, and now we're introducting an action type without a forward declaration. I guess it probably isn't too big a deal right now, but we probably need the action hash attribute declared. Alternatively, we could pass the nlattr and make the implementation convert. > > > /* 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 ^ Are there callers that are NULL? I didn't see any, but we also probably need to explicitly always require it to be non-NULL rather than allowing for separate. Because... > + * 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) { Previously, if an action required DP assistance, and there wasn't one, we would hit the check inside the block and 'continue'. > - /* 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)) { Now they're coupled, and that makes it possible to hit OVS_NOT_REACHED I think, in the event that some action which requires DP assistance doesn't have a dp_execute_action callback. I hope it would be caught in development, before ever merging, but the new comment makes me think this is an unintentional case. MAYBE it's better to have requires_datapath_assistance() block, and provides_datapath_assistance() and we could do something like: static bool dp_execute_action_assist_fn(...) { ... } if (requires_datapath_assistance(a)) { .. dp_execute_action_assist_fn .. } else if (dp_provides_assistance(dp, a)) { ovs_assert(dp_execute_action); .. dp_execute_action_assist_fn .. } Then we get the cases separated out, and have a forced 'if you tell that you provide something, you better provide it' interface? > + /* 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 -- \ _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
