netdev-linux, netdev-bsd: Make access to AF_INET socket thread-safe.
[sliver-openvswitch.git] / lib / netdev-linux.c
index d73115b..9306100 100644 (file)
@@ -22,6 +22,7 @@
 #include <fcntl.h>
 #include <arpa/inet.h>
 #include <inttypes.h>
+#include <linux/filter.h>
 #include <linux/gen_stats.h>
 #include <linux/if_ether.h>
 #include <linux/if_tun.h>
@@ -119,10 +120,6 @@ enum {
     VALID_DRVINFO           = 1 << 7,
     VALID_FEATURES          = 1 << 8,
 };
-
-struct tap_state {
-    int fd;
-};
 \f
 /* Traffic control. */
 
@@ -147,6 +144,7 @@ struct tc {
 struct tc_queue {
     struct hmap_node hmap_node; /* In struct tc's "queues" hmap. */
     unsigned int queue_id;      /* OpenFlow queue ID. */
+    long long int created;      /* Time queue was created, in msecs. */
 };
 
 /* A particular kind of traffic control.  Each implementation generally maps to
@@ -357,7 +355,6 @@ 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;
     unsigned int cache_valid;
     unsigned int change_seq;
 
@@ -387,14 +384,12 @@ 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;
 
-    union {
-        struct tap_state tap;
-    } state;
+    /* For devices of class netdev_tap_class only. */
+    int tap_fd;
 };
 
 struct netdev_rx_linux {
@@ -405,22 +400,14 @@ struct netdev_rx_linux {
 
 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. */
-
-/* A Netlink routing socket that is not subscribed to any multicast groups. */
-static struct nl_sock *rtnl_sock;
-
 /* 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);
@@ -441,7 +428,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
@@ -465,30 +452,6 @@ netdev_rx_linux_cast(const struct netdev_rx *rx)
     return CONTAINER_OF(rx, struct netdev_rx_linux, up);
 }
 \f
-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", strerror(status));
-        }
-
-        /* Create rtnetlink socket. */
-        if (!status) {
-            status = nl_sock_create(NETLINK_ROUTE, &rtnl_sock);
-            if (status) {
-                VLOG_ERR_RL(&rl, "failed to create rtnetlink socket: %s",
-                            strerror(status));
-            }
-        }
-    }
-    return status;
-}
-
 static void
 netdev_linux_run(void)
 {
@@ -554,11 +517,11 @@ 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);
+            netdev_close(base_dev);
         }
     } else {
         struct shash device_shash;
@@ -567,12 +530,13 @@ netdev_linux_cache_cb(const struct rtnetlink_link_change *change,
         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 *dev = netdev_linux_cast(netdev);
             unsigned int flags;
 
-            dev = node->data;
-
             get_flags(&dev->up, &flags);
             netdev_linux_changed(dev, flags, 0);
+            netdev_close(netdev);
         }
         shash_destroy(&device_shash);
     }
@@ -654,13 +618,12 @@ netdev_linux_create_tap(const struct netdev_class *class OVS_UNUSED,
                         const char *name, struct netdev **netdevp)
 {
     struct netdev_linux *netdev;
-    struct tap_state *state;
     static const char tap_dev[] = "/dev/net/tun";
     struct ifreq ifr;
     int error;
 
     netdev = xzalloc(sizeof *netdev);
-    state = &netdev->state.tap;
+    netdev->change_seq = 1;
 
     error = cache_notifier_ref();
     if (error) {
@@ -668,33 +631,35 @@ netdev_linux_create_tap(const struct netdev_class *class OVS_UNUSED,
     }
 
     /* Open tap device. */
-    state->fd = open(tap_dev, O_RDWR);
-    if (state->fd < 0) {
+    netdev->tap_fd = open(tap_dev, O_RDWR);
+    if (netdev->tap_fd < 0) {
         error = errno;
-        VLOG_WARN("opening \"%s\" failed: %s", tap_dev, strerror(error));
+        VLOG_WARN("opening \"%s\" failed: %s", tap_dev, ovs_strerror(error));
         goto error_unref_notifier;
     }
 
     /* Create tap device. */
     ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
     ovs_strzcpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
-    if (ioctl(state->fd, TUNSETIFF, &ifr) == -1) {
+    if (ioctl(netdev->tap_fd, TUNSETIFF, &ifr) == -1) {
         VLOG_WARN("%s: creating tap device failed: %s", name,
-                  strerror(errno));
+                  ovs_strerror(errno));
         error = errno;
-        goto error_unref_notifier;
+        goto error_close;
     }
 
     /* Make non-blocking. */
-    error = set_nonblocking(state->fd);
+    error = set_nonblocking(netdev->tap_fd);
     if (error) {
-        goto error_unref_notifier;
+        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:
@@ -702,17 +667,6 @@ error:
     return error;
 }
 
-static void
-destroy_tap(struct netdev_linux *netdev)
-{
-    struct tap_state *state = &netdev->state.tap;
-
-    if (state->fd >= 0) {
-        close(state->fd);
-    }
-}
-
-/* Destroys the netdev device 'netdev_'. */
 static void
 netdev_linux_destroy(struct netdev *netdev_)
 {
@@ -722,8 +676,10 @@ netdev_linux_destroy(struct netdev *netdev_)
         netdev->tc->ops->tc_destroy(netdev->tc);
     }
 
-    if (netdev_get_class(netdev_) == &netdev_tap_class) {
-        destroy_tap(netdev);
+    if (netdev_get_class(netdev_) == &netdev_tap_class
+        && netdev->tap_fd >= 0)
+    {
+        close(netdev->tap_fd);
     }
     free(netdev);
 
@@ -740,16 +696,26 @@ netdev_linux_rx_open(struct netdev *netdev_, struct netdev_rx **rxp)
     int fd;
 
     if (is_tap) {
-        fd = netdev->state.tap.fd;
+        fd = netdev->tap_fd;
     } else {
         struct sockaddr_ll sll;
         int ifindex;
+        /* Result of tcpdump -dd inbound */
+        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 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) {
             error = errno;
-            VLOG_ERR("failed to create raw socket (%s)", strerror(error));
+            VLOG_ERR("failed to create raw socket (%s)", ovs_strerror(error));
             goto error;
         }
 
@@ -773,7 +739,17 @@ netdev_linux_rx_open(struct netdev *netdev_, struct netdev_rx **rxp)
         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));
+                     netdev_get_name(netdev_), ovs_strerror(error));
+            goto error;
+        }
+
+        /* Filter for only inbound packets. */
+        error = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog,
+                           sizeof fprog);
+        if (error) {
+            error = errno;
+            VLOG_ERR("%s: failed to attach filter (%s)",
+                     netdev_get_name(netdev_), ovs_strerror(error));
             goto error;
         }
     }
@@ -816,14 +792,12 @@ netdev_rx_linux_recv(struct netdev_rx *rx_, void *data, size_t size)
                   : recv(rx->fd, data, size, MSG_TRUNC));
     } while (retval < 0 && errno == EINTR);
 
-    if (retval > size) {
-        return -EMSGSIZE;
-    } else if (retval >= 0) {
-        return retval;
+    if (retval >= 0) {
+        return retval > size ? -EMSGSIZE : retval;
     } else {
         if (errno != EAGAIN) {
             VLOG_WARN_RL(&rl, "error receiving Ethernet packet on %s: %s",
-                         strerror(errno), netdev_rx_get_name(rx_));
+                         ovs_strerror(errno), netdev_rx_get_name(rx_));
         }
         return -errno;
     }
@@ -842,8 +816,8 @@ netdev_rx_linux_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;
         }
@@ -910,10 +884,11 @@ netdev_linux_send(struct netdev *netdev_, const void *data, size_t 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. */
+             * tap device.  This doesn't occur on other interface types
+             * because we attach a socket filter to the rx socket. */
             struct netdev_linux *netdev = netdev_linux_cast(netdev_);
 
-            retval = write(netdev->state.tap.fd, data, size);
+            retval = write(netdev->tap_fd, data, size);
         }
 
         if (retval < 0) {
@@ -926,7 +901,7 @@ netdev_linux_send(struct netdev *netdev_, const void *data, size_t size)
                 continue;
             } else if (errno != EAGAIN) {
                 VLOG_WARN_RL(&rl, "error sending Ethernet packet on %s: %s",
-                             netdev_get_name(netdev_), strerror(errno));
+                             netdev_get_name(netdev_), ovs_strerror(errno));
             }
             return errno;
         } else if (retval != size) {
@@ -1026,8 +1001,8 @@ netdev_linux_get_mtu(const struct netdev *netdev_, int *mtup)
         struct ifreq ifr;
         int error;
 
-        error = netdev_linux_do_ioctl(netdev_get_name(netdev_), &ifr,
-                                      SIOCGIFMTU, "SIOCGIFMTU");
+        error = af_inet_ifreq_ioctl(netdev_get_name(netdev_), &ifr,
+                                    SIOCGIFMTU, "SIOCGIFMTU");
 
         netdev->netdev_mtu_error = error;
         netdev->mtu = ifr.ifr_mtu;
@@ -1060,8 +1035,8 @@ netdev_linux_set_mtu(const struct netdev *netdev_, int mtu)
         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;
@@ -1110,7 +1085,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;
@@ -1184,10 +1159,12 @@ netdev_linux_miimon_run(void)
     shash_init(&device_shash);
     netdev_get_devices(&netdev_linux_class, &device_shash);
     SHASH_FOR_EACH (node, &device_shash) {
-        struct netdev_linux *dev = node->data;
+        struct netdev *netdev = node->data;
+        struct netdev_linux *dev = netdev_linux_cast(netdev);
         bool miimon;
 
         if (dev->miimon_interval <= 0 || !timer_expired(&dev->miimon_timer)) {
+            netdev_close(netdev);
             continue;
         }
 
@@ -1198,6 +1175,7 @@ netdev_linux_miimon_run(void)
         }
 
         timer_set_duration(&dev->miimon_timer, dev->miimon_interval);
+        netdev_close(netdev);
     }
 
     shash_destroy(&device_shash);
@@ -1212,11 +1190,13 @@ netdev_linux_miimon_wait(void)
     shash_init(&device_shash);
     netdev_get_devices(&netdev_linux_class, &device_shash);
     SHASH_FOR_EACH (node, &device_shash) {
-        struct netdev_linux *dev = node->data;
+        struct netdev *netdev = node->data;
+        struct netdev_linux *dev = netdev_linux_cast(netdev);
 
         if (dev->miimon_interval > 0) {
             timer_wait(&dev->miimon_timer);
         }
+        netdev_close(netdev);
     }
     shash_destroy(&device_shash);
 }
@@ -1243,7 +1223,7 @@ check_for_working_netlink_stats(void)
         } else {
             VLOG_INFO("RTM_GETLINK failed (%s), obtaining netdev stats "
                       "via proc (you are probably running a pre-2.6.19 "
-                      "kernel)", strerror(error));
+                      "kernel)", ovs_strerror(error));
             return false;
         }
     }
@@ -1322,7 +1302,8 @@ get_stats_via_vport(const struct netdev *netdev_,
         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));
+                         "(%s)",
+                         netdev_get_name(netdev_), ovs_strerror(error));
         }
         netdev->vport_stats_error = error;
         netdev->cache_valid |= VALID_VPORT_STAT_ERROR;
@@ -1333,11 +1314,13 @@ static int
 netdev_linux_sys_get_stats(const struct netdev *netdev_,
                          struct netdev_stats *stats)
 {
-    static int use_netlink_stats = -1;
+    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+    static int use_netlink_stats;
     int error;
 
-    if (use_netlink_stats < 0) {
+    if (ovsthread_once_start(&once)) {
         use_netlink_stats = check_for_working_netlink_stats();
+        ovsthread_once_done(&once);
     }
 
     if (use_netlink_stats) {
@@ -1408,8 +1391,7 @@ netdev_linux_get_stats(const struct netdev *netdev_,
 /* Retrieves current device stats for 'netdev-tap' netdev or
  * netdev-internal. */
 static int
-netdev_tap_get_stats(const struct netdev *netdev_,
-                        struct netdev_stats *stats)
+netdev_tap_get_stats(const struct netdev *netdev_, struct netdev_stats *stats)
 {
     struct netdev_linux *netdev = netdev_linux_cast(netdev_);
     struct netdev_stats dev_stats;
@@ -1634,18 +1616,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,
@@ -1661,7 +1639,7 @@ netdev_linux_get_features(const struct netdev *netdev_,
         *current = netdev->current;
         *advertised = netdev->advertised;
         *supported = netdev->supported;
-        *peer = netdev->peer;
+        *peer = 0;              /* XXX */
     }
     return netdev->get_features_error;
 }
@@ -1757,7 +1735,7 @@ netdev_linux_set_policing(struct netdev *netdev_,
     error = tc_add_del_ingress_qdisc(netdev_, false);
     if (error) {
         VLOG_WARN_RL(&rl, "%s: removing policing failed: %s",
-                     netdev_name, strerror(error));
+                     netdev_name, ovs_strerror(error));
         goto out;
     }
 
@@ -1765,14 +1743,14 @@ netdev_linux_set_policing(struct netdev *netdev_,
         error = tc_add_del_ingress_qdisc(netdev_, true);
         if (error) {
             VLOG_WARN_RL(&rl, "%s: adding policing qdisc failed: %s",
-                         netdev_name, strerror(error));
+                         netdev_name, ovs_strerror(error));
             goto out;
         }
 
         error = tc_add_policer(netdev_, kbits_rate, kbits_burst);
         if (error){
             VLOG_WARN_RL(&rl, "%s: adding policing action failed: %s",
-                    netdev_name, strerror(error));
+                    netdev_name, ovs_strerror(error));
             goto out;
         }
     }
@@ -1989,9 +1967,11 @@ netdev_linux_get_queue_stats(const struct netdev *netdev_,
         return EOPNOTSUPP;
     } else {
         const struct tc_queue *queue = tc_find_queue(netdev_, queue_id);
-        return (queue
-                ? netdev->tc->ops->class_get_stats(netdev_, queue, stats)
-                : ENOENT);
+        if (!queue) {
+            return ENOENT;
+        }
+        stats->created = queue->created;
+        return netdev->tc->ops->class_get_stats(netdev_, queue, stats);
     }
 }
 
@@ -2006,7 +1986,7 @@ start_queue_dump(const struct netdev *netdev, struct nl_dump *dump)
         return false;
     }
     tcmsg->tcm_parent = 0;
-    nl_dump_start(dump, rtnl_sock, &request);
+    nl_dump_start(dump, NETLINK_ROUTE, &request);
     ofpbuf_uninit(&request);
     return true;
 }
@@ -2193,11 +2173,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. */
@@ -2213,9 +2192,9 @@ 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", strerror(error));
+        VLOG_WARN("ioctl(SIOCADDRT): %s", ovs_strerror(error));
     }
     return error;
 }
@@ -2232,7 +2211,7 @@ netdev_linux_get_next_hop(const struct in_addr *host, struct in_addr *next_hop,
     *netdev_name = NULL;
     stream = fopen(fn, "r");
     if (stream == NULL) {
-        VLOG_WARN_RL(&rl, "%s: open failed: %s", fn, strerror(errno));
+        VLOG_WARN_RL(&rl, "%s: open failed: %s", fn, ovs_strerror(errno));
         return errno;
     }
 
@@ -2339,12 +2318,13 @@ 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) {
         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),
+                     ovs_strerror(retval));
     }
     return retval;
 }
@@ -2404,7 +2384,7 @@ netdev_linux_change_seq(const struct netdev *netdev)
 {                                                               \
     NAME,                                                       \
                                                                 \
-    netdev_linux_init,                                          \
+    NULL,                                                       \
     netdev_linux_run,                                           \
     netdev_linux_wait,                                          \
                                                                 \
@@ -2613,7 +2593,7 @@ htb_setup_class__(struct netdev *netdev, unsigned int handle,
                      tc_get_major(handle), tc_get_minor(handle),
                      tc_get_major(parent), tc_get_minor(parent),
                      class->min_rate, class->max_rate,
-                     class->burst, class->priority, strerror(error));
+                     class->burst, class->priority, ovs_strerror(error));
     }
     return error;
 }
@@ -2797,6 +2777,7 @@ htb_update_queue__(struct netdev *netdev, unsigned int queue_id,
         hcp = xmalloc(sizeof *hcp);
         queue = &hcp->tc_queue;
         queue->queue_id = queue_id;
+        queue->created = time_msec();
         hmap_insert(&htb->tc.queues, &queue->hmap_node, hash);
     }
 
@@ -3030,6 +3011,7 @@ hfsc_update_queue__(struct netdev *netdev, unsigned int queue_id,
         hcp             = xmalloc(sizeof *hcp);
         queue           = &hcp->tc_queue;
         queue->queue_id = queue_id;
+        queue->created  = time_msec();
         hmap_insert(&hfsc->tc.queues, &queue->hmap_node, hash);
     }
 
@@ -3273,7 +3255,7 @@ hfsc_setup_class__(struct netdev *netdev, unsigned int handle,
                      netdev_get_name(netdev),
                      tc_get_major(handle), tc_get_minor(handle),
                      tc_get_major(parent), tc_get_minor(parent),
-                     class->min_rate, class->max_rate, strerror(error));
+                     class->min_rate, class->max_rate, ovs_strerror(error));
     }
 
     return error;
@@ -3624,7 +3606,7 @@ tc_make_request(const struct netdev *netdev, int type, unsigned int flags,
 static int
 tc_transact(struct ofpbuf *request, struct ofpbuf **replyp)
 {
-    int error = nl_sock_transact(rtnl_sock, request, replyp);
+    int error = nl_transact(NETLINK_ROUTE, request, replyp);
     ofpbuf_uninit(request);
     return error;
 }
@@ -3769,30 +3751,35 @@ read_psched(void)
      * [5] 2.6.32.21.22 (approx.) from Ubuntu 10.04 on VMware Fusion
      * [6] 2.6.34 from kernel.org on KVM
      */
+    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
     static const char fn[] = "/proc/net/psched";
     unsigned int a, b, c, d;
     FILE *stream;
 
+    if (!ovsthread_once_start(&once)) {
+        return;
+    }
+
     ticks_per_s = 1.0;
     buffer_hz = 100;
 
     stream = fopen(fn, "r");
     if (!stream) {
-        VLOG_WARN("%s: open failed: %s", fn, strerror(errno));
-        return;
+        VLOG_WARN("%s: open failed: %s", fn, ovs_strerror(errno));
+        goto exit;
     }
 
     if (fscanf(stream, "%x %x %x %x", &a, &b, &c, &d) != 4) {
         VLOG_WARN("%s: read failed", fn);
         fclose(stream);
-        return;
+        goto exit;
     }
     VLOG_DBG("%s: psched parameters are: %u %u %u %u", fn, a, b, c, d);
     fclose(stream);
 
     if (!a || !c) {
         VLOG_WARN("%s: invalid scheduler parameters", fn);
-        return;
+        goto exit;
     }
 
     ticks_per_s = (double) a * c / b;
@@ -3803,6 +3790,9 @@ read_psched(void)
                   fn, a, b, c, d);
     }
     VLOG_DBG("%s: ticks_per_s=%f buffer_hz=%u", fn, ticks_per_s, buffer_hz);
+
+exit:
+    ovsthread_once_done(&once);
 }
 
 /* Returns the number of bytes that can be transmitted in 'ticks' ticks at a
@@ -3810,9 +3800,7 @@ read_psched(void)
 static unsigned int
 tc_ticks_to_bytes(unsigned int rate, unsigned int ticks)
 {
-    if (!buffer_hz) {
-        read_psched();
-    }
+    read_psched();
     return (rate * ticks) / ticks_per_s;
 }
 
@@ -3821,9 +3809,7 @@ tc_ticks_to_bytes(unsigned int rate, unsigned int ticks)
 static unsigned int
 tc_bytes_to_ticks(unsigned int rate, unsigned int size)
 {
-    if (!buffer_hz) {
-        read_psched();
-    }
+    read_psched();
     return rate ? ((unsigned long long int) ticks_per_s * size) / rate : 0;
 }
 
@@ -3832,9 +3818,7 @@ tc_bytes_to_ticks(unsigned int rate, unsigned int size)
 static unsigned int
 tc_buffer_per_jiffy(unsigned int rate)
 {
-    if (!buffer_hz) {
-        read_psched();
-    }
+    read_psched();
     return rate / buffer_hz;
 }
 
@@ -3982,7 +3966,7 @@ tc_query_class(const struct netdev *netdev,
                      netdev_get_name(netdev),
                      tc_get_major(handle), tc_get_minor(handle),
                      tc_get_major(parent), tc_get_minor(parent),
-                     strerror(error));
+                     ovs_strerror(error));
     }
     return error;
 }
@@ -4007,7 +3991,7 @@ tc_delete_class(const struct netdev *netdev, unsigned int handle)
         VLOG_WARN_RL(&rl, "delete %s class %u:%u failed (%s)",
                      netdev_get_name(netdev),
                      tc_get_major(handle), tc_get_minor(handle),
-                     strerror(error));
+                     ovs_strerror(error));
     }
     return error;
 }
@@ -4107,7 +4091,7 @@ tc_query_qdisc(const struct netdev *netdev_)
     } else {
         /* Who knows?  Maybe the device got deleted. */
         VLOG_WARN_RL(&rl, "query %s qdisc failed (%s)",
-                     netdev_get_name(netdev_), strerror(error));
+                     netdev_get_name(netdev_), ovs_strerror(error));
         ops = &tc_ops_other;
     }
 
@@ -4190,14 +4174,6 @@ tc_calc_buffer(unsigned int Bps, int mtu, uint64_t burst_bytes)
 \f
 /* 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
@@ -4300,7 +4276,7 @@ get_stats_via_netlink(int ifindex, struct netdev_stats *stats)
     ifi = ofpbuf_put_zeros(&request, sizeof *ifi);
     ifi->ifi_family = PF_UNSPEC;
     ifi->ifi_index = ifindex;
-    error = nl_sock_transact(rtnl_sock, &request, &reply);
+    error = nl_transact(NETLINK_ROUTE, &request, &reply);
     ofpbuf_uninit(&request);
     if (error) {
         return error;
@@ -4336,7 +4312,7 @@ get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats)
 
     stream = fopen(fn, "r");
     if (!stream) {
-        VLOG_WARN_RL(&rl, "%s: open failed: %s", fn, strerror(errno));
+        VLOG_WARN_RL(&rl, "%s: open failed: %s", fn, ovs_strerror(errno));
         return errno;
     }
 
@@ -4390,8 +4366,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;
     }
@@ -4404,20 +4379,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, strerror(errno));
-        return -errno;
+                     netdev_name, ovs_strerror(error));
+        return -error;
     }
     return ifr.ifr_ifindex;
 }
@@ -4449,18 +4427,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, 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) {
@@ -4476,18 +4456,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, strerror(errno));
-        return errno;
+                 netdev_name, ovs_strerror(error));
     }
-    return 0;
+    return error;
 }
 
 static int
@@ -4495,37 +4476,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, 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,
-                     strerror(errno));
-        return errno;
-    }
-    return 0;
+    return error;
 }
 
 static int
@@ -4536,9 +4504,10 @@ 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 = (struct sockaddr_in *) &ifr.ifr_addr;
+        const struct sockaddr_in *sin = ALIGNED_CAST(struct sockaddr_in *,
+                                                     &ifr.ifr_addr);
         *ip = sin->sin_addr;
     }
     return error;
@@ -4548,9 +4517,10 @@ netdev_linux_get_ipv4(const struct netdev *netdev, struct in_addr *ip,
 static int
 af_packet_sock(void)
 {
-    static int sock = INT_MIN;
+    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+    static int sock;
 
-    if (sock == INT_MIN) {
+    if (ovsthread_once_start(&once)) {
         sock = socket(AF_PACKET, SOCK_RAW, 0);
         if (sock >= 0) {
             int error = set_nonblocking(sock);
@@ -4560,8 +4530,10 @@ af_packet_sock(void)
             }
         } else {
             sock = -errno;
-            VLOG_ERR("failed to create packet socket: %s", strerror(errno));
+            VLOG_ERR("failed to create packet socket: %s",
+                     ovs_strerror(errno));
         }
+        ovsthread_once_done(&once);
     }
 
     return sock;