rtnetlink: implement setting of master device
authorJiri Pirko <jpirko@redhat.com>
Sun, 13 Feb 2011 10:15:37 +0000 (10:15 +0000)
committerDavid S. Miller <davem@davemloft.net>
Mon, 14 Feb 2011 00:58:39 +0000 (16:58 -0800)
This patch allows userspace to enslave/release slave devices via netlink
interface using IFLA_MASTER. This introduces generic way to add/remove
underling devices.

Signed-off-by: Jiri Pirko <jpirko@redhat.com>
Acked-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/netdevice.h
net/core/rtnetlink.c

index 5a42b10037670e893c450cb83d395279c229b832..d08ef6538579eaf3af38374a655cd4f6e4160009 100644 (file)
@@ -783,6 +783,14 @@ struct netdev_tc_txq {
  *     Set hardware filter for RFS.  rxq_index is the target queue index;
  *     flow_id is a flow ID to be passed to rps_may_expire_flow() later.
  *     Return the filter ID on success, or a negative error code.
+ *
+ *     Slave management functions (for bridge, bonding, etc). User should
+ *     call netdev_set_master() to set dev->master properly.
+ * int (*ndo_add_slave)(struct net_device *dev, struct net_device *slave_dev);
+ *     Called to make another netdev an underling.
+ *
+ * int (*ndo_del_slave)(struct net_device *dev, struct net_device *slave_dev);
+ *     Called to release previously enslaved netdev.
  */
 #define HAVE_NET_DEVICE_OPS
 struct net_device_ops {
@@ -862,6 +870,10 @@ struct net_device_ops {
                                                     u16 rxq_index,
                                                     u32 flow_id);
 #endif
+       int                     (*ndo_add_slave)(struct net_device *dev,
+                                                struct net_device *slave_dev);
+       int                     (*ndo_del_slave)(struct net_device *dev,
+                                                struct net_device *slave_dev);
 };
 
 /*
index da0fe457c858fe8e9a1ec9a15f54cb38e6d1880a..49f7ea5b4c7510ca9caeb9d494486fea71c04c9d 100644 (file)
@@ -1036,6 +1036,7 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = {
        [IFLA_MAP]              = { .len = sizeof(struct rtnl_link_ifmap) },
        [IFLA_MTU]              = { .type = NLA_U32 },
        [IFLA_LINK]             = { .type = NLA_U32 },
+       [IFLA_MASTER]           = { .type = NLA_U32 },
        [IFLA_TXQLEN]           = { .type = NLA_U32 },
        [IFLA_WEIGHT]           = { .type = NLA_U32 },
        [IFLA_OPERSTATE]        = { .type = NLA_U8 },
@@ -1178,6 +1179,41 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr *attr)
        return err;
 }
 
+static int do_set_master(struct net_device *dev, int ifindex)
+{
+       struct net_device *master_dev;
+       const struct net_device_ops *ops;
+       int err;
+
+       if (dev->master) {
+               if (dev->master->ifindex == ifindex)
+                       return 0;
+               ops = dev->master->netdev_ops;
+               if (ops->ndo_del_slave) {
+                       err = ops->ndo_del_slave(dev->master, dev);
+                       if (err)
+                               return err;
+               } else {
+                       return -EOPNOTSUPP;
+               }
+       }
+
+       if (ifindex) {
+               master_dev = __dev_get_by_index(dev_net(dev), ifindex);
+               if (!master_dev)
+                       return -EINVAL;
+               ops = master_dev->netdev_ops;
+               if (ops->ndo_add_slave) {
+                       err = ops->ndo_add_slave(master_dev, dev);
+                       if (err)
+                               return err;
+               } else {
+                       return -EOPNOTSUPP;
+               }
+       }
+       return 0;
+}
+
 static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
                      struct nlattr **tb, char *ifname, int modified)
 {
@@ -1301,6 +1337,13 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
                        goto errout;
        }
 
+       if (tb[IFLA_MASTER]) {
+               err = do_set_master(dev, nla_get_u32(tb[IFLA_MASTER]));
+               if (err)
+                       goto errout;
+               modified = 1;
+       }
+
        if (tb[IFLA_TXQLEN])
                dev->tx_queue_len = nla_get_u32(tb[IFLA_TXQLEN]);