netdev-vport: Use vport set_stats instead of internal dev.
[sliver-openvswitch.git] / lib / netdev-linux.c
index 2fa05b6..c0bb247 100644 (file)
 #include "dynamic-string.h"
 #include "fatal-signal.h"
 #include "netdev-provider.h"
+#include "netdev-vport.h"
 #include "netlink.h"
 #include "ofpbuf.h"
 #include "openflow/openflow.h"
-#include "openvswitch/internal_dev.h"
 #include "openvswitch/gre.h"
 #include "packets.h"
 #include "poll-loop.h"
@@ -76,14 +76,15 @@ static struct rtnetlink_notifier netdev_linux_cache_notifier;
 static int cache_notifier_refcount;
 
 enum {
-    VALID_IFINDEX = 1 << 0,
-    VALID_ETHERADDR = 1 << 1,
-    VALID_IN4 = 1 << 2,
-    VALID_IN6 = 1 << 3,
-    VALID_MTU = 1 << 4,
-    VALID_CARRIER = 1 << 5,
-    VALID_IS_PSEUDO = 1 << 6,       /* Represents is_internal and is_tap. */
-    VALID_POLICING = 1 << 7
+    VALID_IFINDEX           = 1 << 0,
+    VALID_ETHERADDR         = 1 << 1,
+    VALID_IN4               = 1 << 2,
+    VALID_IN6               = 1 << 3,
+    VALID_MTU               = 1 << 4,
+    VALID_CARRIER           = 1 << 5,
+    VALID_IS_PSEUDO         = 1 << 6, /* Represents is_internal and is_tap. */
+    VALID_POLICING          = 1 << 7,
+    VALID_HAVE_VPORT_STATS  = 1 << 8
 };
 
 struct tap_state {
@@ -109,6 +110,7 @@ struct netdev_dev_linux {
     bool is_tap;                /* Is this a tuntap device? */
     uint32_t kbits_rate;        /* Policing data. */
     uint32_t kbits_burst;
+    bool have_vport_stats;
 
     union {
         struct tap_state tap;
@@ -795,10 +797,7 @@ swap_uint64(uint64_t *a, uint64_t *b)
     *a ^= *b;
 }
 
-/* Retrieves current device stats for 'netdev'.
- *
- * XXX All of the members of struct netdev_stats are 64 bits wide, but on
- * 32-bit architectures the Linux network stats are only 32 bits. */
+/* Retrieves current device stats for 'netdev'. */
 static int
 netdev_linux_get_stats(const struct netdev *netdev_,
                        struct netdev_stats *stats)
@@ -810,26 +809,39 @@ netdev_linux_get_stats(const struct netdev *netdev_,
 
     COVERAGE_INC(netdev_get_stats);
 
-    if (use_netlink_stats < 0) {
-        use_netlink_stats = check_for_working_netlink_stats();
+    if (netdev_dev->have_vport_stats ||
+        !(netdev_dev->cache_valid & VALID_HAVE_VPORT_STATS)) {
+
+        error = netdev_vport_get_stats(netdev_, stats);
+        netdev_dev->have_vport_stats = !error;
+        netdev_dev->cache_valid |= VALID_HAVE_VPORT_STATS;
     }
-    if (use_netlink_stats) {
-        int ifindex;
 
-        error = get_ifindex(netdev_, &ifindex);
-        if (!error) {
-            error = get_stats_via_netlink(ifindex, stats);
+    if (!netdev_dev->have_vport_stats) {
+        if (use_netlink_stats < 0) {
+            use_netlink_stats = check_for_working_netlink_stats();
+        }
+        if (use_netlink_stats) {
+            int ifindex;
+
+            error = get_ifindex(netdev_, &ifindex);
+            if (!error) {
+                error = get_stats_via_netlink(ifindex, stats);
+            }
+        } else {
+            error = get_stats_via_proc(netdev_get_name(netdev_), stats);
         }
-    } else {
-        error = get_stats_via_proc(netdev_get_name(netdev_), stats);
     }
 
     /* If this port is an internal port then the transmit and receive stats
      * will appear to be swapped relative to the other ports since we are the
      * one sending the data, not a remote computer.  For consistency, we swap
-     * them back here. */
+     * them back here. This does not apply if we are getting stats from the
+     * vport layer because it always tracks stats from the perspective of the
+     * switch. */
     netdev_linux_update_is_pseudo(netdev_dev);
-    if (!error && (netdev_dev->is_internal || netdev_dev->is_tap)) {
+    if (!error && !netdev_dev->have_vport_stats &&
+        (netdev_dev->is_internal || netdev_dev->is_tap)) {
         swap_uint64(&stats->rx_packets, &stats->tx_packets);
         swap_uint64(&stats->rx_bytes, &stats->tx_bytes);
         swap_uint64(&stats->rx_errors, &stats->tx_errors);
@@ -850,41 +862,6 @@ netdev_linux_get_stats(const struct netdev *netdev_,
     return error;
 }
 
-static int
-netdev_linux_set_stats(struct netdev *netdev,
-                       const struct netdev_stats *stats)
-{
-    struct netdev_dev_linux *netdev_dev =
-        netdev_dev_linux_cast(netdev_get_dev(netdev));
-    struct internal_dev_stats dp_dev_stats;
-    struct ifreq ifr;
-
-    /* We must reject this call if 'netdev' is not an Open vSwitch internal
-     * port, because the ioctl that we are about to execute is in the "device
-     * private ioctls" range, which means that executing it on a device that
-     * is not the type we expect could do any random thing.
-     *
-     * (Amusingly, these ioctl numbers are commented "THESE IOCTLS ARE
-     * _DEPRECATED_ AND WILL DISAPPEAR IN 2.5.X" in linux/sockios.h.  I guess
-     * DaveM is a little behind on that.) */
-    netdev_linux_update_is_pseudo(netdev_dev);
-    if (!netdev_dev->is_internal) {
-        return EOPNOTSUPP;
-    }
-
-    /* This actually only sets the *offset* that the dp_dev applies, but in our
-     * usage for fake bond devices the dp_dev never has any traffic of it own
-     * so it has the same effect. */
-    dp_dev_stats.rx_packets = stats->rx_packets;
-    dp_dev_stats.rx_bytes = stats->rx_bytes;
-    dp_dev_stats.tx_packets = stats->tx_packets;
-    dp_dev_stats.tx_bytes = stats->tx_bytes;
-    ifr.ifr_data = (void *) &dp_dev_stats;
-    return netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr,
-                                 INTERNAL_DEV_SET_STATS,
-                                 "INTERNAL_DEV_SET_STATS");
-}
-
 /* Stores the features supported by 'netdev' into each of '*current',
  * '*advertised', '*supported', and '*peer' that are non-null.  Each value is a
  * bitmap of "enum ofp_port_features" bits, in host byte order.  Returns 0 if
@@ -1620,7 +1597,7 @@ const struct netdev_class netdev_linux_class = {
     netdev_linux_get_ifindex,
     netdev_linux_get_carrier,
     netdev_linux_get_stats,
-    netdev_linux_set_stats,
+    netdev_vport_set_stats,
 
     netdev_linux_get_features,
     netdev_linux_set_advertisements,