On 19 Jun 2023, at 7:01, Chris Mi wrote:

> <SNIP>
>>> +    if (err) {
>>> +        VLOG_ERR_RL(&error_rl, "No OVS_ACTION_ATTR_USERSPACE attribute");
>>> +    }
>>> +    return err;
>>> +}
>>> +
>>> +static void
>>> +offload_sample_init(struct offload_sample *sample,
>>> +                    const struct nlattr *next_actions,
>>> +                    size_t next_actions_len,
>>> +                    struct tc_flower *flower,
>>> +                    const struct flow_tnl *tnl)
>>> +{
>>> +    memset(sample, 0, sizeof *sample);
>>> +    if (flower->tunnel) {
>>> +        sample->tunnel = CONST_CAST(struct flow_tnl *, tnl);
>>> +    }
>>> +    sample->userspace_actions = CONST_CAST(struct nlattr *, next_actions);
>>> +    sample->userspace_actions_len = next_actions_len;
>> Here we should initialize the userspace_actions, to make it work :)
>> This was previously in the clone action in patch 3:
>>
>> @@ -2148,8 +2148,11 @@ offload_sample_init(struct offload_sample *sample,
>>       if (flower->tunnel) {
>>           sample->tunnel = CONST_CAST(struct flow_tnl *, tnl);
>>       }
>> -    sample->userspace_actions = CONST_CAST(struct nlattr *, next_actions);
>> -    sample->userspace_actions_len = next_actions_len;
>> +
>> +    sample->userspace_actions = xmalloc(next_actions_len + NLA_HDRLEN);
>> +    nullable_memcpy((char *) sample->userspace_actions + NLA_HDRLEN,
>> +                    next_actions, next_actions_len);
>> +    sample->userspace_actions->nla_len = next_actions_len + NLA_HDRLEN;
>>       sample->ifindex = flower->ifindex;
>>
>> One remaining question is, should we set the nla_type here to 
>> OVS_ACTION_ATTR_SAMPLE or OVS_ACTION_ATTR_USERSPACE as this what they are, 
>> or is it safe to not set this as the upcall handling will ignore the type?
> According to function dpif_get_actions(), nla_type is ignored:
>
>     if (upcall->actions) {
>         /* Actions were passed up from datapath. */
>         *actions = nl_attr_get(upcall->actions);
>         actions_len = nl_attr_get_size(upcall->actions);
>     }

Thanks! I guess I could have done this myself :(

Cheers,

Eelco

Reviewing your v28 right now. And doing some final testing!

>>
>>> +    sample->ifindex = flower->ifindex;
>>> +}
>>> +
>>> +static int
>>> +parse_sample_action(struct tc_flower *flower, struct tc_action *tc_action,
>>> +                    const struct nlattr *next_actions, size_t 
>>> next_actions_len,
>>> +                    const struct nlattr *sample_action,
>>> +                    const struct flow_tnl *tnl)
>>> +{
>>> +    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
>>> +    struct offload_sample sample;
>>> +    const struct nlattr *nla;
>>> +    unsigned int left;
>>> +    uint32_t sgid;
>>> +    int err;
>> As you change the function, you should set err to EINVAL here :)
>>
>>> +
>>> +    offload_sample_init(&sample, next_actions, next_actions_len, flower, 
>>> tnl);
>>> +    sample.action = CONST_CAST(struct nlattr *, sample_action);
>>> +
>>> +    err = EINVAL;
>> Remove this here, as you no longer override it with offload_sample_init()
>>
>>> +    NL_NESTED_FOR_EACH_UNSAFE (nla, left, sample_action) {
>>> +        if (nl_attr_type(nla) == OVS_SAMPLE_ATTR_ACTIONS) {
>>> +            err = parse_sample_actions_attribute(nla, &sample);
>>> +            if (err) {
>>> +                break;
>>> +            }
>>> +        } else if (nl_attr_type(nla) == OVS_SAMPLE_ATTR_PROBABILITY) {
>>> +            tc_action->type = TC_ACT_SAMPLE;
>>> +            tc_action->sample.rate = UINT32_MAX / nl_attr_get_u32(nla);
>>> +            if (!tc_action->sample.rate) {
>>> +                break;
>>> +            }
>>> +        } else {
>> Here you removed err = EINVAL; but it should be here if you want to error 
>> out on unsupported attributes.
>>
>> I re-wrote the function a bit, to clean it up, and made it look a bit more 
>> like the v18 version. I removed the conditional breaks in the if() branches, 
>> as it made it looks like we check for errors in these branch, but in fact 
>> the checks are there to verify the attributes where present.
>> Also made sure we do not trash the tc_action structure on failure.
>>
>> static int
>> parse_sample_action(struct tc_flower *flower, struct tc_action *tc_action,
>>                      const struct nlattr *next_actions, size_t 
>> next_actions_len,
>>                      const struct nlattr *sample_action,
>>                      const struct flow_tnl *tnl)
>> {
>>      static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
>>      struct offload_sample sample;
>>      const struct nlattr *nla;
>>      unsigned int left;
>>      uint32_t rate = 0;
>>      int ret = EINVAL;
>>      uint32_t sgid;
>>
>>      offload_sample_init(&sample, next_actions, next_actions_len, flower, 
>> tnl);
>>      sample.action = CONST_CAST(struct nlattr *, sample_action);
>>
>>      NL_NESTED_FOR_EACH_UNSAFE (nla, left, sample_action) {
>>          if (nl_attr_type(nla) == OVS_SAMPLE_ATTR_ACTIONS) {
>>              ret = parse_sample_actions_attribute(nla, &sample);
>>          } else if (nl_attr_type(nla) == OVS_SAMPLE_ATTR_PROBABILITY) {
>>              rate = UINT32_MAX / nl_attr_get_u32(nla);
>>          } else {
>>              return EINVAL;
>>          }
>>      }
>>
>>      /* This check makes sure both attributes above were present and valid. 
>> */
>>      if (!rate || ret) {
>>          return EINVAL;
>>      }
>>
>>      sgid = sgid_alloc(&sample);
>>      if (!sgid) {
>>          VLOG_DBG_RL(&rl, "Failed allocating group id for sample action");
>>          return ENOENT;
>>      }
>>
>>      tc_action->type = TC_ACT_SAMPLE;
>>      tc_action->sample.rate = rate;
>>      tc_action->sample.group_id = sgid;
>>      flower->action_count++;
>>
>>      return 0;
>> }
>>
>>> +            break;
>>> +        }
>>> +    }
>>> +
>>> +    if (!tc_action->sample.rate || err) {
>>> +        return EINVAL;
>>> +    }
>>> +
>>> +    sgid = sgid_alloc(&sample);
>>> +    if (!sgid) {
>>> +        VLOG_DBG_RL(&rl, "Failed allocating group id for sample action");
>>> +        return ENOENT;
>>> +    }
>>> +    tc_action->sample.group_id = sgid;
>>> +    flower->action_count++;
>>> +
>>> +    return err;
>>> +}
>>> +
>>> +static int
>>> +parse_userspace_action(struct tc_flower *flower, struct tc_action 
>>> *tc_action,
>>> +                       const struct nlattr *next_actions,
>>> +                       size_t next_actions_len,
>>> +                       const struct nlattr *userspace_action,
>>> +                       const struct flow_tnl *tnl)
>>> +{
>>> +    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
>>> +    struct offload_sample sample;
>>> +    uint32_t sgid;
>>> +    int err;
>>> +
>>> +    offload_sample_init(&sample, next_actions, next_actions_len, flower, 
>>> tnl);
>>> +
>>> +    /* If sampling rate is 1, there is only a sFlow cookie inside of a
>>> +     * userspace action, but no sample attribute. That means we can
>>> +     * only offload userspace actions for sFlow. */
>>> +    err = parse_userspace_attributes(userspace_action, &sample);
>>> +    if (err) {
>>> +        return err;
>>> +    }
>>> +    sample.action = (struct nlattr *) userspace_action;
>>> +    sgid = sgid_alloc(&sample);
>>> +    if (!sgid) {
>>> +        VLOG_DBG_RL(&rl, "Failed allocating group id for sample action");
>>> +        return ENOENT;
>>> +    }
>>> +    tc_action->type = TC_ACT_SAMPLE;
>>> +    tc_action->sample.group_id = sgid;
>>> +    tc_action->sample.rate = 1;
>>> +    flower->action_count++;
>>> +
>>> +    return err;
>> Here we can just return 0.
>>
>>> +}
>>>
>>>   static int
>>>   parse_check_pkt_len_action(struct netdev *netdev, struct tc_flower 
>>> *flower,
>>> -                           struct offload_info *info, struct tc_action 
>>> *action,
>>> -                           const struct nlattr *nla, bool last_action,
>>> +                           struct offload_info *info,
>>> +                           const struct flow_tnl *tnl,
>>> +                           struct tc_action *action, const struct nlattr 
>>> *nla,
>>> +                           bool last_action,
>>>                              struct tc_action **need_jump_update,
>>>                              bool *recirc_act)
>>>   {
>>> @@ -2070,7 +2283,7 @@ parse_check_pkt_len_action(struct netdev *netdev, 
>>> struct tc_flower *flower,
>>>        * NOTE: The last_action parameter means that there are no more 
>>> actions
>>>        *       after the if () then ... else () case. */
>>>       nl_actions = a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER];
>>> -    err = netdev_tc_parse_nl_actions(netdev, flower, info,
>>> +    err = netdev_tc_parse_nl_actions(netdev, flower, info, tnl,
>>>                                        nl_attr_get(nl_actions),
>>>                                        nl_attr_get_size(nl_actions),
>>>                                        recirc_act, !last_action,
>>> @@ -2086,7 +2299,7 @@ parse_check_pkt_len_action(struct netdev *netdev, 
>>> struct tc_flower *flower,
>>>
>>>       /* Parse and add the less than action(s). */
>>>       nl_actions = a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL];
>>> -    err = netdev_tc_parse_nl_actions(netdev, flower, info,
>>> +    err = netdev_tc_parse_nl_actions(netdev, flower, info, tnl,
>>>                                        nl_attr_get(nl_actions),
>>>                                        nl_attr_get_size(nl_actions),
>>>                                        recirc_act, !last_action,
>>> @@ -2139,6 +2352,7 @@ parse_check_pkt_len_action(struct netdev *netdev, 
>>> struct tc_flower *flower,
>>>   static int
>>>   netdev_tc_parse_nl_actions(struct netdev *netdev, struct tc_flower 
>>> *flower,
>>>                              struct offload_info *info,
>>> +                           const struct flow_tnl *tnl,
>>>                              const struct nlattr *actions, size_t 
>>> actions_len,
>>>                              bool *recirc_act, bool more_actions,
>>>                              struct tc_action **need_jump_update)
>>> @@ -2268,7 +2482,8 @@ netdev_tc_parse_nl_actions(struct netdev *netdev, 
>>> struct tc_flower *flower,
>>>               action->police.index = police_index;
>>>               flower->action_count++;
>>>           } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_CHECK_PKT_LEN) {
>>> -            err = parse_check_pkt_len_action(netdev, flower, info, action, 
>>> nla,
>>> +            err = parse_check_pkt_len_action(netdev, flower, info, tnl,
>>> +                                             action, nla,
>>>                                                nl_attr_len_pad(nla,
>>>                                                                left) >= left
>>>                                                && !more_actions,
>>> @@ -2277,14 +2492,28 @@ netdev_tc_parse_nl_actions(struct netdev *netdev, 
>>> struct tc_flower *flower,
>>>               if (err) {
>>>                   return err;
>>>               }
>>> -        } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_SAMPLE) {
>>> -            struct offload_sample sample;
>>> -            uint32_t sgid;
>>> -
>>> -            memset(&sample, 0, sizeof sample);
>>> -            sgid = sgid_alloc(&sample);
>>> -            sgid_free(sgid);
>>> -            return EOPNOTSUPP;
>>> +        } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_SAMPLE ||
>>> +                   nl_attr_type(nla) == OVS_ACTION_ATTR_USERSPACE) {
>>> +            if (!psample_supported()) {
>>> +                VLOG_DBG_RL(&rl, "Unsupported put action type: %d, psample 
>>> is "
>>> +                            "not initialized successfully", 
>>> nl_attr_type(nla));
>>> +                return EOPNOTSUPP;
>>> +            }
>>> +            if (get_flower_sgid_count(flower) >= MAX_TC_SAMPLES_PER_FLOW) {
>>> +                VLOG_ERR_RL(&error_rl, "Only %u TC_SAMPLE action per "
>>> +                            "flow is supported", MAX_TC_SAMPLES_PER_FLOW);
>>> +                return EOPNOTSUPP;
>>> +            }
>>> +            if (nl_attr_type(nla) == OVS_ACTION_ATTR_SAMPLE) {
>>> +                err = parse_sample_action(flower, action, actions, 
>>> actions_len,
>>> +                                          nla, tnl);
>>> +            } else {
>>> +                err = parse_userspace_action(flower, action, actions,
>>> +                                             actions_len, nla, tnl);
>>> +            }
>>> +            if (err) {
>>> +                return err;
>>> +            }
>>>           } else {
>>>               VLOG_DBG_RL(&rl, "unsupported put action type: %d",
>>>                           nl_attr_type(nla));
>>> @@ -2324,6 +2553,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct 
>>> match *match,
>>>       }
>>>
>>>       memset(&flower, 0, sizeof flower);
>>> +    flower.ifindex = ifindex;
>>>
>>>       chain = key->recirc_id;
>>>       mask->recirc_id = 0;
>>> @@ -2589,16 +2819,17 @@ netdev_tc_flow_put(struct netdev *netdev, struct 
>>> match *match,
>>>       }
>>>
>>>       /* Parse all (nested) actions. */
>>> -    err = netdev_tc_parse_nl_actions(netdev, &flower, info,
>>> +    err = netdev_tc_parse_nl_actions(netdev, &flower, info, tnl,
>>>                                        actions, actions_len, &recirc_act,
>>>                                        false, NULL);
>>>       if (err) {
>>> -        return err;
>>> +        goto error_out;
>>>       }
>>>
>>>       if ((chain || recirc_act) && !info->recirc_id_shared_with_tc) {
>>>           VLOG_DBG_RL(&rl, "flow_put: recirc_id sharing not supported");
>>> -        return EOPNOTSUPP;
>>> +        err = EOPNOTSUPP;
>>> +        goto error_out;
>>>       }
>>>
>>>       memset(&adjust_stats, 0, sizeof adjust_stats);
>>> @@ -2612,7 +2843,8 @@ netdev_tc_flow_put(struct netdev *netdev, struct 
>>> match *match,
>>>       prio = get_prio_for_tc_flower(&flower);
>>>       if (prio == 0) {
>>>           VLOG_ERR_RL(&rl, "couldn't get tc prio: %s", 
>>> ovs_strerror(ENOSPC));
>>> -        return ENOSPC;
>>> +        err = ENOSPC;
>>> +        goto error_out;
>>>       }
>>>
>>>       flower.act_cookie.data = ufid;
>>> @@ -2621,14 +2853,20 @@ netdev_tc_flow_put(struct netdev *netdev, struct 
>>> match *match,
>>>       block_id = get_block_id_from_netdev(netdev);
>>>       id = tc_make_tcf_id_chain(ifindex, block_id, chain, prio, hook);
>>>       err = tc_replace_flower(&id, &flower);
>>> -    if (!err) {
>>> -        if (stats) {
>>> -            memset(stats, 0, sizeof *stats);
>>> -            netdev_tc_adjust_stats(stats, &adjust_stats);
>>> -        }
>>> -        add_ufid_tc_mapping(netdev, ufid, &id, &adjust_stats);
>>> +    if (err) {
>>> +        goto error_out;
>>>       }
>>>
>>> +    if (stats) {
>>> +        memset(stats, 0, sizeof *stats);
>>> +        netdev_tc_adjust_stats(stats, &adjust_stats);
>>> +    }
>>> +
>>> +    add_ufid_tc_mapping(netdev, ufid, &id, &adjust_stats, &flower);
>>> +    return 0;
>>> +
>>> +error_out:
>>> +    free_all_flower_sgids(&flower);
>>>       return err;
>>>   }
>>>
>>> diff --git a/lib/tc.c b/lib/tc.c
>>> index 5c32c6f97..28ca6325a 100644
>>> --- a/lib/tc.c
>>> +++ b/lib/tc.c
>>> @@ -23,14 +23,15 @@
>>>   #include <linux/if_packet.h>
>>>   #include <linux/rtnetlink.h>
>>>   #include <linux/tc_act/tc_csum.h>
>>> +#include <linux/tc_act/tc_ct.h>
>>>   #include <linux/tc_act/tc_gact.h>
>>>   #include <linux/tc_act/tc_mirred.h>
>>>   #include <linux/tc_act/tc_mpls.h>
>>>   #include <linux/tc_act/tc_pedit.h>
>>> +#include <linux/tc_act/tc_sample.h>
>>>   #include <linux/tc_act/tc_skbedit.h>
>>>   #include <linux/tc_act/tc_tunnel_key.h>
>>>   #include <linux/tc_act/tc_vlan.h>
>>> -#include <linux/tc_act/tc_ct.h>
>>>   #include <linux/gen_stats.h>
>>>   #include <net/if.h>
>>>   #include <unistd.h>
>>> @@ -1484,6 +1485,38 @@ nl_parse_act_police(const struct nlattr *options, 
>>> struct tc_flower *flower)
>>>       return 0;
>>>   }
>>>
>>> +static const struct nl_policy sample_policy[] = {
>>> +    [TCA_SAMPLE_PARMS] = { .type = NL_A_UNSPEC,
>>> +                           .min_len = sizeof(struct tc_sample),
>>> +                           .optional = false, },
>>> +    [TCA_SAMPLE_PSAMPLE_GROUP] = { .type = NL_A_U32,
>>> +                                   .optional = false, },
>>> +    [TCA_SAMPLE_RATE] = { .type = NL_A_U32,
>>> +                          .optional = false, },
>>> +};
>>> +
>>> +static int
>>> +nl_parse_act_sample(struct nlattr *options, struct tc_flower *flower)
>>> +{
>>> +    struct nlattr *sample_attrs[ARRAY_SIZE(sample_policy)];
>>> +    struct tc_action *action;
>>> +
>>> +    if (!nl_parse_nested(options, sample_policy, sample_attrs,
>>> +                         ARRAY_SIZE(sample_policy))) {
>>> +        VLOG_ERR_RL(&error_rl, "Failed to parse sample action options");
>>> +        return EPROTO;
>>> +    }
>>> +
>>> +    action = &flower->actions[flower->action_count++];
>>> +    action->type = TC_ACT_SAMPLE;
>>> +    action->sample.group_id =
>>> +        nl_attr_get_u32(sample_attrs[TCA_SAMPLE_PSAMPLE_GROUP]);
>>> +    action->sample.rate =
>>> +        nl_attr_get_u32(sample_attrs[TCA_SAMPLE_RATE]);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>>   static const struct nl_policy mirred_policy[] = {
>>>       [TCA_MIRRED_PARMS] = { .type = NL_A_UNSPEC,
>>>                              .min_len = sizeof(struct tc_mirred),
>>> @@ -1999,6 +2032,8 @@ nl_parse_single_action(struct nlattr *action, struct 
>>> tc_flower *flower,
>>>           nl_parse_act_ct(act_options, flower);
>>>       } else if (!strcmp(act_kind, "police")) {
>>>           nl_parse_act_police(act_options, flower);
>>> +    } else if (!strcmp(act_kind, "sample")) {
>>> +        nl_parse_act_sample(act_options, flower);
>>>       } else {
>>>           VLOG_ERR_RL(&error_rl, "unknown tc action kind: %s", act_kind);
>>>           err = EINVAL;
>>> @@ -2791,6 +2826,24 @@ nl_msg_put_act_mirred(struct ofpbuf *request, int 
>>> ifindex, int action,
>>>       nl_msg_end_nested(request, offset);
>>>   }
>>>
>>> +static void
>>> +nl_msg_put_act_sample(struct ofpbuf *request, uint32_t rate, uint32_t 
>>> group_id,
>>> +                      uint32_t action_pc)
>>> +{
>>> +    size_t offset;
>>> +
>>> +    nl_msg_put_string(request, TCA_ACT_KIND, "sample");
>>> +    offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED);
>>> +    {
>>> +        struct tc_sample parm = { .action = action_pc };
>>> +
>>> +        nl_msg_put_unspec(request, TCA_SAMPLE_PARMS, &parm, sizeof parm);
>>> +        nl_msg_put_u32(request, TCA_SAMPLE_RATE, rate);
>>> +        nl_msg_put_u32(request, TCA_SAMPLE_PSAMPLE_GROUP, group_id);
>>> +    }
>>> +    nl_msg_end_nested(request, offset);
>>> +}
>>> +
>>>   static inline void
>>>   nl_msg_put_act_cookie(struct ofpbuf *request, struct tc_cookie *ck) {
>>>       if (ck->len) {
>>> @@ -3103,6 +3156,7 @@ get_action_index_for_tc_actions(struct tc_flower 
>>> *flower, uint16_t act_index,
>>>           case TC_ACT_MPLS_SET:
>>>           case TC_ACT_GOTO:
>>>           case TC_ACT_CT:
>>> +        case TC_ACT_SAMPLE:
>>>               /* Increase act_index by one if we are sure this type of 
>>> action
>>>                * will only add one tc action in the kernel. */
>>>               act_index++;
>>> @@ -3310,6 +3364,13 @@ nl_msg_put_flower_acts(struct ofpbuf *request, 
>>> struct tc_flower *flower)
>>>                   nl_msg_end_nested(request, act_offset);
>>>               }
>>>               break;
>>> +            case TC_ACT_SAMPLE: {
>>> +                act_offset = nl_msg_start_nested(request, act_index++);
>>> +                nl_msg_put_act_sample(request, action->sample.rate,
>>> +                                      action->sample.group_id, action_pc);
>>> +                nl_msg_end_nested(request, act_offset);
>>> +            }
>>> +            break;
>>>               case TC_ACT_OUTPUT: {
>>>                   if (!released && flower->tunnel) {
>>>                       nl_msg_put_flower_acts_release(request, act_index++);
>>> diff --git a/lib/tc.h b/lib/tc.h
>>> index cdd3b4f60..5f6e15d5c 100644
>>> --- a/lib/tc.h
>>> +++ b/lib/tc.h
>>> @@ -192,6 +192,7 @@ enum tc_action_type {
>>>       TC_ACT_CT,
>>>       TC_ACT_POLICE,
>>>       TC_ACT_POLICE_MTU,
>>> +    TC_ACT_SAMPLE,
>>>   };
>>>
>>>   enum nat_type {
>>> @@ -283,6 +284,10 @@ struct tc_action {
>>>               uint32_t result_jump;
>>>               uint16_t mtu;
>>>           } police;
>>> +        struct {
>>> +            uint32_t rate;
>>> +            uint32_t group_id;
>>> +        } sample;
>>>       };
>>>
>>>       enum tc_action_type type;
>>> @@ -380,6 +385,7 @@ struct tc_flower {
>>>       enum tc_offloaded_state offloaded_state;
>>>       /* Used to force skip_hw when probing tc features. */
>>>       enum tc_offload_policy tc_policy;
>>> +    uint16_t ifindex;
>>>   };
>>>
>>>   int tc_replace_flower(struct tcf_id *id, struct tc_flower *flower);
>>> -- 
>>> 2.26.3
>> Here is a full diff of the suggested changes, if you sent a v28 with only 
>> these changes I can ack the patch (need an answer on one question):
>>
>> diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c
>> index 14a4cb393..71ec8ef1f 100644
>> --- a/lib/netdev-offload-tc.c
>> +++ b/lib/netdev-offload-tc.c
>> @@ -2146,8 +2146,11 @@ offload_sample_init(struct offload_sample *sample,
>>       if (flower->tunnel) {
>>           sample->tunnel = CONST_CAST(struct flow_tnl *, tnl);
>>       }
>> -    sample->userspace_actions = CONST_CAST(struct nlattr *, next_actions);
>> -    sample->userspace_actions_len = next_actions_len;
>> +
>> +    sample->userspace_actions = xmalloc(next_actions_len + NLA_HDRLEN);
>> +    nullable_memcpy((char *) sample->userspace_actions + NLA_HDRLEN,
>> +                    next_actions, next_actions_len);
>> +    sample->userspace_actions->nla_len = next_actions_len + NLA_HDRLEN;
>>       sample->ifindex = flower->ifindex;
>>   }
>>
>> @@ -2161,31 +2164,25 @@ parse_sample_action(struct tc_flower *flower, struct 
>> tc_action *tc_action,
>>       struct offload_sample sample;
>>       const struct nlattr *nla;
>>       unsigned int left;
>> +    uint32_t rate = 0;
>> +    int ret = EINVAL;
>>       uint32_t sgid;
>> -    int err;
>>
>>       offload_sample_init(&sample, next_actions, next_actions_len, flower, 
>> tnl);
>>       sample.action = CONST_CAST(struct nlattr *, sample_action);
>>
>> -    err = EINVAL;
>>       NL_NESTED_FOR_EACH_UNSAFE (nla, left, sample_action) {
>>           if (nl_attr_type(nla) == OVS_SAMPLE_ATTR_ACTIONS) {
>> -            err = parse_sample_actions_attribute(nla, &sample);
>> -            if (err) {
>> -                break;
>> -            }
>> +            ret = parse_sample_actions_attribute(nla, &sample);
>>           } else if (nl_attr_type(nla) == OVS_SAMPLE_ATTR_PROBABILITY) {
>> -            tc_action->type = TC_ACT_SAMPLE;
>> -            tc_action->sample.rate = UINT32_MAX / nl_attr_get_u32(nla);
>> -            if (!tc_action->sample.rate) {
>> -                break;
>> -            }
>> +            rate = UINT32_MAX / nl_attr_get_u32(nla);
>>           } else {
>> -            break;
>> +            return EINVAL;
>>           }
>>       }
>>
>> -    if (!tc_action->sample.rate || err) {
>> +    /* This check makes sure both attributes above were present and valid. 
>> */
>> +    if (!rate || ret) {
>>           return EINVAL;
>>       }
>>
>> @@ -2194,10 +2191,13 @@ parse_sample_action(struct tc_flower *flower, struct 
>> tc_action *tc_action,
>>           VLOG_DBG_RL(&rl, "Failed allocating group id for sample action");
>>           return ENOENT;
>>       }
>> +
>> +    tc_action->type = TC_ACT_SAMPLE;
>> +    tc_action->sample.rate = rate;
>>       tc_action->sample.group_id = sgid;
>>       flower->action_count++;
>>
>> -    return err;
>> +    return 0;
>>   }
>>
>>   static int
>> @@ -2232,7 +2232,7 @@ parse_userspace_action(struct tc_flower *flower, 
>> struct tc_action *tc_action,
>>       tc_action->sample.rate = 1;
>>       flower->action_count++;
>>
>> -    return err;
>> +    return 0;
>>   }
>>
>>   static int
>>

_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to