netdev: Factor restoring flags into new "struct netdev_saved_flags".
[sliver-openvswitch.git] / lib / netdev-linux.c
index 60f985e..30cd0f6 100644 (file)
@@ -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 <assert.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <arpa/inet.h>
 #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);
@@ -139,6 +139,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
@@ -311,7 +313,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)). */
@@ -420,7 +422,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,
@@ -444,7 +446,7 @@ 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);
 }
@@ -454,7 +456,7 @@ 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, netdev);
 }
@@ -497,28 +499,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;
-    }
-
-    COVERAGE_INC(netdev_get_ethtool);
-    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,
@@ -605,7 +585,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);
@@ -622,9 +602,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;
     }
@@ -938,7 +918,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);
@@ -1024,8 +1004,8 @@ 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;
-    bool up_again = false;
 
     if (netdev_dev->cache_valid & VALID_ETHERADDR) {
         if (netdev_dev->ether_addr_error) {
@@ -1042,8 +1022,7 @@ netdev_linux_set_etheraddr(struct netdev *netdev_,
         enum netdev_flags flags;
 
         if (!netdev_get_flags(netdev_, &flags) && (flags & NETDEV_UP)) {
-            netdev_turn_flags_off(netdev_, NETDEV_UP, false);
-            up_again = true;
+            netdev_turn_flags_off(netdev_, NETDEV_UP, &sf);
         }
     }
     error = set_etheraddr(netdev_get_name(netdev_), mac);
@@ -1055,9 +1034,7 @@ netdev_linux_set_etheraddr(struct netdev *netdev_,
         }
     }
 
-    if (up_again) {
-        netdev_turn_flags_on(netdev_, NETDEV_UP, false);
-    }
+    netdev_restore_flags(sf);
 
     return error;
 }
@@ -1332,6 +1309,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)
@@ -1343,7 +1372,7 @@ 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);
+        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));
@@ -1821,7 +1850,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;
@@ -1835,7 +1864,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;
@@ -1849,7 +1878,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;
@@ -1941,11 +1970,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;
     }
@@ -2323,13 +2352,26 @@ 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 smap *smap)
+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->netdev_dev.name,
+                                        cmd,
+                                        ETHTOOL_GDRVINFO,
+                                        "ETHTOOL_GDRVINFO");
+        if (!error) {
+            netdev_dev->cache_valid |= VALID_DRVINFO;
+        }
+    }
 
-    error = netdev_linux_get_drvinfo(netdev_dev);
     if (!error) {
         smap_add(smap, "driver_name", netdev_dev->drvinfo.driver);
         smap_add(smap, "driver_version", netdev_dev->drvinfo.version);
@@ -2339,8 +2381,8 @@ netdev_linux_get_drv_info(const struct netdev *netdev, struct smap *smap)
 }
 
 static int
-netdev_internal_get_drv_info(const struct netdev *netdev OVS_UNUSED,
-                             struct smap *smap)
+netdev_internal_get_status(const struct netdev *netdev OVS_UNUSED,
+                           struct smap *smap)
 {
     smap_add(smap, "driver_name", "openvswitch");
     return 0;
@@ -2405,19 +2447,19 @@ 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);
+        error = set_flags(netdev_dev_get_name(dev_), new_flags);
         get_flags(&netdev_dev->netdev_dev, &netdev_dev->ifi_flags);
     }
     return error;
@@ -2442,6 +2484,7 @@ 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,                                         \
@@ -2500,7 +2543,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(
@@ -2509,7 +2552,7 @@ 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(
@@ -2518,7 +2561,7 @@ const struct netdev_class netdev_internal_class =
         netdev_internal_get_stats,
         netdev_internal_set_stats,
         NULL,                  /* get_features */
-        netdev_internal_get_drv_info);
+        netdev_internal_get_status);
 \f
 /* HTB traffic control class. */
 
@@ -3515,13 +3558,11 @@ 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
@@ -3564,13 +3605,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;
 }
 
@@ -3739,7 +3778,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);
 
@@ -4153,7 +4192,7 @@ tc_query_qdisc(const struct netdev *netdev)
 
     /* Instantiate it. */
     load_error = ops->tc_load(CONST_CAST(struct netdev *, netdev), qdisc);
-    assert((load_error == 0) == (netdev_dev->tc != NULL));
+    ovs_assert((load_error == 0) == (netdev_dev->tc != NULL));
     ofpbuf_delete(qdisc);
 
     return error ? error : load_error;
@@ -4439,13 +4478,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
@@ -4595,7 +4633,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));