ip: defer lookup interface index

The ip command would always lookup the network device index
even when not necessary. This slows down operations like creating
lots of VLAN's.

David reported the original issue, this is an alternative patch
that solves it in a slightly more general method.

Using iproute2 to create a bridge and add 4094 vlans to it can take from
2 to 3 *minutes*. The reason is the extraneous call to ll_name_to_index.
ll_name_to_index results in an ioctl(SIOCGIFINDEX) call which in turn
invokes dev_load. If the index does not exist, which it won't when
creating a new link, dev_load calls modprobe twice -- once for
netdev-NAME and again for NAME. This is unnecessary overhead for each
link create.

When ip link is invoked for a new device, there is no reason to
call ll_name_to_index for the new device. With this patch, creating
a bridge and adding 4094 vlans takes less than 3 *seconds*.

	old:
	# time ip -batch ip-vlan.batch
	real    3m13.727s
	user    0m0.076s
	sys     0m1.959s

	new:
	# time ip -batch ip-vlan.batch
	real    0m3.222s
	user    0m0.044s
	sys     0m1.777s

Reported-off-by: David Ahern <dsahern@gmail.com>
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
This commit is contained in:
Stephen Hemminger 2018-05-25 07:48:40 -07:00
parent 39d16a02d9
commit 65083b5fe3
4 changed files with 28 additions and 20 deletions

View File

@ -36,7 +36,7 @@ int print_addrlabel(const struct sockaddr_nl *who,
int print_neigh(const struct sockaddr_nl *who, int print_neigh(const struct sockaddr_nl *who,
struct nlmsghdr *n, void *arg); struct nlmsghdr *n, void *arg);
int ipaddr_list_link(int argc, char **argv); int ipaddr_list_link(int argc, char **argv);
void ipaddr_get_vf_rate(int, int *, int *, int); void ipaddr_get_vf_rate(int, int *, int *, const char *);
void iplink_usage(void) __attribute__((noreturn)); void iplink_usage(void) __attribute__((noreturn));
void iproute_reset_filter(int ifindex); void iproute_reset_filter(int ifindex);
@ -145,7 +145,7 @@ int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp);
void lwt_print_encap(FILE *fp, struct rtattr *encap_type, struct rtattr *encap); void lwt_print_encap(FILE *fp, struct rtattr *encap_type, struct rtattr *encap);
/* iplink_xdp.c */ /* iplink_xdp.c */
int xdp_parse(int *argc, char ***argv, struct iplink_req *req, __u32 ifindex, int xdp_parse(int *argc, char ***argv, struct iplink_req *req, const char *ifname,
bool generic, bool drv, bool offload); bool generic, bool drv, bool offload);
void xdp_dump(FILE *fp, struct rtattr *tb, bool link, bool details); void xdp_dump(FILE *fp, struct rtattr *tb, bool link, bool details);

View File

@ -1967,14 +1967,20 @@ ipaddr_loop_each_vf(struct rtattr *tb[], int vfnum, int *min, int *max)
exit(1); exit(1);
} }
void ipaddr_get_vf_rate(int vfnum, int *min, int *max, int idx) void ipaddr_get_vf_rate(int vfnum, int *min, int *max, const char *dev)
{ {
struct nlmsg_chain linfo = { NULL, NULL}; struct nlmsg_chain linfo = { NULL, NULL};
struct rtattr *tb[IFLA_MAX+1]; struct rtattr *tb[IFLA_MAX+1];
struct ifinfomsg *ifi; struct ifinfomsg *ifi;
struct nlmsg_list *l; struct nlmsg_list *l;
struct nlmsghdr *n; struct nlmsghdr *n;
int len; int idx, len;
idx = ll_name_to_index(dev);
if (idx == 0) {
fprintf(stderr, "Device %s does not exist\n", dev);
exit(1);
}
if (rtnl_wilddump_request(&rth, AF_UNSPEC, RTM_GETLINK) < 0) { if (rtnl_wilddump_request(&rth, AF_UNSPEC, RTM_GETLINK) < 0) {
perror("Cannot send dump request"); perror("Cannot send dump request");

View File

@ -242,9 +242,10 @@ static int iplink_have_newlink(void)
} }
#endif /* ! IPLINK_IOCTL_COMPAT */ #endif /* ! IPLINK_IOCTL_COMPAT */
static int nl_get_ll_addr_len(unsigned int dev_index) static int nl_get_ll_addr_len(const char *ifname)
{ {
int len; int len;
int dev_index = ll_name_to_index(ifname);
struct iplink_req req = { struct iplink_req req = {
.n = { .n = {
.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
@ -259,6 +260,9 @@ static int nl_get_ll_addr_len(unsigned int dev_index)
struct nlmsghdr *answer; struct nlmsghdr *answer;
struct rtattr *tb[IFLA_MAX+1]; struct rtattr *tb[IFLA_MAX+1];
if (dev_index == 0)
return -1;
if (rtnl_talk(&rth, &req.n, &answer) < 0) if (rtnl_talk(&rth, &req.n, &answer) < 0)
return -1; return -1;
@ -337,7 +341,7 @@ static void iplink_parse_vf_vlan_info(int vf, int *argcp, char ***argvp,
} }
static int iplink_parse_vf(int vf, int *argcp, char ***argvp, static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
struct iplink_req *req, int dev_index) struct iplink_req *req, const char *dev)
{ {
char new_rate_api = 0, count = 0, override_legacy_rate = 0; char new_rate_api = 0, count = 0, override_legacy_rate = 0;
struct ifla_vf_rate tivt; struct ifla_vf_rate tivt;
@ -373,7 +377,7 @@ static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
NEXT_ARG(); NEXT_ARG();
if (matches(*argv, "mac") == 0) { if (matches(*argv, "mac") == 0) {
struct ifla_vf_mac ivm = { 0 }; struct ifla_vf_mac ivm = { 0 };
int halen = nl_get_ll_addr_len(dev_index); int halen = nl_get_ll_addr_len(dev);
NEXT_ARG(); NEXT_ARG();
ivm.vf = vf; ivm.vf = vf;
@ -542,7 +546,7 @@ static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
int tmin, tmax; int tmin, tmax;
if (tivt.min_tx_rate == -1 || tivt.max_tx_rate == -1) { if (tivt.min_tx_rate == -1 || tivt.max_tx_rate == -1) {
ipaddr_get_vf_rate(tivt.vf, &tmin, &tmax, dev_index); ipaddr_get_vf_rate(tivt.vf, &tmin, &tmax, dev);
if (tivt.min_tx_rate == -1) if (tivt.min_tx_rate == -1)
tivt.min_tx_rate = tmin; tivt.min_tx_rate = tmin;
if (tivt.max_tx_rate == -1) if (tivt.max_tx_rate == -1)
@ -583,7 +587,6 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type)
int vf = -1; int vf = -1;
int numtxqueues = -1; int numtxqueues = -1;
int numrxqueues = -1; int numrxqueues = -1;
int dev_index = 0;
int link_netnsid = -1; int link_netnsid = -1;
int index = 0; int index = 0;
int group = -1; int group = -1;
@ -605,10 +608,8 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type)
if (check_ifname(*argv)) if (check_ifname(*argv))
invarg("\"name\" not a valid ifname", *argv); invarg("\"name\" not a valid ifname", *argv);
name = *argv; name = *argv;
if (!dev) { if (!dev)
dev = name; dev = name;
dev_index = ll_name_to_index(dev);
}
} else if (strcmp(*argv, "index") == 0) { } else if (strcmp(*argv, "index") == 0) {
NEXT_ARG(); NEXT_ARG();
if (index) if (index)
@ -660,7 +661,7 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type)
bool offload = strcmp(*argv, "xdpoffload") == 0; bool offload = strcmp(*argv, "xdpoffload") == 0;
NEXT_ARG(); NEXT_ARG();
if (xdp_parse(&argc, &argv, req, dev_index, if (xdp_parse(&argc, &argv, req, dev,
generic, drv, offload)) generic, drv, offload))
exit(-1); exit(-1);
@ -750,10 +751,10 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type)
vflist = addattr_nest(&req->n, sizeof(*req), vflist = addattr_nest(&req->n, sizeof(*req),
IFLA_VFINFO_LIST); IFLA_VFINFO_LIST);
if (dev_index == 0) if (!dev)
missarg("dev"); missarg("dev");
len = iplink_parse_vf(vf, &argc, &argv, req, dev_index); len = iplink_parse_vf(vf, &argc, &argv, req, dev);
if (len < 0) if (len < 0)
return -1; return -1;
addattr_nest_end(&req->n, vflist); addattr_nest_end(&req->n, vflist);
@ -916,7 +917,6 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type)
if (check_ifname(*argv)) if (check_ifname(*argv))
invarg("\"dev\" not a valid ifname", *argv); invarg("\"dev\" not a valid ifname", *argv);
dev = *argv; dev = *argv;
dev_index = ll_name_to_index(dev);
} }
argc--; argv++; argc--; argv++;
} }
@ -931,8 +931,8 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type)
else if (!strcmp(name, dev)) else if (!strcmp(name, dev))
name = dev; name = dev;
if (dev_index && addr_len) { if (dev && addr_len) {
int halen = nl_get_ll_addr_len(dev_index); int halen = nl_get_ll_addr_len(dev);
if (halen >= 0 && halen != addr_len) { if (halen >= 0 && halen != addr_len) {
fprintf(stderr, fprintf(stderr,

View File

@ -48,8 +48,8 @@ static int xdp_delete(struct xdp_req *xdp)
return 0; return 0;
} }
int xdp_parse(int *argc, char ***argv, struct iplink_req *req, __u32 ifindex, int xdp_parse(int *argc, char ***argv, struct iplink_req *req,
bool generic, bool drv, bool offload) const char *ifname, bool generic, bool drv, bool offload)
{ {
struct bpf_cfg_in cfg = { struct bpf_cfg_in cfg = {
.type = BPF_PROG_TYPE_XDP, .type = BPF_PROG_TYPE_XDP,
@ -61,6 +61,8 @@ int xdp_parse(int *argc, char ***argv, struct iplink_req *req, __u32 ifindex,
}; };
if (offload) { if (offload) {
int ifindex = ll_name_to_index(ifname);
if (!ifindex) if (!ifindex)
incomplete_command(); incomplete_command();
cfg.ifindex = ifindex; cfg.ifindex = ifindex;