diff options
Diffstat (limited to 'net/batman-adv/hard-interface.c')
| -rw-r--r-- | net/batman-adv/hard-interface.c | 66 | 
1 files changed, 54 insertions, 12 deletions
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index 368219e026a..522243aff2f 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -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);  |