diff mbox series

[ovs-dev,09/10] netdev-offload-tc: stats should be captured on first action in the list

Message ID 164329050340.2583748.1716649852891765076.stgit@ebuild
State Superseded
Headers show
Series netdev-offload-tc: Fix various tc-offload related problems | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test fail github build: failed

Commit Message

Eelco Chaudron Jan. 27, 2022, 1:35 p.m. UTC
Statistics should be captured on the first action in a list
of actions, to do this we need to also capture the last update
time stamp from the first action.

Signed-off-by: Eelco Chaudron <echaudro@redhat.com>
---
 lib/tc.c |  119 +++++++++++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 86 insertions(+), 33 deletions(-)

Comments

Roi Dayan Jan. 30, 2022, 1:30 p.m. UTC | #1
On 2022-01-27 3:35 PM, Eelco Chaudron wrote:
> Statistics should be captured on the first action in a list
> of actions, to do this we need to also capture the last update
> time stamp from the first action.

Why stats should be captured on the first action?

this patch breaks some offloading. like tunnel rules.

On tc hw offloaded rules stats
are being updated only on actions implementing stats_update() cb.
Some actions, like tunnel_key, don't implement this and stats are
kept 0 but they are not usually the last action.
last actions like drop, goto, mirred do implement this and have
the stats.


> 
> Signed-off-by: Eelco Chaudron <echaudro@redhat.com>
> ---
>   lib/tc.c |  119 +++++++++++++++++++++++++++++++++++++++++++++-----------------
>   1 file changed, 86 insertions(+), 33 deletions(-)
> 
> diff --git a/lib/tc.c b/lib/tc.c
> index b37dce22f..b430f1bad 100644
> --- a/lib/tc.c
> +++ b/lib/tc.c
> @@ -994,12 +994,35 @@ nl_parse_flower_flags(struct nlattr **attrs, struct tc_flower *flower)
>       flower->offloaded_state = nl_get_flower_offloaded_state(attrs);
>   }
>   
> +static int
> +get_user_hz(void)
> +{
> +    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
> +    static int user_hz = 100;
> +
> +    if (ovsthread_once_start(&once)) {
> +        user_hz = sysconf(_SC_CLK_TCK);
> +        ovsthread_once_done(&once);
> +    }
> +
> +    return user_hz;
> +}
> +
> +static void
> +nl_parse_tcf(const struct tcf_t *tm, struct tc_flower *flower)
> +{
> +    flower->lastused = time_msec() - (tm->lastuse * 1000 / get_user_hz());
> +}
> +
>   static const struct nl_policy pedit_policy[] = {
> -            [TCA_PEDIT_PARMS_EX] = { .type = NL_A_UNSPEC,
> -                                     .min_len = sizeof(struct tc_pedit),
> -                                     .optional = false, },
> -            [TCA_PEDIT_KEYS_EX]   = { .type = NL_A_NESTED,
> -                                      .optional = false, },
> +    [TCA_PEDIT_TM] = { .type = NL_A_UNSPEC,
> +                       .min_len = sizeof(struct tcf_t),
> +                       .optional = false, },
> +    [TCA_PEDIT_PARMS_EX] = { .type = NL_A_UNSPEC,
> +                             .min_len = sizeof(struct tc_pedit),
> +                             .optional = false, },
> +    [TCA_PEDIT_KEYS_EX] = { .type = NL_A_NESTED,
> +                            .optional = false, },
>   };
>   
>   static int
> @@ -1094,6 +1117,9 @@ nl_parse_act_pedit(struct nlattr *options, struct tc_flower *flower)
>       action = &flower->actions[flower->action_count++];
>       action->type = TC_ACT_PEDIT;
>   
> +    nl_parse_tcf(nl_attr_get_unspec(pe_attrs[TCA_PEDIT_TM],
> +                                    sizeof(struct tcf_t)),
> +                 flower);
>       return 0;
>   }
>   
> @@ -1101,6 +1127,9 @@ static const struct nl_policy tunnel_key_policy[] = {
>       [TCA_TUNNEL_KEY_PARMS] = { .type = NL_A_UNSPEC,
>                                  .min_len = sizeof(struct tc_tunnel_key),
>                                  .optional = false, },
> +    [TCA_TUNNEL_KEY_TM] = { .type = NL_A_UNSPEC,
> +                            .min_len = sizeof(struct tcf_t),
> +                            .optional = false, },
>       [TCA_TUNNEL_KEY_ENC_IPV4_SRC] = { .type = NL_A_U32, .optional = true, },
>       [TCA_TUNNEL_KEY_ENC_IPV4_DST] = { .type = NL_A_U32, .optional = true, },
>       [TCA_TUNNEL_KEY_ENC_IPV6_SRC] = { .type = NL_A_UNSPEC,
> @@ -1275,6 +1304,10 @@ nl_parse_act_tunnel_key(struct nlattr *options, struct tc_flower *flower)
>                       tun->action, tun->t_action);
>           return EINVAL;
>       }
> +
> +    nl_parse_tcf(nl_attr_get_unspec(tun_attrs[TCA_TUNNEL_KEY_TM],
> +                                    sizeof(struct tcf_t)),
> +                 flower);
>       return 0;
>   }
>   
> @@ -1287,26 +1320,6 @@ static const struct nl_policy gact_policy[] = {
>                         .optional = false, },
>   };
>   
> -static int
> -get_user_hz(void)
> -{
> -    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
> -    static int user_hz = 100;
> -
> -    if (ovsthread_once_start(&once)) {
> -        user_hz = sysconf(_SC_CLK_TCK);
> -        ovsthread_once_done(&once);
> -    }
> -
> -    return user_hz;
> -}
> -
> -static void
> -nl_parse_tcf(const struct tcf_t *tm, struct tc_flower *flower)
> -{
> -    flower->lastused = time_msec() - (tm->lastuse * 1000 / get_user_hz());
> -}
> -
>   static int
>   nl_parse_act_gact(struct nlattr *options, struct tc_flower *flower)
>   {
> @@ -1394,18 +1407,21 @@ nl_parse_act_mirred(struct nlattr *options, struct tc_flower *flower)
>   
>   static const struct nl_policy ct_policy[] = {
>       [TCA_CT_PARMS] = { .type = NL_A_UNSPEC,
> -                              .min_len = sizeof(struct tc_ct),
> -                              .optional = false, },
> +                       .min_len = sizeof(struct tc_ct),
> +                       .optional = false, },
> +    [TCA_CT_TM] = { .type = NL_A_UNSPEC,
> +                    .min_len = sizeof(struct tcf_t),
> +                    .optional = false, },
>       [TCA_CT_ACTION] = { .type = NL_A_U16,
> -                         .optional = true, },
> +                        .optional = true, },
>       [TCA_CT_ZONE] = { .type = NL_A_U16,
>                         .optional = true, },
>       [TCA_CT_MARK] = { .type = NL_A_U32,
>                          .optional = true, },
>       [TCA_CT_MARK_MASK] = { .type = NL_A_U32,
> -                            .optional = true, },
> +                           .optional = true, },
>       [TCA_CT_LABELS] = { .type = NL_A_UNSPEC,
> -                         .optional = true, },
> +                        .optional = true, },
>       [TCA_CT_LABELS_MASK] = { .type = NL_A_UNSPEC,
>                                 .optional = true, },
>       [TCA_CT_NAT_IPV4_MIN] = { .type = NL_A_U32,
> @@ -1517,6 +1533,9 @@ nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower)
>       }
>       action->type = TC_ACT_CT;
>   
> +    nl_parse_tcf(nl_attr_get_unspec(ct_attrs[TCA_CT_TM],
> +                                    sizeof(struct tcf_t)),
> +                 flower);
>       return 0;
>   }
>   
> @@ -1524,6 +1543,9 @@ static const struct nl_policy vlan_policy[] = {
>       [TCA_VLAN_PARMS] = { .type = NL_A_UNSPEC,
>                            .min_len = sizeof(struct tc_vlan),
>                            .optional = false, },
> +    [TCA_VLAN_TM] = { .type = NL_A_UNSPEC,
> +                      .min_len = sizeof(struct tcf_t),
> +                      .optional = false, },
>       [TCA_VLAN_PUSH_VLAN_ID] = { .type = NL_A_U16, .optional = true, },
>       [TCA_VLAN_PUSH_VLAN_PROTOCOL] = { .type = NL_A_U16, .optional = true, },
>       [TCA_VLAN_PUSH_VLAN_PRIORITY] = { .type = NL_A_U8, .optional = true, },
> @@ -1562,6 +1584,10 @@ nl_parse_act_vlan(struct nlattr *options, struct tc_flower *flower)
>                       v->action, v->v_action);
>           return EINVAL;
>       }
> +
> +    nl_parse_tcf(nl_attr_get_unspec(vlan_attrs[TCA_VLAN_TM],
> +                                    sizeof(struct tcf_t)),
> +                 flower);
>       return 0;
>   }
>   
> @@ -1569,6 +1595,9 @@ static const struct nl_policy mpls_policy[] = {
>       [TCA_MPLS_PARMS] = { .type = NL_A_UNSPEC,
>                            .min_len = sizeof(struct tc_mpls),
>                            .optional = false, },
> +    [TCA_MPLS_TM] = { .type = NL_A_UNSPEC,
> +                      .min_len = sizeof(struct tcf_t),
> +                      .optional = false, },
>       [TCA_MPLS_PROTO] = { .type = NL_A_U16, .optional = true, },
>       [TCA_MPLS_LABEL] = { .type = NL_A_U32, .optional = true, },
>       [TCA_MPLS_TC] = { .type = NL_A_U8, .optional = true, },
> @@ -1655,6 +1684,9 @@ nl_parse_act_mpls(struct nlattr *options, struct tc_flower *flower)
>           return EINVAL;
>       }
>   
> +    nl_parse_tcf(nl_attr_get_unspec(mpls_attrs[TCA_MPLS_TM],
> +                                    sizeof(struct tcf_t)),
> +                 flower);
>       return 0;
>   }
>   
> @@ -1662,6 +1694,9 @@ static const struct nl_policy csum_policy[] = {
>       [TCA_CSUM_PARMS] = { .type = NL_A_UNSPEC,
>                            .min_len = sizeof(struct tc_csum),
>                            .optional = false, },
> +    [TCA_CSUM_TM] = { .type = NL_A_UNSPEC,
> +                      .min_len = sizeof(struct tcf_t),
> +                      .optional = false, },
>   };
>   
>   static int
> @@ -1695,6 +1730,9 @@ nl_parse_act_csum(struct nlattr *options, struct tc_flower *flower)
>           return EINVAL;
>       }
>   
> +    nl_parse_tcf(nl_attr_get_unspec(csum_attrs[TCA_CSUM_TM],
> +                                    sizeof(struct tcf_t)),
> +                 flower);
>       return 0;
>   }
>   
> @@ -1713,7 +1751,7 @@ static const struct nl_policy stats_policy[] = {
>   
>   static int
>   nl_parse_single_action(struct nlattr *action, struct tc_flower *flower,
> -                       bool terse)
> +                       bool terse, bool update_stats)
>   {
>       struct nlattr *act_options;
>       struct nlattr *act_stats;
> @@ -1779,7 +1817,7 @@ nl_parse_single_action(struct nlattr *action, struct tc_flower *flower,
>       }
>   
>       bs = nl_attr_get_unspec(stats_attrs[TCA_STATS_BASIC], sizeof *bs);
> -    if (bs->packets) {
> +    if (bs->packets && update_stats) {
>           put_32aligned_u64(&stats->n_packets, bs->packets);
>           put_32aligned_u64(&stats->n_bytes, bs->bytes);
>       }
> @@ -1797,6 +1835,8 @@ nl_parse_flower_actions(struct nlattr **attrs, struct tc_flower *flower,
>       static struct nl_policy actions_orders_policy[TCA_ACT_MAX_NUM + 1] = {};
>       struct nlattr *actions_orders[ARRAY_SIZE(actions_orders_policy)];
>       const int max_size = ARRAY_SIZE(actions_orders_policy);
> +    bool first_action = true;
> +    uint64_t lastused = 0;
>   
>       for (int i = TCA_ACT_MIN_PRIO; i < max_size; i++) {
>           actions_orders_policy[i].type = NL_A_NESTED;
> @@ -1817,14 +1857,27 @@ nl_parse_flower_actions(struct nlattr **attrs, struct tc_flower *flower,
>                   VLOG_DBG_RL(&error_rl, "Can only support %d actions", TCA_ACT_MAX_NUM);
>                   return EOPNOTSUPP;
>               }
> -            err = nl_parse_single_action(actions_orders[i], flower, terse);
> +            err = nl_parse_single_action(actions_orders[i], flower, terse,
> +                                         first_action);
>   
>               if (err) {
>                   return err;
>               }
> +
> +            if (first_action) {
> +                /* We need to store the last used timestamp from the first
> +                 * action, i.e., the action that was used the track the
> +                 * statistics, as all remaining action decoding overwrite
> +                 * this value. We also need to use the first actions statistics
> +                 * as conditional actions can exist. */
> +                lastused = flower->lastused;
> +                first_action = false;
> +            }
>           }
>       }
>   
> +    flower->lastused = lastused;
> +
>       if (flower->csum_update_flags) {
>           VLOG_WARN_RL(&error_rl,
>                        "expected act csum with flags: 0x%x",
> 
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Eelco Chaudron Jan. 31, 2022, 10:16 a.m. UTC | #2
On 30 Jan 2022, at 14:30, Roi Dayan wrote:

> On 2022-01-27 3:35 PM, Eelco Chaudron wrote:
>> Statistics should be captured on the first action in a list
>> of actions, to do this we need to also capture the last update
>> time stamp from the first action.
>
> Why stats should be captured on the first action?
>
> this patch breaks some offloading. like tunnel rules.
>
> On tc hw offloaded rules stats
> are being updated only on actions implementing stats_update() cb.
> Some actions, like tunnel_key, don't implement this and stats are
> kept 0 but they are not usually the last action.
> last actions like drop, goto, mirred do implement this and have
> the stats.

Thanks, I added this patch as it was part of a debug effort. So I’ll remove it from this patch set (and add a new one I found on Friday ;).

Some background info on why I kept this in the first place… In general, OVS needs stats on the match, not the actions. So with the current code/implementations available, the last action would do, as OVS will execute all actions.

As you know we are looking at the check_pkt_len action for OVS, in which case a different set of actions could be executed, rendering the statistics on the last action taken being wrong as there are two potential different paths. But I’ll figure out how to solve that when I get there :)

Thanks for the review, and I’ll send out a V2 soon!

//Eelco

>>
>> Signed-off-by: Eelco Chaudron <echaudro@redhat.com>
>> ---
>>   lib/tc.c |  119 +++++++++++++++++++++++++++++++++++++++++++++-----------------
>>   1 file changed, 86 insertions(+), 33 deletions(-)
>>
>> diff --git a/lib/tc.c b/lib/tc.c
>> index b37dce22f..b430f1bad 100644
>> --- a/lib/tc.c
>> +++ b/lib/tc.c
>> @@ -994,12 +994,35 @@ nl_parse_flower_flags(struct nlattr **attrs, struct tc_flower *flower)
>>       flower->offloaded_state = nl_get_flower_offloaded_state(attrs);
>>   }
>>  +static int
>> +get_user_hz(void)
>> +{
>> +    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
>> +    static int user_hz = 100;
>> +
>> +    if (ovsthread_once_start(&once)) {
>> +        user_hz = sysconf(_SC_CLK_TCK);
>> +        ovsthread_once_done(&once);
>> +    }
>> +
>> +    return user_hz;
>> +}
>> +
>> +static void
>> +nl_parse_tcf(const struct tcf_t *tm, struct tc_flower *flower)
>> +{
>> +    flower->lastused = time_msec() - (tm->lastuse * 1000 / get_user_hz());
>> +}
>> +
>>   static const struct nl_policy pedit_policy[] = {
>> -            [TCA_PEDIT_PARMS_EX] = { .type = NL_A_UNSPEC,
>> -                                     .min_len = sizeof(struct tc_pedit),
>> -                                     .optional = false, },
>> -            [TCA_PEDIT_KEYS_EX]   = { .type = NL_A_NESTED,
>> -                                      .optional = false, },
>> +    [TCA_PEDIT_TM] = { .type = NL_A_UNSPEC,
>> +                       .min_len = sizeof(struct tcf_t),
>> +                       .optional = false, },
>> +    [TCA_PEDIT_PARMS_EX] = { .type = NL_A_UNSPEC,
>> +                             .min_len = sizeof(struct tc_pedit),
>> +                             .optional = false, },
>> +    [TCA_PEDIT_KEYS_EX] = { .type = NL_A_NESTED,
>> +                            .optional = false, },
>>   };
>>    static int
>> @@ -1094,6 +1117,9 @@ nl_parse_act_pedit(struct nlattr *options, struct tc_flower *flower)
>>       action = &flower->actions[flower->action_count++];
>>       action->type = TC_ACT_PEDIT;
>>  +    nl_parse_tcf(nl_attr_get_unspec(pe_attrs[TCA_PEDIT_TM],
>> +                                    sizeof(struct tcf_t)),
>> +                 flower);
>>       return 0;
>>   }
>>  @@ -1101,6 +1127,9 @@ static const struct nl_policy tunnel_key_policy[] = {
>>       [TCA_TUNNEL_KEY_PARMS] = { .type = NL_A_UNSPEC,
>>                                  .min_len = sizeof(struct tc_tunnel_key),
>>                                  .optional = false, },
>> +    [TCA_TUNNEL_KEY_TM] = { .type = NL_A_UNSPEC,
>> +                            .min_len = sizeof(struct tcf_t),
>> +                            .optional = false, },
>>       [TCA_TUNNEL_KEY_ENC_IPV4_SRC] = { .type = NL_A_U32, .optional = true, },
>>       [TCA_TUNNEL_KEY_ENC_IPV4_DST] = { .type = NL_A_U32, .optional = true, },
>>       [TCA_TUNNEL_KEY_ENC_IPV6_SRC] = { .type = NL_A_UNSPEC,
>> @@ -1275,6 +1304,10 @@ nl_parse_act_tunnel_key(struct nlattr *options, struct tc_flower *flower)
>>                       tun->action, tun->t_action);
>>           return EINVAL;
>>       }
>> +
>> +    nl_parse_tcf(nl_attr_get_unspec(tun_attrs[TCA_TUNNEL_KEY_TM],
>> +                                    sizeof(struct tcf_t)),
>> +                 flower);
>>       return 0;
>>   }
>>  @@ -1287,26 +1320,6 @@ static const struct nl_policy gact_policy[] = {
>>                         .optional = false, },
>>   };
>>  -static int
>> -get_user_hz(void)
>> -{
>> -    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
>> -    static int user_hz = 100;
>> -
>> -    if (ovsthread_once_start(&once)) {
>> -        user_hz = sysconf(_SC_CLK_TCK);
>> -        ovsthread_once_done(&once);
>> -    }
>> -
>> -    return user_hz;
>> -}
>> -
>> -static void
>> -nl_parse_tcf(const struct tcf_t *tm, struct tc_flower *flower)
>> -{
>> -    flower->lastused = time_msec() - (tm->lastuse * 1000 / get_user_hz());
>> -}
>> -
>>   static int
>>   nl_parse_act_gact(struct nlattr *options, struct tc_flower *flower)
>>   {
>> @@ -1394,18 +1407,21 @@ nl_parse_act_mirred(struct nlattr *options, struct tc_flower *flower)
>>    static const struct nl_policy ct_policy[] = {
>>       [TCA_CT_PARMS] = { .type = NL_A_UNSPEC,
>> -                              .min_len = sizeof(struct tc_ct),
>> -                              .optional = false, },
>> +                       .min_len = sizeof(struct tc_ct),
>> +                       .optional = false, },
>> +    [TCA_CT_TM] = { .type = NL_A_UNSPEC,
>> +                    .min_len = sizeof(struct tcf_t),
>> +                    .optional = false, },
>>       [TCA_CT_ACTION] = { .type = NL_A_U16,
>> -                         .optional = true, },
>> +                        .optional = true, },
>>       [TCA_CT_ZONE] = { .type = NL_A_U16,
>>                         .optional = true, },
>>       [TCA_CT_MARK] = { .type = NL_A_U32,
>>                          .optional = true, },
>>       [TCA_CT_MARK_MASK] = { .type = NL_A_U32,
>> -                            .optional = true, },
>> +                           .optional = true, },
>>       [TCA_CT_LABELS] = { .type = NL_A_UNSPEC,
>> -                         .optional = true, },
>> +                        .optional = true, },
>>       [TCA_CT_LABELS_MASK] = { .type = NL_A_UNSPEC,
>>                                 .optional = true, },
>>       [TCA_CT_NAT_IPV4_MIN] = { .type = NL_A_U32,
>> @@ -1517,6 +1533,9 @@ nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower)
>>       }
>>       action->type = TC_ACT_CT;
>>  +    nl_parse_tcf(nl_attr_get_unspec(ct_attrs[TCA_CT_TM],
>> +                                    sizeof(struct tcf_t)),
>> +                 flower);
>>       return 0;
>>   }
>>  @@ -1524,6 +1543,9 @@ static const struct nl_policy vlan_policy[] = {
>>       [TCA_VLAN_PARMS] = { .type = NL_A_UNSPEC,
>>                            .min_len = sizeof(struct tc_vlan),
>>                            .optional = false, },
>> +    [TCA_VLAN_TM] = { .type = NL_A_UNSPEC,
>> +                      .min_len = sizeof(struct tcf_t),
>> +                      .optional = false, },
>>       [TCA_VLAN_PUSH_VLAN_ID] = { .type = NL_A_U16, .optional = true, },
>>       [TCA_VLAN_PUSH_VLAN_PROTOCOL] = { .type = NL_A_U16, .optional = true, },
>>       [TCA_VLAN_PUSH_VLAN_PRIORITY] = { .type = NL_A_U8, .optional = true, },
>> @@ -1562,6 +1584,10 @@ nl_parse_act_vlan(struct nlattr *options, struct tc_flower *flower)
>>                       v->action, v->v_action);
>>           return EINVAL;
>>       }
>> +
>> +    nl_parse_tcf(nl_attr_get_unspec(vlan_attrs[TCA_VLAN_TM],
>> +                                    sizeof(struct tcf_t)),
>> +                 flower);
>>       return 0;
>>   }
>>  @@ -1569,6 +1595,9 @@ static const struct nl_policy mpls_policy[] = {
>>       [TCA_MPLS_PARMS] = { .type = NL_A_UNSPEC,
>>                            .min_len = sizeof(struct tc_mpls),
>>                            .optional = false, },
>> +    [TCA_MPLS_TM] = { .type = NL_A_UNSPEC,
>> +                      .min_len = sizeof(struct tcf_t),
>> +                      .optional = false, },
>>       [TCA_MPLS_PROTO] = { .type = NL_A_U16, .optional = true, },
>>       [TCA_MPLS_LABEL] = { .type = NL_A_U32, .optional = true, },
>>       [TCA_MPLS_TC] = { .type = NL_A_U8, .optional = true, },
>> @@ -1655,6 +1684,9 @@ nl_parse_act_mpls(struct nlattr *options, struct tc_flower *flower)
>>           return EINVAL;
>>       }
>>  +    nl_parse_tcf(nl_attr_get_unspec(mpls_attrs[TCA_MPLS_TM],
>> +                                    sizeof(struct tcf_t)),
>> +                 flower);
>>       return 0;
>>   }
>>  @@ -1662,6 +1694,9 @@ static const struct nl_policy csum_policy[] = {
>>       [TCA_CSUM_PARMS] = { .type = NL_A_UNSPEC,
>>                            .min_len = sizeof(struct tc_csum),
>>                            .optional = false, },
>> +    [TCA_CSUM_TM] = { .type = NL_A_UNSPEC,
>> +                      .min_len = sizeof(struct tcf_t),
>> +                      .optional = false, },
>>   };
>>    static int
>> @@ -1695,6 +1730,9 @@ nl_parse_act_csum(struct nlattr *options, struct tc_flower *flower)
>>           return EINVAL;
>>       }
>>  +    nl_parse_tcf(nl_attr_get_unspec(csum_attrs[TCA_CSUM_TM],
>> +                                    sizeof(struct tcf_t)),
>> +                 flower);
>>       return 0;
>>   }
>>  @@ -1713,7 +1751,7 @@ static const struct nl_policy stats_policy[] = {
>>    static int
>>   nl_parse_single_action(struct nlattr *action, struct tc_flower *flower,
>> -                       bool terse)
>> +                       bool terse, bool update_stats)
>>   {
>>       struct nlattr *act_options;
>>       struct nlattr *act_stats;
>> @@ -1779,7 +1817,7 @@ nl_parse_single_action(struct nlattr *action, struct tc_flower *flower,
>>       }
>>        bs = nl_attr_get_unspec(stats_attrs[TCA_STATS_BASIC], sizeof *bs);
>> -    if (bs->packets) {
>> +    if (bs->packets && update_stats) {
>>           put_32aligned_u64(&stats->n_packets, bs->packets);
>>           put_32aligned_u64(&stats->n_bytes, bs->bytes);
>>       }
>> @@ -1797,6 +1835,8 @@ nl_parse_flower_actions(struct nlattr **attrs, struct tc_flower *flower,
>>       static struct nl_policy actions_orders_policy[TCA_ACT_MAX_NUM + 1] = {};
>>       struct nlattr *actions_orders[ARRAY_SIZE(actions_orders_policy)];
>>       const int max_size = ARRAY_SIZE(actions_orders_policy);
>> +    bool first_action = true;
>> +    uint64_t lastused = 0;
>>        for (int i = TCA_ACT_MIN_PRIO; i < max_size; i++) {
>>           actions_orders_policy[i].type = NL_A_NESTED;
>> @@ -1817,14 +1857,27 @@ nl_parse_flower_actions(struct nlattr **attrs, struct tc_flower *flower,
>>                   VLOG_DBG_RL(&error_rl, "Can only support %d actions", TCA_ACT_MAX_NUM);
>>                   return EOPNOTSUPP;
>>               }
>> -            err = nl_parse_single_action(actions_orders[i], flower, terse);
>> +            err = nl_parse_single_action(actions_orders[i], flower, terse,
>> +                                         first_action);
>>                if (err) {
>>                   return err;
>>               }
>> +
>> +            if (first_action) {
>> +                /* We need to store the last used timestamp from the first
>> +                 * action, i.e., the action that was used the track the
>> +                 * statistics, as all remaining action decoding overwrite
>> +                 * this value. We also need to use the first actions statistics
>> +                 * as conditional actions can exist. */
>> +                lastused = flower->lastused;
>> +                first_action = false;
>> +            }
>>           }
>>       }
>>  +    flower->lastused = lastused;
>> +
>>       if (flower->csum_update_flags) {
>>           VLOG_WARN_RL(&error_rl,
>>                        "expected act csum with flags: 0x%x",
>>
>> _______________________________________________
>> dev mailing list
>> dev@openvswitch.org
>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Roi Dayan Jan. 31, 2022, 11:07 a.m. UTC | #3
On 2022-01-31 12:16 PM, Eelco Chaudron wrote:
> 
> 
> On 30 Jan 2022, at 14:30, Roi Dayan wrote:
> 
>> On 2022-01-27 3:35 PM, Eelco Chaudron wrote:
>>> Statistics should be captured on the first action in a list
>>> of actions, to do this we need to also capture the last update
>>> time stamp from the first action.
>>
>> Why stats should be captured on the first action?
>>
>> this patch breaks some offloading. like tunnel rules.
>>
>> On tc hw offloaded rules stats
>> are being updated only on actions implementing stats_update() cb.
>> Some actions, like tunnel_key, don't implement this and stats are
>> kept 0 but they are not usually the last action.
>> last actions like drop, goto, mirred do implement this and have
>> the stats.
> 
> Thanks, I added this patch as it was part of a debug effort. So I’ll remove it from this patch set (and add a new one I found on Friday ;).
> 
> Some background info on why I kept this in the first place… In general, OVS needs stats on the match, not the actions. So with the current code/implementations available, the last action would do, as OVS will execute all actions.
> 
> As you know we are looking at the check_pkt_len action for OVS, in which case a different set of actions could be executed, rendering the statistics on the last action taken being wrong as there are two potential different paths. But I’ll figure out how to solve that when I get there :)
> 
> Thanks for the review, and I’ll send out a V2 soon!
> 
> //Eelco

great thanks.

> 
>>>
>>> Signed-off-by: Eelco Chaudron <echaudro@redhat.com>
>>> ---
>>>    lib/tc.c |  119 +++++++++++++++++++++++++++++++++++++++++++++-----------------
>>>    1 file changed, 86 insertions(+), 33 deletions(-)
>>>
>>> diff --git a/lib/tc.c b/lib/tc.c
>>> index b37dce22f..b430f1bad 100644
>>> --- a/lib/tc.c
>>> +++ b/lib/tc.c
>>> @@ -994,12 +994,35 @@ nl_parse_flower_flags(struct nlattr **attrs, struct tc_flower *flower)
>>>        flower->offloaded_state = nl_get_flower_offloaded_state(attrs);
>>>    }
>>>   +static int
>>> +get_user_hz(void)
>>> +{
>>> +    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
>>> +    static int user_hz = 100;
>>> +
>>> +    if (ovsthread_once_start(&once)) {
>>> +        user_hz = sysconf(_SC_CLK_TCK);
>>> +        ovsthread_once_done(&once);
>>> +    }
>>> +
>>> +    return user_hz;
>>> +}
>>> +
>>> +static void
>>> +nl_parse_tcf(const struct tcf_t *tm, struct tc_flower *flower)
>>> +{
>>> +    flower->lastused = time_msec() - (tm->lastuse * 1000 / get_user_hz());
>>> +}
>>> +
>>>    static const struct nl_policy pedit_policy[] = {
>>> -            [TCA_PEDIT_PARMS_EX] = { .type = NL_A_UNSPEC,
>>> -                                     .min_len = sizeof(struct tc_pedit),
>>> -                                     .optional = false, },
>>> -            [TCA_PEDIT_KEYS_EX]   = { .type = NL_A_NESTED,
>>> -                                      .optional = false, },
>>> +    [TCA_PEDIT_TM] = { .type = NL_A_UNSPEC,
>>> +                       .min_len = sizeof(struct tcf_t),
>>> +                       .optional = false, },
>>> +    [TCA_PEDIT_PARMS_EX] = { .type = NL_A_UNSPEC,
>>> +                             .min_len = sizeof(struct tc_pedit),
>>> +                             .optional = false, },
>>> +    [TCA_PEDIT_KEYS_EX] = { .type = NL_A_NESTED,
>>> +                            .optional = false, },
>>>    };
>>>     static int
>>> @@ -1094,6 +1117,9 @@ nl_parse_act_pedit(struct nlattr *options, struct tc_flower *flower)
>>>        action = &flower->actions[flower->action_count++];
>>>        action->type = TC_ACT_PEDIT;
>>>   +    nl_parse_tcf(nl_attr_get_unspec(pe_attrs[TCA_PEDIT_TM],
>>> +                                    sizeof(struct tcf_t)),
>>> +                 flower);
>>>        return 0;
>>>    }
>>>   @@ -1101,6 +1127,9 @@ static const struct nl_policy tunnel_key_policy[] = {
>>>        [TCA_TUNNEL_KEY_PARMS] = { .type = NL_A_UNSPEC,
>>>                                   .min_len = sizeof(struct tc_tunnel_key),
>>>                                   .optional = false, },
>>> +    [TCA_TUNNEL_KEY_TM] = { .type = NL_A_UNSPEC,
>>> +                            .min_len = sizeof(struct tcf_t),
>>> +                            .optional = false, },
>>>        [TCA_TUNNEL_KEY_ENC_IPV4_SRC] = { .type = NL_A_U32, .optional = true, },
>>>        [TCA_TUNNEL_KEY_ENC_IPV4_DST] = { .type = NL_A_U32, .optional = true, },
>>>        [TCA_TUNNEL_KEY_ENC_IPV6_SRC] = { .type = NL_A_UNSPEC,
>>> @@ -1275,6 +1304,10 @@ nl_parse_act_tunnel_key(struct nlattr *options, struct tc_flower *flower)
>>>                        tun->action, tun->t_action);
>>>            return EINVAL;
>>>        }
>>> +
>>> +    nl_parse_tcf(nl_attr_get_unspec(tun_attrs[TCA_TUNNEL_KEY_TM],
>>> +                                    sizeof(struct tcf_t)),
>>> +                 flower);
>>>        return 0;
>>>    }
>>>   @@ -1287,26 +1320,6 @@ static const struct nl_policy gact_policy[] = {
>>>                          .optional = false, },
>>>    };
>>>   -static int
>>> -get_user_hz(void)
>>> -{
>>> -    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
>>> -    static int user_hz = 100;
>>> -
>>> -    if (ovsthread_once_start(&once)) {
>>> -        user_hz = sysconf(_SC_CLK_TCK);
>>> -        ovsthread_once_done(&once);
>>> -    }
>>> -
>>> -    return user_hz;
>>> -}
>>> -
>>> -static void
>>> -nl_parse_tcf(const struct tcf_t *tm, struct tc_flower *flower)
>>> -{
>>> -    flower->lastused = time_msec() - (tm->lastuse * 1000 / get_user_hz());
>>> -}
>>> -
>>>    static int
>>>    nl_parse_act_gact(struct nlattr *options, struct tc_flower *flower)
>>>    {
>>> @@ -1394,18 +1407,21 @@ nl_parse_act_mirred(struct nlattr *options, struct tc_flower *flower)
>>>     static const struct nl_policy ct_policy[] = {
>>>        [TCA_CT_PARMS] = { .type = NL_A_UNSPEC,
>>> -                              .min_len = sizeof(struct tc_ct),
>>> -                              .optional = false, },
>>> +                       .min_len = sizeof(struct tc_ct),
>>> +                       .optional = false, },
>>> +    [TCA_CT_TM] = { .type = NL_A_UNSPEC,
>>> +                    .min_len = sizeof(struct tcf_t),
>>> +                    .optional = false, },
>>>        [TCA_CT_ACTION] = { .type = NL_A_U16,
>>> -                         .optional = true, },
>>> +                        .optional = true, },
>>>        [TCA_CT_ZONE] = { .type = NL_A_U16,
>>>                          .optional = true, },
>>>        [TCA_CT_MARK] = { .type = NL_A_U32,
>>>                           .optional = true, },
>>>        [TCA_CT_MARK_MASK] = { .type = NL_A_U32,
>>> -                            .optional = true, },
>>> +                           .optional = true, },
>>>        [TCA_CT_LABELS] = { .type = NL_A_UNSPEC,
>>> -                         .optional = true, },
>>> +                        .optional = true, },
>>>        [TCA_CT_LABELS_MASK] = { .type = NL_A_UNSPEC,
>>>                                  .optional = true, },
>>>        [TCA_CT_NAT_IPV4_MIN] = { .type = NL_A_U32,
>>> @@ -1517,6 +1533,9 @@ nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower)
>>>        }
>>>        action->type = TC_ACT_CT;
>>>   +    nl_parse_tcf(nl_attr_get_unspec(ct_attrs[TCA_CT_TM],
>>> +                                    sizeof(struct tcf_t)),
>>> +                 flower);
>>>        return 0;
>>>    }
>>>   @@ -1524,6 +1543,9 @@ static const struct nl_policy vlan_policy[] = {
>>>        [TCA_VLAN_PARMS] = { .type = NL_A_UNSPEC,
>>>                             .min_len = sizeof(struct tc_vlan),
>>>                             .optional = false, },
>>> +    [TCA_VLAN_TM] = { .type = NL_A_UNSPEC,
>>> +                      .min_len = sizeof(struct tcf_t),
>>> +                      .optional = false, },
>>>        [TCA_VLAN_PUSH_VLAN_ID] = { .type = NL_A_U16, .optional = true, },
>>>        [TCA_VLAN_PUSH_VLAN_PROTOCOL] = { .type = NL_A_U16, .optional = true, },
>>>        [TCA_VLAN_PUSH_VLAN_PRIORITY] = { .type = NL_A_U8, .optional = true, },
>>> @@ -1562,6 +1584,10 @@ nl_parse_act_vlan(struct nlattr *options, struct tc_flower *flower)
>>>                        v->action, v->v_action);
>>>            return EINVAL;
>>>        }
>>> +
>>> +    nl_parse_tcf(nl_attr_get_unspec(vlan_attrs[TCA_VLAN_TM],
>>> +                                    sizeof(struct tcf_t)),
>>> +                 flower);
>>>        return 0;
>>>    }
>>>   @@ -1569,6 +1595,9 @@ static const struct nl_policy mpls_policy[] = {
>>>        [TCA_MPLS_PARMS] = { .type = NL_A_UNSPEC,
>>>                             .min_len = sizeof(struct tc_mpls),
>>>                             .optional = false, },
>>> +    [TCA_MPLS_TM] = { .type = NL_A_UNSPEC,
>>> +                      .min_len = sizeof(struct tcf_t),
>>> +                      .optional = false, },
>>>        [TCA_MPLS_PROTO] = { .type = NL_A_U16, .optional = true, },
>>>        [TCA_MPLS_LABEL] = { .type = NL_A_U32, .optional = true, },
>>>        [TCA_MPLS_TC] = { .type = NL_A_U8, .optional = true, },
>>> @@ -1655,6 +1684,9 @@ nl_parse_act_mpls(struct nlattr *options, struct tc_flower *flower)
>>>            return EINVAL;
>>>        }
>>>   +    nl_parse_tcf(nl_attr_get_unspec(mpls_attrs[TCA_MPLS_TM],
>>> +                                    sizeof(struct tcf_t)),
>>> +                 flower);
>>>        return 0;
>>>    }
>>>   @@ -1662,6 +1694,9 @@ static const struct nl_policy csum_policy[] = {
>>>        [TCA_CSUM_PARMS] = { .type = NL_A_UNSPEC,
>>>                             .min_len = sizeof(struct tc_csum),
>>>                             .optional = false, },
>>> +    [TCA_CSUM_TM] = { .type = NL_A_UNSPEC,
>>> +                      .min_len = sizeof(struct tcf_t),
>>> +                      .optional = false, },
>>>    };
>>>     static int
>>> @@ -1695,6 +1730,9 @@ nl_parse_act_csum(struct nlattr *options, struct tc_flower *flower)
>>>            return EINVAL;
>>>        }
>>>   +    nl_parse_tcf(nl_attr_get_unspec(csum_attrs[TCA_CSUM_TM],
>>> +                                    sizeof(struct tcf_t)),
>>> +                 flower);
>>>        return 0;
>>>    }
>>>   @@ -1713,7 +1751,7 @@ static const struct nl_policy stats_policy[] = {
>>>     static int
>>>    nl_parse_single_action(struct nlattr *action, struct tc_flower *flower,
>>> -                       bool terse)
>>> +                       bool terse, bool update_stats)
>>>    {
>>>        struct nlattr *act_options;
>>>        struct nlattr *act_stats;
>>> @@ -1779,7 +1817,7 @@ nl_parse_single_action(struct nlattr *action, struct tc_flower *flower,
>>>        }
>>>         bs = nl_attr_get_unspec(stats_attrs[TCA_STATS_BASIC], sizeof *bs);
>>> -    if (bs->packets) {
>>> +    if (bs->packets && update_stats) {
>>>            put_32aligned_u64(&stats->n_packets, bs->packets);
>>>            put_32aligned_u64(&stats->n_bytes, bs->bytes);
>>>        }
>>> @@ -1797,6 +1835,8 @@ nl_parse_flower_actions(struct nlattr **attrs, struct tc_flower *flower,
>>>        static struct nl_policy actions_orders_policy[TCA_ACT_MAX_NUM + 1] = {};
>>>        struct nlattr *actions_orders[ARRAY_SIZE(actions_orders_policy)];
>>>        const int max_size = ARRAY_SIZE(actions_orders_policy);
>>> +    bool first_action = true;
>>> +    uint64_t lastused = 0;
>>>         for (int i = TCA_ACT_MIN_PRIO; i < max_size; i++) {
>>>            actions_orders_policy[i].type = NL_A_NESTED;
>>> @@ -1817,14 +1857,27 @@ nl_parse_flower_actions(struct nlattr **attrs, struct tc_flower *flower,
>>>                    VLOG_DBG_RL(&error_rl, "Can only support %d actions", TCA_ACT_MAX_NUM);
>>>                    return EOPNOTSUPP;
>>>                }
>>> -            err = nl_parse_single_action(actions_orders[i], flower, terse);
>>> +            err = nl_parse_single_action(actions_orders[i], flower, terse,
>>> +                                         first_action);
>>>                 if (err) {
>>>                    return err;
>>>                }
>>> +
>>> +            if (first_action) {
>>> +                /* We need to store the last used timestamp from the first
>>> +                 * action, i.e., the action that was used the track the
>>> +                 * statistics, as all remaining action decoding overwrite
>>> +                 * this value. We also need to use the first actions statistics
>>> +                 * as conditional actions can exist. */
>>> +                lastused = flower->lastused;
>>> +                first_action = false;
>>> +            }
>>>            }
>>>        }
>>>   +    flower->lastused = lastused;
>>> +
>>>        if (flower->csum_update_flags) {
>>>            VLOG_WARN_RL(&error_rl,
>>>                         "expected act csum with flags: 0x%x",
>>>
>>> _______________________________________________
>>> dev mailing list
>>> dev@openvswitch.org
>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
diff mbox series

Patch

diff --git a/lib/tc.c b/lib/tc.c
index b37dce22f..b430f1bad 100644
--- a/lib/tc.c
+++ b/lib/tc.c
@@ -994,12 +994,35 @@  nl_parse_flower_flags(struct nlattr **attrs, struct tc_flower *flower)
     flower->offloaded_state = nl_get_flower_offloaded_state(attrs);
 }
 
+static int
+get_user_hz(void)
+{
+    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+    static int user_hz = 100;
+
+    if (ovsthread_once_start(&once)) {
+        user_hz = sysconf(_SC_CLK_TCK);
+        ovsthread_once_done(&once);
+    }
+
+    return user_hz;
+}
+
+static void
+nl_parse_tcf(const struct tcf_t *tm, struct tc_flower *flower)
+{
+    flower->lastused = time_msec() - (tm->lastuse * 1000 / get_user_hz());
+}
+
 static const struct nl_policy pedit_policy[] = {
-            [TCA_PEDIT_PARMS_EX] = { .type = NL_A_UNSPEC,
-                                     .min_len = sizeof(struct tc_pedit),
-                                     .optional = false, },
-            [TCA_PEDIT_KEYS_EX]   = { .type = NL_A_NESTED,
-                                      .optional = false, },
+    [TCA_PEDIT_TM] = { .type = NL_A_UNSPEC,
+                       .min_len = sizeof(struct tcf_t),
+                       .optional = false, },
+    [TCA_PEDIT_PARMS_EX] = { .type = NL_A_UNSPEC,
+                             .min_len = sizeof(struct tc_pedit),
+                             .optional = false, },
+    [TCA_PEDIT_KEYS_EX] = { .type = NL_A_NESTED,
+                            .optional = false, },
 };
 
 static int
@@ -1094,6 +1117,9 @@  nl_parse_act_pedit(struct nlattr *options, struct tc_flower *flower)
     action = &flower->actions[flower->action_count++];
     action->type = TC_ACT_PEDIT;
 
+    nl_parse_tcf(nl_attr_get_unspec(pe_attrs[TCA_PEDIT_TM],
+                                    sizeof(struct tcf_t)),
+                 flower);
     return 0;
 }
 
@@ -1101,6 +1127,9 @@  static const struct nl_policy tunnel_key_policy[] = {
     [TCA_TUNNEL_KEY_PARMS] = { .type = NL_A_UNSPEC,
                                .min_len = sizeof(struct tc_tunnel_key),
                                .optional = false, },
+    [TCA_TUNNEL_KEY_TM] = { .type = NL_A_UNSPEC,
+                            .min_len = sizeof(struct tcf_t),
+                            .optional = false, },
     [TCA_TUNNEL_KEY_ENC_IPV4_SRC] = { .type = NL_A_U32, .optional = true, },
     [TCA_TUNNEL_KEY_ENC_IPV4_DST] = { .type = NL_A_U32, .optional = true, },
     [TCA_TUNNEL_KEY_ENC_IPV6_SRC] = { .type = NL_A_UNSPEC,
@@ -1275,6 +1304,10 @@  nl_parse_act_tunnel_key(struct nlattr *options, struct tc_flower *flower)
                     tun->action, tun->t_action);
         return EINVAL;
     }
+
+    nl_parse_tcf(nl_attr_get_unspec(tun_attrs[TCA_TUNNEL_KEY_TM],
+                                    sizeof(struct tcf_t)),
+                 flower);
     return 0;
 }
 
@@ -1287,26 +1320,6 @@  static const struct nl_policy gact_policy[] = {
                       .optional = false, },
 };
 
-static int
-get_user_hz(void)
-{
-    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
-    static int user_hz = 100;
-
-    if (ovsthread_once_start(&once)) {
-        user_hz = sysconf(_SC_CLK_TCK);
-        ovsthread_once_done(&once);
-    }
-
-    return user_hz;
-}
-
-static void
-nl_parse_tcf(const struct tcf_t *tm, struct tc_flower *flower)
-{
-    flower->lastused = time_msec() - (tm->lastuse * 1000 / get_user_hz());
-}
-
 static int
 nl_parse_act_gact(struct nlattr *options, struct tc_flower *flower)
 {
@@ -1394,18 +1407,21 @@  nl_parse_act_mirred(struct nlattr *options, struct tc_flower *flower)
 
 static const struct nl_policy ct_policy[] = {
     [TCA_CT_PARMS] = { .type = NL_A_UNSPEC,
-                              .min_len = sizeof(struct tc_ct),
-                              .optional = false, },
+                       .min_len = sizeof(struct tc_ct),
+                       .optional = false, },
+    [TCA_CT_TM] = { .type = NL_A_UNSPEC,
+                    .min_len = sizeof(struct tcf_t),
+                    .optional = false, },
     [TCA_CT_ACTION] = { .type = NL_A_U16,
-                         .optional = true, },
+                        .optional = true, },
     [TCA_CT_ZONE] = { .type = NL_A_U16,
                       .optional = true, },
     [TCA_CT_MARK] = { .type = NL_A_U32,
                        .optional = true, },
     [TCA_CT_MARK_MASK] = { .type = NL_A_U32,
-                            .optional = true, },
+                           .optional = true, },
     [TCA_CT_LABELS] = { .type = NL_A_UNSPEC,
-                         .optional = true, },
+                        .optional = true, },
     [TCA_CT_LABELS_MASK] = { .type = NL_A_UNSPEC,
                               .optional = true, },
     [TCA_CT_NAT_IPV4_MIN] = { .type = NL_A_U32,
@@ -1517,6 +1533,9 @@  nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower)
     }
     action->type = TC_ACT_CT;
 
+    nl_parse_tcf(nl_attr_get_unspec(ct_attrs[TCA_CT_TM],
+                                    sizeof(struct tcf_t)),
+                 flower);
     return 0;
 }
 
@@ -1524,6 +1543,9 @@  static const struct nl_policy vlan_policy[] = {
     [TCA_VLAN_PARMS] = { .type = NL_A_UNSPEC,
                          .min_len = sizeof(struct tc_vlan),
                          .optional = false, },
+    [TCA_VLAN_TM] = { .type = NL_A_UNSPEC,
+                      .min_len = sizeof(struct tcf_t),
+                      .optional = false, },
     [TCA_VLAN_PUSH_VLAN_ID] = { .type = NL_A_U16, .optional = true, },
     [TCA_VLAN_PUSH_VLAN_PROTOCOL] = { .type = NL_A_U16, .optional = true, },
     [TCA_VLAN_PUSH_VLAN_PRIORITY] = { .type = NL_A_U8, .optional = true, },
@@ -1562,6 +1584,10 @@  nl_parse_act_vlan(struct nlattr *options, struct tc_flower *flower)
                     v->action, v->v_action);
         return EINVAL;
     }
+
+    nl_parse_tcf(nl_attr_get_unspec(vlan_attrs[TCA_VLAN_TM],
+                                    sizeof(struct tcf_t)),
+                 flower);
     return 0;
 }
 
@@ -1569,6 +1595,9 @@  static const struct nl_policy mpls_policy[] = {
     [TCA_MPLS_PARMS] = { .type = NL_A_UNSPEC,
                          .min_len = sizeof(struct tc_mpls),
                          .optional = false, },
+    [TCA_MPLS_TM] = { .type = NL_A_UNSPEC,
+                      .min_len = sizeof(struct tcf_t),
+                      .optional = false, },
     [TCA_MPLS_PROTO] = { .type = NL_A_U16, .optional = true, },
     [TCA_MPLS_LABEL] = { .type = NL_A_U32, .optional = true, },
     [TCA_MPLS_TC] = { .type = NL_A_U8, .optional = true, },
@@ -1655,6 +1684,9 @@  nl_parse_act_mpls(struct nlattr *options, struct tc_flower *flower)
         return EINVAL;
     }
 
+    nl_parse_tcf(nl_attr_get_unspec(mpls_attrs[TCA_MPLS_TM],
+                                    sizeof(struct tcf_t)),
+                 flower);
     return 0;
 }
 
@@ -1662,6 +1694,9 @@  static const struct nl_policy csum_policy[] = {
     [TCA_CSUM_PARMS] = { .type = NL_A_UNSPEC,
                          .min_len = sizeof(struct tc_csum),
                          .optional = false, },
+    [TCA_CSUM_TM] = { .type = NL_A_UNSPEC,
+                      .min_len = sizeof(struct tcf_t),
+                      .optional = false, },
 };
 
 static int
@@ -1695,6 +1730,9 @@  nl_parse_act_csum(struct nlattr *options, struct tc_flower *flower)
         return EINVAL;
     }
 
+    nl_parse_tcf(nl_attr_get_unspec(csum_attrs[TCA_CSUM_TM],
+                                    sizeof(struct tcf_t)),
+                 flower);
     return 0;
 }
 
@@ -1713,7 +1751,7 @@  static const struct nl_policy stats_policy[] = {
 
 static int
 nl_parse_single_action(struct nlattr *action, struct tc_flower *flower,
-                       bool terse)
+                       bool terse, bool update_stats)
 {
     struct nlattr *act_options;
     struct nlattr *act_stats;
@@ -1779,7 +1817,7 @@  nl_parse_single_action(struct nlattr *action, struct tc_flower *flower,
     }
 
     bs = nl_attr_get_unspec(stats_attrs[TCA_STATS_BASIC], sizeof *bs);
-    if (bs->packets) {
+    if (bs->packets && update_stats) {
         put_32aligned_u64(&stats->n_packets, bs->packets);
         put_32aligned_u64(&stats->n_bytes, bs->bytes);
     }
@@ -1797,6 +1835,8 @@  nl_parse_flower_actions(struct nlattr **attrs, struct tc_flower *flower,
     static struct nl_policy actions_orders_policy[TCA_ACT_MAX_NUM + 1] = {};
     struct nlattr *actions_orders[ARRAY_SIZE(actions_orders_policy)];
     const int max_size = ARRAY_SIZE(actions_orders_policy);
+    bool first_action = true;
+    uint64_t lastused = 0;
 
     for (int i = TCA_ACT_MIN_PRIO; i < max_size; i++) {
         actions_orders_policy[i].type = NL_A_NESTED;
@@ -1817,14 +1857,27 @@  nl_parse_flower_actions(struct nlattr **attrs, struct tc_flower *flower,
                 VLOG_DBG_RL(&error_rl, "Can only support %d actions", TCA_ACT_MAX_NUM);
                 return EOPNOTSUPP;
             }
-            err = nl_parse_single_action(actions_orders[i], flower, terse);
+            err = nl_parse_single_action(actions_orders[i], flower, terse,
+                                         first_action);
 
             if (err) {
                 return err;
             }
+
+            if (first_action) {
+                /* We need to store the last used timestamp from the first
+                 * action, i.e., the action that was used the track the
+                 * statistics, as all remaining action decoding overwrite
+                 * this value. We also need to use the first actions statistics
+                 * as conditional actions can exist. */
+                lastused = flower->lastused;
+                first_action = false;
+            }
         }
     }
 
+    flower->lastused = lastused;
+
     if (flower->csum_update_flags) {
         VLOG_WARN_RL(&error_rl,
                      "expected act csum with flags: 0x%x",