[ovs-dev] [PATCH 2/3] gre: Add support for destroying GRE devices.

Jesse Gross jesse at nicira.com
Tue Jan 12 22:29:14 UTC 2010


This allows GRE tunnel devices to be torn down on graceful exit
of vswitch and cleaned up on restart for non-graceful exits.
---
 datapath/linux-2.6/compat-2.6/ip_gre.c |   17 ++++
 lib/netdev-linux.c                     |  145 ++++++++++++++++++++++++++++----
 lib/netdev.c                           |   16 ++--
 3 files changed, 152 insertions(+), 26 deletions(-)

diff --git a/datapath/linux-2.6/compat-2.6/ip_gre.c b/datapath/linux-2.6/compat-2.6/ip_gre.c
index 246e864..ddc94d6 100644
--- a/datapath/linux-2.6/compat-2.6/ip_gre.c
+++ b/datapath/linux-2.6/compat-2.6/ip_gre.c
@@ -24,6 +24,7 @@
  */
 
 #include <linux/capability.h>
+#include <linux/ethtool.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
@@ -1292,6 +1293,18 @@ static int ipgre_close(struct net_device *dev)
 
 #endif
 
+static void ethtool_getinfo(struct net_device *dev,
+			    struct ethtool_drvinfo *info)
+{
+	strcpy(info->driver, "ip_gre");
+	strcpy(info->version, "Open vSwitch "VERSION BUILDNR);
+	strcpy(info->bus_info, dev->type == ARPHRD_ETHER ? "gretap" : "gre");
+}
+
+static struct ethtool_ops ethtool_ops = {
+	.get_drvinfo = ethtool_getinfo,
+};
+
 #ifdef HAVE_NET_DEVICE_OPS
 static const struct net_device_ops ipgre_netdev_ops = {
 	.ndo_init		= ipgre_tunnel_init,
@@ -1334,6 +1347,8 @@ static void ipgre_tunnel_setup(struct net_device *dev)
 	dev->addr_len		= 4;
 	dev->features		|= NETIF_F_NETNS_LOCAL;
 	dev->priv_flags         &= ~IFF_XMIT_DST_RELEASE;
+
+	SET_ETHTOOL_OPS(dev, &ethtool_ops);
 }
 
 static int ipgre_tunnel_init(struct net_device *dev)
@@ -1540,6 +1555,8 @@ static void ipgre_tap_setup(struct net_device *dev)
 
 	dev->iflink		= 0;
 	dev->features		|= NETIF_F_NETNS_LOCAL;
+
+	SET_ETHTOOL_OPS(dev, &ethtool_ops);
 }
 
 #ifndef GRE_IOCTL_ONLY
diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
index 060e6c9..16d2ca4 100644
--- a/lib/netdev-linux.c
+++ b/lib/netdev-linux.c
@@ -159,7 +159,8 @@ static struct rtnetlink_notifier netdev_linux_poll_notifier;
  * additional log messages. */
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
 
-static int netdev_linux_do_ethtool(const struct netdev *, struct ethtool_cmd *,
+static int destroy_gre(const char *name);
+static int netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *,
                                    int cmd, const char *cmd_name);
 static int netdev_linux_do_ioctl(const char *name, struct ifreq *, int cmd,
                                  const char *cmd_name);
@@ -393,6 +394,85 @@ setup_gre_ioctl(const char *name, struct gre_config *config, bool create)
     return 0;
 }
 
+/* The arguments are marked as unused to prevent warnings on platforms where
+ * the Netlink interface isn't supported. */
+static bool
+check_gre_device_netlink(const char *name UNUSED)
+{
+#ifdef GRE_IOCTL_ONLY
+    return false;
+#else
+    static const struct nl_policy getlink_policy[] = {
+        [IFLA_LINKINFO] = { .type = NL_A_NESTED, .optional = false },
+    };
+
+    static const struct nl_policy linkinfo_policy[] = {
+        [IFLA_INFO_KIND] = { .type = NL_A_STRING, .optional = false },
+    };
+
+    int error;
+    bool ret = false;
+    struct ofpbuf request, *reply;
+    struct ifinfomsg ifinfomsg;
+    struct nlattr *getlink_attrs[ARRAY_SIZE(getlink_policy)];
+    struct nlattr *linkinfo_attrs[ARRAY_SIZE(linkinfo_policy)];
+    const char *device_kind;
+
+    ofpbuf_init(&request, 0);
+
+    nl_msg_put_nlmsghdr(&request, gre_descriptors.nl_sock,
+                        NLMSG_LENGTH(sizeof ifinfomsg), RTM_GETLINK,
+                        NLM_F_REQUEST);
+
+    memset(&ifinfomsg, 0, sizeof ifinfomsg);
+    ifinfomsg.ifi_family = AF_UNSPEC;
+    ifinfomsg.ifi_index =  do_get_ifindex(name);
+    nl_msg_put(&request, &ifinfomsg, sizeof ifinfomsg);
+
+    error = nl_sock_transact(gre_descriptors.nl_sock, &request, &reply);
+    ofpbuf_uninit(&request);
+    if (error) {
+        VLOG_WARN("couldn't transact netlink socket: %s\n", strerror(error));
+        return false;
+    }
+
+    if (!nl_policy_parse(reply, NLMSG_HDRLEN + sizeof(struct ifinfomsg),
+                         getlink_policy, getlink_attrs,
+                         ARRAY_SIZE(getlink_policy))) {
+        VLOG_WARN("received bad rtnl message (getlink policy)");
+        goto error;
+    }
+
+    if (!nl_policy_parse(reply, (char *)RTA_DATA(getlink_attrs[IFLA_LINKINFO]) -
+                        (char *)ofpbuf_at(reply, 0, 0), linkinfo_policy,
+                        linkinfo_attrs, ARRAY_SIZE(linkinfo_policy))) {
+        VLOG_WARN("received bad rtnl message (linkinfo policy)");
+        goto error;
+    }
+
+    device_kind = nl_attr_get_string(linkinfo_attrs[IFLA_INFO_KIND]);
+    ret = !strcmp(device_kind, "gretap");
+
+error:
+    ofpbuf_delete(reply);
+    return ret;
+#endif
+}
+
+static bool
+check_gre_device_ioctl(const char *name)
+{
+    struct ethtool_drvinfo drvinfo;
+    int error;
+
+    memset(&drvinfo, 0, sizeof drvinfo);
+    error = netdev_linux_do_ethtool(name, (struct ethtool_cmd *)&drvinfo,
+                                    ETHTOOL_GDRVINFO, "ETHTOOL_GDRVINFO");
+
+    return !error && !strcmp(drvinfo.driver, "ip_gre")
+           && !strcmp(drvinfo.bus_info, "gretap");
+}
+
 static int
 setup_gre(const char *name, const struct shash *args, bool create)
 {
@@ -455,6 +535,32 @@ setup_gre(const char *name, const struct shash *args, bool create)
         error = setup_gre_ioctl(name, &config, create);
     }
 
+    if (create && error == EEXIST) {
+        bool gre_device;
+
+        if (gre_descriptors.use_ioctl) {
+            gre_device = check_gre_device_ioctl(name);
+        } else {
+            gre_device = check_gre_device_netlink(name);
+        }
+
+        if (!gre_device) {
+            goto error;
+        }
+
+        VLOG_WARN("replacing existing gre device %s", name);
+        error = destroy_gre(name);
+        if (error) {
+            goto error;
+        }
+
+        if (gre_descriptors.use_ioctl) {
+            error = setup_gre_ioctl(name, &config, create);
+        } else {
+            error = setup_gre_netlink(name, &config, create);
+        }
+    }
+
 error:
     return error;
 }
@@ -594,12 +700,11 @@ netdev_linux_reconfigure_gre(struct netdev_dev *netdev_dev_,
 /* The arguments are marked as unused to prevent warnings on platforms where
  * the Netlink interface isn't supported. */
 static int
-destroy_gre_netlink(struct netdev_dev_linux *netdev_dev UNUSED)
+destroy_gre_netlink(const char *name UNUSED)
 {
 #ifdef GRE_IOCTL_ONLY
     return EOPNOTSUPP;
 #else
-    const char *name = netdev_dev_get_name(&netdev_dev->netdev_dev);
     int error;
     struct ofpbuf request, *reply;
     struct ifinfomsg ifinfomsg;
@@ -633,9 +738,8 @@ error:
 }
 
 static int
-destroy_gre_ioctl(struct netdev_dev_linux *netdev_dev)
+destroy_gre_ioctl(const char *name)
 {
-    const char *name = netdev_dev_get_name(&netdev_dev->netdev_dev);
     struct ip_tunnel_parm p;
     struct ifreq ifr;
 
@@ -663,6 +767,16 @@ destroy_tap(struct netdev_dev_linux *netdev_dev)
     }
 }
 
+static int
+destroy_gre(const char *name)
+{
+    if (gre_descriptors.use_ioctl) {
+        return destroy_gre_ioctl(name);
+    } else {
+        return destroy_gre_netlink(name);
+    }
+}
+
 /* Destroys the netdev device 'netdev_dev_'. */
 static void
 netdev_linux_destroy(struct netdev_dev *netdev_dev_)
@@ -679,11 +793,7 @@ netdev_linux_destroy(struct netdev_dev *netdev_dev_)
     } else if (!strcmp(type, "tap")) {
         destroy_tap(netdev_dev);
     } else if (!strcmp(type, "gre")) {
-        if (gre_descriptors.use_ioctl) {
-            destroy_gre_ioctl(netdev_dev);
-        } else {
-            destroy_gre_netlink(netdev_dev);
-        }
+        destroy_gre(netdev_dev_get_name(&netdev_dev->netdev_dev));
     }
 
     free(netdev_dev_);
@@ -1148,7 +1258,7 @@ netdev_linux_get_stats(const struct netdev *netdev_,
             struct ethtool_drvinfo drvinfo;
 
             memset(&drvinfo, 0, sizeof drvinfo);
-            error = netdev_linux_do_ethtool(netdev_,
+            error = netdev_linux_do_ethtool(netdev_get_name(netdev_),
                                             (struct ethtool_cmd *)&drvinfo,
                                             ETHTOOL_GDRVINFO,
                                             "ETHTOOL_GDRVINFO");
@@ -1224,7 +1334,7 @@ netdev_linux_get_features(struct netdev *netdev,
     int error;
 
     memset(&ecmd, 0, sizeof ecmd);
-    error = netdev_linux_do_ethtool(netdev, &ecmd,
+    error = netdev_linux_do_ethtool(netdev_get_name(netdev), &ecmd,
                                     ETHTOOL_GSET, "ETHTOOL_GSET");
     if (error) {
         return error;
@@ -1345,7 +1455,7 @@ netdev_linux_set_advertisements(struct netdev *netdev, uint32_t advertise)
     int error;
 
     memset(&ecmd, 0, sizeof ecmd);
-    error = netdev_linux_do_ethtool(netdev, &ecmd,
+    error = netdev_linux_do_ethtool(netdev_get_name(netdev), &ecmd,
                                     ETHTOOL_GSET, "ETHTOOL_GSET");
     if (error) {
         return error;
@@ -1388,7 +1498,7 @@ netdev_linux_set_advertisements(struct netdev *netdev, uint32_t advertise)
     if (advertise & OFPPF_PAUSE_ASYM) {
         ecmd.advertising |= ADVERTISED_Asym_Pause;
     }
-    return netdev_linux_do_ethtool(netdev, &ecmd,
+    return netdev_linux_do_ethtool(netdev_get_name(netdev), &ecmd,
                                    ETHTOOL_SSET, "ETHTOOL_SSET");
 }
 
@@ -2248,13 +2358,13 @@ set_etheraddr(const char *netdev_name, int hwaddr_family,
 }
 
 static int
-netdev_linux_do_ethtool(const struct netdev *netdev, struct ethtool_cmd *ecmd,
+netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *ecmd,
                         int cmd, const char *cmd_name)
 {
     struct ifreq ifr;
 
     memset(&ifr, 0, sizeof ifr);
-    strncpy(ifr.ifr_name, netdev_get_name(netdev), sizeof ifr.ifr_name);
+    strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
     ifr.ifr_data = (caddr_t) ecmd;
 
     ecmd->cmd = cmd;
@@ -2264,8 +2374,7 @@ netdev_linux_do_ethtool(const struct netdev *netdev, struct ethtool_cmd *ecmd,
     } else {
         if (errno != EOPNOTSUPP) {
             VLOG_WARN_RL(&rl, "ethtool command %s on network device %s "
-                         "failed: %s", cmd_name, netdev_get_name(netdev),
-                         strerror(errno));
+                         "failed: %s", cmd_name, name, strerror(errno));
         } else {
             /* The device doesn't support this operation.  That's pretty
              * common, so there's no point in logging anything. */
diff --git a/lib/netdev.c b/lib/netdev.c
index 8faf424..20b24ab 100644
--- a/lib/netdev.c
+++ b/lib/netdev.c
@@ -57,7 +57,7 @@ static struct list netdev_list = LIST_INITIALIZER(&netdev_list);
  * additional log messages. */
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
 
-static void restore_all_flags(void *aux);
+static void close_all_netdevs(void *aux UNUSED);
 static int restore_flags(struct netdev *netdev);
 
 /* Attempts to initialize the netdev module.  Returns 0 if successful,
@@ -73,7 +73,7 @@ netdev_initialize(void)
     if (status < 0) {
         int i, j;
 
-        fatal_signal_add_hook(restore_all_flags, NULL, true);
+        fatal_signal_add_hook(close_all_netdevs, NULL, true);
 
         status = 0;
         for (i = j = 0; i < n_netdev_classes; i++) {
@@ -1107,13 +1107,13 @@ restore_flags(struct netdev *netdev)
     return 0;
 }
 
-/* Retores all the flags on all network devices that we modified.  Called from
- * a signal handler, so it does not attempt to report error conditions. */
+/* Close all netdevs on shutdown so they can do any needed cleanup such as
+ * destroying devices, restoring flags, etc. */
 static void
-restore_all_flags(void *aux UNUSED)
+close_all_netdevs(void *aux UNUSED)
 {
-    struct netdev *netdev;
-    LIST_FOR_EACH (netdev, struct netdev, node, &netdev_list) {
-        restore_flags(netdev);
+    struct netdev *netdev, *next;
+    LIST_FOR_EACH_SAFE(netdev, next, struct netdev, node, &netdev_list) {
+        netdev_close(netdev);
     }
 }
-- 
1.6.3.3





More information about the dev mailing list