X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fnetdev-linux.c;h=d5a3a935a87714b5c335527622c3b4e0170b4737;hb=994145e0f7c66d4cdada1d15a5fd4699931069fc;hp=4d2f3ac0977c8f3c7dbb71318cd6a7157758dd91;hpb=e0edde6fee279cdbbf3c179f5f50adaf0c7c7f1e;p=sliver-openvswitch.git diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index 4d2f3ac09..d5a3a935a 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc. + * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ #include "netdev-linux.h" -#include #include #include #include @@ -56,18 +55,19 @@ #include "hmap.h" #include "netdev-provider.h" #include "netdev-vport.h" -#include "netlink.h" #include "netlink-notifier.h" #include "netlink-socket.h" +#include "netlink.h" #include "ofpbuf.h" #include "openflow/openflow.h" #include "packets.h" #include "poll-loop.h" #include "rtnetlink-link.h" -#include "socket-util.h" #include "shash.h" +#include "socket-util.h" #include "sset.h" #include "timer.h" +#include "unaligned.h" #include "vlog.h" VLOG_DEFINE_THIS_MODULE(netdev_linux); @@ -77,7 +77,8 @@ COVERAGE_DEFINE(netdev_arp_lookup); COVERAGE_DEFINE(netdev_get_ifindex); COVERAGE_DEFINE(netdev_get_hwaddr); COVERAGE_DEFINE(netdev_set_hwaddr); -COVERAGE_DEFINE(netdev_ethtool); +COVERAGE_DEFINE(netdev_get_ethtool); +COVERAGE_DEFINE(netdev_set_ethtool); /* These were introduced in Linux 2.6.14, so they might be missing if we have @@ -121,7 +122,6 @@ enum { struct tap_state { int fd; - bool opened; }; /* Traffic control. */ @@ -138,6 +138,8 @@ struct tc { * Written only by TC implementation. */ }; +#define TC_INITIALIZER(TC, OPS) { OPS, HMAP_INITIALIZER(&(TC)->queues) } + /* One traffic control queue. * * Each TC implementation subclasses this with whatever additional data it @@ -181,7 +183,7 @@ struct tc_ops { * * (This function is null for tc_ops_other, which cannot be installed. For * other TC classes it should always be nonnull.) */ - int (*tc_install)(struct netdev *netdev, const struct shash *details); + int (*tc_install)(struct netdev *netdev, const struct smap *details); /* Called when the netdev code determines (through a Netlink query) that * this TC class's qdisc is installed on 'netdev', but we didn't install @@ -221,7 +223,7 @@ struct tc_ops { * * This function may be null if 'tc' is not configurable. */ - int (*qdisc_get)(const struct netdev *netdev, struct shash *details); + int (*qdisc_get)(const struct netdev *netdev, struct smap *details); /* Reconfigures 'netdev->tc' according to 'details', performing any * required Netlink calls to complete the reconfiguration. @@ -232,7 +234,7 @@ struct tc_ops { * * This function may be null if 'tc' is not configurable. */ - int (*qdisc_set)(struct netdev *, const struct shash *details); + int (*qdisc_set)(struct netdev *, const struct smap *details); /* Retrieves details of 'queue' on 'netdev->tc' into 'details'. 'queue' is * one of the 'struct tc_queue's within 'netdev->tc->queues'. @@ -248,7 +250,7 @@ struct tc_ops { * This function may be null if 'tc' does not have queues ('n_queues' is * 0). */ int (*class_get)(const struct netdev *netdev, const struct tc_queue *queue, - struct shash *details); + struct smap *details); /* Configures or reconfigures 'queue_id' on 'netdev->tc' according to * 'details', perfoming any required Netlink calls to complete the @@ -262,7 +264,7 @@ struct tc_ops { * This function may be null if 'tc' does not have queues or its queues are * not configurable. */ int (*class_set)(struct netdev *, unsigned int queue_id, - const struct shash *details); + const struct smap *details); /* Deletes 'queue' from 'netdev->tc'. 'queue' is one of the 'struct * tc_queue's within 'netdev->tc->queues'. @@ -310,7 +312,7 @@ static const struct tc_ops tc_ops_hfsc; static const struct tc_ops tc_ops_default; static const struct tc_ops tc_ops_other; -static const struct tc_ops *tcs[] = { +static const struct tc_ops *const tcs[] = { &tc_ops_htb, /* Hierarchy token bucket (see tc-htb(8)). */ &tc_ops_hfsc, /* Hierarchical fair service curve. */ &tc_ops_default, /* Default qdisc (see tc-pfifo_fast(8)). */ @@ -353,7 +355,7 @@ static void tc_put_rtab(struct ofpbuf *, uint16_t type, static int tc_calc_buffer(unsigned int Bps, int mtu, uint64_t burst_bytes); struct netdev_dev_linux { - struct netdev_dev netdev_dev; + struct netdev_dev up; struct shash_node *shash_node; unsigned int cache_valid; @@ -396,10 +398,17 @@ struct netdev_dev_linux { }; struct netdev_linux { - struct netdev netdev; + struct netdev up; +}; + +struct netdev_rx_linux { + struct netdev_rx up; + bool is_tap; 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. */ @@ -419,7 +428,7 @@ static int netdev_linux_do_ioctl(const char *name, struct ifreq *, int cmd, 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_dev *, unsigned int *flags); -static int set_flags(struct netdev *, unsigned int flags); +static int set_flags(const char *, unsigned int flags); 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, @@ -439,13 +448,19 @@ is_netdev_linux_class(const struct netdev_class *netdev_class) return netdev_class->init == netdev_linux_init; } +static bool +is_tap_netdev(const struct netdev *netdev) +{ + return netdev_dev_get_class(netdev_get_dev(netdev)) == &netdev_tap_class; +} + static struct netdev_dev_linux * netdev_dev_linux_cast(const struct netdev_dev *netdev_dev) { const struct netdev_class *netdev_class = netdev_dev_get_class(netdev_dev); - assert(is_netdev_linux_class(netdev_class)); + ovs_assert(is_netdev_linux_class(netdev_class)); - return CONTAINER_OF(netdev_dev, struct netdev_dev_linux, netdev_dev); + return CONTAINER_OF(netdev_dev, struct netdev_dev_linux, up); } static struct netdev_linux * @@ -453,9 +468,16 @@ netdev_linux_cast(const struct netdev *netdev) { struct netdev_dev *netdev_dev = netdev_get_dev(netdev); const struct netdev_class *netdev_class = netdev_dev_get_class(netdev_dev); - assert(is_netdev_linux_class(netdev_class)); + ovs_assert(is_netdev_linux_class(netdev_class)); + + return CONTAINER_OF(netdev, struct netdev_linux, up); +} - return CONTAINER_OF(netdev, struct netdev_linux, netdev); +static struct netdev_rx_linux * +netdev_rx_linux_cast(const struct netdev_rx *rx) +{ + netdev_rx_assert_class(rx, &netdev_rx_linux_class); + return CONTAINER_OF(rx, struct netdev_rx_linux, up); } static int @@ -496,27 +518,6 @@ netdev_linux_wait(void) netdev_linux_miimon_wait(); } -static int -netdev_linux_get_drvinfo(struct netdev_dev_linux *netdev_dev) -{ - - int error; - - if (netdev_dev->cache_valid & VALID_DRVINFO) { - return 0; - } - - memset(&netdev_dev->drvinfo, 0, sizeof netdev_dev->drvinfo); - error = netdev_linux_do_ethtool(netdev_dev->netdev_dev.name, - (struct ethtool_cmd *)&netdev_dev->drvinfo, - ETHTOOL_GDRVINFO, - "ETHTOOL_GDRVINFO"); - if (!error) { - netdev_dev->cache_valid |= VALID_DRVINFO; - } - return error; -} - static void netdev_dev_linux_changed(struct netdev_dev_linux *dev, unsigned int ifi_flags, @@ -592,7 +593,7 @@ netdev_linux_cache_cb(const struct rtnetlink_link_change *change, dev = node->data; - get_flags(&dev->netdev_dev, &flags); + get_flags(&dev->up, &flags); netdev_dev_linux_changed(dev, flags, 0); } shash_destroy(&device_shash); @@ -603,7 +604,7 @@ static int cache_notifier_ref(void) { if (!cache_notifier_refcount) { - assert(!netdev_linux_cache_notifier); + ovs_assert(!netdev_linux_cache_notifier); netdev_linux_cache_notifier = rtnetlink_link_notifier_create(netdev_linux_cache_cb, NULL); @@ -620,9 +621,9 @@ cache_notifier_ref(void) static void cache_notifier_unref(void) { - assert(cache_notifier_refcount > 0); + ovs_assert(cache_notifier_refcount > 0); if (!--cache_notifier_refcount) { - assert(netdev_linux_cache_notifier); + ovs_assert(netdev_linux_cache_notifier); rtnetlink_link_notifier_destroy(netdev_linux_cache_notifier); netdev_linux_cache_notifier = NULL; } @@ -643,10 +644,10 @@ netdev_linux_create(const struct netdev_class *class, const char *name, netdev_dev = xzalloc(sizeof *netdev_dev); netdev_dev->change_seq = 1; - netdev_dev_init(&netdev_dev->netdev_dev, name, class); - get_flags(&netdev_dev->netdev_dev, &netdev_dev->ifi_flags); + netdev_dev_init(&netdev_dev->up, name, class); + get_flags(&netdev_dev->up, &netdev_dev->ifi_flags); - *netdev_devp = &netdev_dev->netdev_dev; + *netdev_devp = &netdev_dev->up; return 0; } @@ -698,8 +699,8 @@ netdev_linux_create_tap(const struct netdev_class *class OVS_UNUSED, goto error_unref_notifier; } - netdev_dev_init(&netdev_dev->netdev_dev, name, &netdev_tap_class); - *netdev_devp = &netdev_dev->netdev_dev; + netdev_dev_init(&netdev_dev->up, name, &netdev_tap_class); + *netdev_devp = &netdev_dev->up; return 0; error_unref_notifier: @@ -741,15 +742,13 @@ netdev_linux_destroy(struct netdev_dev *netdev_dev_) static int netdev_linux_open(struct netdev_dev *netdev_dev_, struct netdev **netdevp) { - struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_dev_); struct netdev_linux *netdev; enum netdev_flags flags; int error; /* Allocate network device. */ netdev = xzalloc(sizeof *netdev); - netdev->fd = -1; - netdev_init(&netdev->netdev, netdev_dev_); + netdev_init(&netdev->up, netdev_dev_); /* Verify that the device really exists, by attempting to read its flags. * (The flags might be cached, in which case this won't actually do an @@ -760,28 +759,17 @@ netdev_linux_open(struct netdev_dev *netdev_dev_, struct netdev **netdevp) * creating them in the kernel happens by passing a netdev object to * dpif_port_add(). */ if (netdev_dev_get_class(netdev_dev_) != &netdev_internal_class) { - error = netdev_get_flags(&netdev->netdev, &flags); + error = netdev_get_flags(&netdev->up, &flags); if (error == ENODEV) { goto error; } } - if (!strcmp(netdev_dev_get_type(netdev_dev_), "tap") && - !netdev_dev->state.tap.opened) { - - /* We assume that the first user of the tap device is the primary user - * and give them the tap FD. Subsequent users probably just expect - * this to be a system device so open it normally to avoid send/receive - * directions appearing to be reversed. */ - netdev->fd = netdev_dev->state.tap.fd; - netdev_dev->state.tap.opened = true; - } - - *netdevp = &netdev->netdev; + *netdevp = &netdev->up; return 0; error: - netdev_uninit(&netdev->netdev, true); + netdev_uninit(&netdev->up, true); return error; } @@ -791,58 +779,65 @@ netdev_linux_close(struct netdev *netdev_) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); - if (netdev->fd > 0 && strcmp(netdev_get_type(netdev_), "tap")) { - close(netdev->fd); - } free(netdev); } static int -netdev_linux_listen(struct netdev *netdev_) +netdev_linux_rx_open(struct netdev *netdev_, struct netdev_rx **rxp) { struct netdev_linux *netdev = netdev_linux_cast(netdev_); - struct sockaddr_ll sll; - int ifindex; + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev_)); + bool is_tap = is_tap_netdev(netdev_); + struct netdev_rx_linux *rx; int error; int fd; - if (netdev->fd >= 0) { - return 0; - } + if (is_tap) { + fd = netdev_dev->state.tap.fd; + } else { + struct sockaddr_ll sll; + int ifindex; - /* Create file descriptor. */ - fd = socket(PF_PACKET, SOCK_RAW, 0); - if (fd < 0) { - error = errno; - VLOG_ERR("failed to create raw socket (%s)", strerror(error)); - goto error; - } + /* Create file descriptor. */ + fd = socket(PF_PACKET, SOCK_RAW, 0); + if (fd < 0) { + error = errno; + VLOG_ERR("failed to create raw socket (%s)", strerror(error)); + goto error; + } - /* Set non-blocking mode. */ - error = set_nonblocking(fd); - if (error) { - goto error; - } + /* Set non-blocking mode. */ + error = set_nonblocking(fd); + if (error) { + goto error; + } - /* Get ethernet device index. */ - error = get_ifindex(&netdev->netdev, &ifindex); - if (error) { - goto error; - } + /* Get ethernet device index. */ + error = get_ifindex(&netdev->up, &ifindex); + if (error) { + goto error; + } - /* Bind to specific ethernet device. */ - memset(&sll, 0, sizeof sll); - 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) { - error = errno; - VLOG_ERR("%s: failed to bind raw socket (%s)", - netdev_get_name(netdev_), strerror(error)); - goto error; + /* Bind to specific ethernet device. */ + memset(&sll, 0, sizeof sll); + 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) { + error = errno; + VLOG_ERR("%s: failed to bind raw socket (%s)", + netdev_get_name(netdev_), strerror(error)); + goto error; + } } - netdev->fd = fd; + rx = xmalloc(sizeof *rx); + netdev_rx_init(&rx->up, netdev_get_dev(netdev_), &netdev_rx_linux_class); + rx->is_tap = is_tap; + rx->fd = fd; + + *rxp = &rx->up; return 0; error: @@ -852,63 +847,64 @@ error: return error; } -static int -netdev_linux_recv(struct netdev *netdev_, void *data, size_t size) +static void +netdev_rx_linux_destroy(struct netdev_rx *rx_) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); + struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_); - if (netdev->fd < 0) { - /* Device is not listening. */ - return -EAGAIN; + if (!rx->is_tap) { + close(rx->fd); } + free(rx); +} - for (;;) { - ssize_t retval; +static int +netdev_rx_linux_recv(struct netdev_rx *rx_, void *data, size_t size) +{ + struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_); + ssize_t retval; - retval = (netdev_->netdev_dev->netdev_class == &netdev_tap_class - ? read(netdev->fd, data, size) - : recv(netdev->fd, data, size, MSG_TRUNC)); - if (retval >= 0) { - return retval <= size ? retval : -EMSGSIZE; - } else if (errno != EINTR) { - if (errno != EAGAIN) { - VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s", - strerror(errno), netdev_get_name(netdev_)); - } - return -errno; + do { + retval = (rx->is_tap + ? read(rx->fd, data, size) + : recv(rx->fd, data, size, MSG_TRUNC)); + } while (retval < 0 && errno == EINTR); + + if (retval > size) { + return -EMSGSIZE; + } else if (retval >= 0) { + return retval; + } else { + if (errno != EAGAIN) { + VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s", + strerror(errno), netdev_rx_get_name(rx_)); } + return -errno; } } -/* Registers with the poll loop to wake up from the next call to poll_block() - * when a packet is ready to be received with netdev_recv() on 'netdev'. */ static void -netdev_linux_recv_wait(struct netdev *netdev_) +netdev_rx_linux_wait(struct netdev_rx *rx_) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); - if (netdev->fd >= 0) { - poll_fd_wait(netdev->fd, POLLIN); - } + struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_); + poll_fd_wait(rx->fd, POLLIN); } -/* Discards all packets waiting to be received from 'netdev'. */ static int -netdev_linux_drain(struct netdev *netdev_) +netdev_rx_linux_drain(struct netdev_rx *rx_) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); - if (netdev->fd < 0) { - return 0; - } else if (!strcmp(netdev_get_type(netdev_), "tap")) { + struct netdev_rx_linux *rx = netdev_rx_linux_cast(rx_); + if (rx->is_tap) { struct ifreq ifr; - int error = netdev_linux_do_ioctl(netdev_get_name(netdev_), &ifr, + int error = netdev_linux_do_ioctl(netdev_rx_get_name(rx_), &ifr, SIOCGIFTXQLEN, "SIOCGIFTXQLEN"); if (error) { return error; } - drain_fd(netdev->fd, ifr.ifr_qlen); + drain_fd(rx->fd, ifr.ifr_qlen); return 0; } else { - return drain_rcvbuf(netdev->fd); + return drain_rcvbuf(rx->fd); } } @@ -924,11 +920,10 @@ netdev_linux_drain(struct netdev *netdev_) static int netdev_linux_send(struct netdev *netdev_, const void *data, size_t size) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); for (;;) { ssize_t retval; - if (netdev->fd < 0) { + if (!is_tap_netdev(netdev_)) { /* Use our AF_PACKET socket to send to this device. */ struct sockaddr_ll sll; struct msghdr msg; @@ -939,7 +934,7 @@ netdev_linux_send(struct netdev *netdev_, const void *data, size_t size) sock = af_packet_sock(); if (sock < 0) { - return sock; + return -sock; } error = get_ifindex(netdev_, &ifindex); @@ -953,7 +948,7 @@ netdev_linux_send(struct netdev *netdev_, const void *data, size_t size) sll.sll_family = AF_PACKET; sll.sll_ifindex = ifindex; - iov.iov_base = (void *) data; + iov.iov_base = CONST_CAST(void *, data); iov.iov_len = size; msg.msg_name = &sll; @@ -966,11 +961,14 @@ netdev_linux_send(struct netdev *netdev_, const void *data, size_t size) retval = sendmsg(sock, &msg, 0); } else { - /* Use the netdev's own fd to send to this device. This is - * essential for tap devices, because packets sent to a tap device - * with an AF_PACKET socket will loop back to be *received* again - * on the tap device. */ - retval = write(netdev->fd, data, size); + /* Use the tap fd to send to this device. This is essential for + * tap devices, because packets sent to a tap device with an + * AF_PACKET socket will loop back to be *received* again on the + * tap device. */ + struct netdev_dev_linux *dev + = netdev_dev_linux_cast(netdev_get_dev(netdev_)); + + retval = write(dev->state.tap.fd, data, size); } if (retval < 0) { @@ -1004,14 +1002,9 @@ netdev_linux_send(struct netdev *netdev_, const void *data, size_t size) * expected to do additional queuing of packets. Thus, this function is * unlikely to ever be used. It is included for completeness. */ static void -netdev_linux_send_wait(struct netdev *netdev_) +netdev_linux_send_wait(struct netdev *netdev) { - struct netdev_linux *netdev = netdev_linux_cast(netdev_); - if (netdev->fd < 0) { - /* Nothing to do. */ - } else if (strcmp(netdev_get_type(netdev_), "tap")) { - poll_fd_wait(netdev->fd, POLLOUT); - } else { + if (is_tap_netdev(netdev)) { /* TAP device always accepts packets.*/ poll_immediate_wake(); } @@ -1025,6 +1018,7 @@ netdev_linux_set_etheraddr(struct netdev *netdev_, { struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev_)); + struct netdev_saved_flags *sf = NULL; int error; if (netdev_dev->cache_valid & VALID_ETHERADDR) { @@ -1037,6 +1031,14 @@ netdev_linux_set_etheraddr(struct netdev *netdev_, netdev_dev->cache_valid &= ~VALID_ETHERADDR; } + /* Tap devices must be brought down before setting the address. */ + if (is_tap_netdev(netdev_)) { + enum netdev_flags flags; + + if (!netdev_get_flags(netdev_, &flags) && (flags & NETDEV_UP)) { + netdev_turn_flags_off(netdev_, NETDEV_UP, &sf); + } + } error = set_etheraddr(netdev_get_name(netdev_), mac); if (!error || error == ENODEV) { netdev_dev->ether_addr_error = error; @@ -1046,6 +1048,8 @@ netdev_linux_set_etheraddr(struct netdev *netdev_, } } + netdev_restore_flags(sf); + return error; } @@ -1203,6 +1207,7 @@ netdev_linux_get_miimon(const char *name, bool *miimon) VLOG_DBG_RL(&rl, "%s: failed to query MII, falling back to ethtool", name); + COVERAGE_INC(netdev_get_ethtool); memset(&ecmd, 0, sizeof ecmd); error = netdev_linux_do_ethtool(name, &ecmd, ETHTOOL_GLINK, "ETHTOOL_GLINK"); @@ -1252,7 +1257,7 @@ netdev_linux_miimon_run(void) continue; } - netdev_linux_get_miimon(dev->netdev_dev.name, &miimon); + netdev_linux_get_miimon(dev->up.name, &miimon); if (miimon != dev->miimon) { dev->miimon = miimon; netdev_dev_linux_changed(dev, dev->ifi_flags, 0); @@ -1318,6 +1323,58 @@ swap_uint64(uint64_t *a, uint64_t *b) *b = tmp; } +/* Copies 'src' into 'dst', performing format conversion in the process. + * + * 'src' is allowed to be misaligned. */ +static void +netdev_stats_from_ovs_vport_stats(struct netdev_stats *dst, + const struct ovs_vport_stats *src) +{ + dst->rx_packets = get_unaligned_u64(&src->rx_packets); + dst->tx_packets = get_unaligned_u64(&src->tx_packets); + dst->rx_bytes = get_unaligned_u64(&src->rx_bytes); + dst->tx_bytes = get_unaligned_u64(&src->tx_bytes); + dst->rx_errors = get_unaligned_u64(&src->rx_errors); + dst->tx_errors = get_unaligned_u64(&src->tx_errors); + dst->rx_dropped = get_unaligned_u64(&src->rx_dropped); + dst->tx_dropped = get_unaligned_u64(&src->tx_dropped); + dst->multicast = 0; + dst->collisions = 0; + dst->rx_length_errors = 0; + dst->rx_over_errors = 0; + dst->rx_crc_errors = 0; + dst->rx_frame_errors = 0; + dst->rx_fifo_errors = 0; + dst->rx_missed_errors = 0; + dst->tx_aborted_errors = 0; + dst->tx_carrier_errors = 0; + dst->tx_fifo_errors = 0; + dst->tx_heartbeat_errors = 0; + dst->tx_window_errors = 0; +} + +static int +get_stats_via_vport__(const struct netdev *netdev, struct netdev_stats *stats) +{ + struct dpif_linux_vport reply; + struct ofpbuf *buf; + int error; + + error = dpif_linux_vport_get(netdev_get_name(netdev), &reply, &buf); + if (error) { + return error; + } else if (!reply.stats) { + ofpbuf_delete(buf); + return EOPNOTSUPP; + } + + netdev_stats_from_ovs_vport_stats(stats, reply.stats); + + ofpbuf_delete(buf); + + return 0; +} + static void get_stats_via_vport(const struct netdev *netdev_, struct netdev_stats *stats) @@ -1329,8 +1386,8 @@ get_stats_via_vport(const struct netdev *netdev_, !(netdev_dev->cache_valid & VALID_VPORT_STAT_ERROR)) { int error; - error = netdev_vport_get_stats(netdev_, stats); - if (error) { + error = get_stats_via_vport__(netdev_, stats); + if (error && error != ENOENT) { VLOG_WARN_RL(&rl, "%s: obtaining netdev stats via vport failed " "(%s)", netdev_get_name(netdev_), strerror(error)); } @@ -1485,6 +1542,41 @@ netdev_internal_get_stats(const struct netdev *netdev_, return netdev_dev->vport_stats_error; } +static int +netdev_internal_set_stats(struct netdev *netdev, + const struct netdev_stats *stats) +{ + struct ovs_vport_stats vport_stats; + struct dpif_linux_vport vport; + int err; + + vport_stats.rx_packets = stats->rx_packets; + vport_stats.tx_packets = stats->tx_packets; + vport_stats.rx_bytes = stats->rx_bytes; + vport_stats.tx_bytes = stats->tx_bytes; + vport_stats.rx_errors = stats->rx_errors; + vport_stats.tx_errors = stats->tx_errors; + vport_stats.rx_dropped = stats->rx_dropped; + vport_stats.tx_dropped = stats->tx_dropped; + + dpif_linux_vport_init(&vport); + vport.cmd = OVS_VPORT_CMD_SET; + vport.name = netdev_get_name(netdev); + vport.stats = &vport_stats; + + err = dpif_linux_vport_transact(&vport, NULL, NULL); + + /* If the vport layer doesn't know about the device, that doesn't mean it + * doesn't exist (after all were able to open it when netdev_open() was + * called), it just means that it isn't attached and we'll be getting + * stats a different way. */ + if (err == ENODEV) { + err = EOPNOTSUPP; + } + + return err; +} + static void netdev_linux_read_features(struct netdev_dev_linux *netdev_dev) { @@ -1496,8 +1588,9 @@ netdev_linux_read_features(struct netdev_dev_linux *netdev_dev) return; } + COVERAGE_INC(netdev_get_ethtool); memset(&ecmd, 0, sizeof ecmd); - error = netdev_linux_do_ethtool(netdev_dev->netdev_dev.name, &ecmd, + error = netdev_linux_do_ethtool(netdev_dev->up.name, &ecmd, ETHTOOL_GSET, "ETHTOOL_GSET"); if (error) { goto out; @@ -1652,6 +1745,7 @@ netdev_linux_set_advertisements(struct netdev *netdev, struct ethtool_cmd ecmd; int error; + COVERAGE_INC(netdev_get_ethtool); memset(&ecmd, 0, sizeof ecmd); error = netdev_linux_do_ethtool(netdev_get_name(netdev), &ecmd, ETHTOOL_GSET, "ETHTOOL_GSET"); @@ -1696,6 +1790,7 @@ netdev_linux_set_advertisements(struct netdev *netdev, if (advertise & NETDEV_F_PAUSE_ASYM) { ecmd.advertising |= ADVERTISED_Asym_Pause; } + COVERAGE_INC(netdev_set_ethtool); return netdev_linux_do_ethtool(netdev_get_name(netdev), &ecmd, ETHTOOL_SSET, "ETHTOOL_SSET"); } @@ -1769,7 +1864,7 @@ static int netdev_linux_get_qos_types(const struct netdev *netdev OVS_UNUSED, struct sset *types) { - const struct tc_ops **opsp; + const struct tc_ops *const *opsp; for (opsp = tcs; *opsp != NULL; opsp++) { const struct tc_ops *ops = *opsp; @@ -1783,7 +1878,7 @@ netdev_linux_get_qos_types(const struct netdev *netdev OVS_UNUSED, static const struct tc_ops * tc_lookup_ovs_name(const char *name) { - const struct tc_ops **opsp; + const struct tc_ops *const *opsp; for (opsp = tcs; *opsp != NULL; opsp++) { const struct tc_ops *ops = *opsp; @@ -1797,7 +1892,7 @@ tc_lookup_ovs_name(const char *name) static const struct tc_ops * tc_lookup_linux_name(const char *name) { - const struct tc_ops **opsp; + const struct tc_ops *const *opsp; for (opsp = tcs; *opsp != NULL; opsp++) { const struct tc_ops *ops = *opsp; @@ -1845,7 +1940,7 @@ netdev_linux_get_qos_capabilities(const struct netdev *netdev OVS_UNUSED, static int netdev_linux_get_qos(const struct netdev *netdev, - const char **typep, struct shash *details) + const char **typep, struct smap *details) { struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev)); @@ -1864,7 +1959,7 @@ netdev_linux_get_qos(const struct netdev *netdev, static int netdev_linux_set_qos(struct netdev *netdev, - const char *type, const struct shash *details) + const char *type, const struct smap *details) { struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev)); @@ -1889,11 +1984,11 @@ netdev_linux_set_qos(struct netdev *netdev, if (error) { return error; } - assert(netdev_dev->tc == NULL); + ovs_assert(netdev_dev->tc == NULL); /* Install new qdisc. */ error = new_ops->tc_install(netdev, details); - assert((error == 0) == (netdev_dev->tc != NULL)); + ovs_assert((error == 0) == (netdev_dev->tc != NULL)); return error; } @@ -1901,7 +1996,7 @@ netdev_linux_set_qos(struct netdev *netdev, static int netdev_linux_get_queue(const struct netdev *netdev, - unsigned int queue_id, struct shash *details) + unsigned int queue_id, struct smap *details) { struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev)); @@ -1920,7 +2015,7 @@ netdev_linux_get_queue(const struct netdev *netdev, static int netdev_linux_set_queue(struct netdev *netdev, - unsigned int queue_id, const struct shash *details) + unsigned int queue_id, const struct smap *details) { struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev)); @@ -2002,7 +2097,7 @@ netdev_linux_dump_queues(const struct netdev *netdev, struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev)); struct tc_queue *queue, *next_queue; - struct shash details; + struct smap details; int last_error; int error; @@ -2014,10 +2109,10 @@ netdev_linux_dump_queues(const struct netdev *netdev, } last_error = 0; - shash_init(&details); + smap_init(&details); HMAP_FOR_EACH_SAFE (queue, next_queue, hmap_node, &netdev_dev->tc->queues) { - shash_clear(&details); + smap_clear(&details); error = netdev_dev->tc->ops->class_get(netdev, queue, &details); if (!error) { @@ -2026,7 +2121,7 @@ netdev_linux_dump_queues(const struct netdev *netdev, last_error = error; } } - shash_destroy(&details); + smap_destroy(&details); return last_error; } @@ -2271,25 +2366,39 @@ netdev_linux_get_next_hop(const struct in_addr *host, struct in_addr *next_hop, } static int -netdev_linux_get_drv_info(const struct netdev *netdev, struct shash *sh) +netdev_linux_get_status(const struct netdev *netdev, struct smap *smap) { - int error; - struct netdev_dev_linux *netdev_dev = - netdev_dev_linux_cast(netdev_get_dev(netdev)); + struct netdev_dev_linux *netdev_dev; + int error = 0; + + netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev)); + if (!(netdev_dev->cache_valid & VALID_DRVINFO)) { + struct ethtool_cmd *cmd = (struct ethtool_cmd *) &netdev_dev->drvinfo; + + COVERAGE_INC(netdev_get_ethtool); + memset(&netdev_dev->drvinfo, 0, sizeof netdev_dev->drvinfo); + error = netdev_linux_do_ethtool(netdev_dev->up.name, + cmd, + ETHTOOL_GDRVINFO, + "ETHTOOL_GDRVINFO"); + if (!error) { + netdev_dev->cache_valid |= VALID_DRVINFO; + } + } - error = netdev_linux_get_drvinfo(netdev_dev); if (!error) { - shash_add(sh, "driver_name", xstrdup(netdev_dev->drvinfo.driver)); - shash_add(sh, "driver_version", xstrdup(netdev_dev->drvinfo.version)); - shash_add(sh, "firmware_version", xstrdup(netdev_dev->drvinfo.fw_version)); + smap_add(smap, "driver_name", netdev_dev->drvinfo.driver); + smap_add(smap, "driver_version", netdev_dev->drvinfo.version); + smap_add(smap, "firmware_version", netdev_dev->drvinfo.fw_version); } return error; } static int -netdev_internal_get_drv_info(const struct netdev *netdev OVS_UNUSED, struct shash *sh) +netdev_internal_get_status(const struct netdev *netdev OVS_UNUSED, + struct smap *smap) { - shash_add(sh, "driver_name", xstrdup("openvswitch")); + smap_add(smap, "driver_name", "openvswitch"); return 0; } @@ -2320,7 +2429,7 @@ netdev_linux_arp_lookup(const struct netdev *netdev, memcpy(mac, r.arp_ha.sa_data, ETH_ADDR_LEN); } else if (retval != ENXIO) { VLOG_WARN_RL(&rl, "%s: could not look up ARP entry for "IP_FMT": %s", - netdev_get_name(netdev), IP_ARGS(&ip), strerror(retval)); + netdev_get_name(netdev), IP_ARGS(ip), strerror(retval)); } return retval; } @@ -2352,20 +2461,20 @@ iff_to_nd_flags(int iff) } static int -netdev_linux_update_flags(struct netdev *netdev, enum netdev_flags off, +netdev_linux_update_flags(struct netdev_dev *dev_, enum netdev_flags off, enum netdev_flags on, enum netdev_flags *old_flagsp) { struct netdev_dev_linux *netdev_dev; int old_flags, new_flags; int error = 0; - netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev)); + netdev_dev = netdev_dev_linux_cast(dev_); old_flags = netdev_dev->ifi_flags; *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, new_flags); - get_flags(&netdev_dev->netdev_dev, &netdev_dev->ifi_flags); + error = set_flags(netdev_dev_get_name(dev_), new_flags); + get_flags(&netdev_dev->up, &netdev_dev->ifi_flags); } return error; } @@ -2389,14 +2498,12 @@ netdev_linux_change_seq(const struct netdev *netdev) netdev_linux_destroy, \ NULL, /* get_config */ \ NULL, /* set_config */ \ + NULL, /* get_tunnel_config */ \ \ netdev_linux_open, \ netdev_linux_close, \ \ - netdev_linux_listen, \ - netdev_linux_recv, \ - netdev_linux_recv_wait, \ - netdev_linux_drain, \ + netdev_linux_rx_open, \ \ netdev_linux_send, \ netdev_linux_send_wait, \ @@ -2447,7 +2554,7 @@ const struct netdev_class netdev_linux_class = netdev_linux_get_stats, NULL, /* set_stats */ netdev_linux_get_features, - netdev_linux_get_drv_info); + netdev_linux_get_status); const struct netdev_class netdev_tap_class = NETDEV_LINUX_CLASS( @@ -2456,16 +2563,23 @@ const struct netdev_class netdev_tap_class = netdev_tap_get_stats, NULL, /* set_stats */ netdev_linux_get_features, - netdev_linux_get_drv_info); + netdev_linux_get_status); const struct netdev_class netdev_internal_class = NETDEV_LINUX_CLASS( "internal", netdev_linux_create, netdev_internal_get_stats, - netdev_vport_set_stats, + netdev_internal_set_stats, NULL, /* get_features */ - netdev_internal_get_drv_info); + 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. */ @@ -2651,17 +2765,17 @@ htb_parse_tcmsg__(struct ofpbuf *tcmsg, unsigned int *queue_id, static void htb_parse_qdisc_details__(struct netdev *netdev, - const struct shash *details, struct htb_class *hc) + const struct smap *details, struct htb_class *hc) { const char *max_rate_s; - max_rate_s = shash_find_data(details, "max-rate"); + max_rate_s = smap_get(details, "max-rate"); hc->max_rate = max_rate_s ? strtoull(max_rate_s, NULL, 10) / 8 : 0; if (!hc->max_rate) { enum netdev_features current; netdev_get_features(netdev, ¤t, NULL, NULL, NULL); - hc->max_rate = netdev_features_to_bps(current) / 8; + hc->max_rate = netdev_features_to_bps(current, 100 * 1000 * 1000) / 8; } hc->min_rate = hc->max_rate; hc->burst = 0; @@ -2670,13 +2784,13 @@ htb_parse_qdisc_details__(struct netdev *netdev, static int htb_parse_class_details__(struct netdev *netdev, - const struct shash *details, struct htb_class *hc) + const struct smap *details, struct htb_class *hc) { const struct htb *htb = htb_get__(netdev); - const char *min_rate_s = shash_find_data(details, "min-rate"); - const char *max_rate_s = shash_find_data(details, "max-rate"); - const char *burst_s = shash_find_data(details, "burst"); - const char *priority_s = shash_find_data(details, "priority"); + const char *min_rate_s = smap_get(details, "min-rate"); + const char *max_rate_s = smap_get(details, "max-rate"); + const char *burst_s = smap_get(details, "burst"); + const char *priority_s = smap_get(details, "priority"); int mtu, error; error = netdev_get_mtu(netdev, &mtu); @@ -2734,7 +2848,7 @@ htb_query_class__(const struct netdev *netdev, unsigned int handle, } static int -htb_tc_install(struct netdev *netdev, const struct shash *details) +htb_tc_install(struct netdev *netdev, const struct smap *details) { int error; @@ -2826,15 +2940,15 @@ htb_tc_destroy(struct tc *tc) } static int -htb_qdisc_get(const struct netdev *netdev, struct shash *details) +htb_qdisc_get(const struct netdev *netdev, struct smap *details) { const struct htb *htb = htb_get__(netdev); - shash_add(details, "max-rate", xasprintf("%llu", 8ULL * htb->max_rate)); + smap_add_format(details, "max-rate", "%llu", 8ULL * htb->max_rate); return 0; } static int -htb_qdisc_set(struct netdev *netdev, const struct shash *details) +htb_qdisc_set(struct netdev *netdev, const struct smap *details) { struct htb_class hc; int error; @@ -2850,24 +2964,24 @@ htb_qdisc_set(struct netdev *netdev, const struct shash *details) static int htb_class_get(const struct netdev *netdev OVS_UNUSED, - const struct tc_queue *queue, struct shash *details) + const struct tc_queue *queue, struct smap *details) { const struct htb_class *hc = htb_class_cast__(queue); - shash_add(details, "min-rate", xasprintf("%llu", 8ULL * hc->min_rate)); + smap_add_format(details, "min-rate", "%llu", 8ULL * hc->min_rate); if (hc->min_rate != hc->max_rate) { - shash_add(details, "max-rate", xasprintf("%llu", 8ULL * hc->max_rate)); + smap_add_format(details, "max-rate", "%llu", 8ULL * hc->max_rate); } - shash_add(details, "burst", xasprintf("%llu", 8ULL * hc->burst)); + smap_add_format(details, "burst", "%llu", 8ULL * hc->burst); if (hc->priority) { - shash_add(details, "priority", xasprintf("%u", hc->priority)); + smap_add_format(details, "priority", "%u", hc->priority); } return 0; } static int htb_class_set(struct netdev *netdev, unsigned int queue_id, - const struct shash *details) + const struct smap *details) { struct htb_class hc; int error; @@ -3127,20 +3241,20 @@ hfsc_query_class__(const struct netdev *netdev, unsigned int handle, } static void -hfsc_parse_qdisc_details__(struct netdev *netdev, const struct shash *details, +hfsc_parse_qdisc_details__(struct netdev *netdev, const struct smap *details, struct hfsc_class *class) { uint32_t max_rate; const char *max_rate_s; - max_rate_s = shash_find_data(details, "max-rate"); + max_rate_s = smap_get(details, "max-rate"); max_rate = max_rate_s ? strtoull(max_rate_s, NULL, 10) / 8 : 0; if (!max_rate) { enum netdev_features current; netdev_get_features(netdev, ¤t, NULL, NULL, NULL); - max_rate = netdev_features_to_bps(current) / 8; + max_rate = netdev_features_to_bps(current, 100 * 1000 * 1000) / 8; } class->min_rate = max_rate; @@ -3149,7 +3263,7 @@ hfsc_parse_qdisc_details__(struct netdev *netdev, const struct shash *details, static int hfsc_parse_class_details__(struct netdev *netdev, - const struct shash *details, + const struct smap *details, struct hfsc_class * class) { const struct hfsc *hfsc; @@ -3157,8 +3271,8 @@ hfsc_parse_class_details__(struct netdev *netdev, const char *min_rate_s, *max_rate_s; hfsc = hfsc_get__(netdev); - min_rate_s = shash_find_data(details, "min-rate"); - max_rate_s = shash_find_data(details, "max-rate"); + min_rate_s = smap_get(details, "min-rate"); + max_rate_s = smap_get(details, "max-rate"); min_rate = min_rate_s ? strtoull(min_rate_s, NULL, 10) / 8 : 0; min_rate = MAX(min_rate, 1); @@ -3259,7 +3373,7 @@ hfsc_setup_class__(struct netdev *netdev, unsigned int handle, } static int -hfsc_tc_install(struct netdev *netdev, const struct shash *details) +hfsc_tc_install(struct netdev *netdev, const struct smap *details) { int error; struct hfsc_class class; @@ -3327,16 +3441,16 @@ hfsc_tc_destroy(struct tc *tc) } static int -hfsc_qdisc_get(const struct netdev *netdev, struct shash *details) +hfsc_qdisc_get(const struct netdev *netdev, struct smap *details) { const struct hfsc *hfsc; hfsc = hfsc_get__(netdev); - shash_add(details, "max-rate", xasprintf("%llu", 8ULL * hfsc->max_rate)); + smap_add_format(details, "max-rate", "%llu", 8ULL * hfsc->max_rate); return 0; } static int -hfsc_qdisc_set(struct netdev *netdev, const struct shash *details) +hfsc_qdisc_set(struct netdev *netdev, const struct smap *details) { int error; struct hfsc_class class; @@ -3354,21 +3468,21 @@ hfsc_qdisc_set(struct netdev *netdev, const struct shash *details) static int hfsc_class_get(const struct netdev *netdev OVS_UNUSED, - const struct tc_queue *queue, struct shash *details) + const struct tc_queue *queue, struct smap *details) { const struct hfsc_class *hc; hc = hfsc_class_cast__(queue); - shash_add(details, "min-rate", xasprintf("%llu", 8ULL * hc->min_rate)); + smap_add_format(details, "min-rate", "%llu", 8ULL * hc->min_rate); if (hc->min_rate != hc->max_rate) { - shash_add(details, "max-rate", xasprintf("%llu", 8ULL * hc->max_rate)); + smap_add_format(details, "max-rate", "%llu", 8ULL * hc->max_rate); } return 0; } static int hfsc_class_set(struct netdev *netdev, unsigned int queue_id, - const struct shash *details) + const struct smap *details) { int error; struct hfsc_class class; @@ -3462,18 +3576,16 @@ default_install__(struct netdev *netdev) { struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev)); - static struct tc *tc; + static const struct tc tc = TC_INITIALIZER(&tc, &tc_ops_default); - if (!tc) { - tc = xmalloc(sizeof *tc); - tc_init(tc, &tc_ops_default); - } - netdev_dev->tc = tc; + /* Nothing but a tc class implementation is allowed to write to a tc. This + * class never does that, so we can legitimately use a const tc object. */ + netdev_dev->tc = CONST_CAST(struct tc *, &tc); } static int default_tc_install(struct netdev *netdev, - const struct shash *details OVS_UNUSED) + const struct smap *details OVS_UNUSED) { default_install__(netdev); return 0; @@ -3511,13 +3623,11 @@ other_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg OVS_UNUSED) { struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_get_dev(netdev)); - static struct tc *tc; + static const struct tc tc = TC_INITIALIZER(&tc, &tc_ops_other); - if (!tc) { - tc = xmalloc(sizeof *tc); - tc_init(tc, &tc_ops_other); - } - netdev_dev->tc = tc; + /* Nothing but a tc class implementation is allowed to write to a tc. This + * class never does that, so we can legitimately use a const tc object. */ + netdev_dev->tc = CONST_CAST(struct tc *, &tc); return 0; } @@ -3686,7 +3796,7 @@ tc_add_policer(struct netdev *netdev, int kbits_rate, int kbits_burst) memset(&tc_police, 0, sizeof tc_police); tc_police.action = TC_POLICE_SHOT; tc_police.mtu = mtu; - tc_fill_rate(&tc_police.rate, kbits_rate/8 * 1000, mtu); + tc_fill_rate(&tc_police.rate, (kbits_rate * 1000)/8, mtu); tc_police.burst = tc_bytes_to_ticks(tc_police.rate.rate, kbits_burst * 1024); @@ -4099,8 +4209,8 @@ tc_query_qdisc(const struct netdev *netdev) } /* Instantiate it. */ - load_error = ops->tc_load((struct netdev *) netdev, qdisc); - assert((load_error == 0) == (netdev_dev->tc != NULL)); + load_error = ops->tc_load(CONST_CAST(struct netdev *, netdev), qdisc); + ovs_assert((load_error == 0) == (netdev_dev->tc != NULL)); ofpbuf_delete(qdisc); return error ? error : load_error; @@ -4196,6 +4306,7 @@ netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t flag, uint32_t new_flags; int error; + COVERAGE_INC(netdev_get_ethtool); memset(&evalue, 0, sizeof evalue); error = netdev_linux_do_ethtool(netdev_name, (struct ethtool_cmd *)&evalue, @@ -4204,6 +4315,7 @@ netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t flag, return error; } + COVERAGE_INC(netdev_set_ethtool); evalue.data = new_flags = (evalue.data & ~flag) | (enable ? flag : 0); error = netdev_linux_do_ethtool(netdev_name, (struct ethtool_cmd *)&evalue, @@ -4212,6 +4324,7 @@ netdev_linux_ethtool_set_flag(struct netdev *netdev, uint32_t flag, return error; } + COVERAGE_INC(netdev_get_ethtool); memset(&evalue, 0, sizeof evalue); error = netdev_linux_do_ethtool(netdev_name, (struct ethtool_cmd *)&evalue, @@ -4383,13 +4496,12 @@ get_flags(const struct netdev_dev *dev, unsigned int *flags) } static int -set_flags(struct netdev *netdev, unsigned int flags) +set_flags(const char *name, unsigned int flags) { struct ifreq ifr; ifr.ifr_flags = flags; - return netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr, SIOCSIFFLAGS, - "SIOCSIFFLAGS"); + return netdev_linux_do_ioctl(name, &ifr, SIOCSIFFLAGS, "SIOCSIFFLAGS"); } static int @@ -4487,7 +4599,6 @@ netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *ecmd, ifr.ifr_data = (caddr_t) ecmd; ecmd->cmd = cmd; - COVERAGE_INC(netdev_ethtool); if (ioctl(af_inet_sock, SIOCETHTOOL, &ifr) == 0) { return 0; } else { @@ -4540,7 +4651,11 @@ af_packet_sock(void) if (sock == INT_MIN) { sock = socket(AF_PACKET, SOCK_RAW, 0); if (sock >= 0) { - set_nonblocking(sock); + int error = set_nonblocking(sock); + if (error) { + close(sock); + sock = -error; + } } else { sock = -errno; VLOG_ERR("failed to create packet socket: %s", strerror(errno));