From d07453f9ecc05d35e4a30730fbba0a07467a31aa Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Mon, 2 Mar 2009 12:43:47 -0800 Subject: [PATCH] netdev: Avoid some system calls in the common case in netdev_open(). The new secchan opens one netdev per OpenFlow port. We should be able to handle this in the common case without one file descriptor per netdev (because most netdev operations can be performed using a single AF_INET socket). This change starts along that path by moving the operations that are required only to receive netdev packets out of the common path. --- lib/netdev.c | 130 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 93 insertions(+), 37 deletions(-) diff --git a/lib/netdev.c b/lib/netdev.c index 113b8471..4a43ffda 100644 --- a/lib/netdev.c +++ b/lib/netdev.c @@ -91,7 +91,7 @@ struct netdev { * network device. */ /* Cached network device information. */ - int ifindex; + int ifindex; /* -1 if not known. */ uint8_t etheraddr[ETH_ADDR_LEN]; struct in6_addr in6; int speed; @@ -139,6 +139,10 @@ static int do_open_netdev(const char *name, int ethertype, int tap_fd, static int restore_flags(struct netdev *netdev); static int get_flags(const char *netdev_name, int *flagsp); static int set_flags(const char *netdev_name, int flags); +static int do_get_ifindex(const char *netdev_name); +static int get_ifindex(const struct netdev *, int *ifindexp); +static int get_etheraddr(const char *netdev_name, uint8_t ea[ETH_ADDR_LEN], + int *hwaddr_familyp); /* Obtains the IPv6 address for 'name' into 'in6'. */ static void @@ -375,7 +379,7 @@ do_open_netdev(const char *name, int ethertype, int tap_fd, int netdev_fd; struct sockaddr_ll sll; struct ifreq ifr; - unsigned int ifindex; + int ifindex = -1; uint8_t etheraddr[ETH_ADDR_LEN]; struct in6_addr in6; int mtu; @@ -397,55 +401,46 @@ do_open_netdev(const char *name, int ethertype, int tap_fd, return errno; } - /* Set non-blocking mode. */ - error = set_nonblocking(netdev_fd); - if (error) { - goto error_already_set; - } + if (ethertype != NETDEV_ETH_TYPE_NONE) { + /* Set non-blocking mode. */ + error = set_nonblocking(netdev_fd); + if (error) { + goto error_already_set; + } - /* Get ethernet device index. */ - strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); - if (ioctl(netdev_fd, SIOCGIFINDEX, &ifr) < 0) { - VLOG_ERR("ioctl(SIOCGIFINDEX) on %s device failed: %s", - name, strerror(errno)); - goto error; - } - ifindex = ifr.ifr_ifindex; - - /* Bind to specific ethernet device. */ - memset(&sll, 0, sizeof sll); - sll.sll_family = AF_PACKET; - sll.sll_ifindex = ifindex; - if (bind(netdev_fd, (struct sockaddr *) &sll, sizeof sll) < 0) { - VLOG_ERR("bind to %s failed: %s", name, strerror(errno)); - goto error; - } + /* Get ethernet device index. */ + ifindex = do_get_ifindex(name); + if (ifindex < 0) { + return -ifindex; + } + + /* Bind to specific ethernet device. */ + memset(&sll, 0, sizeof sll); + sll.sll_family = AF_PACKET; + sll.sll_ifindex = ifindex; + if (bind(netdev_fd, (struct sockaddr *) &sll, sizeof sll) < 0) { + VLOG_ERR("bind to %s failed: %s", name, strerror(errno)); + goto error; + } - if (ethertype != NETDEV_ETH_TYPE_NONE) { /* Between the socket() and bind() calls above, the socket receives all * packets of the requested type on all system interfaces. We do not * want to receive that data, but there is no way to avoid it. So we * must now drain out the receive queue. */ error = drain_rcvbuf(netdev_fd); if (error) { - goto error; + goto error_already_set; } } /* Get MAC address. */ - if (ioctl(netdev_fd, SIOCGIFHWADDR, &ifr) < 0) { - VLOG_ERR("ioctl(SIOCGIFHWADDR) on %s device failed: %s", - name, strerror(errno)); - goto error; - } - hwaddr_family = ifr.ifr_hwaddr.sa_family; - if (hwaddr_family != AF_UNSPEC && hwaddr_family != ARPHRD_ETHER) { - VLOG_WARN("%s device has unknown hardware address family %d", - name, hwaddr_family); + error = get_etheraddr(name, etheraddr, &hwaddr_family); + if (error) { + goto error_already_set; } - memcpy(etheraddr, ifr.ifr_hwaddr.sa_data, sizeof etheraddr); /* Get MTU. */ + strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); if (ioctl(netdev_fd, SIOCGIFMTU, &ifr) < 0) { VLOG_ERR("ioctl(SIOCGIFMTU) on %s device failed: %s", name, strerror(errno)); @@ -943,14 +938,20 @@ netdev_get_stats(const struct netdev *netdev, struct netdev_stats *stats) struct ifinfomsg *ifi; const struct rtnl_link_stats *rtnl_stats; struct nlattr *attrs[ARRAY_SIZE(rtnlgrp_link_policy)]; + int ifindex; int error; + error = get_ifindex(netdev, &ifindex); + if (error) { + goto error; + } + ofpbuf_init(&request, 0); nl_msg_put_nlmsghdr(&request, rtnl_sock, sizeof *ifi, RTM_GETLINK, NLM_F_REQUEST); ifi = ofpbuf_put_zeros(&request, sizeof *ifi); ifi->ifi_family = PF_UNSPEC; - ifi->ifi_index = netdev->ifindex; + ifi->ifi_index = ifindex; error = nl_sock_transact(rtnl_sock, &request, &reply); ofpbuf_uninit(&request); if (error) { @@ -1305,3 +1306,58 @@ set_flags(const char *netdev_name, int flags) } return 0; } + +static int +do_get_ifindex(const char *netdev_name) +{ + struct ifreq ifr; + + strncpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name); + if (ioctl(af_inet_sock, SIOCGIFINDEX, &ifr) < 0) { + VLOG_WARN_RL(&rl, "ioctl(SIOCGIFINDEX) on %s device failed: %s", + netdev_name, strerror(errno)); + return -errno; + } + return ifr.ifr_ifindex; +} + +static int +get_ifindex(const struct netdev *netdev, int *ifindexp) +{ + *ifindexp = 0; + if (netdev->ifindex < 0) { + int ifindex = do_get_ifindex(netdev->name); + if (ifindex < 0) { + return -ifindex; + } + ((struct netdev *) netdev)->ifindex = ifindex; + } + *ifindexp = netdev->ifindex; + return 0; +} + +static int +get_etheraddr(const char *netdev_name, uint8_t ea[ETH_ADDR_LEN], + int *hwaddr_familyp) +{ + struct ifreq ifr; + + memset(&ifr, 0, sizeof ifr); + strncpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name); + if (ioctl(af_inet_sock, SIOCGIFHWADDR, &ifr) < 0) { + VLOG_ERR("ioctl(SIOCGIFHWADDR) on %s device failed: %s", + netdev_name, strerror(errno)); + return errno; + } + if (hwaddr_familyp) { + int hwaddr_family = ifr.ifr_hwaddr.sa_family; + *hwaddr_familyp = hwaddr_family; + if (hwaddr_family != AF_UNSPEC && hwaddr_family != ARPHRD_ETHER) { + VLOG_WARN("%s device has unknown hardware address family %d", + netdev_name, hwaddr_family); + } + } + memcpy(ea, ifr.ifr_hwaddr.sa_data, ETH_ADDR_LEN); + + return 0; +} -- 2.30.2