When a new hard_iface has a master already, ndo_del_slave has to be used in order to let the old master perform its cleaning up routine correctly. Unsetting the master iface only can lead to a inconsistent state in the master driver.
Introduced by 5e57e3bff429cd403f0e30af69cf87a2cd55598f ("batman-adv: free an hard-interface before adding it").
Reported-by: Marek Lindner lindner_marek@yahoo.de Signed-off-by: Simon Wunderlich siwu@hrz.tu-chemnitz.de Signed-off-by: Antonio Quartulli ordex@autistici.org ---
v2: - patch restyled - added compat code for <2.6.39 (Thanks to Simon)
v3: - commit message rewording - change EINVAL to EBUSY to better describe the error
compat.h | 3 +++ hard-interface.c | 29 +++++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-)
diff --git a/compat.h b/compat.h index ba07ae4..0663df5 100644 --- a/compat.h +++ b/compat.h @@ -161,6 +161,9 @@ static const struct { \ } __attribute__((unused)) __useless_ops1 = { \ .ndo_validate_addr
+#define ndo_del_slave ndo_init +#define ndo_init(x, y) ndo_init - master->netdev_ops->ndo_init - EBUSY + #endif /* < KERNEL_VERSION(2, 6, 39) */
diff --git a/hard-interface.c b/hard-interface.c index fd99e42..37c1b51 100644 --- a/hard-interface.c +++ b/hard-interface.c @@ -307,6 +307,30 @@ 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) { @@ -346,8 +370,9 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface, * in that case unlink it first */ master = netdev_master_upper_dev_get(hard_iface->net_dev); - if (master) - netdev_upper_dev_unlink(hard_iface->net_dev, master); + 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);