diff mbox series

[ovs-dev,v3,3/5] netdev-offload-tc: Handle check_pkt_len datapath action.

Message ID 165271544707.991159.3800315046299067355.stgit@ebuild
State Superseded
Headers show
Series netdev-offload-tc: Add support for the check_pkt_len action. | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test success github build: passed
ovsrobot/intel-ovs-compilation success test: success

Commit Message

Eelco Chaudron May 16, 2022, 3:37 p.m. UTC
This change implements support for the check_pkt_len
action using the TC police action, which supports MTU
checking.

Signed-off-by: Eelco Chaudron <echaudro@redhat.com>
Acked-by: Mike Pattrick <mkp@redhat.com>
---
v3:
  Was using TCA_CSUM_PARMS instead of TCA_POLICE_TBF.

 lib/netdev-offload-tc.c |  171 +++++++++++++++++-
 lib/tc.c                |  455 +++++++++++++++++++++++++++++++++++++++++++----
 lib/tc.h                |   12 +
 3 files changed, 594 insertions(+), 44 deletions(-)

Comments

Roi Dayan June 1, 2022, 10:15 a.m. UTC | #1
On 2022-05-16 6:37 PM, Eelco Chaudron wrote:
> This change implements support for the check_pkt_len
> action using the TC police action, which supports MTU
> checking.
> 
> Signed-off-by: Eelco Chaudron <echaudro@redhat.com>
> Acked-by: Mike Pattrick <mkp@redhat.com>
> ---
> v3:
>    Was using TCA_CSUM_PARMS instead of TCA_POLICE_TBF.
> 
>   lib/netdev-offload-tc.c |  171 +++++++++++++++++-
>   lib/tc.c                |  455 +++++++++++++++++++++++++++++++++++++++++++----
>   lib/tc.h                |   12 +
>   3 files changed, 594 insertions(+), 44 deletions(-)
> 
> diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c
> index f657af0fd..9b523c5f5 100644
> --- a/lib/netdev-offload-tc.c
> +++ b/lib/netdev-offload-tc.c
> @@ -62,6 +62,13 @@ struct chain_node {
>       uint32_t chain;
>   };
>   
> +static int
> +netdev_tc_parse_nl_actions(struct netdev *netdev, struct tc_flower *flower,
> +                           struct offload_info *info,
> +                           const struct nlattr *actions, size_t actions_len,
> +                           bool *recirc_act, bool more_actions,
> +                           struct tc_action **need_jump_update);
> +
>   static bool
>   is_internal_port(const char *type)
>   {
> @@ -825,6 +832,45 @@ _parse_tc_flower_to_actions(struct tc_flower *flower, struct ofpbuf *buf,
>               nl_msg_put_u32(buf, OVS_ACTION_ATTR_RECIRC, action->chain);
>           }
>           break;
> +        case TC_ACT_POLICE_MTU: {
> +            size_t offset, act_offset;
> +            uint32_t jump;
> +
> +            offset = nl_msg_start_nested(buf,
> +                                         OVS_ACTION_ATTR_CHECK_PKT_LEN);
> +
> +            nl_msg_put_u16(buf, OVS_CHECK_PKT_LEN_ATTR_PKT_LEN,
> +                           action->police.mtu);
> +
> +            act_offset = nl_msg_start_nested(
> +                buf, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER);
> +            i = _parse_tc_flower_to_actions(flower, buf, i + 1,
> +                                            action->police.result_jump);
> +            nl_msg_end_nested(buf, act_offset);
> +
> +            act_offset = nl_msg_start_nested(
> +                buf, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL);
> +
> +            jump = flower->actions[i - 1].jump_action;
> +            if (jump == JUMP_ACTION_STOP) {
> +                jump = max_index;
> +            }
> +            if (jump != 0) {
> +                i = _parse_tc_flower_to_actions(flower, buf, i, jump);
> +            }
> +            nl_msg_end_nested(buf, act_offset);
> +
> +            i--;
> +            nl_msg_end_nested(buf, offset);
> +        }
> +        break;
> +        }
> +
> +        if (action->jump_action && action->type != TC_ACT_POLICE_MTU) {
> +            /* If there is a jump, it means this was the end of an action
> +             * set and we need to end this branch. */
> +            i++;
> +            break;
>           }
>       }
>       return i;
> @@ -1584,11 +1630,121 @@ parse_match_ct_state_to_flower(struct tc_flower *flower, struct match *match)
>       }
>   }
>   
> +
> +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 tc_action **need_jump_update,
> +                           bool *recirc_act)
> +{
> +    struct tc_action *ge_jump_update = NULL, *le_jump_update = NULL;
> +    const struct nlattr *nl_actions;
> +    int err, le_offset, gt_offset;
> +    uint16_t pkt_len;
> +
> +    static const struct nl_policy ovs_cpl_policy[] = {
> +        [OVS_CHECK_PKT_LEN_ATTR_PKT_LEN] = { .type = NL_A_U16 },
> +        [OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER] = { .type = NL_A_NESTED },
> +        [OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL]
> +            = { .type = NL_A_NESTED },
> +    };
> +    struct nlattr *a[ARRAY_SIZE(ovs_cpl_policy)];
> +
> +    if (!nl_parse_nested(nla, ovs_cpl_policy, a, ARRAY_SIZE(a))) {
> +        VLOG_INFO("Received invalid formatted OVS_ACTION_ATTR_CHECK_PKT_LEN!");
> +        return EOPNOTSUPP;
> +    }
> +
> +    if (!a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER] ||
> +        !a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL]) {
> +        VLOG_INFO("Received invalid OVS_CHECK_PKT_LEN_ATTR_ACTION_IF_*!");
> +        return EOPNOTSUPP;
> +    }
> +
> +    pkt_len = nl_attr_get_u16(a[OVS_CHECK_PKT_LEN_ATTR_PKT_LEN]);
> +
> +    /* Add the police mtu action first in the allocated slot. */
> +    action->police.mtu = pkt_len;
> +    action->type = TC_ACT_POLICE_MTU;
> +    le_offset = flower->action_count++;
> +
> +    /* Parse and add the greater than action(s).
> +     * 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,
> +                                     nl_attr_get(nl_actions),
> +                                     nl_attr_get_size(nl_actions),
> +                                     recirc_act, !last_action,
> +                                     &ge_jump_update);
> +    if (err) {
> +        return err;
> +    }
> +
> +    /* Update goto offset for le actions */
> +    flower->actions[le_offset].police.result_jump = flower->action_count;
> +
> +    gt_offset = flower->action_count;
> +
> +    /* 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,
> +                                     nl_attr_get(nl_actions),
> +                                     nl_attr_get_size(nl_actions),
> +                                     recirc_act, !last_action,
> +                                     &le_jump_update);
> +
> +    if (gt_offset == flower->action_count && last_action) {
> +        /* No le actions where added, fix gt offset */
> +        flower->actions[le_offset].police.result_jump = JUMP_ACTION_STOP;
> +    }
> +
> +    /* Update goto offset for gt actions to skip the le ones. */
> +    if (last_action) {
> +        flower->actions[gt_offset - 1].jump_action = JUMP_ACTION_STOP;
> +
> +        if (need_jump_update) {
> +            *need_jump_update = NULL;
> +        }
> +    } else {
> +        if (gt_offset == flower->action_count) {
> +            flower->actions[gt_offset - 1].jump_action = 0;
> +        } else {
> +            flower->actions[gt_offset - 1].jump_action = flower->action_count;
> +        }
> +        /* If we have nested if() else () the if actions jump over the else
> +         * and will end-up in the outer else () case, which it should have
> +         * skipped. To void this we return the "potential" inner if() goto to
> +         * need_jump_update, so it can be updated on return!
> +         */
> +        if (need_jump_update) {
> +            *need_jump_update = &flower->actions[gt_offset - 1];
> +        }
> +    }
> +
> +    if (le_jump_update != NULL) {
> +        le_jump_update->jump_action =
> +            flower->actions[gt_offset - 1].jump_action;
> +    }
> +    if (ge_jump_update != NULL) {
> +        ge_jump_update->jump_action =
> +            flower->actions[gt_offset - 1].jump_action;
> +    }
> +
> +    if (err) {
> +        return err;
> +    }
> +
> +    return 0;
> +}
> +
>   static int
>   netdev_tc_parse_nl_actions(struct netdev *netdev, struct tc_flower *flower,
>                              struct offload_info *info,
>                              const struct nlattr *actions, size_t actions_len,
> -                           bool *recirc_act)
> +                           bool *recirc_act, bool more_actions,
> +                           struct tc_action **need_jump_update)
>   {
>       static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
>       const struct nlattr *nla;
> @@ -1708,6 +1864,16 @@ netdev_tc_parse_nl_actions(struct netdev *netdev, struct tc_flower *flower,
>               action->type = TC_ACT_GOTO;
>               action->chain = 0;  /* 0 is reserved and not used by recirc. */
>               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,
> +                                             nl_attr_len_pad(nla,
> +                                                             left) >= left
> +                                             && !more_actions,
> +                                             need_jump_update,
> +                                             recirc_act);
> +            if (err) {
> +                return err;
> +            }
>           } else {
>               VLOG_DBG_RL(&rl, "unsupported put action type: %d",
>                           nl_attr_type(nla));
> @@ -1980,7 +2146,8 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
>   
>       /* Parse all (nested) actions. */
>       err = netdev_tc_parse_nl_actions(netdev, &flower, info,
> -                                     actions, actions_len, &recirc_act);
> +                                     actions, actions_len, &recirc_act,
> +                                     false, NULL);
>       if (err) {
>           return err;
>       }
> diff --git a/lib/tc.c b/lib/tc.c
> index df73a43d4..bdaa764f9 100644
> --- a/lib/tc.c
> +++ b/lib/tc.c
> @@ -994,6 +994,18 @@ nl_parse_flower_flags(struct nlattr **attrs, struct tc_flower *flower)
>       flower->offloaded_state = nl_get_flower_offloaded_state(attrs);
>   }
>   
> +static void
> +nl_parse_action_pc(uint32_t action_pc, struct tc_action *action)
> +{
> +    if (action_pc == TC_ACT_STOLEN) {
> +        action->jump_action = JUMP_ACTION_STOP;
> +    } else if (action_pc & TC_ACT_JUMP) {
> +        action->jump_action = action_pc & TC_ACT_EXT_VAL_MASK;
> +    } else {
> +        action->jump_action = 0;
> +    }
> +}
> +
>   static const struct nl_policy pedit_policy[] = {
>               [TCA_PEDIT_PARMS_EX] = { .type = NL_A_UNSPEC,
>                                        .min_len = sizeof(struct tc_pedit),
> @@ -1093,6 +1105,7 @@ nl_parse_act_pedit(struct nlattr *options, struct tc_flower *flower)
>   
>       action->type = TC_ACT_PEDIT;
>   
> +    nl_parse_action_pc(pe->action, action);
>       return 0;
>   }
>   
> @@ -1267,6 +1280,7 @@ nl_parse_act_tunnel_key(struct nlattr *options, struct tc_flower *flower)
>           if (err) {
>               return err;
>           }
> +        nl_parse_action_pc(tun->action, action);
>       } else if (tun->t_action == TCA_TUNNEL_KEY_ACT_RELEASE) {
>           flower->tunnel = true;
>       } else {
> @@ -1303,7 +1317,11 @@ get_user_hz(void)
>   static void
>   nl_parse_tcf(const struct tcf_t *tm, struct tc_flower *flower)
>   {
> -    flower->lastused = time_msec() - (tm->lastuse * 1000 / get_user_hz());
> +    uint64_t lastused = time_msec() - (tm->lastuse * 1000 / get_user_hz());
> +
> +    if (flower->lastused < lastused) {
> +        flower->lastused = lastused;
> +    }
>   }
>   
>   static int
> @@ -1328,6 +1346,7 @@ nl_parse_act_gact(struct nlattr *options, struct tc_flower *flower)
>           action = &flower->actions[flower->action_count++];
>           action->chain = p->action & TC_ACT_EXT_VAL_MASK;
>           action->type = TC_ACT_GOTO;
> +        nl_parse_action_pc(p->action, action);
>       } else if (p->action != TC_ACT_SHOT) {
>           VLOG_ERR_RL(&error_rl, "unknown gact action: %d", p->action);
>           return EINVAL;
> @@ -1386,8 +1405,9 @@ nl_parse_act_mirred(struct nlattr *options, struct tc_flower *flower)
>   
>       mirred_tm = mirred_attrs[TCA_MIRRED_TM];
>       tm = nl_attr_get_unspec(mirred_tm, sizeof *tm);
> -    nl_parse_tcf(tm, flower);
>   
> +    nl_parse_tcf(tm, flower);
> +    nl_parse_action_pc(m->action, action);
>       return 0;
>   }
>   
> @@ -1516,6 +1536,7 @@ nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower)
>       }
>       action->type = TC_ACT_CT;
>   
> +    nl_parse_action_pc(ct->action, action);
>       return 0;
>   }
>   
> @@ -1561,6 +1582,8 @@ nl_parse_act_vlan(struct nlattr *options, struct tc_flower *flower)
>                       v->action, v->v_action);
>           return EINVAL;
>       }
> +
> +    nl_parse_action_pc(v->action, action);
>       return 0;
>   }
>   
> @@ -1654,6 +1677,7 @@ nl_parse_act_mpls(struct nlattr *options, struct tc_flower *flower)
>           return EINVAL;
>       }
>   
> +    nl_parse_action_pc(m->action, action);
>       return 0;
>   }
>   
> @@ -1694,9 +1718,60 @@ nl_parse_act_csum(struct nlattr *options, struct tc_flower *flower)
>           return EINVAL;
>       }
>   
> +    /* The action_pc should be set on the previous action. */
> +    if (flower->action_count < TCA_ACT_MAX_NUM) {
> +        struct tc_action *action = &flower->actions[flower->action_count];
> +
> +        nl_parse_action_pc(c->action, action);
> +    }
>       return 0;
>   }
>   
> +static const struct nl_policy police_policy[] = {
> +    [TCA_POLICE_TBF] = { .type = NL_A_UNSPEC,
> +                         .min_len = sizeof(struct tc_police),
> +                         .optional = false, },
> +    [TCA_POLICE_RESULT] = { .type = NL_A_U32, .optional = false, },
> +    [TCA_POLICE_TM]     = { .type = NL_A_UNSPEC,
> +                            .min_len = sizeof(struct tcf_t),
> +                            .optional = false, },
> +};
> +
> +static int
> +nl_parse_act_police_mtu(struct nlattr *options, struct tc_flower *flower)
> +{
> +    struct nlattr *police_attrs[ARRAY_SIZE(police_policy)];
> +    const struct tc_police *police;
> +    struct nlattr *police_result;
> +    struct tc_action *action;
> +    const struct tcf_t *tm;
> +    uint32_t action_pc;
> +
> +    if (!nl_parse_nested(options, police_policy, police_attrs,
> +                         ARRAY_SIZE(police_policy))) {
> +        VLOG_ERR_RL(&error_rl, "Failed to parse police action options");
> +        return EPROTO;
> +    }
> +    police = nl_attr_get_unspec(police_attrs[TCA_POLICE_TBF], sizeof *police);
> +    police_result = police_attrs[TCA_POLICE_RESULT];
> +
> +    action = &flower->actions[flower->action_count++];
> +    action->type = TC_ACT_POLICE_MTU;
> +    action_pc = nl_attr_get_u32(police_result);
> +    if (action_pc & TC_ACT_JUMP) {
> +        action->police.result_jump = action_pc & TC_ACT_EXT_VAL_MASK;
> +    } else {
> +        action->police.result_jump = JUMP_ACTION_STOP;
> +    }
> +    action->police.mtu = police->mtu;
> +
> +    tm = nl_attr_get_unspec(police_attrs[TCA_POLICE_TM], sizeof *tm);
> +    nl_parse_tcf(tm, flower);
> +    nl_parse_action_pc(police->action, action);
> +    return 0;
> +}
> +
> +
>   static const struct nl_policy act_policy[] = {
>       [TCA_ACT_KIND] = { .type = NL_A_STRING, .optional = false, },
>       [TCA_ACT_COOKIE] = { .type = NL_A_UNSPEC, .optional = true, },
> @@ -1715,7 +1790,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 *csum)
>   {
>       struct nlattr *act_options;
>       struct nlattr *act_stats;
> @@ -1737,6 +1812,7 @@ nl_parse_single_action(struct nlattr *action, struct tc_flower *flower,
>           return EPROTO;
>       }
>   
> +    *csum = false;
>       act_kind = nl_attr_get_string(action_attrs[TCA_ACT_KIND]);
>       act_options = action_attrs[TCA_ACT_OPTIONS];
>       act_cookie = action_attrs[TCA_ACT_COOKIE];
> @@ -1757,10 +1833,13 @@ nl_parse_single_action(struct nlattr *action, struct tc_flower *flower,
>           err = nl_parse_act_pedit(act_options, flower);
>       } else if (!strcmp(act_kind, "csum")) {
>           nl_parse_act_csum(act_options, flower);
> +        *csum = true;
>       } else if (!strcmp(act_kind, "skbedit")) {
>           /* Added for TC rule only (not in OvS rule) so ignore. */
>       } else if (!strcmp(act_kind, "ct")) {
>           nl_parse_act_ct(act_options, flower);
> +    } else if (!strcmp(act_kind, "police")) {
> +        nl_parse_act_police_mtu(act_options, flower);
>       } else {
>           VLOG_ERR_RL(&error_rl, "unknown tc action kind: %s", act_kind);
>           err = EINVAL;
> @@ -1818,6 +1897,10 @@ 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);
> +    int previous_action_count = 0;
> +    bool need_jump_adjust = false;
> +    int jump_adjust = 0;
> +    bool csum = false;
>   
>       for (int i = TCA_ACT_MIN_PRIO; i < max_size; i++) {
>           actions_orders_policy[i].type = NL_A_NESTED;
> @@ -1838,7 +1921,70 @@ 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,
> +                                         &csum);
> +
> +            if (flower->action_count == previous_action_count) {
> +
> +                struct tc_action *action;
> +
> +                /* We had no update on the TC action count, which means
> +                 * we had a none TC type action. So need to adjust existing
> +                 * jump offsets. */
> +                jump_adjust++;
> +
> +                if (need_jump_adjust || (csum && flower->action_count > 0)) {
> +
> +                    if (csum && flower->action_count > 0) {
> +                        /* The csum action is special as it might carry
> +                         * a jump count for the previous TC_ACT and therefore
> +                         * should be adjusted with jump_adjust as it got
> +                         * copied. */
> +                        action = &flower->actions[flower->action_count - 1];
> +                        if (action->jump_action
> +                            && action->jump_action != JUMP_ACTION_STOP) {
> +                            action->jump_action -= (jump_adjust - 1);
> +                        }
> +                    }
> +
> +                    for (int j = 0; j < flower->action_count; j++) {
> +                        action = &flower->actions[j];
> +
> +                        if (action->type == TC_ACT_POLICE_MTU
> +                            && action->police.result_jump != JUMP_ACTION_STOP
> +                            && (action->police.result_jump - 1) >
> +                            flower->action_count) {
> +
> +                            action->police.result_jump--;
> +                        }
> +
> +                        if (action->jump_action
> +                            && action->jump_action != JUMP_ACTION_STOP
> +                            && (action->jump_action - 1) >
> +                            flower->action_count) {
> +
> +                            action->jump_action--;
> +                        }
> +                    }
> +                }
> +            } else {
> +                struct tc_action *action;
> +
> +                action = &flower->actions[previous_action_count];
> +                if (action->type == TC_ACT_POLICE_MTU &&
> +                    action->police.result_jump != JUMP_ACTION_STOP) {
> +                    action->police.result_jump -= jump_adjust;
> +                    need_jump_adjust = true;
> +                }
> +
> +                if (action->jump_action
> +                    && action->jump_action != JUMP_ACTION_STOP) {
> +                    action->jump_action -= jump_adjust;
> +                    need_jump_adjust = true;
> +                }
> +
> +                previous_action_count = flower->action_count;
> +            }
>   
>               if (err) {
>                   return err;
> @@ -2031,14 +2177,14 @@ tc_get_tc_cls_policy(enum tc_offload_policy policy)
>   }
>   
>   static void
> -nl_msg_put_act_csum(struct ofpbuf *request, uint32_t flags)
> +nl_msg_put_act_csum(struct ofpbuf *request, uint32_t flags, uint32_t action_pc)
>   {
>       size_t offset;
>   
>       nl_msg_put_string(request, TCA_ACT_KIND, "csum");
>       offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
>       {
> -        struct tc_csum parm = { .action = TC_ACT_PIPE,
> +        struct tc_csum parm = { .action = action_pc,
>                                   .update_flags = flags };
>   
>           nl_msg_put_unspec(request, TCA_CSUM_PARMS, &parm, sizeof parm);
> @@ -2048,7 +2194,7 @@ nl_msg_put_act_csum(struct ofpbuf *request, uint32_t flags)
>   
>   static void
>   nl_msg_put_act_pedit(struct ofpbuf *request, struct tc_pedit *parm,
> -                     struct tc_pedit_key_ex *ex)
> +                     struct tc_pedit_key_ex *ex, uint32_t action_pc)
>   {
>       size_t ksize = sizeof *parm + parm->nkeys * sizeof(struct tc_pedit_key);
>       size_t offset, offset_keys_ex, offset_key;
> @@ -2057,7 +2203,7 @@ nl_msg_put_act_pedit(struct ofpbuf *request, struct tc_pedit *parm,
>       nl_msg_put_string(request, TCA_ACT_KIND, "pedit");
>       offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
>       {
> -        parm->action = TC_ACT_PIPE;
> +        parm->action = action_pc;
>   
>           nl_msg_put_unspec(request, TCA_PEDIT_PARMS_EX, parm, ksize);
>           offset_keys_ex = nl_msg_start_nested(request, TCA_PEDIT_KEYS_EX);
> @@ -2074,14 +2220,14 @@ nl_msg_put_act_pedit(struct ofpbuf *request, struct tc_pedit *parm,
>   
>   static void
>   nl_msg_put_act_push_vlan(struct ofpbuf *request, ovs_be16 tpid,
> -                         uint16_t vid, uint8_t prio)
> +                         uint16_t vid, uint8_t prio, uint32_t action_pc)
>   {
>       size_t offset;
>   
>       nl_msg_put_string(request, TCA_ACT_KIND, "vlan");
>       offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
>       {
> -        struct tc_vlan parm = { .action = TC_ACT_PIPE,
> +        struct tc_vlan parm = { .action = action_pc,
>                                   .v_action = TCA_VLAN_ACT_PUSH };
>   
>           nl_msg_put_unspec(request, TCA_VLAN_PARMS, &parm, sizeof parm);
> @@ -2093,14 +2239,14 @@ nl_msg_put_act_push_vlan(struct ofpbuf *request, ovs_be16 tpid,
>   }
>   
>   static void
> -nl_msg_put_act_pop_vlan(struct ofpbuf *request)
> +nl_msg_put_act_pop_vlan(struct ofpbuf *request, uint32_t action_pc)
>   {
>       size_t offset;
>   
>       nl_msg_put_string(request, TCA_ACT_KIND, "vlan");
>       offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
>       {
> -        struct tc_vlan parm = { .action = TC_ACT_PIPE,
> +        struct tc_vlan parm = { .action = action_pc,
>                                   .v_action = TCA_VLAN_ACT_POP };
>   
>           nl_msg_put_unspec(request, TCA_VLAN_PARMS, &parm, sizeof parm);
> @@ -2109,14 +2255,15 @@ nl_msg_put_act_pop_vlan(struct ofpbuf *request)
>   }
>   
>   static void
> -nl_msg_put_act_pop_mpls(struct ofpbuf *request, ovs_be16 proto)
> +nl_msg_put_act_pop_mpls(struct ofpbuf *request, ovs_be16 proto,
> +                        uint32_t action_pc)
>   {
>       size_t offset;
>   
>       nl_msg_put_string(request, TCA_ACT_KIND, "mpls");
>       offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED);
>       {
> -        struct tc_mpls parm = { .action = TC_ACT_PIPE,
> +        struct tc_mpls parm = { .action = action_pc,
>                                   .m_action = TCA_MPLS_ACT_POP };
>   
>           nl_msg_put_unspec(request, TCA_MPLS_PARMS, &parm, sizeof parm);
> @@ -2127,14 +2274,15 @@ nl_msg_put_act_pop_mpls(struct ofpbuf *request, ovs_be16 proto)
>   
>   static void
>   nl_msg_put_act_push_mpls(struct ofpbuf *request, ovs_be16 proto,
> -                         uint32_t label, uint8_t tc, uint8_t ttl, uint8_t bos)
> +                         uint32_t label, uint8_t tc, uint8_t ttl, uint8_t bos,
> +                         uint32_t action_pc)
>   {
>       size_t offset;
>   
>       nl_msg_put_string(request, TCA_ACT_KIND, "mpls");
>       offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED);
>       {
> -        struct tc_mpls parm = { .action = TC_ACT_PIPE,
> +        struct tc_mpls parm = { .action = action_pc,
>                                   .m_action = TCA_MPLS_ACT_PUSH };
>   
>           nl_msg_put_unspec(request, TCA_MPLS_PARMS, &parm, sizeof parm);
> @@ -2149,14 +2297,14 @@ nl_msg_put_act_push_mpls(struct ofpbuf *request, ovs_be16 proto,
>   
>   static void
>   nl_msg_put_act_set_mpls(struct ofpbuf *request, uint32_t label, uint8_t tc,
> -                        uint8_t ttl, uint8_t bos)
> +                        uint8_t ttl, uint8_t bos, uint32_t action_pc)
>   {
>       size_t offset;
>   
>       nl_msg_put_string(request, TCA_ACT_KIND, "mpls");
>       offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED);
>       {
> -        struct tc_mpls parm = { .action = TC_ACT_PIPE,
> +        struct tc_mpls parm = { .action = action_pc,
>                                   .m_action = TCA_MPLS_ACT_MODIFY };
>   
>           nl_msg_put_unspec(request, TCA_MPLS_PARMS, &parm, sizeof parm);
> @@ -2225,14 +2373,14 @@ nl_msg_put_act_tunnel_key_set(struct ofpbuf *request, bool id_present,
>                                 struct in6_addr *ipv6_dst,
>                                 ovs_be16 tp_dst, uint8_t tos, uint8_t ttl,
>                                 struct tun_metadata tun_metadata,
> -                              uint8_t no_csum)
> +                              uint8_t no_csum, uint32_t action_pc)
>   {
>       size_t offset;
>   
>       nl_msg_put_string(request, TCA_ACT_KIND, "tunnel_key");
>       offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
>       {
> -        struct tc_tunnel_key tun = { .action = TC_ACT_PIPE,
> +        struct tc_tunnel_key tun = { .action = action_pc,
>                                        .t_action = TCA_TUNNEL_KEY_ACT_SET };
>   
>           nl_msg_put_unspec(request, TCA_TUNNEL_KEY_PARMS, &tun, sizeof tun);
> @@ -2285,7 +2433,8 @@ nl_msg_put_act_gact(struct ofpbuf *request, uint32_t chain)
>   }
>   
>   static void
> -nl_msg_put_act_ct(struct ofpbuf *request, struct tc_action *action)
> +nl_msg_put_act_ct(struct ofpbuf *request, struct tc_action *action,
> +                  uint32_t action_pc)
>   {
>       uint16_t ct_action = 0;
>       size_t offset;
> @@ -2294,7 +2443,7 @@ nl_msg_put_act_ct(struct ofpbuf *request, struct tc_action *action)
>       offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED);
>       {
>           struct tc_ct ct = {
> -                .action = TC_ACT_PIPE,
> +            .action = action_pc,
>           };
>   
>           if (!action->ct.clear) {
> @@ -2499,10 +2648,57 @@ csum_update_flag(struct tc_flower *flower,
>       return EOPNOTSUPP;
>   }
>   
> +static bool
> +rewrite_pedits_need_csum_update(struct tc_action *action)
> +{
> +    int i, j;
> +
> +    for (i = 0; i < ARRAY_SIZE(flower_pedit_map); i++) {
> +        struct flower_key_to_pedit *m = &flower_pedit_map[i];
> +        ovs_be32 *mask, *data, first_word_mask, last_word_mask;
> +        int cnt = 0, cur_offset = 0;
> +
> +        if (!m->size) {
> +            continue;
> +        }
> +
> +        calc_offsets(action, m, &cur_offset, &cnt, &last_word_mask,
> +                     &first_word_mask, &mask, &data);
> +
> +        for (j = 0; j < cnt; j++,  mask++) {
> +            ovs_be32 mask_word = *mask;
> +
> +            if (j == 0) {
> +                mask_word &= first_word_mask;
> +            }
> +            if (j == cnt - 1) {
> +                mask_word &= last_word_mask;
> +            }
> +            if (!mask_word) {
> +                continue;
> +            }
> +
> +            switch (m->htype) {
> +            case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
> +            case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
> +            case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
> +            case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
> +                return true;
> +            case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK:
> +            case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
> +            case __PEDIT_HDR_TYPE_MAX:
> +                break;
> +            }
> +        }
> +    }
> +    return false;
> +}
> +
>   static int
>   nl_msg_put_flower_rewrite_pedits(struct ofpbuf *request,
>                                    struct tc_flower *flower,
> -                                 struct tc_action *action)
> +                                 struct tc_action *action,
> +                                 uint32_t action_pc)
>   {
>       struct {
>           struct tc_pedit sel;
> @@ -2569,7 +2765,8 @@ nl_msg_put_flower_rewrite_pedits(struct ofpbuf *request,
>               }
>           }
>       }
> -    nl_msg_put_act_pedit(request, &sel.sel, sel.keys_ex);
> +    nl_msg_put_act_pedit(request, &sel.sel, sel.keys_ex,
> +                         flower->csum_update_flags ? TC_ACT_PIPE : action_pc);
>   
>       return 0;
>   }
> @@ -2590,7 +2787,7 @@ nl_msg_put_flower_acts_release(struct ofpbuf *request, uint16_t act_index)
>    * last pedit action. */
>   static void
>   nl_msg_put_csum_act(struct ofpbuf *request, struct tc_flower *flower,
> -                    uint16_t *act_index)
> +                    uint32_t action_pc, uint16_t *act_index)
>   {
>       size_t act_offset;
>   
> @@ -2600,7 +2797,7 @@ nl_msg_put_csum_act(struct ofpbuf *request, struct tc_flower *flower,
>       }
>   
>       act_offset = nl_msg_start_nested(request, (*act_index)++);
> -    nl_msg_put_act_csum(request, flower->csum_update_flags);
> +    nl_msg_put_act_csum(request, flower->csum_update_flags, action_pc);
>       nl_msg_put_act_flags(request);
>       nl_msg_end_nested(request, act_offset);
>   
> @@ -2608,6 +2805,124 @@ nl_msg_put_csum_act(struct ofpbuf *request, struct tc_flower *flower,
>       flower->csum_update_flags = 0;
>   }
>   
> +static int
> +get_action_index_for_tc_actions(struct tc_flower *flower, uint16_t act_index,
> +                                struct tc_action *action, int action_count,
> +                                bool tunnel_key_released)
> +{
> +    bool need_csum = false;
> +
> +    if (action_count < 0) {
> +        /* Only forward jumps are supported */
> +        return -EINVAL;
> +    }
> +
> +    for (int i = 0; i < action_count; i++, action++) {
> +        if (action->type != TC_ACT_PEDIT && need_csum) {
> +            need_csum = false;
> +            act_index++;
> +        }
> +
> +        switch (action->type) {
> +
> +        case TC_ACT_OUTPUT:
> +            if (!tunnel_key_released && flower->tunnel) {
> +                act_index++;
> +                tunnel_key_released = true;
> +            }
> +            if (action->out.ingress) {
> +                act_index++;
> +            }
> +            act_index++;
> +            break;
> +
> +        case TC_ACT_ENCAP:
> +            if (!tunnel_key_released && flower->tunnel) {
> +                act_index++;
> +                tunnel_key_released = true;
> +            }
> +            act_index++;
> +            break;
> +
> +        case TC_ACT_PEDIT:
> +            if (!need_csum) {
> +                need_csum = rewrite_pedits_need_csum_update(action);
> +            }
> +            if (i == (action_count - 1) && need_csum) {
> +                need_csum = false;
> +                act_index++;
> +            }
> +            act_index++;
> +            break;
> +
> +        case TC_ACT_POLICE_MTU:
> +        case TC_ACT_VLAN_POP:
> +        case TC_ACT_VLAN_PUSH:
> +        case TC_ACT_MPLS_POP:
> +        case TC_ACT_MPLS_PUSH:
> +        case TC_ACT_MPLS_SET:
> +        case TC_ACT_GOTO:
> +        case TC_ACT_CT:
> +            /* Increase act_index by one if we are sure this type of action
> +             * will only add one tc action in the kernel. */
> +            act_index++;
> +            break;
> +
> +            /* If we can't determine how many tc actions will be added by the
> +             * kernel return -EOPNOTSUPP.
> +             *
> +             * Please do NOT add a default case. */
> +        }
> +    }
> +
> +    return act_index;
> +}
> +
> +static int
> +nl_msg_put_act_police_mtu(struct ofpbuf *request, struct tc_flower *flower,
> +                          struct tc_action *action, uint32_t action_pc,
> +                          int action_index, uint16_t act_index, bool released)
> +{
> +    uint32_t tc_action;
> +    size_t offset;
> +
> +    if (action->police.result_jump != JUMP_ACTION_STOP) {
> +        int jump_index;
> +        int action_count = action->police.result_jump - action_index - 1;
> +
> +        jump_index = get_action_index_for_tc_actions(flower,
> +                                                     act_index - 1,
> +                                                     action + 1,
> +                                                     action_count,
> +                                                     released);
> +        if (jump_index < 0) {
> +            return -jump_index;
> +        }
> +        tc_action = TC_ACT_JUMP | (jump_index & TC_ACT_EXT_VAL_MASK);
> +    } else {
> +        tc_action = TC_ACT_STOLEN;
> +    }
> +
> +    nl_msg_put_string(request, TCA_ACT_KIND, "police");
> +    offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
> +    {
> +        struct tc_police p = { .action = action_pc,
> +            .mtu = action->police.mtu };
> +
> +        nl_msg_put_unspec(request, TCA_POLICE_TBF, &p, sizeof p);
> +
> +        /* The value in jump_action is the total number of TC_ACT_*
> +         * we need to jump, not the actual number of TCA_ACT_KIND
> +         * (act_index) actions. As certain TC_ACT_* actions can be
> +         * translated into multiple TCA_ACT_KIND ones.
> +         *
> +         * See nl_msg_put_flower_acts() below for more details. */
> +        nl_msg_put_u32(request, TCA_POLICE_RESULT, tc_action);
> +    }
> +    nl_msg_end_nested(request, offset);
> +    return 0;
> +}
> +
>   static int
>   nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
>   {
> @@ -2621,17 +2936,59 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
>       offset = nl_msg_start_nested(request, TCA_FLOWER_ACT);
>       {
>           int error;
> +        uint32_t prev_action_pc = TC_ACT_PIPE;
>   
>           action = flower->actions;
>           for (i = 0; i < flower->action_count; i++, action++) {
> -            if (action->type != TC_ACT_PEDIT) {
> -                nl_msg_put_csum_act(request, flower, &act_index);
> +            uint32_t action_pc; /* Programmatic Control */
> +
> +            if (!action->jump_action) {
> +                action_pc = TC_ACT_PIPE;
> +            } else if (action->jump_action == JUMP_ACTION_STOP) {
> +                action_pc = TC_ACT_STOLEN;
> +            } else {
> +                /* The value in jump_action is the total number of TC_ACT_*
> +                 * we need to jump, not the actual number of TCA_ACT_KIND
> +                 * (act_index) actions. As certain TC_ACT_* actions can be
> +                 * translated into multiple TCA_ACT_KIND ones.
> +                 *
> +                 * If we can determine the number of actual actions being
> +                 * inserted we will update the count, if not we will return
> +                 * -EOPNOTSUPP.
> +                 */
> +                int jump_index;
> +                int act_index_start = act_index - 1;
> +                int action_count = (action->jump_action &
> +                                    TC_ACT_EXT_VAL_MASK) - i;
> +
> +                if (flower->csum_update_flags &&
> +                    (action->type != TC_ACT_PEDIT
> +                     || prev_action_pc & TC_ACT_JUMP)) {
> +                    act_index_start++;
> +                }
> +
> +                jump_index = get_action_index_for_tc_actions(flower,
> +                                                             act_index_start,
> +                                                             action,
> +                                                             action_count,
> +                                                             released);
> +                if (jump_index < 0) {
> +                    return -jump_index;
> +                }
> +
> +                action_pc = TC_ACT_JUMP | jump_index;
>               }
> +
> +            if (action->type != TC_ACT_PEDIT || prev_action_pc & TC_ACT_JUMP) {
> +                nl_msg_put_csum_act(request, flower, prev_action_pc,
> +                                    &act_index);
> +            }
> +
>               switch (action->type) {
>               case TC_ACT_PEDIT: {
>                   act_offset = nl_msg_start_nested(request, act_index++);
>                   error = nl_msg_put_flower_rewrite_pedits(request, flower,
> -                                                         action);
> +                                                         action, action_pc);
>                   if (error) {
>                       return error;
>                   }
> @@ -2639,7 +2996,8 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
>   
>                   if (i == flower->action_count - 1) {
>                       /* If this is the last action check csum calc again. */
> -                    nl_msg_put_csum_act(request, flower, &act_index);
> +                    nl_msg_put_csum_act(request, flower, action_pc,
> +                                        &act_index);
>                   }
>               }
>               break;
> @@ -2660,14 +3018,15 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
>                                                 action->encap.tos,
>                                                 action->encap.ttl,
>                                                 action->encap.data,
> -                                              action->encap.no_csum);
> +                                              action->encap.no_csum,
> +                                              action_pc);
>                   nl_msg_put_act_flags(request);
>                   nl_msg_end_nested(request, act_offset);
>               }
>               break;
>               case TC_ACT_VLAN_POP: {
>                   act_offset = nl_msg_start_nested(request, act_index++);
> -                nl_msg_put_act_pop_vlan(request);
> +                nl_msg_put_act_pop_vlan(request, action_pc);
>                   nl_msg_put_act_flags(request);
>                   nl_msg_end_nested(request, act_offset);
>               }
> @@ -2677,14 +3036,16 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
>                   nl_msg_put_act_push_vlan(request,
>                                            action->vlan.vlan_push_tpid,
>                                            action->vlan.vlan_push_id,
> -                                         action->vlan.vlan_push_prio);
> +                                         action->vlan.vlan_push_prio,
> +                                         action_pc);
>                   nl_msg_put_act_flags(request);
>                   nl_msg_end_nested(request, act_offset);
>               }
>               break;
>               case TC_ACT_MPLS_POP: {
>                   act_offset = nl_msg_start_nested(request, act_index++);
> -                nl_msg_put_act_pop_mpls(request, action->mpls.proto);
> +                nl_msg_put_act_pop_mpls(request, action->mpls.proto,
> +                                        action_pc);
>                   nl_msg_end_nested(request, act_offset);
>               }
>               break;
> @@ -2692,7 +3053,8 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
>                   act_offset = nl_msg_start_nested(request, act_index++);
>                   nl_msg_put_act_push_mpls(request, action->mpls.proto,
>                                            action->mpls.label, action->mpls.tc,
> -                                         action->mpls.ttl, action->mpls.bos);
> +                                         action->mpls.ttl, action->mpls.bos,
> +                                         action_pc);
>                   nl_msg_end_nested(request, act_offset);
>               }
>               break;
> @@ -2700,7 +3062,7 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
>                   act_offset = nl_msg_start_nested(request, act_index++);
>                   nl_msg_put_act_set_mpls(request, action->mpls.label,
>                                           action->mpls.tc, action->mpls.ttl,
> -                                        action->mpls.bos);
> +                                        action->mpls.bos, action_pc);
>                   nl_msg_end_nested(request, act_offset);
>               }
>               break;
> @@ -2735,12 +3097,13 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
>                           nl_msg_put_act_mirred(request, ifindex, TC_ACT_STOLEN,
>                                                 TCA_EGRESS_REDIR);
>                       }
> +                    action->jump_action = JUMP_ACTION_STOP;
>                   } else {
>                       if (ingress) {
> -                        nl_msg_put_act_mirred(request, ifindex, TC_ACT_PIPE,
> +                        nl_msg_put_act_mirred(request, ifindex, action_pc,
>                                                 TCA_INGRESS_MIRROR);
>                       } else {
> -                        nl_msg_put_act_mirred(request, ifindex, TC_ACT_PIPE,
> +                        nl_msg_put_act_mirred(request, ifindex, action_pc,
>                                                 TCA_EGRESS_MIRROR);
>                       }
>                   }
> @@ -2768,12 +3131,26 @@ nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
>               break;
>               case TC_ACT_CT: {
>                   act_offset = nl_msg_start_nested(request, act_index++);
> -                nl_msg_put_act_ct(request, action);
> +                nl_msg_put_act_ct(request, action, action_pc);
>                   nl_msg_put_act_cookie(request, &flower->act_cookie);
>                   nl_msg_end_nested(request, act_offset);
>               }
>               break;
> +            case TC_ACT_POLICE_MTU: {
> +                act_offset = nl_msg_start_nested(request, act_index++);
> +                if (nl_msg_put_act_police_mtu(request, flower, action,
> +                                              action_pc, i, act_index,
> +                                              released)) {
> +                    return -EOPNOTSUPP;
> +                }
> +                nl_msg_put_act_cookie(request, &flower->act_cookie);
> +                nl_msg_put_act_flags(request);
> +                nl_msg_end_nested(request, act_offset);
> +            }
> +            break;
>               }
> +
> +            prev_action_pc = action_pc;
>           }
>       }
>   
> diff --git a/lib/tc.h b/lib/tc.h
> index d6cdddd16..7ccd71d01 100644
> --- a/lib/tc.h
> +++ b/lib/tc.h
> @@ -174,6 +174,7 @@ enum tc_action_type {
>       TC_ACT_MPLS_SET,
>       TC_ACT_GOTO,
>       TC_ACT_CT,
> +    TC_ACT_POLICE_MTU,
>   };
>   
>   enum nat_type {
> @@ -256,14 +257,19 @@ struct tc_action {
>               bool force;
>               bool commit;
>           } ct;
> -
>           struct {
>               struct tc_flower_key key;
>               struct tc_flower_key mask;
>           } rewrite;
> -     };
> +        struct {
> +            uint32_t result_jump;
> +            uint16_t mtu;
> +        } police;
> +    };
>   
> -     enum tc_action_type type;
> +    enum tc_action_type type;
> +    uint32_t jump_action;
> +#define JUMP_ACTION_STOP 0xffffffff
>   };
>   
>   /* assert that if we overflow with a masked write of uint32_t to the last byte
> 


Acked-by: Roi Dayan <roid@nvidia.com>
diff mbox series

Patch

diff --git a/lib/netdev-offload-tc.c b/lib/netdev-offload-tc.c
index f657af0fd..9b523c5f5 100644
--- a/lib/netdev-offload-tc.c
+++ b/lib/netdev-offload-tc.c
@@ -62,6 +62,13 @@  struct chain_node {
     uint32_t chain;
 };
 
+static int
+netdev_tc_parse_nl_actions(struct netdev *netdev, struct tc_flower *flower,
+                           struct offload_info *info,
+                           const struct nlattr *actions, size_t actions_len,
+                           bool *recirc_act, bool more_actions,
+                           struct tc_action **need_jump_update);
+
 static bool
 is_internal_port(const char *type)
 {
@@ -825,6 +832,45 @@  _parse_tc_flower_to_actions(struct tc_flower *flower, struct ofpbuf *buf,
             nl_msg_put_u32(buf, OVS_ACTION_ATTR_RECIRC, action->chain);
         }
         break;
+        case TC_ACT_POLICE_MTU: {
+            size_t offset, act_offset;
+            uint32_t jump;
+
+            offset = nl_msg_start_nested(buf,
+                                         OVS_ACTION_ATTR_CHECK_PKT_LEN);
+
+            nl_msg_put_u16(buf, OVS_CHECK_PKT_LEN_ATTR_PKT_LEN,
+                           action->police.mtu);
+
+            act_offset = nl_msg_start_nested(
+                buf, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER);
+            i = _parse_tc_flower_to_actions(flower, buf, i + 1,
+                                            action->police.result_jump);
+            nl_msg_end_nested(buf, act_offset);
+
+            act_offset = nl_msg_start_nested(
+                buf, OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL);
+
+            jump = flower->actions[i - 1].jump_action;
+            if (jump == JUMP_ACTION_STOP) {
+                jump = max_index;
+            }
+            if (jump != 0) {
+                i = _parse_tc_flower_to_actions(flower, buf, i, jump);
+            }
+            nl_msg_end_nested(buf, act_offset);
+
+            i--;
+            nl_msg_end_nested(buf, offset);
+        }
+        break;
+        }
+
+        if (action->jump_action && action->type != TC_ACT_POLICE_MTU) {
+            /* If there is a jump, it means this was the end of an action
+             * set and we need to end this branch. */
+            i++;
+            break;
         }
     }
     return i;
@@ -1584,11 +1630,121 @@  parse_match_ct_state_to_flower(struct tc_flower *flower, struct match *match)
     }
 }
 
+
+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 tc_action **need_jump_update,
+                           bool *recirc_act)
+{
+    struct tc_action *ge_jump_update = NULL, *le_jump_update = NULL;
+    const struct nlattr *nl_actions;
+    int err, le_offset, gt_offset;
+    uint16_t pkt_len;
+
+    static const struct nl_policy ovs_cpl_policy[] = {
+        [OVS_CHECK_PKT_LEN_ATTR_PKT_LEN] = { .type = NL_A_U16 },
+        [OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER] = { .type = NL_A_NESTED },
+        [OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL]
+            = { .type = NL_A_NESTED },
+    };
+    struct nlattr *a[ARRAY_SIZE(ovs_cpl_policy)];
+
+    if (!nl_parse_nested(nla, ovs_cpl_policy, a, ARRAY_SIZE(a))) {
+        VLOG_INFO("Received invalid formatted OVS_ACTION_ATTR_CHECK_PKT_LEN!");
+        return EOPNOTSUPP;
+    }
+
+    if (!a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER] ||
+        !a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL]) {
+        VLOG_INFO("Received invalid OVS_CHECK_PKT_LEN_ATTR_ACTION_IF_*!");
+        return EOPNOTSUPP;
+    }
+
+    pkt_len = nl_attr_get_u16(a[OVS_CHECK_PKT_LEN_ATTR_PKT_LEN]);
+
+    /* Add the police mtu action first in the allocated slot. */
+    action->police.mtu = pkt_len;
+    action->type = TC_ACT_POLICE_MTU;
+    le_offset = flower->action_count++;
+
+    /* Parse and add the greater than action(s).
+     * 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,
+                                     nl_attr_get(nl_actions),
+                                     nl_attr_get_size(nl_actions),
+                                     recirc_act, !last_action,
+                                     &ge_jump_update);
+    if (err) {
+        return err;
+    }
+
+    /* Update goto offset for le actions */
+    flower->actions[le_offset].police.result_jump = flower->action_count;
+
+    gt_offset = flower->action_count;
+
+    /* 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,
+                                     nl_attr_get(nl_actions),
+                                     nl_attr_get_size(nl_actions),
+                                     recirc_act, !last_action,
+                                     &le_jump_update);
+
+    if (gt_offset == flower->action_count && last_action) {
+        /* No le actions where added, fix gt offset */
+        flower->actions[le_offset].police.result_jump = JUMP_ACTION_STOP;
+    }
+
+    /* Update goto offset for gt actions to skip the le ones. */
+    if (last_action) {
+        flower->actions[gt_offset - 1].jump_action = JUMP_ACTION_STOP;
+
+        if (need_jump_update) {
+            *need_jump_update = NULL;
+        }
+    } else {
+        if (gt_offset == flower->action_count) {
+            flower->actions[gt_offset - 1].jump_action = 0;
+        } else {
+            flower->actions[gt_offset - 1].jump_action = flower->action_count;
+        }
+        /* If we have nested if() else () the if actions jump over the else
+         * and will end-up in the outer else () case, which it should have
+         * skipped. To void this we return the "potential" inner if() goto to
+         * need_jump_update, so it can be updated on return!
+         */
+        if (need_jump_update) {
+            *need_jump_update = &flower->actions[gt_offset - 1];
+        }
+    }
+
+    if (le_jump_update != NULL) {
+        le_jump_update->jump_action =
+            flower->actions[gt_offset - 1].jump_action;
+    }
+    if (ge_jump_update != NULL) {
+        ge_jump_update->jump_action =
+            flower->actions[gt_offset - 1].jump_action;
+    }
+
+    if (err) {
+        return err;
+    }
+
+    return 0;
+}
+
 static int
 netdev_tc_parse_nl_actions(struct netdev *netdev, struct tc_flower *flower,
                            struct offload_info *info,
                            const struct nlattr *actions, size_t actions_len,
-                           bool *recirc_act)
+                           bool *recirc_act, bool more_actions,
+                           struct tc_action **need_jump_update)
 {
     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
     const struct nlattr *nla;
@@ -1708,6 +1864,16 @@  netdev_tc_parse_nl_actions(struct netdev *netdev, struct tc_flower *flower,
             action->type = TC_ACT_GOTO;
             action->chain = 0;  /* 0 is reserved and not used by recirc. */
             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,
+                                             nl_attr_len_pad(nla,
+                                                             left) >= left
+                                             && !more_actions,
+                                             need_jump_update,
+                                             recirc_act);
+            if (err) {
+                return err;
+            }
         } else {
             VLOG_DBG_RL(&rl, "unsupported put action type: %d",
                         nl_attr_type(nla));
@@ -1980,7 +2146,8 @@  netdev_tc_flow_put(struct netdev *netdev, struct match *match,
 
     /* Parse all (nested) actions. */
     err = netdev_tc_parse_nl_actions(netdev, &flower, info,
-                                     actions, actions_len, &recirc_act);
+                                     actions, actions_len, &recirc_act,
+                                     false, NULL);
     if (err) {
         return err;
     }
diff --git a/lib/tc.c b/lib/tc.c
index df73a43d4..bdaa764f9 100644
--- a/lib/tc.c
+++ b/lib/tc.c
@@ -994,6 +994,18 @@  nl_parse_flower_flags(struct nlattr **attrs, struct tc_flower *flower)
     flower->offloaded_state = nl_get_flower_offloaded_state(attrs);
 }
 
+static void
+nl_parse_action_pc(uint32_t action_pc, struct tc_action *action)
+{
+    if (action_pc == TC_ACT_STOLEN) {
+        action->jump_action = JUMP_ACTION_STOP;
+    } else if (action_pc & TC_ACT_JUMP) {
+        action->jump_action = action_pc & TC_ACT_EXT_VAL_MASK;
+    } else {
+        action->jump_action = 0;
+    }
+}
+
 static const struct nl_policy pedit_policy[] = {
             [TCA_PEDIT_PARMS_EX] = { .type = NL_A_UNSPEC,
                                      .min_len = sizeof(struct tc_pedit),
@@ -1093,6 +1105,7 @@  nl_parse_act_pedit(struct nlattr *options, struct tc_flower *flower)
 
     action->type = TC_ACT_PEDIT;
 
+    nl_parse_action_pc(pe->action, action);
     return 0;
 }
 
@@ -1267,6 +1280,7 @@  nl_parse_act_tunnel_key(struct nlattr *options, struct tc_flower *flower)
         if (err) {
             return err;
         }
+        nl_parse_action_pc(tun->action, action);
     } else if (tun->t_action == TCA_TUNNEL_KEY_ACT_RELEASE) {
         flower->tunnel = true;
     } else {
@@ -1303,7 +1317,11 @@  get_user_hz(void)
 static void
 nl_parse_tcf(const struct tcf_t *tm, struct tc_flower *flower)
 {
-    flower->lastused = time_msec() - (tm->lastuse * 1000 / get_user_hz());
+    uint64_t lastused = time_msec() - (tm->lastuse * 1000 / get_user_hz());
+
+    if (flower->lastused < lastused) {
+        flower->lastused = lastused;
+    }
 }
 
 static int
@@ -1328,6 +1346,7 @@  nl_parse_act_gact(struct nlattr *options, struct tc_flower *flower)
         action = &flower->actions[flower->action_count++];
         action->chain = p->action & TC_ACT_EXT_VAL_MASK;
         action->type = TC_ACT_GOTO;
+        nl_parse_action_pc(p->action, action);
     } else if (p->action != TC_ACT_SHOT) {
         VLOG_ERR_RL(&error_rl, "unknown gact action: %d", p->action);
         return EINVAL;
@@ -1386,8 +1405,9 @@  nl_parse_act_mirred(struct nlattr *options, struct tc_flower *flower)
 
     mirred_tm = mirred_attrs[TCA_MIRRED_TM];
     tm = nl_attr_get_unspec(mirred_tm, sizeof *tm);
-    nl_parse_tcf(tm, flower);
 
+    nl_parse_tcf(tm, flower);
+    nl_parse_action_pc(m->action, action);
     return 0;
 }
 
@@ -1516,6 +1536,7 @@  nl_parse_act_ct(struct nlattr *options, struct tc_flower *flower)
     }
     action->type = TC_ACT_CT;
 
+    nl_parse_action_pc(ct->action, action);
     return 0;
 }
 
@@ -1561,6 +1582,8 @@  nl_parse_act_vlan(struct nlattr *options, struct tc_flower *flower)
                     v->action, v->v_action);
         return EINVAL;
     }
+
+    nl_parse_action_pc(v->action, action);
     return 0;
 }
 
@@ -1654,6 +1677,7 @@  nl_parse_act_mpls(struct nlattr *options, struct tc_flower *flower)
         return EINVAL;
     }
 
+    nl_parse_action_pc(m->action, action);
     return 0;
 }
 
@@ -1694,9 +1718,60 @@  nl_parse_act_csum(struct nlattr *options, struct tc_flower *flower)
         return EINVAL;
     }
 
+    /* The action_pc should be set on the previous action. */
+    if (flower->action_count < TCA_ACT_MAX_NUM) {
+        struct tc_action *action = &flower->actions[flower->action_count];
+
+        nl_parse_action_pc(c->action, action);
+    }
     return 0;
 }
 
+static const struct nl_policy police_policy[] = {
+    [TCA_POLICE_TBF] = { .type = NL_A_UNSPEC,
+                         .min_len = sizeof(struct tc_police),
+                         .optional = false, },
+    [TCA_POLICE_RESULT] = { .type = NL_A_U32, .optional = false, },
+    [TCA_POLICE_TM]     = { .type = NL_A_UNSPEC,
+                            .min_len = sizeof(struct tcf_t),
+                            .optional = false, },
+};
+
+static int
+nl_parse_act_police_mtu(struct nlattr *options, struct tc_flower *flower)
+{
+    struct nlattr *police_attrs[ARRAY_SIZE(police_policy)];
+    const struct tc_police *police;
+    struct nlattr *police_result;
+    struct tc_action *action;
+    const struct tcf_t *tm;
+    uint32_t action_pc;
+
+    if (!nl_parse_nested(options, police_policy, police_attrs,
+                         ARRAY_SIZE(police_policy))) {
+        VLOG_ERR_RL(&error_rl, "Failed to parse police action options");
+        return EPROTO;
+    }
+    police = nl_attr_get_unspec(police_attrs[TCA_POLICE_TBF], sizeof *police);
+    police_result = police_attrs[TCA_POLICE_RESULT];
+
+    action = &flower->actions[flower->action_count++];
+    action->type = TC_ACT_POLICE_MTU;
+    action_pc = nl_attr_get_u32(police_result);
+    if (action_pc & TC_ACT_JUMP) {
+        action->police.result_jump = action_pc & TC_ACT_EXT_VAL_MASK;
+    } else {
+        action->police.result_jump = JUMP_ACTION_STOP;
+    }
+    action->police.mtu = police->mtu;
+
+    tm = nl_attr_get_unspec(police_attrs[TCA_POLICE_TM], sizeof *tm);
+    nl_parse_tcf(tm, flower);
+    nl_parse_action_pc(police->action, action);
+    return 0;
+}
+
+
 static const struct nl_policy act_policy[] = {
     [TCA_ACT_KIND] = { .type = NL_A_STRING, .optional = false, },
     [TCA_ACT_COOKIE] = { .type = NL_A_UNSPEC, .optional = true, },
@@ -1715,7 +1790,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 *csum)
 {
     struct nlattr *act_options;
     struct nlattr *act_stats;
@@ -1737,6 +1812,7 @@  nl_parse_single_action(struct nlattr *action, struct tc_flower *flower,
         return EPROTO;
     }
 
+    *csum = false;
     act_kind = nl_attr_get_string(action_attrs[TCA_ACT_KIND]);
     act_options = action_attrs[TCA_ACT_OPTIONS];
     act_cookie = action_attrs[TCA_ACT_COOKIE];
@@ -1757,10 +1833,13 @@  nl_parse_single_action(struct nlattr *action, struct tc_flower *flower,
         err = nl_parse_act_pedit(act_options, flower);
     } else if (!strcmp(act_kind, "csum")) {
         nl_parse_act_csum(act_options, flower);
+        *csum = true;
     } else if (!strcmp(act_kind, "skbedit")) {
         /* Added for TC rule only (not in OvS rule) so ignore. */
     } else if (!strcmp(act_kind, "ct")) {
         nl_parse_act_ct(act_options, flower);
+    } else if (!strcmp(act_kind, "police")) {
+        nl_parse_act_police_mtu(act_options, flower);
     } else {
         VLOG_ERR_RL(&error_rl, "unknown tc action kind: %s", act_kind);
         err = EINVAL;
@@ -1818,6 +1897,10 @@  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);
+    int previous_action_count = 0;
+    bool need_jump_adjust = false;
+    int jump_adjust = 0;
+    bool csum = false;
 
     for (int i = TCA_ACT_MIN_PRIO; i < max_size; i++) {
         actions_orders_policy[i].type = NL_A_NESTED;
@@ -1838,7 +1921,70 @@  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,
+                                         &csum);
+
+            if (flower->action_count == previous_action_count) {
+
+                struct tc_action *action;
+
+                /* We had no update on the TC action count, which means
+                 * we had a none TC type action. So need to adjust existing
+                 * jump offsets. */
+                jump_adjust++;
+
+                if (need_jump_adjust || (csum && flower->action_count > 0)) {
+
+                    if (csum && flower->action_count > 0) {
+                        /* The csum action is special as it might carry
+                         * a jump count for the previous TC_ACT and therefore
+                         * should be adjusted with jump_adjust as it got
+                         * copied. */
+                        action = &flower->actions[flower->action_count - 1];
+                        if (action->jump_action
+                            && action->jump_action != JUMP_ACTION_STOP) {
+                            action->jump_action -= (jump_adjust - 1);
+                        }
+                    }
+
+                    for (int j = 0; j < flower->action_count; j++) {
+                        action = &flower->actions[j];
+
+                        if (action->type == TC_ACT_POLICE_MTU
+                            && action->police.result_jump != JUMP_ACTION_STOP
+                            && (action->police.result_jump - 1) >
+                            flower->action_count) {
+
+                            action->police.result_jump--;
+                        }
+
+                        if (action->jump_action
+                            && action->jump_action != JUMP_ACTION_STOP
+                            && (action->jump_action - 1) >
+                            flower->action_count) {
+
+                            action->jump_action--;
+                        }
+                    }
+                }
+            } else {
+                struct tc_action *action;
+
+                action = &flower->actions[previous_action_count];
+                if (action->type == TC_ACT_POLICE_MTU &&
+                    action->police.result_jump != JUMP_ACTION_STOP) {
+                    action->police.result_jump -= jump_adjust;
+                    need_jump_adjust = true;
+                }
+
+                if (action->jump_action
+                    && action->jump_action != JUMP_ACTION_STOP) {
+                    action->jump_action -= jump_adjust;
+                    need_jump_adjust = true;
+                }
+
+                previous_action_count = flower->action_count;
+            }
 
             if (err) {
                 return err;
@@ -2031,14 +2177,14 @@  tc_get_tc_cls_policy(enum tc_offload_policy policy)
 }
 
 static void
-nl_msg_put_act_csum(struct ofpbuf *request, uint32_t flags)
+nl_msg_put_act_csum(struct ofpbuf *request, uint32_t flags, uint32_t action_pc)
 {
     size_t offset;
 
     nl_msg_put_string(request, TCA_ACT_KIND, "csum");
     offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
     {
-        struct tc_csum parm = { .action = TC_ACT_PIPE,
+        struct tc_csum parm = { .action = action_pc,
                                 .update_flags = flags };
 
         nl_msg_put_unspec(request, TCA_CSUM_PARMS, &parm, sizeof parm);
@@ -2048,7 +2194,7 @@  nl_msg_put_act_csum(struct ofpbuf *request, uint32_t flags)
 
 static void
 nl_msg_put_act_pedit(struct ofpbuf *request, struct tc_pedit *parm,
-                     struct tc_pedit_key_ex *ex)
+                     struct tc_pedit_key_ex *ex, uint32_t action_pc)
 {
     size_t ksize = sizeof *parm + parm->nkeys * sizeof(struct tc_pedit_key);
     size_t offset, offset_keys_ex, offset_key;
@@ -2057,7 +2203,7 @@  nl_msg_put_act_pedit(struct ofpbuf *request, struct tc_pedit *parm,
     nl_msg_put_string(request, TCA_ACT_KIND, "pedit");
     offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
     {
-        parm->action = TC_ACT_PIPE;
+        parm->action = action_pc;
 
         nl_msg_put_unspec(request, TCA_PEDIT_PARMS_EX, parm, ksize);
         offset_keys_ex = nl_msg_start_nested(request, TCA_PEDIT_KEYS_EX);
@@ -2074,14 +2220,14 @@  nl_msg_put_act_pedit(struct ofpbuf *request, struct tc_pedit *parm,
 
 static void
 nl_msg_put_act_push_vlan(struct ofpbuf *request, ovs_be16 tpid,
-                         uint16_t vid, uint8_t prio)
+                         uint16_t vid, uint8_t prio, uint32_t action_pc)
 {
     size_t offset;
 
     nl_msg_put_string(request, TCA_ACT_KIND, "vlan");
     offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
     {
-        struct tc_vlan parm = { .action = TC_ACT_PIPE,
+        struct tc_vlan parm = { .action = action_pc,
                                 .v_action = TCA_VLAN_ACT_PUSH };
 
         nl_msg_put_unspec(request, TCA_VLAN_PARMS, &parm, sizeof parm);
@@ -2093,14 +2239,14 @@  nl_msg_put_act_push_vlan(struct ofpbuf *request, ovs_be16 tpid,
 }
 
 static void
-nl_msg_put_act_pop_vlan(struct ofpbuf *request)
+nl_msg_put_act_pop_vlan(struct ofpbuf *request, uint32_t action_pc)
 {
     size_t offset;
 
     nl_msg_put_string(request, TCA_ACT_KIND, "vlan");
     offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
     {
-        struct tc_vlan parm = { .action = TC_ACT_PIPE,
+        struct tc_vlan parm = { .action = action_pc,
                                 .v_action = TCA_VLAN_ACT_POP };
 
         nl_msg_put_unspec(request, TCA_VLAN_PARMS, &parm, sizeof parm);
@@ -2109,14 +2255,15 @@  nl_msg_put_act_pop_vlan(struct ofpbuf *request)
 }
 
 static void
-nl_msg_put_act_pop_mpls(struct ofpbuf *request, ovs_be16 proto)
+nl_msg_put_act_pop_mpls(struct ofpbuf *request, ovs_be16 proto,
+                        uint32_t action_pc)
 {
     size_t offset;
 
     nl_msg_put_string(request, TCA_ACT_KIND, "mpls");
     offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED);
     {
-        struct tc_mpls parm = { .action = TC_ACT_PIPE,
+        struct tc_mpls parm = { .action = action_pc,
                                 .m_action = TCA_MPLS_ACT_POP };
 
         nl_msg_put_unspec(request, TCA_MPLS_PARMS, &parm, sizeof parm);
@@ -2127,14 +2274,15 @@  nl_msg_put_act_pop_mpls(struct ofpbuf *request, ovs_be16 proto)
 
 static void
 nl_msg_put_act_push_mpls(struct ofpbuf *request, ovs_be16 proto,
-                         uint32_t label, uint8_t tc, uint8_t ttl, uint8_t bos)
+                         uint32_t label, uint8_t tc, uint8_t ttl, uint8_t bos,
+                         uint32_t action_pc)
 {
     size_t offset;
 
     nl_msg_put_string(request, TCA_ACT_KIND, "mpls");
     offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED);
     {
-        struct tc_mpls parm = { .action = TC_ACT_PIPE,
+        struct tc_mpls parm = { .action = action_pc,
                                 .m_action = TCA_MPLS_ACT_PUSH };
 
         nl_msg_put_unspec(request, TCA_MPLS_PARMS, &parm, sizeof parm);
@@ -2149,14 +2297,14 @@  nl_msg_put_act_push_mpls(struct ofpbuf *request, ovs_be16 proto,
 
 static void
 nl_msg_put_act_set_mpls(struct ofpbuf *request, uint32_t label, uint8_t tc,
-                        uint8_t ttl, uint8_t bos)
+                        uint8_t ttl, uint8_t bos, uint32_t action_pc)
 {
     size_t offset;
 
     nl_msg_put_string(request, TCA_ACT_KIND, "mpls");
     offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED);
     {
-        struct tc_mpls parm = { .action = TC_ACT_PIPE,
+        struct tc_mpls parm = { .action = action_pc,
                                 .m_action = TCA_MPLS_ACT_MODIFY };
 
         nl_msg_put_unspec(request, TCA_MPLS_PARMS, &parm, sizeof parm);
@@ -2225,14 +2373,14 @@  nl_msg_put_act_tunnel_key_set(struct ofpbuf *request, bool id_present,
                               struct in6_addr *ipv6_dst,
                               ovs_be16 tp_dst, uint8_t tos, uint8_t ttl,
                               struct tun_metadata tun_metadata,
-                              uint8_t no_csum)
+                              uint8_t no_csum, uint32_t action_pc)
 {
     size_t offset;
 
     nl_msg_put_string(request, TCA_ACT_KIND, "tunnel_key");
     offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
     {
-        struct tc_tunnel_key tun = { .action = TC_ACT_PIPE,
+        struct tc_tunnel_key tun = { .action = action_pc,
                                      .t_action = TCA_TUNNEL_KEY_ACT_SET };
 
         nl_msg_put_unspec(request, TCA_TUNNEL_KEY_PARMS, &tun, sizeof tun);
@@ -2285,7 +2433,8 @@  nl_msg_put_act_gact(struct ofpbuf *request, uint32_t chain)
 }
 
 static void
-nl_msg_put_act_ct(struct ofpbuf *request, struct tc_action *action)
+nl_msg_put_act_ct(struct ofpbuf *request, struct tc_action *action,
+                  uint32_t action_pc)
 {
     uint16_t ct_action = 0;
     size_t offset;
@@ -2294,7 +2443,7 @@  nl_msg_put_act_ct(struct ofpbuf *request, struct tc_action *action)
     offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS | NLA_F_NESTED);
     {
         struct tc_ct ct = {
-                .action = TC_ACT_PIPE,
+            .action = action_pc,
         };
 
         if (!action->ct.clear) {
@@ -2499,10 +2648,57 @@  csum_update_flag(struct tc_flower *flower,
     return EOPNOTSUPP;
 }
 
+static bool
+rewrite_pedits_need_csum_update(struct tc_action *action)
+{
+    int i, j;
+
+    for (i = 0; i < ARRAY_SIZE(flower_pedit_map); i++) {
+        struct flower_key_to_pedit *m = &flower_pedit_map[i];
+        ovs_be32 *mask, *data, first_word_mask, last_word_mask;
+        int cnt = 0, cur_offset = 0;
+
+        if (!m->size) {
+            continue;
+        }
+
+        calc_offsets(action, m, &cur_offset, &cnt, &last_word_mask,
+                     &first_word_mask, &mask, &data);
+
+        for (j = 0; j < cnt; j++,  mask++) {
+            ovs_be32 mask_word = *mask;
+
+            if (j == 0) {
+                mask_word &= first_word_mask;
+            }
+            if (j == cnt - 1) {
+                mask_word &= last_word_mask;
+            }
+            if (!mask_word) {
+                continue;
+            }
+
+            switch (m->htype) {
+            case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
+            case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
+            case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
+            case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
+                return true;
+            case TCA_PEDIT_KEY_EX_HDR_TYPE_NETWORK:
+            case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
+            case __PEDIT_HDR_TYPE_MAX:
+                break;
+            }
+        }
+    }
+    return false;
+}
+
 static int
 nl_msg_put_flower_rewrite_pedits(struct ofpbuf *request,
                                  struct tc_flower *flower,
-                                 struct tc_action *action)
+                                 struct tc_action *action,
+                                 uint32_t action_pc)
 {
     struct {
         struct tc_pedit sel;
@@ -2569,7 +2765,8 @@  nl_msg_put_flower_rewrite_pedits(struct ofpbuf *request,
             }
         }
     }
-    nl_msg_put_act_pedit(request, &sel.sel, sel.keys_ex);
+    nl_msg_put_act_pedit(request, &sel.sel, sel.keys_ex,
+                         flower->csum_update_flags ? TC_ACT_PIPE : action_pc);
 
     return 0;
 }
@@ -2590,7 +2787,7 @@  nl_msg_put_flower_acts_release(struct ofpbuf *request, uint16_t act_index)
  * last pedit action. */
 static void
 nl_msg_put_csum_act(struct ofpbuf *request, struct tc_flower *flower,
-                    uint16_t *act_index)
+                    uint32_t action_pc, uint16_t *act_index)
 {
     size_t act_offset;
 
@@ -2600,7 +2797,7 @@  nl_msg_put_csum_act(struct ofpbuf *request, struct tc_flower *flower,
     }
 
     act_offset = nl_msg_start_nested(request, (*act_index)++);
-    nl_msg_put_act_csum(request, flower->csum_update_flags);
+    nl_msg_put_act_csum(request, flower->csum_update_flags, action_pc);
     nl_msg_put_act_flags(request);
     nl_msg_end_nested(request, act_offset);
 
@@ -2608,6 +2805,124 @@  nl_msg_put_csum_act(struct ofpbuf *request, struct tc_flower *flower,
     flower->csum_update_flags = 0;
 }
 
+static int
+get_action_index_for_tc_actions(struct tc_flower *flower, uint16_t act_index,
+                                struct tc_action *action, int action_count,
+                                bool tunnel_key_released)
+{
+    bool need_csum = false;
+
+    if (action_count < 0) {
+        /* Only forward jumps are supported */
+        return -EINVAL;
+    }
+
+    for (int i = 0; i < action_count; i++, action++) {
+        if (action->type != TC_ACT_PEDIT && need_csum) {
+            need_csum = false;
+            act_index++;
+        }
+
+        switch (action->type) {
+
+        case TC_ACT_OUTPUT:
+            if (!tunnel_key_released && flower->tunnel) {
+                act_index++;
+                tunnel_key_released = true;
+            }
+            if (action->out.ingress) {
+                act_index++;
+            }
+            act_index++;
+            break;
+
+        case TC_ACT_ENCAP:
+            if (!tunnel_key_released && flower->tunnel) {
+                act_index++;
+                tunnel_key_released = true;
+            }
+            act_index++;
+            break;
+
+        case TC_ACT_PEDIT:
+            if (!need_csum) {
+                need_csum = rewrite_pedits_need_csum_update(action);
+            }
+            if (i == (action_count - 1) && need_csum) {
+                need_csum = false;
+                act_index++;
+            }
+            act_index++;
+            break;
+
+        case TC_ACT_POLICE_MTU:
+        case TC_ACT_VLAN_POP:
+        case TC_ACT_VLAN_PUSH:
+        case TC_ACT_MPLS_POP:
+        case TC_ACT_MPLS_PUSH:
+        case TC_ACT_MPLS_SET:
+        case TC_ACT_GOTO:
+        case TC_ACT_CT:
+            /* Increase act_index by one if we are sure this type of action
+             * will only add one tc action in the kernel. */
+            act_index++;
+            break;
+
+            /* If we can't determine how many tc actions will be added by the
+             * kernel return -EOPNOTSUPP.
+             *
+             * Please do NOT add a default case. */
+        }
+    }
+
+    return act_index;
+}
+
+static int
+nl_msg_put_act_police_mtu(struct ofpbuf *request, struct tc_flower *flower,
+                          struct tc_action *action, uint32_t action_pc,
+                          int action_index, uint16_t act_index, bool released)
+{
+    uint32_t tc_action;
+    size_t offset;
+
+    if (action->police.result_jump != JUMP_ACTION_STOP) {
+        int jump_index;
+        int action_count = action->police.result_jump - action_index - 1;
+
+        jump_index = get_action_index_for_tc_actions(flower,
+                                                     act_index - 1,
+                                                     action + 1,
+                                                     action_count,
+                                                     released);
+        if (jump_index < 0) {
+            return -jump_index;
+        }
+        tc_action = TC_ACT_JUMP | (jump_index & TC_ACT_EXT_VAL_MASK);
+    } else {
+        tc_action = TC_ACT_STOLEN;
+    }
+
+    nl_msg_put_string(request, TCA_ACT_KIND, "police");
+    offset = nl_msg_start_nested(request, TCA_ACT_OPTIONS);
+    {
+        struct tc_police p = { .action = action_pc,
+            .mtu = action->police.mtu };
+
+        nl_msg_put_unspec(request, TCA_POLICE_TBF, &p, sizeof p);
+
+        /* The value in jump_action is the total number of TC_ACT_*
+         * we need to jump, not the actual number of TCA_ACT_KIND
+         * (act_index) actions. As certain TC_ACT_* actions can be
+         * translated into multiple TCA_ACT_KIND ones.
+         *
+         * See nl_msg_put_flower_acts() below for more details. */
+        nl_msg_put_u32(request, TCA_POLICE_RESULT, tc_action);
+    }
+    nl_msg_end_nested(request, offset);
+    return 0;
+}
+
 static int
 nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
 {
@@ -2621,17 +2936,59 @@  nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
     offset = nl_msg_start_nested(request, TCA_FLOWER_ACT);
     {
         int error;
+        uint32_t prev_action_pc = TC_ACT_PIPE;
 
         action = flower->actions;
         for (i = 0; i < flower->action_count; i++, action++) {
-            if (action->type != TC_ACT_PEDIT) {
-                nl_msg_put_csum_act(request, flower, &act_index);
+            uint32_t action_pc; /* Programmatic Control */
+
+            if (!action->jump_action) {
+                action_pc = TC_ACT_PIPE;
+            } else if (action->jump_action == JUMP_ACTION_STOP) {
+                action_pc = TC_ACT_STOLEN;
+            } else {
+                /* The value in jump_action is the total number of TC_ACT_*
+                 * we need to jump, not the actual number of TCA_ACT_KIND
+                 * (act_index) actions. As certain TC_ACT_* actions can be
+                 * translated into multiple TCA_ACT_KIND ones.
+                 *
+                 * If we can determine the number of actual actions being
+                 * inserted we will update the count, if not we will return
+                 * -EOPNOTSUPP.
+                 */
+                int jump_index;
+                int act_index_start = act_index - 1;
+                int action_count = (action->jump_action &
+                                    TC_ACT_EXT_VAL_MASK) - i;
+
+                if (flower->csum_update_flags &&
+                    (action->type != TC_ACT_PEDIT
+                     || prev_action_pc & TC_ACT_JUMP)) {
+                    act_index_start++;
+                }
+
+                jump_index = get_action_index_for_tc_actions(flower,
+                                                             act_index_start,
+                                                             action,
+                                                             action_count,
+                                                             released);
+                if (jump_index < 0) {
+                    return -jump_index;
+                }
+
+                action_pc = TC_ACT_JUMP | jump_index;
             }
+
+            if (action->type != TC_ACT_PEDIT || prev_action_pc & TC_ACT_JUMP) {
+                nl_msg_put_csum_act(request, flower, prev_action_pc,
+                                    &act_index);
+            }
+
             switch (action->type) {
             case TC_ACT_PEDIT: {
                 act_offset = nl_msg_start_nested(request, act_index++);
                 error = nl_msg_put_flower_rewrite_pedits(request, flower,
-                                                         action);
+                                                         action, action_pc);
                 if (error) {
                     return error;
                 }
@@ -2639,7 +2996,8 @@  nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
 
                 if (i == flower->action_count - 1) {
                     /* If this is the last action check csum calc again. */
-                    nl_msg_put_csum_act(request, flower, &act_index);
+                    nl_msg_put_csum_act(request, flower, action_pc,
+                                        &act_index);
                 }
             }
             break;
@@ -2660,14 +3018,15 @@  nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
                                               action->encap.tos,
                                               action->encap.ttl,
                                               action->encap.data,
-                                              action->encap.no_csum);
+                                              action->encap.no_csum,
+                                              action_pc);
                 nl_msg_put_act_flags(request);
                 nl_msg_end_nested(request, act_offset);
             }
             break;
             case TC_ACT_VLAN_POP: {
                 act_offset = nl_msg_start_nested(request, act_index++);
-                nl_msg_put_act_pop_vlan(request);
+                nl_msg_put_act_pop_vlan(request, action_pc);
                 nl_msg_put_act_flags(request);
                 nl_msg_end_nested(request, act_offset);
             }
@@ -2677,14 +3036,16 @@  nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
                 nl_msg_put_act_push_vlan(request,
                                          action->vlan.vlan_push_tpid,
                                          action->vlan.vlan_push_id,
-                                         action->vlan.vlan_push_prio);
+                                         action->vlan.vlan_push_prio,
+                                         action_pc);
                 nl_msg_put_act_flags(request);
                 nl_msg_end_nested(request, act_offset);
             }
             break;
             case TC_ACT_MPLS_POP: {
                 act_offset = nl_msg_start_nested(request, act_index++);
-                nl_msg_put_act_pop_mpls(request, action->mpls.proto);
+                nl_msg_put_act_pop_mpls(request, action->mpls.proto,
+                                        action_pc);
                 nl_msg_end_nested(request, act_offset);
             }
             break;
@@ -2692,7 +3053,8 @@  nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
                 act_offset = nl_msg_start_nested(request, act_index++);
                 nl_msg_put_act_push_mpls(request, action->mpls.proto,
                                          action->mpls.label, action->mpls.tc,
-                                         action->mpls.ttl, action->mpls.bos);
+                                         action->mpls.ttl, action->mpls.bos,
+                                         action_pc);
                 nl_msg_end_nested(request, act_offset);
             }
             break;
@@ -2700,7 +3062,7 @@  nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
                 act_offset = nl_msg_start_nested(request, act_index++);
                 nl_msg_put_act_set_mpls(request, action->mpls.label,
                                         action->mpls.tc, action->mpls.ttl,
-                                        action->mpls.bos);
+                                        action->mpls.bos, action_pc);
                 nl_msg_end_nested(request, act_offset);
             }
             break;
@@ -2735,12 +3097,13 @@  nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
                         nl_msg_put_act_mirred(request, ifindex, TC_ACT_STOLEN,
                                               TCA_EGRESS_REDIR);
                     }
+                    action->jump_action = JUMP_ACTION_STOP;
                 } else {
                     if (ingress) {
-                        nl_msg_put_act_mirred(request, ifindex, TC_ACT_PIPE,
+                        nl_msg_put_act_mirred(request, ifindex, action_pc,
                                               TCA_INGRESS_MIRROR);
                     } else {
-                        nl_msg_put_act_mirred(request, ifindex, TC_ACT_PIPE,
+                        nl_msg_put_act_mirred(request, ifindex, action_pc,
                                               TCA_EGRESS_MIRROR);
                     }
                 }
@@ -2768,12 +3131,26 @@  nl_msg_put_flower_acts(struct ofpbuf *request, struct tc_flower *flower)
             break;
             case TC_ACT_CT: {
                 act_offset = nl_msg_start_nested(request, act_index++);
-                nl_msg_put_act_ct(request, action);
+                nl_msg_put_act_ct(request, action, action_pc);
                 nl_msg_put_act_cookie(request, &flower->act_cookie);
                 nl_msg_end_nested(request, act_offset);
             }
             break;
+            case TC_ACT_POLICE_MTU: {
+                act_offset = nl_msg_start_nested(request, act_index++);
+                if (nl_msg_put_act_police_mtu(request, flower, action,
+                                              action_pc, i, act_index,
+                                              released)) {
+                    return -EOPNOTSUPP;
+                }
+                nl_msg_put_act_cookie(request, &flower->act_cookie);
+                nl_msg_put_act_flags(request);
+                nl_msg_end_nested(request, act_offset);
+            }
+            break;
             }
+
+            prev_action_pc = action_pc;
         }
     }
 
diff --git a/lib/tc.h b/lib/tc.h
index d6cdddd16..7ccd71d01 100644
--- a/lib/tc.h
+++ b/lib/tc.h
@@ -174,6 +174,7 @@  enum tc_action_type {
     TC_ACT_MPLS_SET,
     TC_ACT_GOTO,
     TC_ACT_CT,
+    TC_ACT_POLICE_MTU,
 };
 
 enum nat_type {
@@ -256,14 +257,19 @@  struct tc_action {
             bool force;
             bool commit;
         } ct;
-
         struct {
             struct tc_flower_key key;
             struct tc_flower_key mask;
         } rewrite;
-     };
+        struct {
+            uint32_t result_jump;
+            uint16_t mtu;
+        } police;
+    };
 
-     enum tc_action_type type;
+    enum tc_action_type type;
+    uint32_t jump_action;
+#define JUMP_ACTION_STOP 0xffffffff
 };
 
 /* assert that if we overflow with a masked write of uint32_t to the last byte