On 23 Jun 2026, at 19:03, Aaron Conole wrote:

> 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.
>>
>> Signed-off-by: Eelco Chaudron <[email protected]>
>> ---
>
> Hi Eelco,
>
> Following are not exhaustive.

Thanks for taking the time to review!

//Eelco

>> Changes from RFC v3 -> v1
>>   - Rebased to the latest main branch.
>>
>> Changes from RFC v2 -> RFC v3:
>>   - Remove the const qualifier for the packet in the API.
>>   - Updated depending patches.
>> ---
>>  lib/dpif-netdev.c           | 102 ++++++++++++++++++++++++++++++++----
>>  lib/dpif-offload-dummy.c    |  98 ++++++++++++++++++++++++++++++++++
>>  lib/dpif-offload-provider.h |  12 +++++
>>  lib/dpif-offload.c          |  19 +++++++
>>  lib/dpif-offload.h          |   4 ++
>>  lib/dpif.c                  |   7 ++-
>>  lib/odp-execute.c           |  15 ++++--
>>  lib/odp-execute.h           |   2 +-
>>  tests/dpif-netdev.at        |  47 +++++++++++++++++
>>  9 files changed, 291 insertions(+), 15 deletions(-)
>>
>> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
>> index ffda2b4d6..0459cf8dc 100644
>> --- a/lib/dpif-netdev.c
>> +++ b/lib/dpif-netdev.c
>> @@ -8039,7 +8039,28 @@ dp_execute_lb_output_action(struct 
>> dp_netdev_pmd_thread *pmd,
>>      }
>>  }
>>
>> -static void
>> +static inline bool
>
> static inline in a C file is usually a bad idea.  I see it happens a few
> times throughout this file (and I'll submit a checkpatch check for it
> this week).

ACK.

>> +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)
>
> Do we need have the new semantic?  Is there a case that the action list
> will have 'hash' as the last action (and then packet is droped anyway)?
> If that's not the case, could we just handle it without needing to now
> signal back that the list is freed?  Currently, the callback is
> considered to be a one-way interface, so I'm just wondering if it is
> possible to keep it that way.
>
> Also, this now means 'requires_datapath_assistance' is also changed in a
> way that is weird.  It now is more like
> 'could_maybe_be_handled_by_datapath'.  Just for HASH.
>
> And it duplicates things later on.
>
> Maybe in odp_execute_actions we can have something specific in hash that
> can check the dpif layer there.  That will preserve the semantics
> and I think look a bit cleaner.  We could even consider an 'assist'
> table that gets passed to the odp_execute_cb (or pulled by it).
>
>   if (assist_table && assist_table[a]) {
>       assisted = assist_table[a](...);
>       if (assisted) {
>          continue;
>       }
>   }
>
> Then adding new handling modes for the different dpif could just be
> updating the table and 'requires_datapath_assistance' are safe.
>
> Alternatively, we could add a specific hash cb function that we send to
> `odp_execute_actions()`::
>
>    /* just a quick guess */
>    bool (*odp_hash_cb)(void *dp, struct dp_packet *,
>                       const struct ovs_action_hash *, uint32_t *hash);
>
>    void odp_execute_actions(...,
>                             odp_hash_cb hash_override) {
>        ...
>        case OVS_ACTION_ATTR_HASH: {
>           if (hash_override && hash_override(dp, packet, hash_act,
>                             &hash)) {
>              packet->md.dp_hash = hash;
>              continue;
>           }
>           /* .. existing code .. */
>        }
>       ...
>
> This would also be compact, and I think still a bit cleaner than the
> semantic change.

I looked into it, and for now, I guess the additional callback would work; it 
does at some additional callback passing, but in general, it's clean.

I will send a v2 later tonight.

>>      OVS_NO_THREAD_SAFETY_ANALYSIS
>> @@ -8056,12 +8077,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) {
>> @@ -8077,7 +8098,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) {
>> @@ -8105,7 +8126,7 @@ dp_execute_cb(void *aux_, struct dp_packet_batch 
>> *packets_,
>>                                   packets_dropped);
>>                  }
>>                  if (dp_packet_batch_is_empty(packets_)) {
>> -                    return;
>> +                    return true;
>>                  }
>>
>>                  struct dp_packet *packet;
>> @@ -8116,7 +8137,7 @@ dp_execute_cb(void *aux_, struct dp_packet_batch 
>> *packets_,
>>                  (*depth)++;
>>                  dp_netdev_recirculate(pmd, packets_);
>>                  (*depth)--;
>> -                return;
>> +                return true;
>>              }
>>              COVERAGE_ADD(datapath_drop_invalid_tnl_port,
>>                           dp_packet_batch_size(packets_));
>> @@ -8165,7 +8186,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_));
>> @@ -8189,7 +8210,7 @@ dp_execute_cb(void *aux_, struct dp_packet_batch 
>> *packets_,
>>              dp_netdev_recirculate(pmd, packets_);
>>              (*depth)--;
>>
>> -            return;
>> +            return true;
>>          }
>>
>>          COVERAGE_ADD(datapath_drop_recirc_error,
>> @@ -8341,6 +8362,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_) {
>
>                                        ^ missing space (robot / checkpatch)

Code is gone in v2.

>> +                /* 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_) {
>
>                                        ^ missing space (robot / checkpatch)
>
>> +                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();
>> +        }
>> +        return true;
>> +    }
>> +
>>      case OVS_ACTION_ATTR_PUSH_VLAN:
>>      case OVS_ACTION_ATTR_POP_VLAN:
>>      case OVS_ACTION_ATTR_PUSH_MPLS:
>> @@ -8348,7 +8432,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:
>> @@ -8367,6 +8450,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 cc9fc5792..ebbfe68e7 100644
>> --- a/lib/dpif-offload-dummy.c
>> +++ b/lib/dpif-offload-dummy.c
>> @@ -640,6 +640,103 @@ dummy_offload_udp_tnl_get_src_port(
>>      return true;
>>  }
>>
>> +static inline uint32_t
>
> For this file, should just be 'static XXXX' instead of 'static inline'.

ACK, will change in v2.

>> +fnv1a_add(uint32_t hash, uint32_t word)
>> +{
>> +    const uint32_t prime = 16777619U;
>> +
>> +    for (size_t i = 0; i < sizeof(uint32_t); i++) {
>> +        hash ^= (word >> (i * 8)) & 0xff;
>> +        hash *= prime;
>> +    }
>> +    return hash;
>> +}
>> +
>> +static inline uint32_t
>> +fnv1a_add64(uint32_t hash, uint64_t word)
>> +{
>> +    return fnv1a_add(fnv1a_add(hash, word), word >> 32);
>> +}
>> +
>> +static uint32_t
>> +fnv1a_init(uint32_t seed)
>> +{
>> +    const uint32_t fnv_offset_basis = 2166136261U;
>> +
>> +    return seed ? fnv1a_add(fnv_offset_basis, seed) : fnv_offset_basis;
>> +}
>> +
>> +static bool
>> +get_l4_sym_hash(struct flow *flow, uint32_t seed, uint32_t *hash_)
>> +{
>> +    struct ds ds = DS_EMPTY_INITIALIZER;
>> +    uint32_t hash = fnv1a_init(seed);
>> +
>> +    if (flow->dl_type == htons(ETH_TYPE_IP)) {
>> +        hash = fnv1a_add(hash,
>> +                         (OVS_FORCE uint32_t)(flow->nw_src ^ flow->nw_dst));
>> +    } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
>> +        /* IPv6 addresses are 64-bit aligned inside struct flow. */
>> +        const ovs_be64 *a = ALIGNED_CAST(const ovs_be64 *,
>> +                                         flow->ipv6_src.s6_addr);
>> +        const ovs_be64 *b = ALIGNED_CAST(const ovs_be64 *,
>> +                                         flow->ipv6_dst.s6_addr);
>> +
>> +        for (int i = 0; i < sizeof flow->ipv6_src / sizeof *a; i++) {
>> +            hash = fnv1a_add64(hash, ntohll(a[i]) ^ ntohll(b[i]));
>> +        }
>> +    } else {
>> +        return false;
>> +    }
>> +
>> +    hash = fnv1a_add(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 = fnv1a_add(hash,
>> +                         (OVS_FORCE uint16_t)(flow->tp_src ^ 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 just use a symmetric L4 hash.
>> +         * Note that we will use our own hash that will give consistent 
>> results
>> +         * across all platforms/compilers. */
>> +        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 +1259,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 587360c51..21215d874 100644
>> --- a/lib/dpif-offload.c
>> +++ b/lib/dpif-offload.c
>> @@ -1482,6 +1482,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 1afc7c662..bea977b42 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 4642f3375..f4f7f0d76 100644
>> --- a/lib/odp-execute.c
>> +++ b/lib/odp-execute.c
>> @@ -861,13 +861,13 @@ requires_datapath_assistance(const struct nlattr *a)
>>      case OVS_ACTION_ATTR_CT:
>>      case OVS_ACTION_ATTR_METER:
>>      case OVS_ACTION_ATTR_PSAMPLE:
>> +    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:
>> @@ -941,14 +941,17 @@ odp_execute_actions(void *dp, struct dp_packet_batch 
>> *batch, bool steal,
>>          bool last_action = (left <= NLA_ALIGN(a->nla_len));
>>
>>          if (requires_datapath_assistance(a)) {
>> +            bool handled = true;
>> +
>>              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);
>> +                handled = dp_execute_action(dp, batch, a, should_steal);
>>
>> -                if (last_action || dp_packet_batch_is_empty(batch)) {
>> +                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
>> @@ -957,7 +960,11 @@ odp_execute_actions(void *dp, struct dp_packet_batch 
>> *batch, bool steal,
>>                      return;
>>                  }
>>              }
>> -            continue;
>> +            if (handled) {
>> +                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..bb09154e4 100644
>> --- a/lib/odp-execute.h
>> +++ b/lib/odp-execute.h
>> @@ -26,7 +26,7 @@
>>  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
>> diff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at
>> index b48b40a11..b6277dd5b 100644
>> --- a/tests/dpif-netdev.at
>> +++ b/tests/dpif-netdev.at
>> @@ -662,6 +662,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(0x34a08190/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: 34a08190 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