netdev-bsd: Make use of AF_LINK socket thread-safe in NetBSD.
authorBen Pfaff <blp@nicira.com>
Thu, 8 Aug 2013 06:48:59 +0000 (23:48 -0700)
committerBen Pfaff <blp@nicira.com>
Sat, 10 Aug 2013 04:15:15 +0000 (21:15 -0700)
Signed-off-by: Ben Pfaff <blp@nicira.com>
CC: Ed Maste <emaste@freebsd.org>
CC: YAMAMOTO Takashi <yamt@mwd.biglobe.ne.jp>
lib/netdev-bsd.c

index b799cb5..67c0c18 100644 (file)
@@ -50,6 +50,7 @@
 #include "fatal-signal.h"
 #include "ofpbuf.h"
 #include "openflow/openflow.h"
+#include "ovs-thread.h"
 #include "packets.h"
 #include "poll-loop.h"
 #include "socket-util.h"
@@ -107,11 +108,6 @@ enum {
     VALID_CARRIER = 1 << 5
 };
 
-#if defined(__NetBSD__)
-/* AF_LINK socket used for netdev_bsd_get_stats and set_etheraddr */
-static int af_link_sock = -1;
-#endif /* defined(__NetBSD__) */
-
 #define PCAP_SNAPLEN 2048
 
 
@@ -144,12 +140,16 @@ static int get_ifindex(const struct netdev *, int *ifindexp);
 static int ifr_get_flags(const struct ifreq *);
 static void ifr_set_flags(struct ifreq *, int flags);
 
-static int netdev_bsd_init(void);
+#ifdef __NetBSD__
+static int af_link_ioctl(int command, const void *arg);
+#endif
+
+static void netdev_bsd_run(void);
 
 static bool
 is_netdev_bsd_class(const struct netdev_class *netdev_class)
 {
-    return netdev_class->init == netdev_bsd_init;
+    return netdev_class->run == netdev_bsd_run;
 }
 
 static struct netdev_bsd *
@@ -172,29 +172,6 @@ netdev_get_kernel_name(const struct netdev *netdev)
     return netdev_bsd_cast(netdev)->kernel_name;
 }
 
-/* Initialize the AF_INET socket used for ioctl operations */
-static int
-netdev_bsd_init(void)
-{
-#if defined(__NetBSD__)
-    static int status = -1;
-
-    if (status >= 0) {  /* already initialized */
-        return status;
-    }
-
-    af_link_sock = socket(AF_LINK, SOCK_DGRAM, 0);
-    status = af_link_sock >= 0 ? 0 : errno;
-    if (status) {
-        VLOG_ERR("failed to create link socket: %s", ovs_strerror(status));
-    }
-
-    return status;
-#else
-    return 0;
-#endif /* defined(__NetBSD__) */
-}
-
 /*
  * Perform periodic work needed by netdev. In BSD netdevs it checks for any
  * interface status changes, and eventually calls all the user callbacks.
@@ -931,19 +908,16 @@ netdev_bsd_get_stats(const struct netdev *netdev_, struct netdev_stats *stats)
     return 0;
 #elif defined(__NetBSD__)
     struct ifdatareq ifdr;
-    int saved_errno;
-    int ret;
+    int error;
 
     memset(&ifdr, 0, sizeof(ifdr));
     strncpy(ifdr.ifdr_name, netdev_get_kernel_name(netdev_),
             sizeof(ifdr.ifdr_name));
-    ret = ioctl(af_link_sock, SIOCGIFDATA, &ifdr);
-    saved_errno = errno;
-    if (ret == -1) {
-        return saved_errno;
+    error = af_link_ioctl(SIOCGIFDATA, &ifdr);
+    if (!error) {
+        convert_stats(stats, &ifdr.ifdr_data);
     }
-    convert_stats(stats, &ifdr.ifdr_data);
-    return 0;
+    return error;
 #else
 #error not implemented
 #endif
@@ -1402,7 +1376,7 @@ netdev_bsd_change_seq(const struct netdev *netdev)
 const struct netdev_class netdev_bsd_class = {
     "system",
 
-    netdev_bsd_init,
+    NULL, /* init */
     netdev_bsd_run,
     netdev_bsd_wait,
     netdev_bsd_create_system,
@@ -1457,7 +1431,7 @@ const struct netdev_class netdev_bsd_class = {
 const struct netdev_class netdev_tap_class = {
     "tap",
 
-    netdev_bsd_init,
+    NULL, /* init */
     netdev_bsd_run,
     netdev_bsd_wait,
     netdev_bsd_create_tap,
@@ -1625,7 +1599,7 @@ set_etheraddr(const char *netdev_name OVS_UNUSED, int hwaddr_family OVS_UNUSED,
     struct if_laddrreq req;
     struct sockaddr_dl *sdl;
     struct sockaddr_storage oldaddr;
-    int ret;
+    int error;
 
     /*
      * get the old address, add new one, and then remove old one.
@@ -1641,9 +1615,10 @@ set_etheraddr(const char *netdev_name OVS_UNUSED, int hwaddr_family OVS_UNUSED,
     req.addr.ss_family = hwaddr_family;
     sdl = (struct sockaddr_dl *)&req.addr;
     sdl->sdl_alen = hwaddr_len;
-    ret = ioctl(af_link_sock, SIOCGLIFADDR, &req);
-    if (ret == -1) {
-        return errno;
+
+    error = af_link_ioctl(SIOCGLIFADDR, &req);
+    if (error) {
+        return error;
     }
     if (!memcmp(&sdl->sdl_data[sdl->sdl_nlen], mac, hwaddr_len)) {
         return 0;
@@ -1658,19 +1633,15 @@ set_etheraddr(const char *netdev_name OVS_UNUSED, int hwaddr_family OVS_UNUSED,
     sdl->sdl_alen = hwaddr_len;
     sdl->sdl_family = hwaddr_family;
     memcpy(sdl->sdl_data, mac, hwaddr_len);
-    ret = ioctl(af_link_sock, SIOCALIFADDR, &req);
-    if (ret == -1) {
-        return errno;
+    error = af_link_ioctl(SIOCALIFADDR, &req);
+    if (error) {
+        return error;
     }
 
     memset(&req, 0, sizeof(req));
     strncpy(req.iflr_name, netdev_name, sizeof(req.iflr_name));
     req.addr = oldaddr;
-    ret = ioctl(af_link_sock, SIOCDLIFADDR, &req);
-    if (ret == -1) {
-        return errno;
-    }
-    return 0;
+    return af_link_ioctl(SIOCDLIFADDR, &req);
 #else
 #error not implemented
 #endif
@@ -1694,3 +1665,25 @@ ifr_set_flags(struct ifreq *ifr, int flags)
     ifr->ifr_flagshigh = flags >> 16;
 #endif
 }
+
+/* Calls ioctl() on an AF_LINK sock, passing the specified 'command' and
+ * 'arg'.  Returns 0 if successful, otherwise a positive errno value. */
+int
+af_link_ioctl(int command, const void *arg)
+{
+    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
+    static int sock;
+
+    if (ovsthread_once_start(&once)) {
+        sock = socket(AF_LINK, SOCK_DGRAM, 0);
+        if (sock < 0) {
+            sock = -errno;
+            VLOG_ERR("failed to create link socket: %s", ovs_strerror(errno));
+        }
+        ovsthread_once_done(&once);
+    }
+
+    return (sock < 0 ? -sock
+            : ioctl(sock, command, arg) == -1 ? errno
+            : 0);
+}