vswitchd: Add miimon support.
[sliver-openvswitch.git] / lib / netdev-linux.c
index cbe4222..f953cfc 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/ip.h>
 #include <linux/types.h>
 #include <linux/ethtool.h>
+#include <linux/mii.h>
 #include <linux/pkt_sched.h>
 #include <linux/rtnetlink.h>
 #include <linux/sockios.h>
 #include "netdev-provider.h"
 #include "netdev-vport.h"
 #include "netlink.h"
+#include "netlink-socket.h"
 #include "ofpbuf.h"
 #include "openflow/openflow.h"
 #include "packets.h"
 #include "poll-loop.h"
 #include "rtnetlink.h"
+#include "rtnetlink-link.h"
 #include "socket-util.h"
 #include "shash.h"
 #include "svec.h"
@@ -456,17 +459,17 @@ netdev_linux_init(void)
 static void
 netdev_linux_run(void)
 {
-    rtnetlink_notifier_run();
+    rtnetlink_link_notifier_run();
 }
 
 static void
 netdev_linux_wait(void)
 {
-    rtnetlink_notifier_wait();
+    rtnetlink_link_notifier_wait();
 }
 
 static void
-netdev_linux_cache_cb(const struct rtnetlink_change *change,
+netdev_linux_cache_cb(const struct rtnetlink_link_change *change,
                       void *aux OVS_UNUSED)
 {
     struct netdev_dev_linux *dev;
@@ -510,8 +513,8 @@ netdev_linux_create(const struct netdev_class *class,
     }
 
     if (!cache_notifier_refcount) {
-        error = rtnetlink_notifier_register(&netdev_linux_cache_notifier,
-                                            netdev_linux_cache_cb, NULL);
+        error = rtnetlink_link_notifier_register(&netdev_linux_cache_notifier,
+                                                 netdev_linux_cache_cb, NULL);
         if (error) {
             return error;
         }
@@ -597,20 +600,22 @@ static void
 netdev_linux_destroy(struct netdev_dev *netdev_dev_)
 {
     struct netdev_dev_linux *netdev_dev = netdev_dev_linux_cast(netdev_dev_);
-    const char *type = netdev_dev_get_type(netdev_dev_);
+    const struct netdev_class *class = netdev_dev_get_class(netdev_dev_);
 
     if (netdev_dev->tc && netdev_dev->tc->ops->tc_destroy) {
         netdev_dev->tc->ops->tc_destroy(netdev_dev->tc);
     }
 
-    if (!strcmp(type, "system")) {
+    if (class == &netdev_linux_class || class == &netdev_internal_class) {
         cache_notifier_refcount--;
 
         if (!cache_notifier_refcount) {
-            rtnetlink_notifier_unregister(&netdev_linux_cache_notifier);
+            rtnetlink_link_notifier_unregister(&netdev_linux_cache_notifier);
         }
-    } else if (!strcmp(type, "tap")) {
+    } else if (class == &netdev_tap_class) {
         destroy_tap(netdev_dev);
+    } else {
+        NOT_REACHED();
     }
 
     free(netdev_dev);
@@ -1003,6 +1008,49 @@ exit:
     return error;
 }
 
+static int
+netdev_linux_get_miimon(const struct netdev *netdev_, bool *miimon)
+{
+    int error;
+    struct ifreq ifr;
+    const char *name = netdev_get_name(netdev_);
+
+    *miimon = false;
+    memset(&ifr, 0, sizeof ifr);
+
+    error = netdev_linux_do_ioctl(name, &ifr, SIOCGMIIPHY, "SIOCGMIIPHY");
+    if (!error) {
+        struct mii_ioctl_data *data = (struct mii_ioctl_data *)&ifr.ifr_data;
+
+        /* data->phy_id is filled out by previous SIOCGMIIPHY ioctl call. */
+        data->reg_num = MII_BMSR;
+        error = netdev_linux_do_ioctl(name, &ifr, SIOCGMIIREG, "SIOCGMIIREG");
+
+        if (!error) {
+            *miimon = !!(data->val_out & BMSR_LSTATUS);
+        } else {
+            VLOG_WARN_RL(&rl, "%s: failed to query MII", name);
+        }
+    } else {
+        struct ethtool_cmd ecmd;
+        struct ethtool_value *eval = (struct ethtool_value *) &ecmd;
+
+        VLOG_DBG_RL(&rl, "%s: failed to query MII, falling back to ethtool",
+                    name);
+
+        memset(&ecmd, 0, sizeof ecmd);
+        error = netdev_linux_do_ethtool(name, &ecmd, ETHTOOL_GLINK,
+                                        "ETHTOOL_GLINK");
+        if (!error) {
+            *miimon = !!eval->data;
+        } else {
+            VLOG_WARN_RL(&rl, "%s: ethtool link status failed", name);
+        }
+    }
+
+    return error;
+}
+
 /* Check whether we can we use RTM_GETLINK to get network device statistics.
  * In pre-2.6.19 kernels, this was only available if wireless extensions were
  * enabled. */
@@ -2046,7 +2094,7 @@ poll_notify(struct list *list)
 }
 
 static void
-netdev_linux_poll_cb(const struct rtnetlink_change *change,
+netdev_linux_poll_cb(const struct rtnetlink_link_change *change,
                      void *aux OVS_UNUSED)
 {
     if (change) {
@@ -2073,8 +2121,9 @@ netdev_linux_poll_add(struct netdev *netdev,
     struct list *list;
 
     if (shash_is_empty(&netdev_linux_notifiers)) {
-        int error = rtnetlink_notifier_register(&netdev_linux_poll_notifier,
-                                                   netdev_linux_poll_cb, NULL);
+        int error;
+        error = rtnetlink_link_notifier_register(&netdev_linux_poll_notifier,
+                                                 netdev_linux_poll_cb, NULL);
         if (error) {
             return error;
         }
@@ -2114,7 +2163,7 @@ netdev_linux_poll_remove(struct netdev_notifier *notifier_)
 
     /* If that was the last notifier, unregister. */
     if (shash_is_empty(&netdev_linux_notifiers)) {
-        rtnetlink_notifier_unregister(&netdev_linux_poll_notifier);
+        rtnetlink_link_notifier_unregister(&netdev_linux_poll_notifier);
     }
 }
 
@@ -2147,6 +2196,7 @@ netdev_linux_poll_remove(struct netdev_notifier *notifier_)
     netdev_linux_get_mtu,                                       \
     netdev_linux_get_ifindex,                                   \
     netdev_linux_get_carrier,                                   \
+    netdev_linux_get_miimon,                                    \
     netdev_linux_get_stats,                                     \
     SET_STATS,                                                  \
                                                                 \
@@ -2171,6 +2221,7 @@ netdev_linux_poll_remove(struct netdev_notifier *notifier_)
     netdev_linux_get_in6,                                       \
     netdev_linux_add_router,                                    \
     netdev_linux_get_next_hop,                                  \
+    NULL,                       /* get_status */                \
     netdev_linux_arp_lookup,                                    \
                                                                 \
     netdev_linux_update_flags,                                  \
@@ -2184,7 +2235,7 @@ const struct netdev_class netdev_linux_class =
         "system",
         netdev_linux_create,
         netdev_linux_enumerate,
-        netdev_vport_set_stats);
+        NULL);                  /* set_stats */
 
 const struct netdev_class netdev_tap_class =
     NETDEV_LINUX_CLASS(