IB/ipoib: Add rtnl_link_ops support
authorOr Gerlitz <ogerlitz@mellanox.com>
Thu, 13 Sep 2012 05:56:36 +0000 (05:56 +0000)
committerDavid S. Miller <davem@davemloft.net>
Thu, 20 Sep 2012 20:49:17 +0000 (16:49 -0400)
Add rtnl_link_ops to IPoIB, with the first usage being child device
create/delete through them. Childs devices are now either legacy ones,
created/deleted through the ipoib sysfs entries, or RTNL ones.

Adding support for RTNL childs involved refactoring of ipoib_vlan_add
which is now used by both the sysfs and the link_ops code.

Also, added ndo_uninit entry to support calling unregister_netdevice_queue
from the rtnl dellink entry. This required removal of calls to
ipoib_dev_cleanup from the driver in flows which use unregister_netdevice,
since the networking core will invoke ipoib_uninit which does exactly that.

Signed-off-by: Erez Shitrit <erezsh@mellanox.co.il>
Signed-off-by: Or Gerlitz <ogerlitz@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Documentation/infiniband/ipoib.txt
drivers/infiniband/ulp/ipoib/Makefile
drivers/infiniband/ulp/ipoib/ipoib.h
drivers/infiniband/ulp/ipoib/ipoib_main.c
drivers/infiniband/ulp/ipoib/ipoib_netlink.c [new file with mode: 0644]
drivers/infiniband/ulp/ipoib/ipoib_vlan.c
include/linux/if_link.h

index 64eeb55d0c09d05f6ec5788a150ddcf2119fdfd8..f2cfe265e836e082727a4c5004d1fed264d5d526 100644 (file)
@@ -24,6 +24,9 @@ Partitions and P_Keys
   The P_Key for any interface is given by the "pkey" file, and the
   main interface for a subinterface is in "parent."
 
+  Child interface create/delete can also be done using IPoIB's
+  rtnl_link_ops, where childs created using either way behave the same.
+
 Datagram vs Connected modes
 
   The IPoIB driver supports two modes of operation: datagram and
index 3090100f0de7598fac29096f9b9e490397d60338..e5430dd50764c9c6e13b871a51ee8c273a3f9941 100644 (file)
@@ -5,7 +5,8 @@ ib_ipoib-y                                      := ipoib_main.o \
                                                   ipoib_multicast.o \
                                                   ipoib_verbs.o \
                                                   ipoib_vlan.o \
-                                                  ipoib_ethtool.o
+                                                  ipoib_ethtool.o \
+                                                  ipoib_netlink.o
 ib_ipoib-$(CONFIG_INFINIBAND_IPOIB_CM)         += ipoib_cm.o
 ib_ipoib-$(CONFIG_INFINIBAND_IPOIB_DEBUG)      += ipoib_fs.o
 
index ca43901ed861b8d2bc31c70a9e570d9db3d2f8c1..381f51b2ed6198da544e7b19592f69e8f7c07c65 100644 (file)
@@ -104,6 +104,10 @@ enum {
 
        MAX_SEND_CQE              = 16,
        IPOIB_CM_COPYBREAK        = 256,
+
+       IPOIB_NON_CHILD           = 0,
+       IPOIB_LEGACY_CHILD        = 1,
+       IPOIB_RTNL_CHILD          = 2,
 };
 
 #define        IPOIB_OP_RECV   (1ul << 31)
@@ -350,6 +354,7 @@ struct ipoib_dev_priv {
        struct net_device *parent;
        struct list_head child_intfs;
        struct list_head list;
+       int    child_type;
 
 #ifdef CONFIG_INFINIBAND_IPOIB_CM
        struct ipoib_cm_dev_priv cm;
@@ -509,6 +514,14 @@ void ipoib_event(struct ib_event_handler *handler,
 int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey);
 int ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey);
 
+int __ipoib_vlan_add(struct ipoib_dev_priv *ppriv, struct ipoib_dev_priv *priv,
+                    u16 pkey, int child_type);
+
+int  __init ipoib_netlink_init(void);
+void __exit ipoib_netlink_fini(void);
+
+void ipoib_setup(struct net_device *dev);
+
 void ipoib_pkey_poll(struct work_struct *work);
 int ipoib_pkey_dev_delay_open(struct net_device *dev);
 void ipoib_drain_cq(struct net_device *dev);
index 3e2085a3ee474fc0111c7034ca013bfda7d63d7e..b3e97096c446a68441eea47afe9767117b10c153 100644 (file)
@@ -173,6 +173,11 @@ static int ipoib_stop(struct net_device *dev)
        return 0;
 }
 
+static void ipoib_uninit(struct net_device *dev)
+{
+       ipoib_dev_cleanup(dev);
+}
+
 static netdev_features_t ipoib_fix_features(struct net_device *dev, netdev_features_t features)
 {
        struct ipoib_dev_priv *priv = netdev_priv(dev);
@@ -1262,6 +1267,9 @@ out:
 void ipoib_dev_cleanup(struct net_device *dev)
 {
        struct ipoib_dev_priv *priv = netdev_priv(dev), *cpriv, *tcpriv;
+       LIST_HEAD(head);
+
+       ASSERT_RTNL();
 
        ipoib_delete_debug_files(dev);
 
@@ -1270,10 +1278,9 @@ void ipoib_dev_cleanup(struct net_device *dev)
                /* Stop GC on child */
                set_bit(IPOIB_STOP_NEIGH_GC, &cpriv->flags);
                cancel_delayed_work(&cpriv->neigh_reap_task);
-               unregister_netdev(cpriv->dev);
-               ipoib_dev_cleanup(cpriv->dev);
-               free_netdev(cpriv->dev);
+               unregister_netdevice_queue(cpriv->dev, &head);
        }
+       unregister_netdevice_many(&head);
 
        ipoib_ib_dev_cleanup(dev);
 
@@ -1291,6 +1298,7 @@ static const struct header_ops ipoib_header_ops = {
 };
 
 static const struct net_device_ops ipoib_netdev_ops = {
+       .ndo_uninit              = ipoib_uninit,
        .ndo_open                = ipoib_open,
        .ndo_stop                = ipoib_stop,
        .ndo_change_mtu          = ipoib_change_mtu,
@@ -1300,7 +1308,7 @@ static const struct net_device_ops ipoib_netdev_ops = {
        .ndo_set_rx_mode         = ipoib_set_mcast_list,
 };
 
-static void ipoib_setup(struct net_device *dev)
+void ipoib_setup(struct net_device *dev)
 {
        struct ipoib_dev_priv *priv = netdev_priv(dev);
 
@@ -1662,7 +1670,6 @@ static void ipoib_remove_one(struct ib_device *device)
                flush_workqueue(ipoib_workqueue);
 
                unregister_netdev(priv->dev);
-               ipoib_dev_cleanup(priv->dev);
                free_netdev(priv->dev);
        }
 
@@ -1714,8 +1721,15 @@ static int __init ipoib_init_module(void)
        if (ret)
                goto err_sa;
 
+       ret = ipoib_netlink_init();
+       if (ret)
+               goto err_client;
+
        return 0;
 
+err_client:
+       ib_unregister_client(&ipoib_client);
+
 err_sa:
        ib_sa_unregister_client(&ipoib_sa_client);
        destroy_workqueue(ipoib_workqueue);
@@ -1728,6 +1742,7 @@ err_fs:
 
 static void __exit ipoib_cleanup_module(void)
 {
+       ipoib_netlink_fini();
        ib_unregister_client(&ipoib_client);
        ib_sa_unregister_client(&ipoib_sa_client);
        ipoib_unregister_debugfs();
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_netlink.c b/drivers/infiniband/ulp/ipoib/ipoib_netlink.c
new file mode 100644 (file)
index 0000000..a7dc5ea
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2012 Mellanox Technologies. -  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/netdevice.h>
+#include <linux/module.h>
+#include <net/rtnetlink.h>
+#include "ipoib.h"
+
+static const struct nla_policy ipoib_policy[IFLA_IPOIB_MAX + 1] = {
+       [IFLA_IPOIB_PKEY]       = { .type = NLA_U16 },
+};
+
+static int ipoib_new_child_link(struct net *src_net, struct net_device *dev,
+                              struct nlattr *tb[], struct nlattr *data[])
+{
+       struct net_device *pdev;
+       struct ipoib_dev_priv *ppriv;
+       u16 child_pkey;
+       int err;
+
+       if (!tb[IFLA_LINK])
+               return -EINVAL;
+
+       pdev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK]));
+       if (!pdev)
+               return -ENODEV;
+
+       ppriv = netdev_priv(pdev);
+
+       if (test_bit(IPOIB_FLAG_SUBINTERFACE, &ppriv->flags)) {
+               ipoib_warn(ppriv, "child creation disallowed for child devices\n");
+               return -EINVAL;
+       }
+
+       if (!data || !data[IFLA_IPOIB_PKEY]) {
+               ipoib_dbg(ppriv, "no pkey specified, using parent pkey\n");
+               child_pkey  = ppriv->pkey;
+       } else
+               child_pkey  = nla_get_u16(data[IFLA_IPOIB_PKEY]);
+
+       err = __ipoib_vlan_add(ppriv, netdev_priv(dev), child_pkey, IPOIB_RTNL_CHILD);
+
+       return err;
+}
+
+static void ipoib_unregister_child_dev(struct net_device *dev, struct list_head *head)
+{
+       struct ipoib_dev_priv *priv, *ppriv;
+
+       priv = netdev_priv(dev);
+       ppriv = netdev_priv(priv->parent);
+
+       mutex_lock(&ppriv->vlan_mutex);
+       unregister_netdevice_queue(dev, head);
+       list_del(&priv->list);
+       mutex_unlock(&ppriv->vlan_mutex);
+}
+
+static size_t ipoib_get_size(const struct net_device *dev)
+{
+       return nla_total_size(2);       /* IFLA_IPOIB_PKEY */
+}
+
+static struct rtnl_link_ops ipoib_link_ops __read_mostly = {
+       .kind           = "ipoib",
+       .maxtype        = IFLA_IPOIB_MAX,
+       .policy         = ipoib_policy,
+       .priv_size      = sizeof(struct ipoib_dev_priv),
+       .setup          = ipoib_setup,
+       .newlink        = ipoib_new_child_link,
+       .dellink        = ipoib_unregister_child_dev,
+       .get_size       = ipoib_get_size,
+};
+
+int __init ipoib_netlink_init(void)
+{
+       return rtnl_link_register(&ipoib_link_ops);
+}
+
+void __exit ipoib_netlink_fini(void)
+{
+       rtnl_link_unregister(&ipoib_link_ops);
+}
+
+MODULE_ALIAS_RTNL_LINK("ipoib");
index d7e9740c724804afcdf20ba9c54fbe4fe91fdb87..238bbf9b2bea8bba67fdcefc42245037df6da23c 100644 (file)
@@ -49,47 +49,11 @@ static ssize_t show_parent(struct device *d, struct device_attribute *attr,
 }
 static DEVICE_ATTR(parent, S_IRUGO, show_parent, NULL);
 
-int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey)
+int __ipoib_vlan_add(struct ipoib_dev_priv *ppriv, struct ipoib_dev_priv *priv,
+                    u16 pkey, int type)
 {
-       struct ipoib_dev_priv *ppriv, *priv;
-       char intf_name[IFNAMSIZ];
        int result;
 
-       if (!capable(CAP_NET_ADMIN))
-               return -EPERM;
-
-       ppriv = netdev_priv(pdev);
-
-       if (!rtnl_trylock())
-               return restart_syscall();
-       mutex_lock(&ppriv->vlan_mutex);
-
-       /*
-        * First ensure this isn't a duplicate. We check the parent device and
-        * then all of the child interfaces to make sure the Pkey doesn't match.
-        */
-       if (ppriv->pkey == pkey) {
-               result = -ENOTUNIQ;
-               priv = NULL;
-               goto err;
-       }
-
-       list_for_each_entry(priv, &ppriv->child_intfs, list) {
-               if (priv->pkey == pkey) {
-                       result = -ENOTUNIQ;
-                       priv = NULL;
-                       goto err;
-               }
-       }
-
-       snprintf(intf_name, sizeof intf_name, "%s.%04x",
-                ppriv->dev->name, pkey);
-       priv = ipoib_intf_alloc(intf_name);
-       if (!priv) {
-               result = -ENOMEM;
-               goto err;
-       }
-
        priv->max_ib_mtu = ppriv->max_ib_mtu;
        /* MTU will be reset when mcast join happens */
        priv->dev->mtu   = IPOIB_UD_MTU(priv->max_ib_mtu);
@@ -134,14 +98,13 @@ int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey)
        if (device_create_file(&priv->dev->dev, &dev_attr_parent))
                goto sysfs_failed;
 
+       priv->child_type = type;
        list_add_tail(&priv->list, &ppriv->child_intfs);
 
-       mutex_unlock(&ppriv->vlan_mutex);
-       rtnl_unlock();
-
        return 0;
 
 sysfs_failed:
+       result = -ENOMEM;
        ipoib_delete_debug_files(priv->dev);
        unregister_netdevice(priv->dev);
 
@@ -149,11 +112,60 @@ register_failed:
        ipoib_dev_cleanup(priv->dev);
 
 err:
+       return result;
+}
+
+int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey)
+{
+       struct ipoib_dev_priv *ppriv, *priv;
+       char intf_name[IFNAMSIZ];
+       struct ipoib_dev_priv *tpriv;
+       int result;
+
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+
+       ppriv = netdev_priv(pdev);
+
+       snprintf(intf_name, sizeof intf_name, "%s.%04x",
+                ppriv->dev->name, pkey);
+       priv = ipoib_intf_alloc(intf_name);
+       if (!priv)
+               return -ENOMEM;
+
+       if (!rtnl_trylock())
+               return restart_syscall();
+
+       mutex_lock(&ppriv->vlan_mutex);
+
+       /*
+        * First ensure this isn't a duplicate. We check the parent device and
+        * then all of the legacy child interfaces to make sure the Pkey
+        * doesn't match.
+        */
+       if (ppriv->pkey == pkey) {
+               result = -ENOTUNIQ;
+               goto out;
+       }
+
+       list_for_each_entry(tpriv, &ppriv->child_intfs, list) {
+               if (tpriv->pkey == pkey &&
+                   tpriv->child_type == IPOIB_LEGACY_CHILD) {
+                       result = -ENOTUNIQ;
+                       goto out;
+               }
+       }
+
+       result = __ipoib_vlan_add(ppriv, priv, pkey, IPOIB_LEGACY_CHILD);
+
+out:
        mutex_unlock(&ppriv->vlan_mutex);
-       rtnl_unlock();
-       if (priv)
+
+       if (result)
                free_netdev(priv->dev);
 
+       rtnl_unlock();
+
        return result;
 }
 
@@ -171,9 +183,9 @@ int ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey)
                return restart_syscall();
        mutex_lock(&ppriv->vlan_mutex);
        list_for_each_entry_safe(priv, tpriv, &ppriv->child_intfs, list) {
-               if (priv->pkey == pkey) {
+               if (priv->pkey == pkey &&
+                   priv->child_type == IPOIB_LEGACY_CHILD) {
                        unregister_netdevice(priv->dev);
-                       ipoib_dev_cleanup(priv->dev);
                        list_del(&priv->list);
                        dev = priv->dev;
                        break;
index ac173bd2ab65e40c8759eb181460b79dd588b5c6..24c0dd09af54d3b529f5b0e93742344f28a66a0b 100644 (file)
@@ -398,4 +398,15 @@ struct ifla_port_vsi {
        __u8 pad[3];
 };
 
+
+/* IPoIB section */
+
+enum {
+       IFLA_IPOIB_UNSPEC,
+       IFLA_IPOIB_PKEY,
+       __IFLA_IPOIB_MAX
+};
+
+#define IFLA_IPOIB_MAX (__IFLA_IPOIB_MAX - 1)
+
 #endif /* _LINUX_IF_LINK_H */