Signed-off-by: Chris Mi <chr...@mellanox.com> --- tc/m_action.c | 91 +++++++++++++++++++++++++++++++++---------- tc/tc.c | 47 ++++++++++++++++++---- tc/tc_common.h | 8 +++- tc/tc_filter.c | 121 +++++++++++++++++++++++++++++++++++++++++---------------- 4 files changed, 204 insertions(+), 63 deletions(-)
diff --git a/tc/m_action.c b/tc/m_action.c index fc422364..c4c3b862 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,88 @@ 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) { + ret = rtnl_talk_msg(&rth, &msg, NULL); + if (ret < 0) { fprintf(stderr, "We have an error talking to the kernel\n"); ret = -1; } - *argc_p = argc; - *argv_p = argv; - return ret; } @@ -679,7 +728,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 +738,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..7ea2fc89 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,16 @@ 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) { + int msg_iov_index = 0; char *line = NULL; size_t len = 0; int ret = 0; + bool send; + + if (batch_size > 1) + setcmdlinetotal(name); batch_mode = 1; if (name && strcmp(name, "-") != 0) { @@ -248,15 +253,30 @@ static int batch(const char *name) if (largc == 0) continue; /* blank line */ - if (do_cmd(largc, largv)) { + /* + * 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 + && cmdlineno != cmdlinetotal) + 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; } + msg_iov_index %= batch_size; } if (line) free(line); + free_filter_reqs(); + free_action_reqs(); rtnl_close(&rth); return ret; @@ -267,6 +287,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 +318,14 @@ 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 (matches(argv[1], "-netns") == 0) { NEXT_ARG(); if (netns_switch(argv[1])) @@ -323,7 +352,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 +370,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