vrf: fix a kernel warning
[cascardo/linux.git] / drivers / net / vrf.c
index 2d7418e..4743963 100644 (file)
@@ -34,7 +34,7 @@
 #include <net/rtnetlink.h>
 #include <net/route.h>
 #include <net/addrconf.h>
-#include <net/vrf.h>
+#include <net/l3mdev.h>
 
 #define DRV_NAME       "vrf"
 #define DRV_VERSION    "1.0"
 #define vrf_master_get_rcu(dev) \
        ((struct net_device *)rcu_dereference(dev->rx_handler_data))
 
+struct slave {
+       struct list_head        list;
+       struct net_device       *dev;
+};
+
+struct slave_queue {
+       struct list_head        all_slaves;
+};
+
+struct net_vrf {
+       struct slave_queue      queue;
+       struct rtable           *rth;
+       u32                     tb_id;
+};
+
 struct pcpu_dstats {
        u64                     tx_pkts;
        u64                     tx_bytes;
@@ -395,18 +410,15 @@ static void __vrf_insert_slave(struct slave_queue *queue, struct slave *slave)
 
 static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
 {
-       struct net_vrf_dev *vrf_ptr = kmalloc(sizeof(*vrf_ptr), GFP_KERNEL);
        struct slave *slave = kzalloc(sizeof(*slave), GFP_KERNEL);
        struct net_vrf *vrf = netdev_priv(dev);
        struct slave_queue *queue = &vrf->queue;
        int ret = -ENOMEM;
 
-       if (!slave || !vrf_ptr)
+       if (!slave)
                goto out_fail;
 
        slave->dev = port_dev;
-       vrf_ptr->ifindex = dev->ifindex;
-       vrf_ptr->tb_id = vrf->tb_id;
 
        /* register the packet handler for slave ports */
        ret = netdev_rx_handler_register(port_dev, vrf_handle_frame, dev);
@@ -423,7 +435,6 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
 
        port_dev->flags |= IFF_SLAVE;
        __vrf_insert_slave(queue, slave);
-       rcu_assign_pointer(port_dev->vrf_ptr, vrf_ptr);
        cycle_netdev(port_dev);
 
        return 0;
@@ -431,7 +442,6 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
 out_unregister:
        netdev_rx_handler_unregister(port_dev);
 out_fail:
-       kfree(vrf_ptr);
        kfree(slave);
        return ret;
 }
@@ -447,21 +457,15 @@ static int vrf_add_slave(struct net_device *dev, struct net_device *port_dev)
 /* inverse of do_vrf_add_slave */
 static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev)
 {
-       struct net_vrf_dev *vrf_ptr = rtnl_dereference(port_dev->vrf_ptr);
        struct net_vrf *vrf = netdev_priv(dev);
        struct slave_queue *queue = &vrf->queue;
        struct slave *slave;
 
-       RCU_INIT_POINTER(port_dev->vrf_ptr, NULL);
-
        netdev_upper_dev_unlink(port_dev, dev);
        port_dev->flags &= ~IFF_SLAVE;
 
        netdev_rx_handler_unregister(port_dev);
 
-       /* after netdev_rx_handler_unregister for synchronize_rcu */
-       kfree(vrf_ptr);
-
        cycle_netdev(port_dev);
 
        slave = __vrf_find_slave_dev(queue, port_dev);
@@ -529,6 +533,33 @@ static const struct net_device_ops vrf_netdev_ops = {
        .ndo_del_slave          = vrf_del_slave,
 };
 
+static u32 vrf_fib_table(const struct net_device *dev)
+{
+       struct net_vrf *vrf = netdev_priv(dev);
+
+       return vrf->tb_id;
+}
+
+static struct rtable *vrf_get_rtable(const struct net_device *dev,
+                                    const struct flowi4 *fl4)
+{
+       struct rtable *rth = NULL;
+
+       if (!(fl4->flowi4_flags & FLOWI_FLAG_VRFSRC)) {
+               struct net_vrf *vrf = netdev_priv(dev);
+
+               rth = vrf->rth;
+               atomic_inc(&rth->dst.__refcnt);
+       }
+
+       return rth;
+}
+
+static const struct l3mdev_ops vrf_l3mdev_ops = {
+       .l3mdev_fib_table       = vrf_fib_table,
+       .l3mdev_get_rtable      = vrf_get_rtable,
+};
+
 static void vrf_get_drvinfo(struct net_device *dev,
                            struct ethtool_drvinfo *info)
 {
@@ -546,6 +577,7 @@ static void vrf_setup(struct net_device *dev)
 
        /* Initialize the device structure. */
        dev->netdev_ops = &vrf_netdev_ops;
+       dev->l3mdev_ops = &vrf_l3mdev_ops;
        dev->ethtool_ops = &vrf_ethtool_ops;
        dev->destructor = free_netdev;
 
@@ -572,10 +604,6 @@ static int vrf_validate(struct nlattr *tb[], struct nlattr *data[])
 
 static void vrf_dellink(struct net_device *dev, struct list_head *head)
 {
-       struct net_vrf_dev *vrf_ptr = rtnl_dereference(dev->vrf_ptr);
-
-       RCU_INIT_POINTER(dev->vrf_ptr, NULL);
-       kfree_rcu(vrf_ptr, rcu);
        unregister_netdevice_queue(dev, head);
 }
 
@@ -583,7 +611,6 @@ static int vrf_newlink(struct net *src_net, struct net_device *dev,
                       struct nlattr *tb[], struct nlattr *data[])
 {
        struct net_vrf *vrf = netdev_priv(dev);
-       struct net_vrf_dev *vrf_ptr;
        int err;
 
        if (!data || !data[IFLA_VRF_TABLE])
@@ -593,24 +620,13 @@ static int vrf_newlink(struct net *src_net, struct net_device *dev,
 
        dev->priv_flags |= IFF_L3MDEV_MASTER;
 
-       err = -ENOMEM;
-       vrf_ptr = kmalloc(sizeof(*dev->vrf_ptr), GFP_KERNEL);
-       if (!vrf_ptr)
-               goto out_fail;
-
-       vrf_ptr->ifindex = dev->ifindex;
-       vrf_ptr->tb_id = vrf->tb_id;
-
        err = register_netdevice(dev);
        if (err < 0)
                goto out_fail;
 
-       rcu_assign_pointer(dev->vrf_ptr, vrf_ptr);
-
        return 0;
 
 out_fail:
-       kfree(vrf_ptr);
        free_netdev(dev);
        return err;
 }
@@ -654,10 +670,9 @@ static int vrf_device_event(struct notifier_block *unused,
 
        /* only care about unregister events to drop slave references */
        if (event == NETDEV_UNREGISTER) {
-               struct net_vrf_dev *vrf_ptr = rtnl_dereference(dev->vrf_ptr);
                struct net_device *vrf_dev;
 
-               if (!vrf_ptr || netif_is_l3_master(dev))
+               if (!vrf_is_slave(dev) || netif_is_l3_master(dev))
                        goto out;
 
                vrf_dev = netdev_master_upper_dev_get(dev);