X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fnetdev-linux.c;h=26fb32d825c1819c845c1f9d18f7230c2b3fa09c;hb=fd76a6f94338b668175336434b156827458b5e7d;hp=768b4e8d2e5d28b5bc580b65db2346e0914a6c3d;hpb=d0d08f8aa5272943f2388a7cc18eeeda0288627d;p=sliver-openvswitch.git diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index 768b4e8d2..26fb32d82 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -106,9 +106,6 @@ COVERAGE_DEFINE(netdev_set_ethtool); #define TC_RTAB_SIZE 1024 #endif -static struct nln_notifier *netdev_linux_cache_notifier = NULL; -static int cache_notifier_refcount; - enum { VALID_IFINDEX = 1 << 0, VALID_ETHERADDR = 1 << 1, @@ -355,7 +352,9 @@ static int tc_calc_buffer(unsigned int Bps, int mtu, uint64_t burst_bytes); struct netdev_linux { struct netdev up; - struct shash_node *shash_node; + /* Protects all members below. */ + struct ovs_mutex mutex; + unsigned int cache_valid; unsigned int change_seq; @@ -385,7 +384,6 @@ struct netdev_linux { enum netdev_features current; /* Cached from ETHTOOL_GSET. */ enum netdev_features advertised; /* Cached from ETHTOOL_GSET. */ enum netdev_features supported; /* Cached from ETHTOOL_GSET. */ - enum netdev_features peer; /* Cached from ETHTOOL_GSET. */ struct ethtool_drvinfo drvinfo; /* Cached from ETHTOOL_GDRVINFO. */ struct tc *tc; @@ -400,25 +398,21 @@ struct netdev_rx_linux { int fd; }; -static const struct netdev_rx_class netdev_rx_linux_class; - -/* Sockets used for ioctl operations. */ -static int af_inet_sock = -1; /* AF_INET, SOCK_DGRAM. */ - /* This is set pretty low because we probably won't learn anything from the * additional log messages. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); -static int netdev_linux_init(void); +static void netdev_linux_run(void); static int netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *, int cmd, const char *cmd_name); -static int netdev_linux_do_ioctl(const char *name, struct ifreq *, int cmd, - const char *cmd_name); static int netdev_linux_get_ipv4(const struct netdev *, struct in_addr *, int cmd, const char *cmd_name); static int get_flags(const struct netdev *, unsigned int *flags); static int set_flags(const char *, unsigned int flags); +static int update_flags(struct netdev_linux *netdev, enum netdev_flags off, + enum netdev_flags on, enum netdev_flags *old_flagsp) + OVS_REQUIRES(netdev->mutex); static int do_get_ifindex(const char *netdev_name); static int get_ifindex(const struct netdev *, int *ifindexp); static int do_set_addr(struct netdev *netdev, @@ -435,7 +429,7 @@ static void netdev_linux_miimon_wait(void); static bool is_netdev_linux_class(const struct netdev_class *netdev_class) { - return netdev_class->init == netdev_linux_init; + return netdev_class->run == netdev_linux_run; } static bool @@ -455,42 +449,121 @@ netdev_linux_cast(const struct netdev *netdev) static struct netdev_rx_linux * netdev_rx_linux_cast(const struct netdev_rx *rx) { - netdev_rx_assert_class(rx, &netdev_rx_linux_class); + ovs_assert(is_netdev_linux_class(netdev_get_class(rx->netdev))); return CONTAINER_OF(rx, struct netdev_rx_linux, up); } -static int -netdev_linux_init(void) -{ - static int status = -1; - if (status < 0) { - /* Create AF_INET socket. */ - af_inet_sock = socket(AF_INET, SOCK_DGRAM, 0); - status = af_inet_sock >= 0 ? 0 : errno; - if (status) { - VLOG_ERR("failed to create inet socket: %s", ovs_strerror(status)); +static void netdev_linux_update(struct netdev_linux *netdev, + const struct rtnetlink_link_change *) + OVS_REQUIRES(netdev->mutex); +static void netdev_linux_changed(struct netdev_linux *netdev, + unsigned int ifi_flags, unsigned int mask) + OVS_REQUIRES(netdev->mutex); + +/* Returns a NETLINK_ROUTE socket listening for RTNLGRP_LINK changes, or NULL + * if no such socket could be created. */ +static struct nl_sock * +netdev_linux_notify_sock(void) +{ + static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; + static struct nl_sock *sock; + + if (ovsthread_once_start(&once)) { + int error; + + error = nl_sock_create(NETLINK_ROUTE, &sock); + if (!error) { + error = nl_sock_join_mcgroup(sock, RTNLGRP_LINK); + if (error) { + nl_sock_destroy(sock); + sock = NULL; + } } + ovsthread_once_done(&once); } - return status; + + return sock; } static void netdev_linux_run(void) { - rtnetlink_link_run(); + struct nl_sock *sock; + int error; + netdev_linux_miimon_run(); + + sock = netdev_linux_notify_sock(); + if (!sock) { + return; + } + + do { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + uint64_t buf_stub[4096 / 8]; + struct ofpbuf buf; + + ofpbuf_use_stub(&buf, buf_stub, sizeof buf_stub); + error = nl_sock_recv(sock, &buf, false); + if (!error) { + struct rtnetlink_link_change change; + + if (rtnetlink_link_parse(&buf, &change)) { + struct netdev *netdev_ = netdev_from_name(change.ifname); + if (netdev_ && is_netdev_linux_class(netdev_->netdev_class)) { + struct netdev_linux *netdev = netdev_linux_cast(netdev_); + + ovs_mutex_lock(&netdev->mutex); + netdev_linux_update(netdev, &change); + ovs_mutex_unlock(&netdev->mutex); + } + netdev_close(netdev_); + } + } else if (error == ENOBUFS) { + struct shash device_shash; + struct shash_node *node; + + nl_sock_drain(sock); + + shash_init(&device_shash); + netdev_get_devices(&netdev_linux_class, &device_shash); + SHASH_FOR_EACH (node, &device_shash) { + struct netdev *netdev_ = node->data; + struct netdev_linux *netdev = netdev_linux_cast(netdev_); + unsigned int flags; + + ovs_mutex_lock(&netdev->mutex); + get_flags(netdev_, &flags); + netdev_linux_changed(netdev, flags, 0); + ovs_mutex_unlock(&netdev->mutex); + + netdev_close(netdev_); + } + shash_destroy(&device_shash); + } else if (error != EAGAIN) { + VLOG_WARN_RL(&rl, "error reading or parsing netlink (%s)", + ovs_strerror(error)); + } + ofpbuf_uninit(&buf); + } while (!error); } static void netdev_linux_wait(void) { - rtnetlink_link_wait(); + struct nl_sock *sock; + netdev_linux_miimon_wait(); + sock = netdev_linux_notify_sock(); + if (sock) { + nl_sock_wait(sock, POLLIN); + } } static void netdev_linux_changed(struct netdev_linux *dev, unsigned int ifi_flags, unsigned int mask) + OVS_REQUIRES(dev->mutex) { dev->change_seq++; if (!dev->change_seq) { @@ -508,6 +581,7 @@ netdev_linux_changed(struct netdev_linux *dev, static void netdev_linux_update(struct netdev_linux *dev, const struct rtnetlink_link_change *change) + OVS_REQUIRES(dev->mutex) { if (change->nlmsg_type == RTM_NEWLINK) { /* Keep drv-info */ @@ -535,87 +609,33 @@ netdev_linux_update(struct netdev_linux *dev, } } -static void -netdev_linux_cache_cb(const struct rtnetlink_link_change *change, - void *aux OVS_UNUSED) -{ - struct netdev_linux *dev; - if (change) { - struct netdev *base_dev = netdev_from_name(change->ifname); - if (base_dev && is_netdev_linux_class(netdev_get_class(base_dev))) { - netdev_linux_update(netdev_linux_cast(base_dev), change); - } - } else { - struct shash device_shash; - struct shash_node *node; - - shash_init(&device_shash); - netdev_get_devices(&netdev_linux_class, &device_shash); - SHASH_FOR_EACH (node, &device_shash) { - struct netdev *netdev = node->data; - unsigned int flags; - - dev = netdev_linux_cast(netdev); - - get_flags(&dev->up, &flags); - netdev_linux_changed(dev, flags, 0); - } - shash_destroy(&device_shash); - } -} - -static int -cache_notifier_ref(void) +static struct netdev * +netdev_linux_alloc(void) { - if (!cache_notifier_refcount) { - ovs_assert(!netdev_linux_cache_notifier); - - netdev_linux_cache_notifier = - rtnetlink_link_notifier_create(netdev_linux_cache_cb, NULL); - - if (!netdev_linux_cache_notifier) { - return EINVAL; - } - } - cache_notifier_refcount++; - - return 0; + struct netdev_linux *netdev = xzalloc(sizeof *netdev); + return &netdev->up; } static void -cache_notifier_unref(void) +netdev_linux_common_construct(struct netdev_linux *netdev) { - ovs_assert(cache_notifier_refcount > 0); - if (!--cache_notifier_refcount) { - ovs_assert(netdev_linux_cache_notifier); - rtnetlink_link_notifier_destroy(netdev_linux_cache_notifier); - netdev_linux_cache_notifier = NULL; - } + ovs_mutex_init(&netdev->mutex); + netdev->change_seq = 1; } /* Creates system and internal devices. */ static int -netdev_linux_create(const struct netdev_class *class, const char *name, - struct netdev **netdevp) +netdev_linux_construct(struct netdev *netdev_) { - struct netdev_linux *netdev; + struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; - error = cache_notifier_ref(); - if (error) { - return error; - } + netdev_linux_common_construct(netdev); - netdev = xzalloc(sizeof *netdev); - netdev->change_seq = 1; - netdev_init(&netdev->up, name, class); error = get_flags(&netdev->up, &netdev->ifi_flags); if (error == ENODEV) { - if (class != &netdev_internal_class) { + if (netdev->up.netdev_class != &netdev_internal_class) { /* The device does not exist, so don't allow it to be opened. */ - netdev_uninit(&netdev->up, false); - cache_notifier_unref(); - free(netdev); return ENODEV; } else { /* "Internal" netdevs have to be created as netdev objects before @@ -625,7 +645,6 @@ netdev_linux_create(const struct netdev_class *class, const char *name, } } - *netdevp = &netdev->up; return 0; } @@ -636,28 +655,22 @@ netdev_linux_create(const struct netdev_class *class, const char *name, * buffers, across all readers. Therefore once data is read it will * be unavailable to other reads for tap devices. */ static int -netdev_linux_create_tap(const struct netdev_class *class OVS_UNUSED, - const char *name, struct netdev **netdevp) +netdev_linux_construct_tap(struct netdev *netdev_) { - struct netdev_linux *netdev; + struct netdev_linux *netdev = netdev_linux_cast(netdev_); static const char tap_dev[] = "/dev/net/tun"; + const char *name = netdev_->name; struct ifreq ifr; int error; - netdev = xzalloc(sizeof *netdev); - netdev->change_seq = 1; - - error = cache_notifier_ref(); - if (error) { - goto error; - } + netdev_linux_common_construct(netdev); /* Open tap device. */ netdev->tap_fd = open(tap_dev, O_RDWR); if (netdev->tap_fd < 0) { error = errno; VLOG_WARN("opening \"%s\" failed: %s", tap_dev, ovs_strerror(error)); - goto error_unref_notifier; + return error; } /* Create tap device. */ @@ -676,21 +689,15 @@ netdev_linux_create_tap(const struct netdev_class *class OVS_UNUSED, goto error_close; } - netdev_init(&netdev->up, name, &netdev_tap_class); - *netdevp = &netdev->up; return 0; error_close: close(netdev->tap_fd); -error_unref_notifier: - cache_notifier_unref(); -error: - free(netdev); return error; } static void -netdev_linux_destroy(struct netdev *netdev_) +netdev_linux_destruct(struct netdev *netdev_) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); @@ -703,44 +710,60 @@ netdev_linux_destroy(struct netdev *netdev_) { close(netdev->tap_fd); } + + ovs_mutex_destroy(&netdev->mutex); +} + +static void +netdev_linux_dealloc(struct netdev *netdev_) +{ + struct netdev_linux *netdev = netdev_linux_cast(netdev_); free(netdev); +} - cache_notifier_unref(); +static struct netdev_rx * +netdev_linux_rx_alloc(void) +{ + struct netdev_rx_linux *rx = xzalloc(sizeof *rx); + return &rx->up; } static int -netdev_linux_rx_open(struct netdev *netdev_, struct netdev_rx **rxp) +netdev_linux_rx_construct(struct netdev_rx *rx_) { + struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_); + struct netdev *netdev_ = rx->up.netdev; struct netdev_linux *netdev = netdev_linux_cast(netdev_); - bool is_tap = is_tap_netdev(netdev_); - struct netdev_rx_linux *rx; int error; - int fd; - if (is_tap) { - fd = netdev->tap_fd; + ovs_mutex_lock(&netdev->mutex); + rx->is_tap = is_tap_netdev(netdev_); + if (rx->is_tap) { + rx->fd = netdev->tap_fd; } else { struct sockaddr_ll sll; int ifindex; /* Result of tcpdump -dd inbound */ - static struct sock_filter filt[] = { + static const struct sock_filter filt[] = { { 0x28, 0, 0, 0xfffff004 }, /* ldh [0] */ { 0x15, 0, 1, 0x00000004 }, /* jeq #4 jt 2 jf 3 */ { 0x6, 0, 0, 0x00000000 }, /* ret #0 */ { 0x6, 0, 0, 0x0000ffff } /* ret #65535 */ }; - static struct sock_fprog fprog = { ARRAY_SIZE(filt), filt }; + static const struct sock_fprog fprog = { + ARRAY_SIZE(filt), (struct sock_filter *) filt + }; /* Create file descriptor. */ - fd = socket(PF_PACKET, SOCK_RAW, 0); - if (fd < 0) { + rx->fd = socket(PF_PACKET, SOCK_RAW, 0); + if (rx->fd < 0) { error = errno; VLOG_ERR("failed to create raw socket (%s)", ovs_strerror(error)); goto error; } /* Set non-blocking mode. */ - error = set_nonblocking(fd); + error = set_nonblocking(rx->fd); if (error) { goto error; } @@ -756,7 +779,7 @@ netdev_linux_rx_open(struct netdev *netdev_, struct netdev_rx **rxp) sll.sll_family = AF_PACKET; sll.sll_ifindex = ifindex; sll.sll_protocol = (OVS_FORCE unsigned short int) htons(ETH_P_ALL); - if (bind(fd, (struct sockaddr *) &sll, sizeof sll) < 0) { + if (bind(rx->fd, (struct sockaddr *) &sll, sizeof sll) < 0) { error = errno; VLOG_ERR("%s: failed to bind raw socket (%s)", netdev_get_name(netdev_), ovs_strerror(error)); @@ -764,44 +787,47 @@ netdev_linux_rx_open(struct netdev *netdev_, struct netdev_rx **rxp) } /* Filter for only inbound packets. */ - error = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, + error = setsockopt(rx->fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof fprog); if (error) { error = errno; - VLOG_ERR("%s: failed attach filter (%s)", + VLOG_ERR("%s: failed to attach filter (%s)", netdev_get_name(netdev_), ovs_strerror(error)); goto error; } } + ovs_mutex_unlock(&netdev->mutex); - rx = xmalloc(sizeof *rx); - netdev_rx_init(&rx->up, netdev_, &netdev_rx_linux_class); - rx->is_tap = is_tap; - rx->fd = fd; - - *rxp = &rx->up; return 0; error: - if (fd >= 0) { - close(fd); + if (rx->fd >= 0) { + close(rx->fd); } + ovs_mutex_unlock(&netdev->mutex); return error; } static void -netdev_rx_linux_destroy(struct netdev_rx *rx_) +netdev_linux_rx_destruct(struct netdev_rx *rx_) { struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_); if (!rx->is_tap) { close(rx->fd); } +} + +static void +netdev_linux_rx_dealloc(struct netdev_rx *rx_) +{ + struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_); + free(rx); } static int -netdev_rx_linux_recv(struct netdev_rx *rx_, void *data, size_t size) +netdev_linux_rx_recv(struct netdev_rx *rx_, void *data, size_t size) { struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_); ssize_t retval; @@ -824,20 +850,20 @@ netdev_rx_linux_recv(struct netdev_rx *rx_, void *data, size_t size) } static void -netdev_rx_linux_wait(struct netdev_rx *rx_) +netdev_linux_rx_wait(struct netdev_rx *rx_) { struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_); poll_fd_wait(rx->fd, POLLIN); } static int -netdev_rx_linux_drain(struct netdev_rx *rx_) +netdev_linux_rx_drain(struct netdev_rx *rx_) { struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_); if (rx->is_tap) { struct ifreq ifr; - int error = netdev_linux_do_ioctl(netdev_rx_get_name(rx_), &ifr, - SIOCGIFTXQLEN, "SIOCGIFTXQLEN"); + int error = af_inet_ifreq_ioctl(netdev_rx_get_name(rx_), &ifr, + SIOCGIFTXQLEN, "SIOCGIFTXQLEN"); if (error) { return error; } @@ -869,7 +895,6 @@ netdev_linux_send(struct netdev *netdev_, const void *data, size_t size) struct msghdr msg; struct iovec iov; int ifindex; - int error; int sock; sock = af_packet_sock(); @@ -877,9 +902,9 @@ netdev_linux_send(struct netdev *netdev_, const void *data, size_t size) return -sock; } - error = get_ifindex(netdev_, &ifindex); - if (error) { - return error; + ifindex = netdev_get_ifindex(netdev_); + if (ifindex < 0) { + return -ifindex; } /* We don't bother setting most fields in sockaddr_ll because the @@ -957,22 +982,22 @@ netdev_linux_set_etheraddr(struct netdev *netdev_, const uint8_t mac[ETH_ADDR_LEN]) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); - struct netdev_saved_flags *sf = NULL; + enum netdev_flags old_flags = 0; int error; + ovs_mutex_lock(&netdev->mutex); + if (netdev->cache_valid & VALID_ETHERADDR) { - if (netdev->ether_addr_error) { - return netdev->ether_addr_error; - } - if (eth_addr_equals(netdev->etheraddr, mac)) { - return 0; + error = netdev->ether_addr_error; + if (error || eth_addr_equals(netdev->etheraddr, mac)) { + goto exit; } netdev->cache_valid &= ~VALID_ETHERADDR; } /* Tap devices must be brought down before setting the address. */ if (is_tap_netdev(netdev_)) { - netdev_turn_flags_off(netdev_, NETDEV_UP, &sf); + update_flags(netdev, NETDEV_UP, 0, &old_flags); } error = set_etheraddr(netdev_get_name(netdev_), mac); if (!error || error == ENODEV) { @@ -983,8 +1008,12 @@ netdev_linux_set_etheraddr(struct netdev *netdev_, } } - netdev_restore_flags(sf); + if (is_tap_netdev(netdev_) && old_flags & NETDEV_UP) { + update_flags(netdev, 0, NETDEV_UP, &old_flags); + } +exit: + ovs_mutex_unlock(&netdev->mutex); return error; } @@ -994,45 +1023,60 @@ netdev_linux_get_etheraddr(const struct netdev *netdev_, uint8_t mac[ETH_ADDR_LEN]) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); + int error; + ovs_mutex_lock(&netdev->mutex); if (!(netdev->cache_valid & VALID_ETHERADDR)) { - int error = get_etheraddr(netdev_get_name(netdev_), - netdev->etheraddr); - - netdev->ether_addr_error = error; + netdev->ether_addr_error = get_etheraddr(netdev_get_name(netdev_), + netdev->etheraddr); netdev->cache_valid |= VALID_ETHERADDR; } - if (!netdev->ether_addr_error) { + error = netdev->ether_addr_error; + if (!error) { memcpy(mac, netdev->etheraddr, ETH_ADDR_LEN); } + ovs_mutex_unlock(&netdev->mutex); - return netdev->ether_addr_error; + return error; } -/* Returns the maximum size of transmitted (and received) packets on 'netdev', - * in bytes, not including the hardware header; thus, this is typically 1500 - * bytes for Ethernet devices. */ static int -netdev_linux_get_mtu(const struct netdev *netdev_, int *mtup) +netdev_linux_get_mtu__(struct netdev_linux *netdev, int *mtup) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); + int error; + if (!(netdev->cache_valid & VALID_MTU)) { struct ifreq ifr; - int error; - - error = netdev_linux_do_ioctl(netdev_get_name(netdev_), &ifr, - SIOCGIFMTU, "SIOCGIFMTU"); - netdev->netdev_mtu_error = error; + netdev->netdev_mtu_error = af_inet_ifreq_ioctl( + netdev_get_name(&netdev->up), &ifr, SIOCGIFMTU, "SIOCGIFMTU"); netdev->mtu = ifr.ifr_mtu; netdev->cache_valid |= VALID_MTU; } - if (!netdev->netdev_mtu_error) { + error = netdev->netdev_mtu_error; + if (!error) { *mtup = netdev->mtu; } - return netdev->netdev_mtu_error; + + return error; +} + +/* Returns the maximum size of transmitted (and received) packets on 'netdev', + * in bytes, not including the hardware header; thus, this is typically 1500 + * bytes for Ethernet devices. */ +static int +netdev_linux_get_mtu(const struct netdev *netdev_, int *mtup) +{ + struct netdev_linux *netdev = netdev_linux_cast(netdev_); + int error; + + ovs_mutex_lock(&netdev->mutex); + error = netdev_linux_get_mtu__(netdev, mtup); + ovs_mutex_unlock(&netdev->mutex); + + return error; } /* Sets the maximum size of transmitted (MTU) for given device using linux @@ -1045,34 +1089,39 @@ netdev_linux_set_mtu(const struct netdev *netdev_, int mtu) struct ifreq ifr; int error; + ovs_mutex_lock(&netdev->mutex); if (netdev->cache_valid & VALID_MTU) { - if (netdev->netdev_mtu_error) { - return netdev->netdev_mtu_error; - } - if (netdev->mtu == mtu) { - return 0; + error = netdev->netdev_mtu_error; + if (error || netdev->mtu == mtu) { + goto exit; } netdev->cache_valid &= ~VALID_MTU; } ifr.ifr_mtu = mtu; - error = netdev_linux_do_ioctl(netdev_get_name(netdev_), &ifr, - SIOCSIFMTU, "SIOCSIFMTU"); + error = af_inet_ifreq_ioctl(netdev_get_name(netdev_), &ifr, + SIOCSIFMTU, "SIOCSIFMTU"); if (!error || error == ENODEV) { netdev->netdev_mtu_error = error; netdev->mtu = ifr.ifr_mtu; netdev->cache_valid |= VALID_MTU; } +exit: + ovs_mutex_unlock(&netdev->mutex); return error; } /* Returns the ifindex of 'netdev', if successful, as a positive number. * On failure, returns a negative errno value. */ static int -netdev_linux_get_ifindex(const struct netdev *netdev) +netdev_linux_get_ifindex(const struct netdev *netdev_) { + struct netdev_linux *netdev = netdev_linux_cast(netdev_); int ifindex, error; - error = get_ifindex(netdev, &ifindex); + ovs_mutex_lock(&netdev->mutex); + error = get_ifindex(netdev_, &ifindex); + ovs_mutex_unlock(&netdev->mutex); + return error ? -error : ifindex; } @@ -1081,19 +1130,28 @@ netdev_linux_get_carrier(const struct netdev *netdev_, bool *carrier) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); + ovs_mutex_lock(&netdev->mutex); if (netdev->miimon_interval > 0) { *carrier = netdev->miimon; } else { *carrier = (netdev->ifi_flags & IFF_RUNNING) != 0; } + ovs_mutex_unlock(&netdev->mutex); return 0; } static long long int -netdev_linux_get_carrier_resets(const struct netdev *netdev) +netdev_linux_get_carrier_resets(const struct netdev *netdev_) { - return netdev_linux_cast(netdev)->carrier_resets; + struct netdev_linux *netdev = netdev_linux_cast(netdev_); + long long int carrier_resets; + + ovs_mutex_lock(&netdev->mutex); + carrier_resets = netdev->carrier_resets; + ovs_mutex_unlock(&netdev->mutex); + + return carrier_resets; } static int @@ -1105,7 +1163,7 @@ netdev_linux_do_miimon(const char *name, int cmd, const char *cmd_name, memset(&ifr, 0, sizeof ifr); memcpy(&ifr.ifr_data, data, sizeof *data); - error = netdev_linux_do_ioctl(name, &ifr, cmd, cmd_name); + error = af_inet_ifreq_ioctl(name, &ifr, cmd, cmd_name); memcpy(data, &ifr.ifr_data, sizeof *data); return error; @@ -1161,11 +1219,13 @@ netdev_linux_set_miimon_interval(struct netdev *netdev_, { struct netdev_linux *netdev = netdev_linux_cast(netdev_); + ovs_mutex_lock(&netdev->mutex); interval = interval > 0 ? MAX(interval, 100) : 0; if (netdev->miimon_interval != interval) { netdev->miimon_interval = interval; timer_set_expired(&netdev->miimon_timer); } + ovs_mutex_unlock(&netdev->mutex); return 0; } @@ -1183,17 +1243,18 @@ netdev_linux_miimon_run(void) struct netdev_linux *dev = netdev_linux_cast(netdev); bool miimon; - if (dev->miimon_interval <= 0 || !timer_expired(&dev->miimon_timer)) { - continue; - } + ovs_mutex_lock(&dev->mutex); + if (dev->miimon_interval > 0 && timer_expired(&dev->miimon_timer)) { + netdev_linux_get_miimon(dev->up.name, &miimon); + if (miimon != dev->miimon) { + dev->miimon = miimon; + netdev_linux_changed(dev, dev->ifi_flags, 0); + } - netdev_linux_get_miimon(dev->up.name, &miimon); - if (miimon != dev->miimon) { - dev->miimon = miimon; - netdev_linux_changed(dev, dev->ifi_flags, 0); + timer_set_duration(&dev->miimon_timer, dev->miimon_interval); } - - timer_set_duration(&dev->miimon_timer, dev->miimon_interval); + ovs_mutex_unlock(&dev->mutex); + netdev_close(netdev); } shash_destroy(&device_shash); @@ -1211,9 +1272,12 @@ netdev_linux_miimon_wait(void) struct netdev *netdev = node->data; struct netdev_linux *dev = netdev_linux_cast(netdev); + ovs_mutex_lock(&dev->mutex); if (dev->miimon_interval > 0) { timer_wait(&dev->miimon_timer); } + ovs_mutex_unlock(&dev->mutex); + netdev_close(netdev); } shash_destroy(&device_shash); } @@ -1329,7 +1393,7 @@ get_stats_via_vport(const struct netdev *netdev_, static int netdev_linux_sys_get_stats(const struct netdev *netdev_, - struct netdev_stats *stats) + struct netdev_stats *stats) { static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; static int use_netlink_stats; @@ -1368,19 +1432,14 @@ netdev_linux_get_stats(const struct netdev *netdev_, struct netdev_stats dev_stats; int error; + ovs_mutex_lock(&netdev->mutex); get_stats_via_vport(netdev_, stats); - error = netdev_linux_sys_get_stats(netdev_, &dev_stats); - if (error) { - if (netdev->vport_stats_error) { - return error; - } else { - return 0; + if (!netdev->vport_stats_error) { + error = 0; } - } - - if (netdev->vport_stats_error) { + } else if (netdev->vport_stats_error) { /* stats not available from OVS then use ioctl stats. */ *stats = dev_stats; } else { @@ -1402,7 +1461,9 @@ netdev_linux_get_stats(const struct netdev *netdev_, stats->tx_heartbeat_errors += dev_stats.tx_heartbeat_errors; stats->tx_window_errors += dev_stats.tx_window_errors; } - return 0; + ovs_mutex_unlock(&netdev->mutex); + + return error; } /* Retrieves current device stats for 'netdev-tap' netdev or @@ -1414,24 +1475,20 @@ netdev_tap_get_stats(const struct netdev *netdev_, struct netdev_stats *stats) struct netdev_stats dev_stats; int error; + ovs_mutex_lock(&netdev->mutex); get_stats_via_vport(netdev_, stats); - error = netdev_linux_sys_get_stats(netdev_, &dev_stats); if (error) { - if (netdev->vport_stats_error) { - return error; - } else { - return 0; + if (!netdev->vport_stats_error) { + error = 0; } - } + } else if (netdev->vport_stats_error) { + /* Transmit and receive stats will appear to be swapped relative to the + * other ports since we are the one sending the data, not a remote + * computer. For consistency, we swap them back here. This does not + * apply if we are getting stats from the vport layer because it always + * tracks stats from the perspective of the switch. */ - /* If this port is an internal port then the transmit and receive stats - * will appear to be swapped relative to the other ports since we are the - * one sending the data, not a remote computer. For consistency, we swap - * them back here. This does not apply if we are getting stats from the - * vport layer because it always tracks stats from the perspective of the - * switch. */ - if (netdev->vport_stats_error) { *stats = dev_stats; swap_uint64(&stats->rx_packets, &stats->tx_packets); swap_uint64(&stats->rx_bytes, &stats->tx_bytes); @@ -1458,7 +1515,9 @@ netdev_tap_get_stats(const struct netdev *netdev_, struct netdev_stats *stats) stats->multicast += dev_stats.multicast; stats->collisions += dev_stats.collisions; } - return 0; + ovs_mutex_unlock(&netdev->mutex); + + return error; } static int @@ -1466,9 +1525,14 @@ netdev_internal_get_stats(const struct netdev *netdev_, struct netdev_stats *stats) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); + int error; + ovs_mutex_lock(&netdev->mutex); get_stats_via_vport(netdev_, stats); - return netdev->vport_stats_error; + error = netdev->vport_stats_error; + ovs_mutex_unlock(&netdev->mutex); + + return error; } static int @@ -1633,18 +1697,14 @@ netdev_linux_read_features(struct netdev_linux *netdev) netdev->current |= NETDEV_F_AUTONEG; } - /* Peer advertisements. */ - netdev->peer = 0; /* XXX */ - out: netdev->cache_valid |= VALID_FEATURES; netdev->get_features_error = error; } -/* Stores the features supported by 'netdev' into each of '*current', - * '*advertised', '*supported', and '*peer' that are non-null. Each value is a - * bitmap of NETDEV_* bits. Returns 0 if successful, otherwise a positive - * errno value. */ +/* Stores the features supported by 'netdev' into of '*current', '*advertised', + * '*supported', and '*peer'. Each value is a bitmap of NETDEV_* bits. + * Returns 0 if successful, otherwise a positive errno value. */ static int netdev_linux_get_features(const struct netdev *netdev_, enum netdev_features *current, @@ -1653,32 +1713,39 @@ netdev_linux_get_features(const struct netdev *netdev_, enum netdev_features *peer) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); + int error; + ovs_mutex_lock(&netdev->mutex); netdev_linux_read_features(netdev); - if (!netdev->get_features_error) { *current = netdev->current; *advertised = netdev->advertised; *supported = netdev->supported; - *peer = netdev->peer; + *peer = 0; /* XXX */ } - return netdev->get_features_error; + error = netdev->get_features_error; + ovs_mutex_unlock(&netdev->mutex); + + return error; } /* Set the features advertised by 'netdev' to 'advertise'. */ static int -netdev_linux_set_advertisements(struct netdev *netdev, +netdev_linux_set_advertisements(struct netdev *netdev_, enum netdev_features advertise) { + struct netdev_linux *netdev = netdev_linux_cast(netdev_); struct ethtool_cmd ecmd; int error; + ovs_mutex_lock(&netdev->mutex); + COVERAGE_INC(netdev_get_ethtool); memset(&ecmd, 0, sizeof ecmd); - error = netdev_linux_do_ethtool(netdev_get_name(netdev), &ecmd, + error = netdev_linux_do_ethtool(netdev_get_name(netdev_), &ecmd, ETHTOOL_GSET, "ETHTOOL_GSET"); if (error) { - return error; + goto exit; } ecmd.advertising = 0; @@ -1719,8 +1786,12 @@ netdev_linux_set_advertisements(struct netdev *netdev, ecmd.advertising |= ADVERTISED_Asym_Pause; } COVERAGE_INC(netdev_set_ethtool); - return netdev_linux_do_ethtool(netdev_get_name(netdev), &ecmd, - ETHTOOL_SSET, "ETHTOOL_SSET"); + error = netdev_linux_do_ethtool(netdev_get_name(netdev_), &ecmd, + ETHTOOL_SSET, "ETHTOOL_SSET"); + +exit: + ovs_mutex_unlock(&netdev->mutex); + return error; } /* Attempts to set input rate limiting (policing) policy. Returns 0 if @@ -1733,20 +1804,17 @@ netdev_linux_set_policing(struct netdev *netdev_, const char *netdev_name = netdev_get_name(netdev_); int error; - kbits_burst = (!kbits_rate ? 0 /* Force to 0 if no rate specified. */ : !kbits_burst ? 1000 /* Default to 1000 kbits if 0. */ : kbits_burst); /* Stick with user-specified value. */ + ovs_mutex_lock(&netdev->mutex); if (netdev->cache_valid & VALID_POLICING) { - if (netdev->netdev_policing_error) { - return netdev->netdev_policing_error; - } - - if (netdev->kbits_rate == kbits_rate && - netdev->kbits_burst == kbits_burst) { + error = netdev->netdev_policing_error; + if (error || (netdev->kbits_rate == kbits_rate && + netdev->kbits_burst == kbits_burst)) { /* Assume that settings haven't changed since we last set them. */ - return 0; + goto out; } netdev->cache_valid &= ~VALID_POLICING; } @@ -1784,6 +1852,7 @@ out: netdev->netdev_policing_error = error; netdev->cache_valid |= VALID_POLICING; } + ovs_mutex_unlock(&netdev->mutex); return error; } @@ -1871,15 +1940,17 @@ netdev_linux_get_qos(const struct netdev *netdev_, struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; + ovs_mutex_lock(&netdev->mutex); error = tc_query_qdisc(netdev_); - if (error) { - return error; + if (!error) { + *typep = netdev->tc->ops->ovs_name; + error = (netdev->tc->ops->qdisc_get + ? netdev->tc->ops->qdisc_get(netdev_, details) + : 0); } + ovs_mutex_unlock(&netdev->mutex); - *typep = netdev->tc->ops->ovs_name; - return (netdev->tc->ops->qdisc_get - ? netdev->tc->ops->qdisc_get(netdev_, details) - : 0); + return error; } static int @@ -1895,27 +1966,30 @@ netdev_linux_set_qos(struct netdev *netdev_, return EOPNOTSUPP; } + ovs_mutex_lock(&netdev->mutex); error = tc_query_qdisc(netdev_); if (error) { - return error; + goto exit; } if (new_ops == netdev->tc->ops) { - return new_ops->qdisc_set ? new_ops->qdisc_set(netdev_, details) : 0; + error = new_ops->qdisc_set ? new_ops->qdisc_set(netdev_, details) : 0; } else { /* Delete existing qdisc. */ error = tc_del_qdisc(netdev_); if (error) { - return error; + goto exit; } ovs_assert(netdev->tc == NULL); /* Install new qdisc. */ error = new_ops->tc_install(netdev_, details); ovs_assert((error == 0) == (netdev->tc != NULL)); - - return error; } + +exit: + ovs_mutex_unlock(&netdev->mutex); + return error; } static int @@ -1925,15 +1999,17 @@ netdev_linux_get_queue(const struct netdev *netdev_, struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; + ovs_mutex_lock(&netdev->mutex); error = tc_query_qdisc(netdev_); - if (error) { - return error; - } else { + if (!error) { struct tc_queue *queue = tc_find_queue(netdev_, queue_id); - return (queue + error = (queue ? netdev->tc->ops->class_get(netdev_, queue, details) : ENOENT); } + ovs_mutex_unlock(&netdev->mutex); + + return error; } static int @@ -1943,15 +2019,17 @@ netdev_linux_set_queue(struct netdev *netdev_, struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; + ovs_mutex_lock(&netdev->mutex); error = tc_query_qdisc(netdev_); - if (error) { - return error; - } else if (queue_id >= netdev->tc->ops->n_queues - || !netdev->tc->ops->class_set) { - return EINVAL; + if (!error) { + error = (queue_id < netdev->tc->ops->n_queues + && netdev->tc->ops->class_set + ? netdev->tc->ops->class_set(netdev_, queue_id, details) + : EINVAL); } + ovs_mutex_unlock(&netdev->mutex); - return netdev->tc->ops->class_set(netdev_, queue_id, details); + return error; } static int @@ -1960,17 +2038,21 @@ netdev_linux_delete_queue(struct netdev *netdev_, unsigned int queue_id) struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; + ovs_mutex_lock(&netdev->mutex); error = tc_query_qdisc(netdev_); - if (error) { - return error; - } else if (!netdev->tc->ops->class_delete) { - return EINVAL; - } else { - struct tc_queue *queue = tc_find_queue(netdev_, queue_id); - return (queue - ? netdev->tc->ops->class_delete(netdev_, queue) - : ENOENT); + if (!error) { + if (netdev->tc->ops->class_delete) { + struct tc_queue *queue = tc_find_queue(netdev_, queue_id); + error = (queue + ? netdev->tc->ops->class_delete(netdev_, queue) + : ENOENT); + } else { + error = EINVAL; + } } + ovs_mutex_unlock(&netdev->mutex); + + return error; } static int @@ -1981,19 +2063,25 @@ netdev_linux_get_queue_stats(const struct netdev *netdev_, struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; + ovs_mutex_lock(&netdev->mutex); error = tc_query_qdisc(netdev_); - if (error) { - return error; - } else if (!netdev->tc->ops->class_get_stats) { - return EOPNOTSUPP; - } else { - const struct tc_queue *queue = tc_find_queue(netdev_, queue_id); - if (!queue) { - return ENOENT; + if (!error) { + if (netdev->tc->ops->class_get_stats) { + const struct tc_queue *queue = tc_find_queue(netdev_, queue_id); + if (queue) { + stats->created = queue->created; + error = netdev->tc->ops->class_get_stats(netdev_, queue, + stats); + } else { + error = ENOENT; + } + } else { + error = EOPNOTSUPP; } - stats->created = queue->created; - return netdev->tc->ops->class_get_stats(netdev_, queue, stats); } + ovs_mutex_unlock(&netdev->mutex); + + return error; } static bool @@ -2012,39 +2100,77 @@ start_queue_dump(const struct netdev *netdev, struct nl_dump *dump) return true; } +struct netdev_linux_queue_state { + unsigned int *queues; + size_t cur_queue; + size_t n_queues; +}; + static int -netdev_linux_dump_queues(const struct netdev *netdev_, - netdev_dump_queues_cb *cb, void *aux) +netdev_linux_queue_dump_start(const struct netdev *netdev_, void **statep) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); - struct tc_queue *queue, *next_queue; - struct smap details; - int last_error; + const struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; + ovs_mutex_lock(&netdev->mutex); error = tc_query_qdisc(netdev_); - if (error) { - return error; - } else if (!netdev->tc->ops->class_get) { - return EOPNOTSUPP; + if (!error) { + if (netdev->tc->ops->class_get) { + struct netdev_linux_queue_state *state; + struct tc_queue *queue; + size_t i; + + *statep = state = xmalloc(sizeof *state); + state->n_queues = hmap_count(&netdev->tc->queues); + state->cur_queue = 0; + state->queues = xmalloc(state->n_queues * sizeof *state->queues); + + i = 0; + HMAP_FOR_EACH (queue, hmap_node, &netdev->tc->queues) { + state->queues[i++] = queue->queue_id; + } + } else { + error = EOPNOTSUPP; + } } + ovs_mutex_unlock(&netdev->mutex); - last_error = 0; - smap_init(&details); - HMAP_FOR_EACH_SAFE (queue, next_queue, hmap_node, - &netdev->tc->queues) { - smap_clear(&details); + return error; +} - error = netdev->tc->ops->class_get(netdev_, queue, &details); - if (!error) { - (*cb)(queue->queue_id, &details, aux); - } else { - last_error = error; +static int +netdev_linux_queue_dump_next(const struct netdev *netdev_, void *state_, + unsigned int *queue_idp, struct smap *details) +{ + const struct netdev_linux *netdev = netdev_linux_cast(netdev_); + struct netdev_linux_queue_state *state = state_; + int error = EOF; + + ovs_mutex_lock(&netdev->mutex); + while (state->cur_queue < state->n_queues) { + unsigned int queue_id = state->queues[state->cur_queue++]; + struct tc_queue *queue = tc_find_queue(netdev_, queue_id); + + if (queue) { + *queue_idp = queue_id; + error = netdev->tc->ops->class_get(netdev_, queue, details); + break; } } - smap_destroy(&details); + ovs_mutex_unlock(&netdev->mutex); - return last_error; + return error; +} + +static int +netdev_linux_queue_dump_done(const struct netdev *netdev OVS_UNUSED, + void *state_) +{ + struct netdev_linux_queue_state *state = state_; + + free(state->queues); + free(state); + return 0; } static int @@ -2052,31 +2178,38 @@ netdev_linux_dump_queue_stats(const struct netdev *netdev_, netdev_dump_queue_stats_cb *cb, void *aux) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); - struct nl_dump dump; - struct ofpbuf msg; - int last_error; int error; + ovs_mutex_lock(&netdev->mutex); error = tc_query_qdisc(netdev_); - if (error) { - return error; - } else if (!netdev->tc->ops->class_dump_stats) { - return EOPNOTSUPP; - } + if (!error) { + struct nl_dump dump; - last_error = 0; - if (!start_queue_dump(netdev_, &dump)) { - return ENODEV; - } - while (nl_dump_next(&dump, &msg)) { - error = netdev->tc->ops->class_dump_stats(netdev_, &msg, cb, aux); - if (error) { - last_error = error; + if (!netdev->tc->ops->class_dump_stats) { + error = EOPNOTSUPP; + } else if (!start_queue_dump(netdev_, &dump)) { + error = ENODEV; + } else { + struct ofpbuf msg; + int retval; + + while (nl_dump_next(&dump, &msg)) { + retval = netdev->tc->ops->class_dump_stats(netdev_, &msg, + cb, aux); + if (retval) { + error = retval; + } + } + + retval = nl_dump_done(&dump); + if (retval) { + error = retval; + } } } + ovs_mutex_unlock(&netdev->mutex); - error = nl_dump_done(&dump); - return error ? error : last_error; + return error; } static int @@ -2084,27 +2217,34 @@ netdev_linux_get_in4(const struct netdev *netdev_, struct in_addr *address, struct in_addr *netmask) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); + int error; + ovs_mutex_lock(&netdev->mutex); if (!(netdev->cache_valid & VALID_IN4)) { - int error; - error = netdev_linux_get_ipv4(netdev_, &netdev->address, SIOCGIFADDR, "SIOCGIFADDR"); - if (error) { - return error; + if (!error) { + error = netdev_linux_get_ipv4(netdev_, &netdev->netmask, + SIOCGIFNETMASK, "SIOCGIFNETMASK"); + if (!error) { + netdev->cache_valid |= VALID_IN4; + } } + } else { + error = 0; + } - error = netdev_linux_get_ipv4(netdev_, &netdev->netmask, - SIOCGIFNETMASK, "SIOCGIFNETMASK"); - if (error) { - return error; + if (!error) { + if (netdev->address.s_addr != INADDR_ANY) { + *address = netdev->address; + *netmask = netdev->netmask; + } else { + error = EADDRNOTAVAIL; } - - netdev->cache_valid |= VALID_IN4; } - *address = netdev->address; - *netmask = netdev->netmask; - return address->s_addr == INADDR_ANY ? EADDRNOTAVAIL : 0; + ovs_mutex_unlock(&netdev->mutex); + + return error; } static int @@ -2114,6 +2254,7 @@ netdev_linux_set_in4(struct netdev *netdev_, struct in_addr address, struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error; + ovs_mutex_lock(&netdev->mutex); error = do_set_addr(netdev_, SIOCSIFADDR, "SIOCSIFADDR", address); if (!error) { netdev->cache_valid |= VALID_IN4; @@ -2124,6 +2265,8 @@ netdev_linux_set_in4(struct netdev *netdev_, struct in_addr address, "SIOCSIFNETMASK", netmask); } } + ovs_mutex_unlock(&netdev->mutex); + return error; } @@ -2149,6 +2292,8 @@ static int netdev_linux_get_in6(const struct netdev *netdev_, struct in6_addr *in6) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); + + ovs_mutex_lock(&netdev->mutex); if (!(netdev->cache_valid & VALID_IN6)) { FILE *file; char line[128]; @@ -2173,6 +2318,8 @@ netdev_linux_get_in6(const struct netdev *netdev_, struct in6_addr *in6) netdev->cache_valid |= VALID_IN6; } *in6 = netdev->in6; + ovs_mutex_unlock(&netdev->mutex); + return 0; } @@ -2194,11 +2341,10 @@ do_set_addr(struct netdev *netdev, int ioctl_nr, const char *ioctl_name, struct in_addr addr) { struct ifreq ifr; - ovs_strzcpy(ifr.ifr_name, netdev_get_name(netdev), sizeof ifr.ifr_name); - make_in4_sockaddr(&ifr.ifr_addr, addr); - return netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr, ioctl_nr, - ioctl_name); + make_in4_sockaddr(&ifr.ifr_addr, addr); + return af_inet_ifreq_ioctl(netdev_get_name(netdev), &ifr, ioctl_nr, + ioctl_name); } /* Adds 'router' as a default IP gateway. */ @@ -2214,7 +2360,7 @@ netdev_linux_add_router(struct netdev *netdev OVS_UNUSED, struct in_addr router) make_in4_sockaddr(&rt.rt_gateway, router); make_in4_sockaddr(&rt.rt_genmask, any); rt.rt_flags = RTF_UP | RTF_GATEWAY; - error = ioctl(af_inet_sock, SIOCADDRT, &rt) < 0 ? errno : 0; + error = af_inet_ioctl(SIOCADDRT, &rt); if (error) { VLOG_WARN("ioctl(SIOCADDRT): %s", ovs_strerror(error)); } @@ -2288,6 +2434,7 @@ netdev_linux_get_status(const struct netdev *netdev_, struct smap *smap) struct netdev_linux *netdev = netdev_linux_cast(netdev_); int error = 0; + ovs_mutex_lock(&netdev->mutex); if (!(netdev->cache_valid & VALID_DRVINFO)) { struct ethtool_cmd *cmd = (struct ethtool_cmd *) &netdev->drvinfo; @@ -2307,6 +2454,8 @@ netdev_linux_get_status(const struct netdev *netdev_, struct smap *smap) smap_add(smap, "driver_version", netdev->drvinfo.version); smap_add(smap, "firmware_version", netdev->drvinfo.fw_version); } + ovs_mutex_unlock(&netdev->mutex); + return error; } @@ -2340,7 +2489,7 @@ netdev_linux_arp_lookup(const struct netdev *netdev, r.arp_flags = 0; ovs_strzcpy(r.arp_dev, netdev_get_name(netdev), sizeof r.arp_dev); COVERAGE_INC(netdev_arp_lookup); - retval = ioctl(af_inet_sock, SIOCGARP, &r) < 0 ? errno : 0; + retval = af_inet_ioctl(SIOCGARP, &r); if (!retval) { memcpy(mac, r.arp_ha.sa_data, ETH_ADDR_LEN); } else if (retval != ENXIO) { @@ -2378,10 +2527,10 @@ iff_to_nd_flags(int iff) } static int -netdev_linux_update_flags(struct netdev *netdev_, enum netdev_flags off, - enum netdev_flags on, enum netdev_flags *old_flagsp) +update_flags(struct netdev_linux *netdev, enum netdev_flags off, + enum netdev_flags on, enum netdev_flags *old_flagsp) + OVS_REQUIRES(netdev->mutex) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); int old_flags, new_flags; int error = 0; @@ -2389,35 +2538,57 @@ netdev_linux_update_flags(struct netdev *netdev_, enum netdev_flags off, *old_flagsp = iff_to_nd_flags(old_flags); new_flags = (old_flags & ~nd_to_iff_flags(off)) | nd_to_iff_flags(on); if (new_flags != old_flags) { - error = set_flags(netdev_get_name(netdev_), new_flags); - get_flags(netdev_, &netdev->ifi_flags); + error = set_flags(netdev_get_name(&netdev->up), new_flags); + get_flags(&netdev->up, &netdev->ifi_flags); } + + return error; +} + +static int +netdev_linux_update_flags(struct netdev *netdev_, enum netdev_flags off, + enum netdev_flags on, enum netdev_flags *old_flagsp) +{ + struct netdev_linux *netdev = netdev_linux_cast(netdev_); + int error; + + ovs_mutex_lock(&netdev->mutex); + error = update_flags(netdev, off, on, old_flagsp); + ovs_mutex_unlock(&netdev->mutex); + return error; } static unsigned int -netdev_linux_change_seq(const struct netdev *netdev) +netdev_linux_change_seq(const struct netdev *netdev_) { - return netdev_linux_cast(netdev)->change_seq; + struct netdev_linux *netdev = netdev_linux_cast(netdev_); + unsigned int change_seq; + + ovs_mutex_lock(&netdev->mutex); + change_seq = netdev->change_seq; + ovs_mutex_unlock(&netdev->mutex); + + return change_seq; } -#define NETDEV_LINUX_CLASS(NAME, CREATE, GET_STATS, SET_STATS, \ +#define NETDEV_LINUX_CLASS(NAME, CONSTRUCT, GET_STATS, SET_STATS, \ GET_FEATURES, GET_STATUS) \ { \ NAME, \ \ - netdev_linux_init, \ + NULL, \ netdev_linux_run, \ netdev_linux_wait, \ \ - CREATE, \ - netdev_linux_destroy, \ + netdev_linux_alloc, \ + CONSTRUCT, \ + netdev_linux_destruct, \ + netdev_linux_dealloc, \ NULL, /* get_config */ \ NULL, /* set_config */ \ NULL, /* get_tunnel_config */ \ \ - netdev_linux_rx_open, \ - \ netdev_linux_send, \ netdev_linux_send_wait, \ \ @@ -2444,7 +2615,9 @@ netdev_linux_change_seq(const struct netdev *netdev) netdev_linux_set_queue, \ netdev_linux_delete_queue, \ netdev_linux_get_queue_stats, \ - netdev_linux_dump_queues, \ + netdev_linux_queue_dump_start, \ + netdev_linux_queue_dump_next, \ + netdev_linux_queue_dump_done, \ netdev_linux_dump_queue_stats, \ \ netdev_linux_get_in4, \ @@ -2457,13 +2630,21 @@ netdev_linux_change_seq(const struct netdev *netdev) \ netdev_linux_update_flags, \ \ - netdev_linux_change_seq \ + netdev_linux_change_seq, \ + \ + netdev_linux_rx_alloc, \ + netdev_linux_rx_construct, \ + netdev_linux_rx_destruct, \ + netdev_linux_rx_dealloc, \ + netdev_linux_rx_recv, \ + netdev_linux_rx_wait, \ + netdev_linux_rx_drain, \ } const struct netdev_class netdev_linux_class = NETDEV_LINUX_CLASS( "system", - netdev_linux_create, + netdev_linux_construct, netdev_linux_get_stats, NULL, /* set_stats */ netdev_linux_get_features, @@ -2472,7 +2653,7 @@ const struct netdev_class netdev_linux_class = const struct netdev_class netdev_tap_class = NETDEV_LINUX_CLASS( "tap", - netdev_linux_create_tap, + netdev_linux_construct_tap, netdev_tap_get_stats, NULL, /* set_stats */ netdev_linux_get_features, @@ -2481,18 +2662,11 @@ const struct netdev_class netdev_tap_class = const struct netdev_class netdev_internal_class = NETDEV_LINUX_CLASS( "internal", - netdev_linux_create, + netdev_linux_construct, netdev_internal_get_stats, netdev_internal_set_stats, NULL, /* get_features */ netdev_internal_get_status); - -static const struct netdev_rx_class netdev_rx_linux_class = { - netdev_rx_linux_destroy, - netdev_rx_linux_recv, - netdev_rx_linux_wait, - netdev_rx_linux_drain, -}; /* HTB traffic control class. */ @@ -2579,7 +2753,7 @@ htb_setup_class__(struct netdev *netdev, unsigned int handle, int error; int mtu; - error = netdev_get_mtu(netdev, &mtu); + error = netdev_linux_get_mtu__(netdev_linux_cast(netdev), &mtu); if (error) { VLOG_WARN_RL(&rl, "cannot set up HTB on device %s that lacks MTU", netdev_get_name(netdev)); @@ -2675,9 +2849,10 @@ htb_parse_tcmsg__(struct ofpbuf *tcmsg, unsigned int *queue_id, } static void -htb_parse_qdisc_details__(struct netdev *netdev, +htb_parse_qdisc_details__(struct netdev *netdev_, const struct smap *details, struct htb_class *hc) { + struct netdev_linux *netdev = netdev_linux_cast(netdev_); const char *max_rate_s; max_rate_s = smap_get(details, "max-rate"); @@ -2685,7 +2860,8 @@ htb_parse_qdisc_details__(struct netdev *netdev, if (!hc->max_rate) { enum netdev_features current; - netdev_get_features(netdev, ¤t, NULL, NULL, NULL); + netdev_linux_read_features(netdev); + current = !netdev->get_features_error ? netdev->current : 0; hc->max_rate = netdev_features_to_bps(current, 100 * 1000 * 1000) / 8; } hc->min_rate = hc->max_rate; @@ -2704,7 +2880,7 @@ htb_parse_class_details__(struct netdev *netdev, const char *priority_s = smap_get(details, "priority"); int mtu, error; - error = netdev_get_mtu(netdev, &mtu); + error = netdev_linux_get_mtu__(netdev_linux_cast(netdev), &mtu); if (error) { VLOG_WARN_RL(&rl, "cannot parse HTB class on device %s that lacks MTU", netdev_get_name(netdev)); @@ -3152,9 +3328,10 @@ hfsc_query_class__(const struct netdev *netdev, unsigned int handle, } static void -hfsc_parse_qdisc_details__(struct netdev *netdev, const struct smap *details, +hfsc_parse_qdisc_details__(struct netdev *netdev_, const struct smap *details, struct hfsc_class *class) { + struct netdev_linux *netdev = netdev_linux_cast(netdev_); uint32_t max_rate; const char *max_rate_s; @@ -3164,7 +3341,8 @@ hfsc_parse_qdisc_details__(struct netdev *netdev, const struct smap *details, if (!max_rate) { enum netdev_features current; - netdev_get_features(netdev, ¤t, NULL, NULL, NULL); + netdev_linux_read_features(netdev); + current = !netdev->get_features_error ? netdev->current : 0; max_rate = netdev_features_to_bps(current, 100 * 1000 * 1000) / 8; } @@ -4196,14 +4374,6 @@ tc_calc_buffer(unsigned int Bps, int mtu, uint64_t burst_bytes) /* Linux-only functions declared in netdev-linux.h */ -/* Returns a fd for an AF_INET socket or a negative errno value. */ -int -netdev_linux_get_af_inet_sock(void) -{ - int error = netdev_linux_init(); - return error ? -error : af_inet_sock; -} - /* Modifies the 'flag' bit in ethtool's flags field for 'netdev'. If * 'enable' is true, the bit is set. Otherwise, it is cleared. */ int @@ -4396,8 +4566,7 @@ get_flags(const struct netdev *dev, unsigned int *flags) int error; *flags = 0; - error = netdev_linux_do_ioctl(dev->name, &ifr, SIOCGIFFLAGS, - "SIOCGIFFLAGS"); + error = af_inet_ifreq_ioctl(dev->name, &ifr, SIOCGIFFLAGS, "SIOCGIFFLAGS"); if (!error) { *flags = ifr.ifr_flags; } @@ -4410,20 +4579,23 @@ set_flags(const char *name, unsigned int flags) struct ifreq ifr; ifr.ifr_flags = flags; - return netdev_linux_do_ioctl(name, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS"); + return af_inet_ifreq_ioctl(name, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS"); } static int do_get_ifindex(const char *netdev_name) { struct ifreq ifr; + int error; ovs_strzcpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name); COVERAGE_INC(netdev_get_ifindex); - if (ioctl(af_inet_sock, SIOCGIFINDEX, &ifr) < 0) { + + error = af_inet_ioctl(SIOCGIFINDEX, &ifr); + if (error) { VLOG_WARN_RL(&rl, "ioctl(SIOCGIFINDEX) on %s device failed: %s", - netdev_name, ovs_strerror(errno)); - return -errno; + netdev_name, ovs_strerror(error)); + return -error; } return ifr.ifr_ifindex; } @@ -4455,18 +4627,20 @@ get_etheraddr(const char *netdev_name, uint8_t ea[ETH_ADDR_LEN]) { struct ifreq ifr; int hwaddr_family; + int error; memset(&ifr, 0, sizeof ifr); ovs_strzcpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name); COVERAGE_INC(netdev_get_hwaddr); - if (ioctl(af_inet_sock, SIOCGIFHWADDR, &ifr) < 0) { + error = af_inet_ioctl(SIOCGIFHWADDR, &ifr); + if (error) { /* ENODEV probably means that a vif disappeared asynchronously and * hasn't been removed from the database yet, so reduce the log level * to INFO for that case. */ - VLOG(errno == ENODEV ? VLL_INFO : VLL_ERR, + VLOG(error == ENODEV ? VLL_INFO : VLL_ERR, "ioctl(SIOCGIFHWADDR) on %s device failed: %s", - netdev_name, ovs_strerror(errno)); - return errno; + netdev_name, ovs_strerror(error)); + return error; } hwaddr_family = ifr.ifr_hwaddr.sa_family; if (hwaddr_family != AF_UNSPEC && hwaddr_family != ARPHRD_ETHER) { @@ -4482,18 +4656,19 @@ set_etheraddr(const char *netdev_name, const uint8_t mac[ETH_ADDR_LEN]) { struct ifreq ifr; + int error; memset(&ifr, 0, sizeof ifr); ovs_strzcpy(ifr.ifr_name, netdev_name, sizeof ifr.ifr_name); ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; memcpy(ifr.ifr_hwaddr.sa_data, mac, ETH_ADDR_LEN); COVERAGE_INC(netdev_set_hwaddr); - if (ioctl(af_inet_sock, SIOCSIFHWADDR, &ifr) < 0) { + error = af_inet_ioctl(SIOCSIFHWADDR, &ifr); + if (error) { VLOG_ERR("ioctl(SIOCSIFHWADDR) on %s device failed: %s", - netdev_name, ovs_strerror(errno)); - return errno; + netdev_name, ovs_strerror(error)); } - return 0; + return error; } static int @@ -4501,37 +4676,24 @@ netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *ecmd, int cmd, const char *cmd_name) { struct ifreq ifr; + int error; memset(&ifr, 0, sizeof ifr); ovs_strzcpy(ifr.ifr_name, name, sizeof ifr.ifr_name); ifr.ifr_data = (caddr_t) ecmd; ecmd->cmd = cmd; - if (ioctl(af_inet_sock, SIOCETHTOOL, &ifr) == 0) { - return 0; - } else { - if (errno != EOPNOTSUPP) { + error = af_inet_ioctl(SIOCETHTOOL, &ifr); + if (error) { + if (error != EOPNOTSUPP) { VLOG_WARN_RL(&rl, "ethtool command %s on network device %s " - "failed: %s", cmd_name, name, ovs_strerror(errno)); + "failed: %s", cmd_name, name, ovs_strerror(error)); } else { /* The device doesn't support this operation. That's pretty * common, so there's no point in logging anything. */ } - return errno; - } -} - -static int -netdev_linux_do_ioctl(const char *name, struct ifreq *ifr, int cmd, - const char *cmd_name) -{ - ovs_strzcpy(ifr->ifr_name, name, sizeof ifr->ifr_name); - if (ioctl(af_inet_sock, cmd, ifr) == -1) { - VLOG_DBG_RL(&rl, "%s: ioctl(%s) failed: %s", name, cmd_name, - ovs_strerror(errno)); - return errno; } - return 0; + return error; } static int @@ -4542,7 +4704,7 @@ netdev_linux_get_ipv4(const struct netdev *netdev, struct in_addr *ip, int error; ifr.ifr_addr.sa_family = AF_INET; - error = netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr, cmd, cmd_name); + error = af_inet_ifreq_ioctl(netdev_get_name(netdev), &ifr, cmd, cmd_name); if (!error) { const struct sockaddr_in *sin = ALIGNED_CAST(struct sockaddr_in *, &ifr.ifr_addr);