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

Reply via email to