Implement setting and printing flags for actions that support this new
field (gact, csum, mirred, tunnel_key, vlan, ct). Update docs for
affected actions.

Example usage of fast init flag (only supported flag value at the
moment):

 # tc actions add action gact drop fast_init index 1
 # tc -s actions ls action gact
 total acts 1

        action order 0: gact action drop
         random type none pass val 0
         fast_init
         index 1 ref 1 bind 0 installed 7 sec used 7 sec
        Action statistics:
        Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0)
        backlog 0b 0p requeues 0

Signed-off-by: Vlad Buslov <vla...@mellanox.com>
Acked-by: Jiri Pirko <j...@mellanox.com>
---
 include/uapi/linux/pkt_cls.h              |  8 ++++++++
 include/uapi/linux/tc_act/tc_csum.h       |  1 +
 include/uapi/linux/tc_act/tc_ct.h         |  1 +
 include/uapi/linux/tc_act/tc_gact.h       |  1 +
 include/uapi/linux/tc_act/tc_mirred.h     |  1 +
 include/uapi/linux/tc_act/tc_tunnel_key.h |  1 +
 include/uapi/linux/tc_act/tc_vlan.h       |  1 +
 man/man8/tc-csum.8                        |  4 ++++
 man/man8/tc-mirred.8                      |  4 ++++
 man/man8/tc-tunnel_key.8                  |  4 ++++
 man/man8/tc-vlan.8                        |  7 ++++++-
 tc/m_csum.c                               | 17 ++++++++++++++++-
 tc/m_ct.c                                 | 19 ++++++++++++++++---
 tc/m_gact.c                               | 22 ++++++++++++++++++++--
 tc/m_mirred.c                             | 19 +++++++++++++++++--
 tc/m_tunnel_key.c                         | 17 +++++++++++++++--
 tc/m_vlan.c                               | 19 ++++++++++++++++---
 17 files changed, 132 insertions(+), 14 deletions(-)

diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h
index a6aa466fac9e..56664854a5ab 100644
--- a/include/uapi/linux/pkt_cls.h
+++ b/include/uapi/linux/pkt_cls.h
@@ -113,6 +113,14 @@ enum tca_id {
 
 #define TCA_ID_MAX __TCA_ID_MAX
 
+/* act flags definitions */
+#define TCA_ACT_FLAGS_FAST_INIT        (1 << 0) /* prefer action update rate
+                                         * (slow-path), even at the cost of
+                                         * reduced software data-path
+                                         * performance (intended to be used for
+                                         * hardware-offloaded actions)
+                                         */
+
 struct tc_police {
        __u32                   index;
        int                     action;
diff --git a/include/uapi/linux/tc_act/tc_csum.h 
b/include/uapi/linux/tc_act/tc_csum.h
index 94b2044929de..14eddaca85f8 100644
--- a/include/uapi/linux/tc_act/tc_csum.h
+++ b/include/uapi/linux/tc_act/tc_csum.h
@@ -10,6 +10,7 @@ enum {
        TCA_CSUM_PARMS,
        TCA_CSUM_TM,
        TCA_CSUM_PAD,
+       TCA_CSUM_FLAGS,
        __TCA_CSUM_MAX
 };
 #define TCA_CSUM_MAX (__TCA_CSUM_MAX - 1)
diff --git a/include/uapi/linux/tc_act/tc_ct.h 
b/include/uapi/linux/tc_act/tc_ct.h
index 5fb1d7ac1027..82ea92b59d3d 100644
--- a/include/uapi/linux/tc_act/tc_ct.h
+++ b/include/uapi/linux/tc_act/tc_ct.h
@@ -22,6 +22,7 @@ enum {
        TCA_CT_NAT_PORT_MIN,    /* be16 */
        TCA_CT_NAT_PORT_MAX,    /* be16 */
        TCA_CT_PAD,
+       TCA_CT_FLAGS,
        __TCA_CT_MAX
 };
 
diff --git a/include/uapi/linux/tc_act/tc_gact.h 
b/include/uapi/linux/tc_act/tc_gact.h
index 37e5392e02c7..b621b7df9919 100644
--- a/include/uapi/linux/tc_act/tc_gact.h
+++ b/include/uapi/linux/tc_act/tc_gact.h
@@ -26,6 +26,7 @@ enum {
        TCA_GACT_PARMS,
        TCA_GACT_PROB,
        TCA_GACT_PAD,
+       TCA_GACT_FLAGS,
        __TCA_GACT_MAX
 };
 #define TCA_GACT_MAX (__TCA_GACT_MAX - 1)
diff --git a/include/uapi/linux/tc_act/tc_mirred.h 
b/include/uapi/linux/tc_act/tc_mirred.h
index 2500a0005d05..322108890807 100644
--- a/include/uapi/linux/tc_act/tc_mirred.h
+++ b/include/uapi/linux/tc_act/tc_mirred.h
@@ -21,6 +21,7 @@ enum {
        TCA_MIRRED_TM,
        TCA_MIRRED_PARMS,
        TCA_MIRRED_PAD,
+       TCA_MIRRED_FLAGS,
        __TCA_MIRRED_MAX
 };
 #define TCA_MIRRED_MAX (__TCA_MIRRED_MAX - 1)
diff --git a/include/uapi/linux/tc_act/tc_tunnel_key.h 
b/include/uapi/linux/tc_act/tc_tunnel_key.h
index 41c8b462c177..f572101f6adc 100644
--- a/include/uapi/linux/tc_act/tc_tunnel_key.h
+++ b/include/uapi/linux/tc_act/tc_tunnel_key.h
@@ -39,6 +39,7 @@ enum {
                                         */
        TCA_TUNNEL_KEY_ENC_TOS,         /* u8 */
        TCA_TUNNEL_KEY_ENC_TTL,         /* u8 */
+       TCA_TUNNEL_KEY_FLAGS,
        __TCA_TUNNEL_KEY_MAX,
 };
 
diff --git a/include/uapi/linux/tc_act/tc_vlan.h 
b/include/uapi/linux/tc_act/tc_vlan.h
index 168995b54a70..b2847ed14f08 100644
--- a/include/uapi/linux/tc_act/tc_vlan.h
+++ b/include/uapi/linux/tc_act/tc_vlan.h
@@ -30,6 +30,7 @@ enum {
        TCA_VLAN_PUSH_VLAN_PROTOCOL,
        TCA_VLAN_PAD,
        TCA_VLAN_PUSH_VLAN_PRIORITY,
+       TCA_VLAN_FLAGS,
        __TCA_VLAN_MAX,
 };
 #define TCA_VLAN_MAX (__TCA_VLAN_MAX - 1)
diff --git a/man/man8/tc-csum.8 b/man/man8/tc-csum.8
index 65724b88d0b6..c93707948d80 100644
--- a/man/man8/tc-csum.8
+++ b/man/man8/tc-csum.8
@@ -6,6 +6,7 @@ csum - checksum update action
 .in +8
 .ti -8
 .BR tc " ... " "action csum"
+.RB "[ " fast_init " ] "
 .I UPDATE
 
 .ti -8
@@ -52,6 +53,9 @@ SCTP header
 .TP
 .B SWEETS
 These are merely syntactic sugar and ignored internally.
+.TP
+.B fast_init
+Prefer initialization speed over run time fast-path performance.
 .SH EXAMPLES
 The following performs stateless NAT for incoming packets from 192.0.2.100 to
 new destination 198.51.100.1. Assuming these are UDP
diff --git a/man/man8/tc-mirred.8 b/man/man8/tc-mirred.8
index 38833b452d92..0cd39f454347 100644
--- a/man/man8/tc-mirred.8
+++ b/man/man8/tc-mirred.8
@@ -7,6 +7,7 @@ mirred - mirror/redirect action
 .ti -8
 .BR tc " ... " "action mirred"
 .I DIRECTION ACTION
+.RB "[ " fast_init " ] "
 .RB "[ " index
 .IR INDEX " ] "
 .BI dev " DEVICENAME"
@@ -49,6 +50,9 @@ is a 32bit unsigned integer greater than zero.
 .TP
 .BI dev " DEVICENAME"
 Specify the network interface to redirect or mirror to.
+.TP
+.B fast_init
+Prefer initialization speed over run time fast-path performance.
 .SH EXAMPLES
 Limit ingress bandwidth on eth0 to 1mbit/s, redirect exceeding traffic to lo 
for
 debugging purposes:
diff --git a/man/man8/tc-tunnel_key.8 b/man/man8/tc-tunnel_key.8
index 2145eb62e70e..7b827ae0d257 100644
--- a/man/man8/tc-tunnel_key.8
+++ b/man/man8/tc-tunnel_key.8
@@ -7,6 +7,7 @@ tunnel_key - Tunnel metadata manipulation
 .ti -8
 .BR tc " ... " "action tunnel_key" " { " unset " | "
 .IR SET " }"
+.RB "[ " fast_init " ] "
 
 .ti -8
 .IR SET " := "
@@ -114,6 +115,9 @@ If using
 with IPv6, be sure you know what you are doing. Zero UDP checksums provide
 weaker protection against corrupted packets. See RFC6935 for details.
 .RE
+.TP
+.B fast_init
+Prefer initialization speed over run time fast-path performance.
 .SH EXAMPLES
 The following example encapsulates incoming ICMP packets on eth0 into a vxlan
 tunnel, by setting metadata to VNI 11, source IP 11.11.0.1 and destination IP
diff --git a/man/man8/tc-vlan.8 b/man/man8/tc-vlan.8
index f5ffc25f054e..e69322fefd70 100644
--- a/man/man8/tc-vlan.8
+++ b/man/man8/tc-vlan.8
@@ -6,7 +6,9 @@ vlan - vlan manipulation module
 .in +8
 .ti -8
 .BR tc " ... " "action vlan" " { " pop " |"
-.IR PUSH " | " MODIFY " } [ " CONTROL " ]"
+.IR PUSH " | " MODIFY " } [ "
+.BR fast_init " ] [ "
+.IR CONTROL " ]"
 
 .ti -8
 .IR PUSH " := "
@@ -94,6 +96,9 @@ Continue classification with next filter in line.
 Return to calling qdisc for packet processing. This ends the classification
 process.
 .RE
+.TP
+.B fast_init
+Prefer initialization speed over run time fast-path performance.
 .SH EXAMPLES
 The following example encapsulates incoming ICMP packets on eth0 from 10.0.0.2
 into VLAN ID 123:
diff --git a/tc/m_csum.c b/tc/m_csum.c
index 3e3dc251ea38..64cc5a96ff5c 100644
--- a/tc/m_csum.c
+++ b/tc/m_csum.c
@@ -22,7 +22,7 @@
 static void
 explain(void)
 {
-       fprintf(stderr, "Usage: ... csum <UPDATE>\n"
+       fprintf(stderr, "Usage: ... csum [fast_init] <UPDATE>\n"
                        "Where: UPDATE := <TARGET> [<UPDATE>]\n"
                        "       TARGET := { ip4h | icmp | igmp | tcp | udp | 
udplite | sctp | <SWEETS> }\n"
                        "       SWEETS := { and | or | \'+\' }\n");
@@ -88,6 +88,7 @@ static int
 parse_csum(struct action_util *a, int *argc_p,
           char ***argv_p, int tca_id, struct nlmsghdr *n)
 {
+       struct nla_bitfield32 flags = { 0 };
        struct tc_csum sel = {};
 
        int argc = *argc_p;
@@ -106,6 +107,11 @@ parse_csum(struct action_util *a, int *argc_p,
                        }
                        ok++;
                        continue;
+               } else if (matches(*argv, "fast_init") == 0) {
+                       flags.value |= TCA_ACT_FLAGS_FAST_INIT;
+                       flags.selector |= TCA_ACT_FLAGS_FAST_INIT;
+                       NEXT_ARG_FWD();
+                       continue;
                } else if (matches(*argv, "help") == 0) {
                        usage();
                } else {
@@ -140,6 +146,8 @@ parse_csum(struct action_util *a, int *argc_p,
 
        tail = addattr_nest(n, MAX_MSG, tca_id);
        addattr_l(n, MAX_MSG, TCA_CSUM_PARMS, &sel, sizeof(sel));
+       addattr_l(n, MAX_MSG, TCA_CSUM_FLAGS, &flags,
+                 sizeof(struct nla_bitfield32));
        addattr_nest_end(n, tail);
 
        *argc_p = argc;
@@ -206,6 +214,13 @@ print_csum(struct action_util *au, FILE *f, struct rtattr 
*arg)
        print_string(PRINT_ANY, "csum", "(%s) ", buf);
 
        print_action_control(f, "action ", sel->action, "\n");
+       if (tb[TCA_CSUM_FLAGS]) {
+               struct nla_bitfield32 *flags = RTA_DATA(tb[TCA_CSUM_FLAGS]);
+
+               if (flags->selector & TCA_ACT_FLAGS_FAST_INIT)
+                       print_bool(PRINT_ANY, "fast_init", "\n\t fast_init",
+                                  flags->value & TCA_ACT_FLAGS_FAST_INIT);
+       }
        print_uint(PRINT_ANY, "index", "\tindex %u", sel->index);
        print_int(PRINT_ANY, "ref", " ref %d", sel->refcnt);
        print_int(PRINT_ANY, "bind", " bind %d", sel->bindcnt);
diff --git a/tc/m_ct.c b/tc/m_ct.c
index 8589cb9a3c51..02947e5075c8 100644
--- a/tc/m_ct.c
+++ b/tc/m_ct.c
@@ -19,9 +19,9 @@ static void
 usage(void)
 {
        fprintf(stderr,
-               "Usage: ct clear\n"
-               "       ct commit [force] [zone ZONE] [mark MASKED_MARK] [label 
MASKED_LABEL] [nat NAT_SPEC]\n"
-               "       ct [nat] [zone ZONE]\n"
+               "Usage: ct clear [fast_init]\n"
+               "       ct commit [fast_init] [force] [zone ZONE] [mark 
MASKED_MARK] [label MASKED_LABEL] [nat NAT_SPEC]\n"
+               "       ct [fast_init] [nat] [zone ZONE]\n"
                "Where: ZONE is the conntrack zone table number\n"
                "       NAT_SPEC is {src|dst} addr addr1[-addr2] [port 
port1[-port2]]\n"
                "\n");
@@ -200,6 +200,7 @@ static int
 parse_ct(struct action_util *a, int *argc_p, char ***argv_p, int tca_id,
                struct nlmsghdr *n)
 {
+       struct nla_bitfield32 flags = { 0 };
        struct tc_ct sel = {};
        char **argv = *argv_p;
        struct rtattr *tail;
@@ -281,6 +282,9 @@ parse_ct(struct action_util *a, int *argc_p, char 
***argv_p, int tca_id,
                                fprintf(stderr, "ct: Illegal \"label\"\n");
                                return -1;
                        }
+               } else if (matches(*argv, "fast_init") == 0) {
+                       flags.value |= TCA_ACT_FLAGS_FAST_INIT;
+                       flags.selector |= TCA_ACT_FLAGS_FAST_INIT;
                } else if (matches(*argv, "help") == 0) {
                        usage();
                } else {
@@ -320,6 +324,8 @@ parse_ct(struct action_util *a, int *argc_p, char 
***argv_p, int tca_id,
 
        addattr16(n, MAX_MSG, TCA_CT_ACTION, ct_action);
        addattr_l(n, MAX_MSG, TCA_CT_PARMS, &sel, sizeof(sel));
+       addattr_l(n, MAX_MSG, TCA_CT_FLAGS, &flags,
+                 sizeof(struct nla_bitfield32));
        addattr_nest_end(n, tail);
 
        *argc_p = argc;
@@ -473,6 +479,13 @@ static int print_ct(struct action_util *au, FILE *f, 
struct rtattr *arg)
        ct_print_nat(ct_action, tb);
 
        print_action_control(f, " ", p->action, "");
+       if (tb[TCA_CT_FLAGS]) {
+               struct nla_bitfield32 *flags = RTA_DATA(tb[TCA_CT_FLAGS]);
+
+               if (flags->selector & TCA_ACT_FLAGS_FAST_INIT)
+                       print_bool(PRINT_ANY, "fast_init", "\n\t fast_init",
+                                  flags->value & TCA_ACT_FLAGS_FAST_INIT);
+       }
 
        print_uint(PRINT_ANY, "index", "\n\t index %u", p->index);
        print_int(PRINT_ANY, "ref", " ref %d", p->refcnt);
diff --git a/tc/m_gact.c b/tc/m_gact.c
index dca2a2f9692f..ce5f22b59da0 100644
--- a/tc/m_gact.c
+++ b/tc/m_gact.c
@@ -42,7 +42,7 @@ static void
 explain(void)
 {
 #ifdef CONFIG_GACT_PROB
-       fprintf(stderr, "Usage: ... gact <ACTION> [RAND] [INDEX]\n");
+       fprintf(stderr, "Usage: ... gact <ACTION> [RAND] [fast_init] 
[INDEX]\n");
        fprintf(stderr,
                "Where: \tACTION := reclassify | drop | continue | pass | pipe 
|\n"
                "       \t          goto chain <CHAIN_INDEX> | jump 
<JUMP_COUNT>\n"
@@ -53,7 +53,7 @@ explain(void)
                        "\tINDEX := index value used\n"
                        "\n");
 #else
-       fprintf(stderr, "Usage: ... gact <ACTION> [INDEX]\n"
+       fprintf(stderr, "Usage: ... gact <ACTION> [fast_init] [INDEX]\n"
                "Where: \tACTION := reclassify | drop | continue | pass | pipe 
|\n"
                "       \t          goto chain <CHAIN_INDEX> | jump 
<JUMP_COUNT>\n"
                "\tINDEX := index value used\n"
@@ -74,6 +74,7 @@ static int
 parse_gact(struct action_util *a, int *argc_p, char ***argv_p,
           int tca_id, struct nlmsghdr *n)
 {
+       struct nla_bitfield32 flags = { 0 };
        int argc = *argc_p;
        char **argv = *argv_p;
        struct tc_gact p = { 0 };
@@ -133,6 +134,12 @@ parse_gact(struct action_util *a, int *argc_p, char 
***argv_p,
        }
 #endif
 
+       if (argc > 0 && matches(*argv, "fast_init") == 0) {
+               flags.value |= TCA_ACT_FLAGS_FAST_INIT;
+               flags.selector |= TCA_ACT_FLAGS_FAST_INIT;
+               NEXT_ARG_FWD();
+       }
+
        if (argc > 0) {
                if (matches(*argv, "index") == 0) {
 skip_args:
@@ -154,6 +161,9 @@ skip_args:
        if (rd)
                addattr_l(n, MAX_MSG, TCA_GACT_PROB, &pp, sizeof(pp));
 #endif
+       addattr_l(n, MAX_MSG, TCA_GACT_FLAGS, &flags,
+                 sizeof(struct nla_bitfield32));
+
        addattr_nest_end(n, tail);
 
        *argc_p = argc;
@@ -199,6 +209,14 @@ print_gact(struct action_util *au, FILE *f, struct rtattr 
*arg)
        print_int(PRINT_ANY, "val", "val %d", pp->pval);
        close_json_object();
 #endif
+       if (tb[TCA_GACT_FLAGS]) {
+               struct nla_bitfield32 *flags = RTA_DATA(tb[TCA_GACT_FLAGS]);
+
+               if (flags->selector & TCA_ACT_FLAGS_FAST_INIT)
+                       print_bool(PRINT_ANY, "fast_init", "\n\t fast_init",
+                                  flags->value & TCA_ACT_FLAGS_FAST_INIT);
+       }
+
        print_uint(PRINT_ANY, "index", "\n\t index %u", p->index);
        print_int(PRINT_ANY, "ref", " ref %d", p->refcnt);
        print_int(PRINT_ANY, "bind", " bind %d", p->bindcnt);
diff --git a/tc/m_mirred.c b/tc/m_mirred.c
index 132095237929..b95f4611891e 100644
--- a/tc/m_mirred.c
+++ b/tc/m_mirred.c
@@ -29,7 +29,7 @@ static void
 explain(void)
 {
        fprintf(stderr,
-               "Usage: mirred <DIRECTION> <ACTION> [index INDEX] <dev 
DEVICENAME>\n"
+               "Usage: mirred <DIRECTION> <ACTION> [fast_init] [index INDEX] 
<dev DEVICENAME>\n"
                "where:\n"
                "\tDIRECTION := <ingress | egress>\n"
                "\tACTION := <mirror | redirect>\n"
@@ -92,7 +92,7 @@ static int
 parse_direction(struct action_util *a, int *argc_p, char ***argv_p,
                int tca_id, struct nlmsghdr *n)
 {
-
+       struct nla_bitfield32 flags = { 0 };
        int argc = *argc_p;
        char **argv = *argv_p;
        int ok = 0, iok = 0, mirror = 0, redir = 0, ingress = 0, egress = 0;
@@ -125,6 +125,11 @@ parse_direction(struct action_util *a, int *argc_p, char 
***argv_p,
                        NEXT_ARG();
                        ok++;
                        continue;
+               } else if (matches(*argv, "fast_init") == 0) {
+                       flags.value |= TCA_ACT_FLAGS_FAST_INIT;
+                       flags.selector |= TCA_ACT_FLAGS_FAST_INIT;
+                       NEXT_ARG_FWD();
+                       continue;
                } else {
 
                        if (matches(*argv, "index") == 0) {
@@ -225,6 +230,8 @@ parse_direction(struct action_util *a, int *argc_p, char 
***argv_p,
 
        tail = addattr_nest(n, MAX_MSG, tca_id);
        addattr_l(n, MAX_MSG, TCA_MIRRED_PARMS, &p, sizeof(p));
+       addattr_l(n, MAX_MSG, TCA_MIRRED_FLAGS, &flags,
+                 sizeof(struct nla_bitfield32));
        addattr_nest_end(n, tail);
 
        *argc_p = argc;
@@ -307,6 +314,14 @@ print_mirred(struct action_util *au, FILE *f, struct 
rtattr *arg)
        print_string(PRINT_ANY, "to_dev", " to device %s)", dev);
        print_action_control(f, " ", p->action, "");
 
+       if (tb[TCA_MIRRED_FLAGS]) {
+               struct nla_bitfield32 *flags = RTA_DATA(tb[TCA_MIRRED_FLAGS]);
+
+               if (flags->selector & TCA_ACT_FLAGS_FAST_INIT)
+                       print_bool(PRINT_ANY, "fast_init", "\n\t fast_init",
+                                  flags->value & TCA_ACT_FLAGS_FAST_INIT);
+       }
+
        print_uint(PRINT_ANY, "index", "\n \tindex %u", p->index);
        print_int(PRINT_ANY, "ref", " ref %d", p->refcnt);
        print_int(PRINT_ANY, "bind", " bind %d", p->bindcnt);
diff --git a/tc/m_tunnel_key.c b/tc/m_tunnel_key.c
index 4e65e444776a..e8b96f8c008f 100644
--- a/tc/m_tunnel_key.c
+++ b/tc/m_tunnel_key.c
@@ -22,8 +22,8 @@
 static void explain(void)
 {
        fprintf(stderr,
-               "Usage: tunnel_key unset\n"
-               "       tunnel_key set <TUNNEL_KEY>\n"
+               "Usage: tunnel_key [fast_init] unset\n"
+               "       tunnel_key [fast_init] set <TUNNEL_KEY>\n"
                "Where TUNNEL_KEY is a combination of:\n"
                "id <TUNNELID>\n"
                "src_ip <IP> (mandatory)\n"
@@ -209,6 +209,7 @@ static int tunnel_key_parse_tos_ttl(char *str, int type, 
struct nlmsghdr *n)
 static int parse_tunnel_key(struct action_util *a, int *argc_p, char ***argv_p,
                            int tca_id, struct nlmsghdr *n)
 {
+       struct nla_bitfield32 flags = { 0 };
        struct tc_tunnel_key parm = {};
        char **argv = *argv_p;
        int argc = *argc_p;
@@ -309,6 +310,9 @@ static int parse_tunnel_key(struct action_util *a, int 
*argc_p, char ***argv_p,
                        csum = 0;
                } else if (matches(*argv, "help") == 0) {
                        usage();
+               } else if (matches(*argv, "fast_init") == 0) {
+                       flags.value |= TCA_ACT_FLAGS_FAST_INIT;
+                       flags.selector |= TCA_ACT_FLAGS_FAST_INIT;
                } else {
                        break;
                }
@@ -341,6 +345,8 @@ static int parse_tunnel_key(struct action_util *a, int 
*argc_p, char ***argv_p,
 
        parm.t_action = action;
        addattr_l(n, MAX_MSG, TCA_TUNNEL_KEY_PARMS, &parm, sizeof(parm));
+       addattr_l(n, MAX_MSG, TCA_TUNNEL_KEY_FLAGS, &flags,
+                 sizeof(struct nla_bitfield32));
        addattr_nest_end(n, tail);
 
        *argc_p = argc;
@@ -529,6 +535,13 @@ static int print_tunnel_key(struct action_util *au, FILE 
*f, struct rtattr *arg)
                break;
        }
        print_action_control(f, " ", parm->action, "");
+       if (tb[TCA_TUNNEL_KEY_FLAGS]) {
+               struct nla_bitfield32 *flags = 
RTA_DATA(tb[TCA_TUNNEL_KEY_FLAGS]);
+
+               if (flags->selector & TCA_ACT_FLAGS_FAST_INIT)
+                       print_bool(PRINT_ANY, "fast_init", "\n\t fast_init",
+                                  flags->value & TCA_ACT_FLAGS_FAST_INIT);
+       }
 
        print_string(PRINT_FP, NULL, "%s", _SL_);
        print_uint(PRINT_ANY, "index", "\t index %u", parm->index);
diff --git a/tc/m_vlan.c b/tc/m_vlan.c
index 9c8071e9dbbe..c54829ce297f 100644
--- a/tc/m_vlan.c
+++ b/tc/m_vlan.c
@@ -28,9 +28,9 @@ static const char * const action_names[] = {
 static void explain(void)
 {
        fprintf(stderr,
-               "Usage: vlan pop\n"
-               "       vlan push [ protocol VLANPROTO ] id VLANID [ priority 
VLANPRIO ] [CONTROL]\n"
-               "       vlan modify [ protocol VLANPROTO ] id VLANID [ priority 
VLANPRIO ] [CONTROL]\n"
+               "Usage: vlan pop [fast_init]\n"
+               "       vlan push [fast_init] [ protocol VLANPROTO ] id VLANID 
[ priority VLANPRIO ] [CONTROL]\n"
+               "       vlan modify [fast_init] [ protocol VLANPROTO ] id 
VLANID [ priority VLANPRIO ] [CONTROL]\n"
                "       VLANPROTO is one of 802.1Q or 802.1AD\n"
                "            with default: 802.1Q\n"
                "       CONTROL := reclassify | pipe | drop | continue | pass 
|\n"
@@ -59,6 +59,7 @@ static void unexpected(const char *arg)
 static int parse_vlan(struct action_util *a, int *argc_p, char ***argv_p,
                      int tca_id, struct nlmsghdr *n)
 {
+       struct nla_bitfield32 flags = { 0 };
        int argc = *argc_p;
        char **argv = *argv_p;
        struct rtattr *tail;
@@ -119,6 +120,9 @@ static int parse_vlan(struct action_util *a, int *argc_p, 
char ***argv_p,
                        if (get_u8(&prio, *argv, 0) || (prio & ~0x7))
                                invarg("prio is invalid", *argv);
                        prio_set = 1;
+               } else if (matches(*argv, "fast_init") == 0) {
+                       flags.value |= TCA_ACT_FLAGS_FAST_INIT;
+                       flags.selector |= TCA_ACT_FLAGS_FAST_INIT;
                } else if (matches(*argv, "help") == 0) {
                        usage();
                } else {
@@ -167,6 +171,8 @@ static int parse_vlan(struct action_util *a, int *argc_p, 
char ***argv_p,
        }
        if (prio_set)
                addattr8(n, MAX_MSG, TCA_VLAN_PUSH_VLAN_PRIORITY, prio);
+       addattr_l(n, MAX_MSG, TCA_VLAN_FLAGS, &flags,
+                 sizeof(struct nla_bitfield32));
 
        addattr_nest_end(n, tail);
 
@@ -218,6 +224,13 @@ static int print_vlan(struct action_util *au, FILE *f, 
struct rtattr *arg)
                break;
        }
        print_action_control(f, " ", parm->action, "");
+       if (tb[TCA_VLAN_FLAGS]) {
+               struct nla_bitfield32 *flags = RTA_DATA(tb[TCA_VLAN_FLAGS]);
+
+               if (flags->selector & TCA_ACT_FLAGS_FAST_INIT)
+                       print_bool(PRINT_ANY, "fast_init", "\n\t fast_init",
+                                  flags->value & TCA_ACT_FLAGS_FAST_INIT);
+       }
 
        print_uint(PRINT_ANY, "index", "\n\t index %u", parm->index);
        print_int(PRINT_ANY, "ref", " ref %d", parm->refcnt);
-- 
2.21.0

Reply via email to