vswitchd: Add miimon support.
[sliver-openvswitch.git] / lib / netdev.c
index 5cd252c..b2c4d66 100644 (file)
 
 VLOG_DEFINE_THIS_MODULE(netdev);
 
+COVERAGE_DEFINE(netdev_received);
+COVERAGE_DEFINE(netdev_sent);
+COVERAGE_DEFINE(netdev_add_router);
+COVERAGE_DEFINE(netdev_get_stats);
+
 static struct shash netdev_classes = SHASH_INITIALIZER(&netdev_classes);
 
 /* All created network devices. */
@@ -70,6 +75,7 @@ netdev_initialize(void)
 
 #ifdef HAVE_NETLINK
         netdev_register_provider(&netdev_linux_class);
+        netdev_register_provider(&netdev_internal_class);
         netdev_register_provider(&netdev_tap_class);
         netdev_vport_register();
 #endif
@@ -162,6 +168,13 @@ netdev_unregister_provider(const char *type)
     return 0;
 }
 
+const struct netdev_class *
+netdev_lookup_provider(const char *type)
+{
+    netdev_initialize();
+    return shash_find_data(&netdev_classes, type && type[0] ? type : "system");
+}
+
 /* Clears 'types' and enumerates the types of all currently registered netdev
  * providers into it.  The caller must first initialize the svec. */
 void
@@ -247,25 +260,6 @@ update_device_args(struct netdev_dev *dev, const struct shash *args)
     qsort(dev->args, dev->n_args, sizeof *dev->args, compare_args);
 }
 
-static int
-create_device(struct netdev_options *options, struct netdev_dev **netdev_devp)
-{
-    struct netdev_class *netdev_class;
-
-    if (!options->type || strlen(options->type) == 0) {
-        /* Default to system. */
-        options->type = "system";
-    }
-
-    netdev_class = shash_find_data(&netdev_classes, options->type);
-    if (!netdev_class) {
-        return EAFNOSUPPORT;
-    }
-
-    return netdev_class->create(netdev_class, options->name, options->args,
-                                netdev_devp);
-}
-
 /* Opens the network device named 'name' (e.g. "eth0") and returns zero if
  * successful, otherwise a positive errno value.  On success, sets '*netdevp'
  * to the new network device, otherwise to null.
@@ -294,14 +288,20 @@ netdev_open(struct netdev_options *options, struct netdev **netdevp)
     netdev_dev = shash_find_data(&netdev_dev_shash, options->name);
 
     if (!netdev_dev) {
-        error = create_device(options, &netdev_dev);
+        const struct netdev_class *class;
+
+        class = netdev_lookup_provider(options->type);
+        if (!class) {
+            VLOG_WARN("could not create netdev %s of unknown type %s",
+                      options->name, options->type);
+            return EAFNOSUPPORT;
+        }
+        error = class->create(class, options->name, options->args,
+                              &netdev_dev);
         if (error) {
-            if (error == EAFNOSUPPORT) {
-                VLOG_WARN("could not create netdev %s of unknown type %s",
-                          options->name, options->type);
-            }
             return error;
         }
+        assert(netdev_dev->netdev_class == class);
         update_device_args(netdev_dev, options->args);
 
     } else if (!shash_is_empty(options->args) &&
@@ -769,6 +769,21 @@ netdev_get_next_hop(const struct netdev *netdev,
     return error;
 }
 
+/* Populates 'sh' with status information.
+ *
+ * Populates 'sh' with 'netdev' specific status information.  This information
+ * may be used to populate the status column of the Interface table as defined
+ * in ovs-vswitchd.conf.db(5). */
+int
+netdev_get_status(const struct netdev *netdev, struct shash *sh)
+{
+    struct netdev_dev *dev = netdev_get_dev(netdev);
+
+    return (dev->netdev_class->get_status
+            ? dev->netdev_class->get_status(netdev, sh)
+            : EOPNOTSUPP);
+}
+
 /* If 'netdev' has an assigned IPv6 address, sets '*in6' to that address and
  * returns 0.  Otherwise, returns a positive errno value and sets '*in6' to
  * all-zero-bits (in6addr_any).
@@ -920,6 +935,34 @@ netdev_get_carrier(const struct netdev *netdev)
     return carrier;
 }
 
+/* Returns true if 'netdev' is up according to its MII. */
+bool
+netdev_get_miimon(const struct netdev *netdev)
+{
+    int error;
+    enum netdev_flags flags;
+    bool miimon;
+
+    netdev_get_flags(netdev, &flags);
+    if (!(flags & NETDEV_UP)) {
+        return false;
+    }
+
+    if (!netdev_get_dev(netdev)->netdev_class->get_miimon) {
+        return true;
+    }
+
+    error = netdev_get_dev(netdev)->netdev_class->get_miimon(netdev, &miimon);
+
+    if (error) {
+        VLOG_DBG("%s: failed to get network device MII status, assuming "
+                 "down: %s", netdev_get_name(netdev), strerror(error));
+        miimon = false;
+    }
+
+    return miimon;
+}
+
 /* Retrieves current device stats for 'netdev'. */
 int
 netdev_get_stats(const struct netdev *netdev, struct netdev_stats *stats)