Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[firefly-linux-kernel-4.4.55.git] / net / batman-adv / hard-interface.c
index 368219e026a96fbf2ca50df30cb49bd2c54d5978..522243aff2f3b4a45895efea0752405bbee95fa9 100644 (file)
@@ -307,11 +307,35 @@ batadv_hardif_deactivate_interface(struct batadv_hard_iface *hard_iface)
        batadv_update_min_mtu(hard_iface->soft_iface);
 }
 
+/**
+ * batadv_master_del_slave - remove hard_iface from the current master interface
+ * @slave: the interface enslaved in another master
+ * @master: the master from which slave has to be removed
+ *
+ * Invoke ndo_del_slave on master passing slave as argument. In this way slave
+ * is free'd and master can correctly change its internal state.
+ * Return 0 on success, a negative value representing the error otherwise
+ */
+static int batadv_master_del_slave(struct batadv_hard_iface *slave,
+                                  struct net_device *master)
+{
+       int ret;
+
+       if (!master)
+               return 0;
+
+       ret = -EBUSY;
+       if (master->netdev_ops->ndo_del_slave)
+               ret = master->netdev_ops->ndo_del_slave(master, slave->net_dev);
+
+       return ret;
+}
+
 int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
                                   const char *iface_name)
 {
        struct batadv_priv *bat_priv;
-       struct net_device *soft_iface;
+       struct net_device *soft_iface, *master;
        __be16 ethertype = __constant_htons(ETH_P_BATMAN);
        int ret;
 
@@ -321,11 +345,6 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
        if (!atomic_inc_not_zero(&hard_iface->refcount))
                goto out;
 
-       /* hard-interface is part of a bridge */
-       if (hard_iface->net_dev->priv_flags & IFF_BRIDGE_PORT)
-               pr_err("You are about to enable batman-adv on '%s' which already is part of a bridge. Unless you know exactly what you are doing this is probably wrong and won't work the way you think it would.\n",
-                      hard_iface->net_dev->name);
-
        soft_iface = dev_get_by_name(&init_net, iface_name);
 
        if (!soft_iface) {
@@ -347,12 +366,24 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
                goto err_dev;
        }
 
+       /* check if the interface is enslaved in another virtual one and
+        * in that case unlink it first
+        */
+       master = netdev_master_upper_dev_get(hard_iface->net_dev);
+       ret = batadv_master_del_slave(hard_iface, master);
+       if (ret)
+               goto err_dev;
+
        hard_iface->soft_iface = soft_iface;
        bat_priv = netdev_priv(hard_iface->soft_iface);
 
+       ret = netdev_master_upper_dev_link(hard_iface->net_dev, soft_iface);
+       if (ret)
+               goto err_dev;
+
        ret = bat_priv->bat_algo_ops->bat_iface_enable(hard_iface);
        if (ret < 0)
-               goto err_dev;
+               goto err_upper;
 
        hard_iface->if_num = bat_priv->num_ifaces;
        bat_priv->num_ifaces++;
@@ -362,7 +393,7 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
                bat_priv->bat_algo_ops->bat_iface_disable(hard_iface);
                bat_priv->num_ifaces--;
                hard_iface->if_status = BATADV_IF_NOT_IN_USE;
-               goto err_dev;
+               goto err_upper;
        }
 
        hard_iface->batman_adv_ptype.type = ethertype;
@@ -401,14 +432,18 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
 out:
        return 0;
 
+err_upper:
+       netdev_upper_dev_unlink(hard_iface->net_dev, soft_iface);
 err_dev:
+       hard_iface->soft_iface = NULL;
        dev_put(soft_iface);
 err:
        batadv_hardif_free_ref(hard_iface);
        return ret;
 }
 
-void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
+void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface,
+                                    enum batadv_hard_if_cleanup autodel)
 {
        struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
        struct batadv_hard_iface *primary_if = NULL;
@@ -446,9 +481,10 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
        dev_put(hard_iface->soft_iface);
 
        /* nobody uses this interface anymore */
-       if (!bat_priv->num_ifaces)
-               batadv_softif_destroy(hard_iface->soft_iface);
+       if (!bat_priv->num_ifaces && autodel == BATADV_IF_CLEANUP_AUTO)
+               batadv_softif_destroy_sysfs(hard_iface->soft_iface);
 
+       netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->soft_iface);
        hard_iface->soft_iface = NULL;
        batadv_hardif_free_ref(hard_iface);
 
@@ -533,7 +569,8 @@ static void batadv_hardif_remove_interface(struct batadv_hard_iface *hard_iface)
 
        /* first deactivate interface */
        if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
-               batadv_hardif_disable_interface(hard_iface);
+               batadv_hardif_disable_interface(hard_iface,
+                                               BATADV_IF_CLEANUP_AUTO);
 
        if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
                return;
@@ -563,6 +600,11 @@ static int batadv_hard_if_event(struct notifier_block *this,
        struct batadv_hard_iface *primary_if = NULL;
        struct batadv_priv *bat_priv;
 
+       if (batadv_softif_is_valid(net_dev) && event == NETDEV_REGISTER) {
+               batadv_sysfs_add_meshif(net_dev);
+               return NOTIFY_DONE;
+       }
+
        hard_iface = batadv_hardif_get_by_netdev(net_dev);
        if (!hard_iface && event == NETDEV_REGISTER)
                hard_iface = batadv_hardif_add_interface(net_dev);