Commit 5bc44dc8 authored by Simon Wunderlich's avatar Simon Wunderlich Committed by Antonio Quartulli

batman-adv: postpone sysfs removal when unregistering

When processing the unregister notify for a hard interface, removing
the sysfs files may lead to a circular deadlock (rtnl mutex <->
s_active).

To overcome this problem, postpone the sysfs removal in a worker.
Reported-by: default avatarSasha Levin <sasha.levin@oracle.com>
Reported-by: default avatarSven Eckelmann <sven@narfation.org>
Signed-off-by: default avatarSimon Wunderlich <siwu@hrz.tu-chemnitz.de>
Signed-off-by: default avatarMarek Lindner <lindner_marek@yahoo.de>
Signed-off-by: default avatarAntonio Quartulli <ordex@autistici.org>
parent e76e4320
...@@ -457,6 +457,24 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface) ...@@ -457,6 +457,24 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
batadv_hardif_free_ref(primary_if); batadv_hardif_free_ref(primary_if);
} }
/**
* batadv_hardif_remove_interface_finish - cleans up the remains of a hardif
* @work: work queue item
*
* Free the parts of the hard interface which can not be removed under
* rtnl lock (to prevent deadlock situations).
*/
static void batadv_hardif_remove_interface_finish(struct work_struct *work)
{
struct batadv_hard_iface *hard_iface;
hard_iface = container_of(work, struct batadv_hard_iface,
cleanup_work);
batadv_sysfs_del_hardif(&hard_iface->hardif_obj);
batadv_hardif_free_ref(hard_iface);
}
static struct batadv_hard_iface * static struct batadv_hard_iface *
batadv_hardif_add_interface(struct net_device *net_dev) batadv_hardif_add_interface(struct net_device *net_dev)
{ {
...@@ -484,6 +502,9 @@ batadv_hardif_add_interface(struct net_device *net_dev) ...@@ -484,6 +502,9 @@ batadv_hardif_add_interface(struct net_device *net_dev)
hard_iface->soft_iface = NULL; hard_iface->soft_iface = NULL;
hard_iface->if_status = BATADV_IF_NOT_IN_USE; hard_iface->if_status = BATADV_IF_NOT_IN_USE;
INIT_LIST_HEAD(&hard_iface->list); INIT_LIST_HEAD(&hard_iface->list);
INIT_WORK(&hard_iface->cleanup_work,
batadv_hardif_remove_interface_finish);
/* extra reference for return */ /* extra reference for return */
atomic_set(&hard_iface->refcount, 2); atomic_set(&hard_iface->refcount, 2);
...@@ -518,8 +539,7 @@ static void batadv_hardif_remove_interface(struct batadv_hard_iface *hard_iface) ...@@ -518,8 +539,7 @@ static void batadv_hardif_remove_interface(struct batadv_hard_iface *hard_iface)
return; return;
hard_iface->if_status = BATADV_IF_TO_BE_REMOVED; hard_iface->if_status = BATADV_IF_TO_BE_REMOVED;
batadv_sysfs_del_hardif(&hard_iface->hardif_obj); queue_work(batadv_event_workqueue, &hard_iface->cleanup_work);
batadv_hardif_free_ref(hard_iface);
} }
void batadv_hardif_remove_interfaces(void) void batadv_hardif_remove_interfaces(void)
......
...@@ -449,6 +449,30 @@ static void batadv_interface_setup(struct net_device *dev) ...@@ -449,6 +449,30 @@ static void batadv_interface_setup(struct net_device *dev)
memset(priv, 0, sizeof(*priv)); memset(priv, 0, sizeof(*priv));
} }
/**
* batadv_softif_destroy_finish - cleans up the remains of a softif
* @work: work queue item
*
* Free the parts of the soft interface which can not be removed under
* rtnl lock (to prevent deadlock situations).
*/
static void batadv_softif_destroy_finish(struct work_struct *work)
{
struct batadv_priv *bat_priv;
struct net_device *soft_iface;
bat_priv = container_of(work, struct batadv_priv,
cleanup_work);
soft_iface = bat_priv->soft_iface;
batadv_debugfs_del_meshif(soft_iface);
batadv_sysfs_del_meshif(soft_iface);
rtnl_lock();
unregister_netdevice(soft_iface);
rtnl_unlock();
}
struct net_device *batadv_softif_create(const char *name) struct net_device *batadv_softif_create(const char *name)
{ {
struct net_device *soft_iface; struct net_device *soft_iface;
...@@ -463,6 +487,8 @@ struct net_device *batadv_softif_create(const char *name) ...@@ -463,6 +487,8 @@ struct net_device *batadv_softif_create(const char *name)
goto out; goto out;
bat_priv = netdev_priv(soft_iface); bat_priv = netdev_priv(soft_iface);
bat_priv->soft_iface = soft_iface;
INIT_WORK(&bat_priv->cleanup_work, batadv_softif_destroy_finish);
/* batadv_interface_stats() needs to be available as soon as /* batadv_interface_stats() needs to be available as soon as
* register_netdevice() has been called * register_netdevice() has been called
...@@ -551,10 +577,10 @@ struct net_device *batadv_softif_create(const char *name) ...@@ -551,10 +577,10 @@ struct net_device *batadv_softif_create(const char *name)
void batadv_softif_destroy(struct net_device *soft_iface) void batadv_softif_destroy(struct net_device *soft_iface)
{ {
batadv_debugfs_del_meshif(soft_iface); struct batadv_priv *bat_priv = netdev_priv(soft_iface);
batadv_sysfs_del_meshif(soft_iface);
batadv_mesh_free(soft_iface); batadv_mesh_free(soft_iface);
unregister_netdevice(soft_iface); queue_work(batadv_event_workqueue, &bat_priv->cleanup_work);
} }
int batadv_softif_is_valid(const struct net_device *net_dev) int batadv_softif_is_valid(const struct net_device *net_dev)
......
...@@ -68,6 +68,7 @@ struct batadv_hard_iface_bat_iv { ...@@ -68,6 +68,7 @@ struct batadv_hard_iface_bat_iv {
* @soft_iface: the batman-adv interface which uses this network interface * @soft_iface: the batman-adv interface which uses this network interface
* @rcu: struct used for freeing in an RCU-safe manner * @rcu: struct used for freeing in an RCU-safe manner
* @bat_iv: BATMAN IV specific per hard interface data * @bat_iv: BATMAN IV specific per hard interface data
* @cleanup_work: work queue callback item for hard interface deinit
*/ */
struct batadv_hard_iface { struct batadv_hard_iface {
struct list_head list; struct list_head list;
...@@ -81,6 +82,7 @@ struct batadv_hard_iface { ...@@ -81,6 +82,7 @@ struct batadv_hard_iface {
struct net_device *soft_iface; struct net_device *soft_iface;
struct rcu_head rcu; struct rcu_head rcu;
struct batadv_hard_iface_bat_iv bat_iv; struct batadv_hard_iface_bat_iv bat_iv;
struct work_struct cleanup_work;
}; };
/** /**
...@@ -428,6 +430,7 @@ struct batadv_priv_dat { ...@@ -428,6 +430,7 @@ struct batadv_priv_dat {
/** /**
* struct batadv_priv - per mesh interface data * struct batadv_priv - per mesh interface data
* @mesh_state: current status of the mesh (inactive/active/deactivating) * @mesh_state: current status of the mesh (inactive/active/deactivating)
* @soft_iface: net device which holds this struct as private data
* @stats: structure holding the data for the ndo_get_stats() call * @stats: structure holding the data for the ndo_get_stats() call
* @bat_counters: mesh internal traffic statistic counters (see batadv_counters) * @bat_counters: mesh internal traffic statistic counters (see batadv_counters)
* @aggregated_ogms: bool indicating whether OGM aggregation is enabled * @aggregated_ogms: bool indicating whether OGM aggregation is enabled
...@@ -457,6 +460,7 @@ struct batadv_priv_dat { ...@@ -457,6 +460,7 @@ struct batadv_priv_dat {
* @forw_bat_list_lock: lock protecting forw_bat_list * @forw_bat_list_lock: lock protecting forw_bat_list
* @forw_bcast_list_lock: lock protecting forw_bcast_list * @forw_bcast_list_lock: lock protecting forw_bcast_list
* @orig_work: work queue callback item for orig node purging * @orig_work: work queue callback item for orig node purging
* @cleanup_work: work queue callback item for soft interface deinit
* @primary_if: one of the hard interfaces assigned to this mesh interface * @primary_if: one of the hard interfaces assigned to this mesh interface
* becomes the primary interface * becomes the primary interface
* @bat_algo_ops: routing algorithm used by this mesh interface * @bat_algo_ops: routing algorithm used by this mesh interface
...@@ -469,6 +473,7 @@ struct batadv_priv_dat { ...@@ -469,6 +473,7 @@ struct batadv_priv_dat {
*/ */
struct batadv_priv { struct batadv_priv {
atomic_t mesh_state; atomic_t mesh_state;
struct net_device *soft_iface;
struct net_device_stats stats; struct net_device_stats stats;
uint64_t __percpu *bat_counters; /* Per cpu counters */ uint64_t __percpu *bat_counters; /* Per cpu counters */
atomic_t aggregated_ogms; atomic_t aggregated_ogms;
...@@ -502,6 +507,7 @@ struct batadv_priv { ...@@ -502,6 +507,7 @@ struct batadv_priv {
spinlock_t forw_bat_list_lock; /* protects forw_bat_list */ spinlock_t forw_bat_list_lock; /* protects forw_bat_list */
spinlock_t forw_bcast_list_lock; /* protects forw_bcast_list */ spinlock_t forw_bcast_list_lock; /* protects forw_bcast_list */
struct delayed_work orig_work; struct delayed_work orig_work;
struct work_struct cleanup_work;
struct batadv_hard_iface __rcu *primary_if; /* rcu protected pointer */ struct batadv_hard_iface __rcu *primary_if; /* rcu protected pointer */
struct batadv_algo_ops *bat_algo_ops; struct batadv_algo_ops *bat_algo_ops;
#ifdef CONFIG_BATMAN_ADV_BLA #ifdef CONFIG_BATMAN_ADV_BLA
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment