Signed-off-by: Chris Mi <chr...@mellanox.com>
---
 tc/m_action.c  |  90 ++++++++++++++++++++++++++++++++----------
 tc/tc.c        |  70 ++++++++++++++++++++++++++-------
 tc/tc_common.h |   8 +++-
 tc/tc_filter.c | 121 +++++++++++++++++++++++++++++++++++++++++----------------
 4 files changed, 221 insertions(+), 68 deletions(-)

diff --git a/tc/m_action.c b/tc/m_action.c
index fc422364..2e79034d 100644
--- a/tc/m_action.c
+++ b/tc/m_action.c
@@ -23,6 +23,7 @@
 #include <arpa/inet.h>
 #include <string.h>
 #include <dlfcn.h>
+#include <errno.h>
 
 #include "utils.h"
 #include "tc_common.h"
@@ -546,40 +547,87 @@ bad_val:
        return ret;
 }
 
+typedef struct {
+       struct nlmsghdr         n;
+       struct tcamsg           t;
+       char                    buf[MAX_MSG];
+} tc_action_req;
+
+static tc_action_req *action_reqs;
+static struct iovec msg_iov[MSG_IOV_MAX];
+
+void free_action_reqs(void)
+{
+       free(action_reqs);
+}
+
+static tc_action_req *get_action_req(int batch_size, int index)
+{
+       tc_action_req *req;
+
+       if (action_reqs == NULL) {
+               action_reqs = malloc(batch_size * sizeof (tc_action_req));
+               if (action_reqs == NULL)
+                       return NULL;
+       }
+       req = &action_reqs[index];
+       memset(req, 0, sizeof (*req));
+
+       return req;
+}
+
 static int tc_action_modify(int cmd, unsigned int flags,
-                           int *argc_p, char ***argv_p)
+                           int *argc_p, char ***argv_p,
+                           int batch_size, int index, bool send)
 {
        int argc = *argc_p;
        char **argv = *argv_p;
        int ret = 0;
-       struct {
-               struct nlmsghdr         n;
-               struct tcamsg           t;
-               char                    buf[MAX_MSG];
-       } req = {
-               .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg)),
-               .n.nlmsg_flags = NLM_F_REQUEST | flags,
-               .n.nlmsg_type = cmd,
-               .t.tca_family = AF_UNSPEC,
+       tc_action_req *req;
+       struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
+       struct iovec *iov = &msg_iov[index];
+
+       req = get_action_req(batch_size, index);
+       if (req == NULL) {
+               fprintf(stderr, "get_action_req error: not enough buffer\n");
+               return -ENOMEM;
+       }
+
+       req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcamsg));
+       req->n.nlmsg_flags = NLM_F_REQUEST | flags;
+       req->n.nlmsg_type = cmd;
+       req->t.tca_family = AF_UNSPEC;
+       struct rtattr *tail = NLMSG_TAIL(&req->n);
+
+       struct msghdr msg = {
+               .msg_name = &nladdr,
+               .msg_namelen = sizeof(nladdr),
+               .msg_iov = msg_iov,
+               .msg_iovlen = index + 1,
        };
-       struct rtattr *tail = NLMSG_TAIL(&req.n);
 
        argc -= 1;
        argv += 1;
-       if (parse_action(&argc, &argv, TCA_ACT_TAB, &req.n)) {
+       if (parse_action(&argc, &argv, TCA_ACT_TAB, &req->n)) {
                fprintf(stderr, "Illegal \"action\"\n");
                return -1;
        }
-       tail->rta_len = (void *) NLMSG_TAIL(&req.n) - (void *) tail;
+       tail->rta_len = (void *) NLMSG_TAIL(&req->n) - (void *) tail;
+
+       *argc_p = argc;
+       *argv_p = argv;
+
+       iov->iov_base = &req->n;
+       iov->iov_len = req->n.nlmsg_len;
+
+       if (!send)
+               return 0;
 
-       if (rtnl_talk(&rth, &req.n, NULL) < 0) {
+       if (rtnl_talk_msg(&rth, &msg, NULL) < 0) {
                fprintf(stderr, "We have an error talking to the kernel\n");
                ret = -1;
        }
 
-       *argc_p = argc;
-       *argv_p = argv;
-
        return ret;
 }
 
@@ -679,7 +727,7 @@ bad_val:
        return ret;
 }
 
-int do_action(int argc, char **argv)
+int do_action(int argc, char **argv, int batch_size, int index, bool send)
 {
 
        int ret = 0;
@@ -689,12 +737,14 @@ int do_action(int argc, char **argv)
                if (matches(*argv, "add") == 0) {
                        ret =  tc_action_modify(RTM_NEWACTION,
                                                NLM_F_EXCL | NLM_F_CREATE,
-                                               &argc, &argv);
+                                               &argc, &argv, batch_size,
+                                               index, send);
                } else if (matches(*argv, "change") == 0 ||
                          matches(*argv, "replace") == 0) {
                        ret = tc_action_modify(RTM_NEWACTION,
                                               NLM_F_CREATE | NLM_F_REPLACE,
-                                              &argc, &argv);
+                                              &argc, &argv, batch_size,
+                                              index, send);
                } else if (matches(*argv, "delete") == 0) {
                        argc -= 1;
                        argv += 1;
diff --git a/tc/tc.c b/tc/tc.c
index ad9f07e9..90ce4ce2 100644
--- a/tc/tc.c
+++ b/tc/tc.c
@@ -189,20 +189,20 @@ static void usage(void)
        fprintf(stderr, "Usage: tc [ OPTIONS ] OBJECT { COMMAND | help }\n"
                        "       tc [-force] -batch filename\n"
                        "where  OBJECT := { qdisc | class | filter | action | 
monitor | exec }\n"
-                       "       OPTIONS := { -s[tatistics] | -d[etails] | 
-r[aw] | -p[retty] | -b[atch] [filename] | -n[etns] name |\n"
+                       "       OPTIONS := { -s[tatistics] | -d[etails] | 
-r[aw] | -p[retty] | -b[atch] [filename] | -bs | -batchsize [size] | -n[etns] 
name |\n"
                        "                    -nm | -nam[es] | { -cf | -conf } 
path } | -j[son]\n");
 }
 
-static int do_cmd(int argc, char **argv)
+static int do_cmd(int argc, char **argv, int batch_size, int index, bool send)
 {
        if (matches(*argv, "qdisc") == 0)
                return do_qdisc(argc-1, argv+1);
        if (matches(*argv, "class") == 0)
                return do_class(argc-1, argv+1);
        if (matches(*argv, "filter") == 0)
-               return do_filter(argc-1, argv+1);
+               return do_filter(argc-1, argv+1, batch_size, index, send);
        if (matches(*argv, "actions") == 0)
-               return do_action(argc-1, argv+1);
+               return do_action(argc-1, argv+1, batch_size, index, send);
        if (matches(*argv, "monitor") == 0)
                return do_tcmonitor(argc-1, argv+1);
        if (matches(*argv, "exec") == 0)
@@ -217,11 +217,15 @@ static int do_cmd(int argc, char **argv)
        return -1;
 }
 
-static int batch(const char *name)
+static int batch(const char *name, int batch_size)
 {
+       bool lastline = false;
+       int msg_iov_index = 0;
+       char *line2 = NULL;
        char *line = NULL;
        size_t len = 0;
        int ret = 0;
+       bool send;
 
        batch_mode = 1;
        if (name && strcmp(name, "-") != 0) {
@@ -240,23 +244,50 @@ static int batch(const char *name)
        }
 
        cmdlineno = 0;
-       while (getcmdline(&line, &len, stdin) != -1) {
+       if (getcmdline(&line, &len, stdin) == -1)
+               goto Exit;
+       do {
                char *largv[100];
                int largc;
 
+               if (getcmdline(&line2, &len, stdin) == -1)
+                       lastline = true;
+
                largc = makeargs(line, largv, 100);
+
+               line = line2;
+               line2 = NULL;
+               len = 0;
+
                if (largc == 0)
                        continue;       /* blank line */
 
-               if (do_cmd(largc, largv)) {
-                       fprintf(stderr, "Command failed %s:%d\n", name, 
cmdlineno);
+               /*
+                * In batch mode, if we haven't accumulated enough commands
+                * and this is not the last command, don't send the message
+                * immediately.
+                */
+               if (batch_size > 1 && msg_iov_index + 1 != batch_size
+                   && !lastline)
+                       send = false;
+               else
+                       send = true;
+
+               ret = do_cmd(largc, largv, batch_size, msg_iov_index++, send);
+               if (ret < 0) {
+                       fprintf(stderr, "Command failed %s:%d\n", name,
+                               cmdlineno);
                        ret = 1;
                        if (!force)
                                break;
                }
-       }
-       if (line)
-               free(line);
+               msg_iov_index %= batch_size;
+       } while (!lastline);
+
+       free_filter_reqs();
+       free_action_reqs();
+Exit:
+       free(line);
 
        rtnl_close(&rth);
        return ret;
@@ -267,6 +298,7 @@ int main(int argc, char **argv)
 {
        int ret;
        char *batch_file = NULL;
+       int batch_size = 1;
 
        while (argc > 1) {
                if (argv[1][0] != '-')
@@ -297,6 +329,16 @@ int main(int argc, char **argv)
                        if (argc <= 1)
                                usage();
                        batch_file = argv[1];
+               } else if (matches(argv[1], "-batchsize") == 0 ||
+                               matches(argv[1], "-bs") == 0) {
+                       argc--; argv++;
+                       if (argc <= 1)
+                               usage();
+                       batch_size = atoi(argv[1]);
+                       if (batch_size > MSG_IOV_MAX)
+                               batch_size = MSG_IOV_MAX;
+                       else if (batch_size < 0)
+                               batch_size = 1;
                } else if (matches(argv[1], "-netns") == 0) {
                        NEXT_ARG();
                        if (netns_switch(argv[1]))
@@ -323,7 +365,7 @@ int main(int argc, char **argv)
        }
 
        if (batch_file)
-               return batch(batch_file);
+               return batch(batch_file, batch_size);
 
        if (argc <= 1) {
                usage();
@@ -341,7 +383,9 @@ int main(int argc, char **argv)
                goto Exit;
        }
 
-       ret = do_cmd(argc-1, argv+1);
+       ret = do_cmd(argc-1, argv+1, 1, 0, true);
+       free_filter_reqs();
+       free_action_reqs();
 Exit:
        rtnl_close(&rth);
 
diff --git a/tc/tc_common.h b/tc/tc_common.h
index 264fbdac..8a82439f 100644
--- a/tc/tc_common.h
+++ b/tc/tc_common.h
@@ -1,13 +1,14 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 
 #define TCA_BUF_MAX    (64*1024)
+#define MSG_IOV_MAX    256
 
 extern struct rtnl_handle rth;
 
 extern int do_qdisc(int argc, char **argv);
 extern int do_class(int argc, char **argv);
-extern int do_filter(int argc, char **argv);
-extern int do_action(int argc, char **argv);
+extern int do_filter(int argc, char **argv, int batch_size, int index, bool 
send);
+extern int do_action(int argc, char **argv, int batch_size, int index, bool 
send);
 extern int do_tcmonitor(int argc, char **argv);
 extern int do_exec(int argc, char **argv);
 
@@ -24,5 +25,8 @@ struct tc_sizespec;
 extern int parse_size_table(int *p_argc, char ***p_argv, struct tc_sizespec 
*s);
 extern int check_size_table_opts(struct tc_sizespec *s);
 
+extern void free_filter_reqs(void);
+extern void free_action_reqs(void);
+
 extern int show_graph;
 extern bool use_names;
diff --git a/tc/tc_filter.c b/tc/tc_filter.c
index 545cc3a1..6fecbb45 100644
--- a/tc/tc_filter.c
+++ b/tc/tc_filter.c
@@ -19,6 +19,7 @@
 #include <arpa/inet.h>
 #include <string.h>
 #include <linux/if_ether.h>
+#include <errno.h>
 
 #include "rt_names.h"
 #include "utils.h"
@@ -42,18 +43,44 @@ static void usage(void)
                "OPTIONS := ... try tc filter add <desired FILTER_KIND> 
help\n");
 }
 
-static int tc_filter_modify(int cmd, unsigned int flags, int argc, char **argv)
+typedef struct {
+       struct nlmsghdr         n;
+       struct tcmsg            t;
+       char                    buf[MAX_MSG];
+} tc_filter_req;
+
+static tc_filter_req *filter_reqs;
+static struct iovec msg_iov[MSG_IOV_MAX];
+
+void free_filter_reqs(void)
 {
-       struct {
-               struct nlmsghdr n;
-               struct tcmsg            t;
-               char                    buf[MAX_MSG];
-       } req = {
-               .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
-               .n.nlmsg_flags = NLM_F_REQUEST | flags,
-               .n.nlmsg_type = cmd,
-               .t.tcm_family = AF_UNSPEC,
-       };
+       free(filter_reqs);
+}
+
+static tc_filter_req *get_filter_req(int batch_size, int index)
+{
+       tc_filter_req *req;
+
+       if (filter_reqs == NULL) {
+               filter_reqs = malloc(batch_size * sizeof (tc_filter_req));
+               if (filter_reqs == NULL)
+                       return NULL;
+       }
+       req = &filter_reqs[index];
+       memset(req, 0, sizeof (*req));
+
+       return req;
+}
+
+static int tc_filter_modify(int cmd, unsigned int flags, int argc, char **argv,
+                           int batch_size, int index, bool send)
+{
+       tc_filter_req *req;
+       int ret;
+
+       struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
+       struct iovec *iov = &msg_iov[index];
+
        struct filter_util *q = NULL;
        __u32 prio = 0;
        __u32 protocol = 0;
@@ -65,6 +92,24 @@ static int tc_filter_modify(int cmd, unsigned int flags, int 
argc, char **argv)
        char  k[FILTER_NAMESZ] = {};
        struct tc_estimator est = {};
 
+       req = get_filter_req(batch_size, index);
+       if (req == NULL) {
+               fprintf(stderr, "get_filter_req error: not enough buffer\n");
+               return -ENOMEM;
+       }
+
+       req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
+       req->n.nlmsg_flags = NLM_F_REQUEST | flags;
+       req->n.nlmsg_type = cmd;
+       req->t.tcm_family = AF_UNSPEC;
+
+       struct msghdr msg = {
+               .msg_name = &nladdr,
+               .msg_namelen = sizeof(nladdr),
+               .msg_iov = msg_iov,
+               .msg_iovlen = index + 1,
+       };
+
        if (cmd == RTM_NEWTFILTER && flags & NLM_F_CREATE)
                protocol = htons(ETH_P_ALL);
 
@@ -75,37 +120,37 @@ static int tc_filter_modify(int cmd, unsigned int flags, 
int argc, char **argv)
                                duparg("dev", *argv);
                        strncpy(d, *argv, sizeof(d)-1);
                } else if (strcmp(*argv, "root") == 0) {
-                       if (req.t.tcm_parent) {
+                       if (req->t.tcm_parent) {
                                fprintf(stderr,
                                        "Error: \"root\" is duplicate parent 
ID\n");
                                return -1;
                        }
-                       req.t.tcm_parent = TC_H_ROOT;
+                       req->t.tcm_parent = TC_H_ROOT;
                } else if (strcmp(*argv, "ingress") == 0) {
-                       if (req.t.tcm_parent) {
+                       if (req->t.tcm_parent) {
                                fprintf(stderr,
                                        "Error: \"ingress\" is duplicate parent 
ID\n");
                                return -1;
                        }
-                       req.t.tcm_parent = TC_H_MAKE(TC_H_CLSACT,
+                       req->t.tcm_parent = TC_H_MAKE(TC_H_CLSACT,
                                                     TC_H_MIN_INGRESS);
                } else if (strcmp(*argv, "egress") == 0) {
-                       if (req.t.tcm_parent) {
+                       if (req->t.tcm_parent) {
                                fprintf(stderr,
                                        "Error: \"egress\" is duplicate parent 
ID\n");
                                return -1;
                        }
-                       req.t.tcm_parent = TC_H_MAKE(TC_H_CLSACT,
+                       req->t.tcm_parent = TC_H_MAKE(TC_H_CLSACT,
                                                     TC_H_MIN_EGRESS);
                } else if (strcmp(*argv, "parent") == 0) {
                        __u32 handle;
 
                        NEXT_ARG();
-                       if (req.t.tcm_parent)
+                       if (req->t.tcm_parent)
                                duparg("parent", *argv);
                        if (get_tc_classid(&handle, *argv))
                                invarg("Invalid parent ID", *argv);
-                       req.t.tcm_parent = handle;
+                       req->t.tcm_parent = handle;
                } else if (strcmp(*argv, "handle") == 0) {
                        NEXT_ARG();
                        if (fhandle)
@@ -152,26 +197,26 @@ static int tc_filter_modify(int cmd, unsigned int flags, 
int argc, char **argv)
                argc--; argv++;
        }
 
-       req.t.tcm_info = TC_H_MAKE(prio<<16, protocol);
+       req->t.tcm_info = TC_H_MAKE(prio<<16, protocol);
 
        if (chain_index_set)
-               addattr32(&req.n, sizeof(req), TCA_CHAIN, chain_index);
+               addattr32(&req->n, sizeof(*req), TCA_CHAIN, chain_index);
 
        if (k[0])
-               addattr_l(&req.n, sizeof(req), TCA_KIND, k, strlen(k)+1);
+               addattr_l(&req->n, sizeof(*req), TCA_KIND, k, strlen(k)+1);
 
        if (d[0])  {
                ll_init_map(&rth);
 
-               req.t.tcm_ifindex = ll_name_to_index(d);
-               if (req.t.tcm_ifindex == 0) {
+               req->t.tcm_ifindex = ll_name_to_index(d);
+               if (req->t.tcm_ifindex == 0) {
                        fprintf(stderr, "Cannot find device \"%s\"\n", d);
                        return 1;
                }
        }
 
        if (q) {
-               if (q->parse_fopt(q, fhandle, argc, argv, &req.n))
+               if (q->parse_fopt(q, fhandle, argc, argv, &req->n))
                        return 1;
        } else {
                if (fhandle) {
@@ -190,10 +235,17 @@ static int tc_filter_modify(int cmd, unsigned int flags, 
int argc, char **argv)
        }
 
        if (est.ewma_log)
-               addattr_l(&req.n, sizeof(req), TCA_RATE, &est, sizeof(est));
+               addattr_l(&req->n, sizeof(*req), TCA_RATE, &est, sizeof(est));
 
-       if (rtnl_talk(&rth, &req.n, NULL) < 0) {
-               fprintf(stderr, "We have an error talking to the kernel\n");
+       iov->iov_base = &req->n;
+       iov->iov_len = req->n.nlmsg_len;
+
+       if (!send)
+               return 0;
+
+       ret = rtnl_talk_msg(&rth, &msg, NULL);
+       if (ret < 0) {
+               fprintf(stderr, "We have an error talking to the kernel, %d\n", 
ret);
                return 2;
        }
 
@@ -636,20 +688,23 @@ static int tc_filter_list(int argc, char **argv)
        return 0;
 }
 
-int do_filter(int argc, char **argv)
+int do_filter(int argc, char **argv, int batch_size, int index, bool send)
 {
        if (argc < 1)
                return tc_filter_list(0, NULL);
        if (matches(*argv, "add") == 0)
                return tc_filter_modify(RTM_NEWTFILTER, NLM_F_EXCL|NLM_F_CREATE,
-                                       argc-1, argv+1);
+                                       argc-1, argv+1,
+                                       batch_size, index, send);
        if (matches(*argv, "change") == 0)
-               return tc_filter_modify(RTM_NEWTFILTER, 0, argc-1, argv+1);
+               return tc_filter_modify(RTM_NEWTFILTER, 0, argc-1, argv+1,
+                                       batch_size, index, send);
        if (matches(*argv, "replace") == 0)
                return tc_filter_modify(RTM_NEWTFILTER, NLM_F_CREATE, argc-1,
-                                       argv+1);
+                                       argv+1, batch_size, index, send);
        if (matches(*argv, "delete") == 0)
-               return tc_filter_modify(RTM_DELTFILTER, 0,  argc-1, argv+1);
+               return tc_filter_modify(RTM_DELTFILTER, 0, argc-1, argv+1,
+                                       batch_size, index, send);
        if (matches(*argv, "get") == 0)
                return tc_filter_get(RTM_GETTFILTER, 0,  argc-1, argv+1);
        if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
-- 
2.14.3

Reply via email to