ofproto-dpif: Guard rule statistics with a mutex.
[sliver-openvswitch.git] / lib / netdev-linux.c
index 8790f14..cc86ec6 100644 (file)
@@ -148,6 +148,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
@@ -409,9 +410,6 @@ 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);
@@ -477,15 +475,6 @@ netdev_linux_init(void)
         if (status) {
             VLOG_ERR("failed to create inet socket: %s", ovs_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",
-                            ovs_strerror(status));
-            }
-        }
     }
     return status;
 }
@@ -568,9 +557,10 @@ 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;
             unsigned int flags;
 
-            dev = node->data;
+            dev = netdev_linux_cast(netdev);
 
             get_flags(&dev->up, &flags);
             netdev_linux_changed(dev, flags, 0);
@@ -661,6 +651,7 @@ netdev_linux_create_tap(const struct netdev_class *class OVS_UNUSED,
     int error;
 
     netdev = xzalloc(sizeof *netdev);
+    netdev->change_seq = 1;
     state = &netdev->state.tap;
 
     error = cache_notifier_ref();
@@ -683,19 +674,21 @@ netdev_linux_create_tap(const struct netdev_class *class OVS_UNUSED,
         VLOG_WARN("%s: creating tap device failed: %s", name,
                   ovs_strerror(errno));
         error = errno;
-        goto error_unref_notifier;
+        goto error_close;
     }
 
     /* Make non-blocking. */
     error = set_nonblocking(state->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(state->fd);
 error_unref_notifier:
     cache_notifier_unref();
 error:
@@ -835,10 +828,8 @@ 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",
@@ -1204,7 +1195,8 @@ 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)) {
@@ -1232,7 +1224,8 @@ 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);
@@ -1354,11 +1347,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) {
@@ -1429,8 +1424,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;
@@ -2010,9 +2004,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);
     }
 }
 
@@ -2027,7 +2023,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;
 }
@@ -2819,6 +2815,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);
     }
 
@@ -3052,6 +3049,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);
     }
 
@@ -3646,7 +3644,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;
 }
@@ -3791,30 +3789,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, ovs_strerror(errno));
-        return;
+        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;
@@ -3825,6 +3828,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
@@ -3832,9 +3838,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;
 }
 
@@ -3843,9 +3847,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;
 }
 
@@ -3854,9 +3856,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;
 }
 
@@ -4322,7 +4322,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;
@@ -4560,7 +4560,8 @@ netdev_linux_get_ipv4(const struct netdev *netdev, struct in_addr *ip,
     ifr.ifr_addr.sa_family = AF_INET;
     error = netdev_linux_do_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;
@@ -4570,9 +4571,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);
@@ -4585,6 +4587,7 @@ af_packet_sock(void)
             VLOG_ERR("failed to create packet socket: %s",
                      ovs_strerror(errno));
         }
+        ovsthread_once_done(&once);
     }
 
     return sock;