Re: [B.A.T.M.A.N.] [PATCH] batman-adv: postpone sysfs removal when unregistering

2013-01-10 Thread Simon Wunderlich
Hey Antonio,


On Thu, Jan 10, 2013 at 04:34:47AM +0200, Antonio Quartulli wrote:
 Hello Simon,
 
 thanks a lot for taking care of this and sorry for my very late reply..

no problem, thanks for your review!

 
 
 Il 31.12.2012 01:40 Simon Wunderlich ha scritto:
 diff --git a/hard-interface.c b/hard-interface.c
 index f1d37cd..eb3a12d 100644
 --- a/hard-interface.c
 +++ b/hard-interface.c
 @@ -506,6 +506,17 @@ out:
  return NULL;
  }
 
 +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 void batadv_hardif_remove_interface(struct batadv_hard_iface
 *hard_iface)
  {
  ASSERT_RTNL();
 @@ -518,8 +529,9 @@ static void batadv_hardif_remove_interface(struct
 batadv_hard_iface *hard_iface)
  return;
 
  hard_iface-if_status = BATADV_IF_TO_BE_REMOVED;
 -batadv_sysfs_del_hardif(hard_iface-hardif_obj);
 -batadv_hardif_free_ref(hard_iface);
 +INIT_WORK(hard_iface-cleanup_work,
 +  batadv_hardif_remove_interface_finish);
 +queue_work(batadv_event_workqueue, hard_iface-cleanup_work);
  }
 
 
 why do we need to postpone the invocation of
 batadv_hardif_remove_interface_finish() ? Is it also creating a
 possible
 deadlock? As far as I understood only rtnl_lock() would create the
 problem, but it
 is not invoked in batadv_hardif_remove_interface_finish(). It seems
 I am missing
 something?

batadv_hardif_remove_interface() is called by batadv_hard_if_event(),
which is protected rtnl_lock(). You can find an ASSERT_RTNL() there too.
As batadv_hardif_remove_interface() then removes sysfs, this could
be problematic.

 
 Other than this, remember that the INIT_WORK macro does not need to
 be invoked
 each and every time you want to enqueue the work. It should be
 invoked once only
 (A patch fixing this issue for all the delayed works we have in
 the code has
 been recently applied). However it is not a real issue, but we want
 to keep the
 code consistent :)
 

Well, the work item is actually only initialized once, before taking
the interface down. Then the struct is destroyed, so it can't be called
again. But you are right, for consistency I will move the INIT_WORK
macros to positions where the structs are initialized.

  int batadv_softif_is_valid(const struct net_device *net_dev)
 diff --git a/types.h b/types.h
 index d8061ac..a9800ee 100644
 --- a/types.h
 +++ b/types.h
 @@ -63,6 +63,7 @@ struct batadv_hard_iface {
  struct net_device *soft_iface;
  struct rcu_head rcu;
  struct batadv_hard_iface_bat_iv bat_iv;
 +struct work_struct cleanup_work;
  };
 
 
 kernel doc :)
 

The Mareks kernel doc patch wasn't applied at this time, so I didn't
bother documenting my single new variable, but will do that in the
next revision. :)

Thanks,
Simon


signature.asc
Description: Digital signature


[B.A.T.M.A.N.] [PATCHv2] batman-adv: postpone sysfs removal when unregistering

2013-01-10 Thread Simon Wunderlich
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: Sasha Levin sasha.le...@oracle.com
Reported-by: Sven Eckelmann s...@narfation.org
Signed-off-by: Simon Wunderlich s...@hrz.tu-chemnitz.de
---
Changes from PATCHv1:
 * INIT_WORK() in respective struct initialization functions
 * kerneldoc

Changes from RFCv1:
 * use work_struct properly, instead of delayed_work
 * postpone for softifs as well as for hardifs

Postponing the sysfs removal for the hardif unregister is easier than
other alternatives involving deferring. This should bring us to the
same level to the bridge code, which also messes with sysfs in the
notifier processing function, and uses rtnl_trylock when called
from within sysfs.

As far as I could understand the net/core code, only the unregister
case is the critical one, so the original bug should hopefully be
fixed.
---
 hard-interface.c |   17 +++--
 soft-interface.c |   25 ++---
 types.h  |6 ++
 3 files changed, 43 insertions(+), 5 deletions(-)

diff --git a/hard-interface.c b/hard-interface.c
index 78cf350..8b84321 100644
--- a/hard-interface.c
+++ b/hard-interface.c
@@ -457,6 +457,17 @@ out:
batadv_hardif_free_ref(primary_if);
 }
 
+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 *
 batadv_hardif_add_interface(struct net_device *net_dev)
 {
@@ -484,6 +495,9 @@ batadv_hardif_add_interface(struct net_device *net_dev)
hard_iface-soft_iface = NULL;
hard_iface-if_status = BATADV_IF_NOT_IN_USE;
INIT_LIST_HEAD(hard_iface-list);
+   INIT_WORK(hard_iface-cleanup_work,
+ batadv_hardif_remove_interface_finish);
+
/* extra reference for return */
atomic_set(hard_iface-refcount, 2);
 
@@ -518,8 +532,7 @@ static void batadv_hardif_remove_interface(struct 
batadv_hard_iface *hard_iface)
return;
 
hard_iface-if_status = BATADV_IF_TO_BE_REMOVED;
-   batadv_sysfs_del_hardif(hard_iface-hardif_obj);
-   batadv_hardif_free_ref(hard_iface);
+   queue_work(batadv_event_workqueue, hard_iface-cleanup_work);
 }
 
 void batadv_hardif_remove_interfaces(void)
diff --git a/soft-interface.c b/soft-interface.c
index e67e013..b8e46bb 100644
--- a/soft-interface.c
+++ b/soft-interface.c
@@ -449,6 +449,23 @@ static void batadv_interface_setup(struct net_device *dev)
memset(priv, 0, sizeof(*priv));
 }
 
+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 *soft_iface;
@@ -463,6 +480,8 @@ struct net_device *batadv_softif_create(const char *name)
goto out;
 
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
 * register_netdevice() has been called
@@ -551,10 +570,10 @@ out:
 
 void batadv_softif_destroy(struct net_device *soft_iface)
 {
-   batadv_debugfs_del_meshif(soft_iface);
-   batadv_sysfs_del_meshif(soft_iface);
+   struct batadv_priv *bat_priv = netdev_priv(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)
diff --git a/types.h b/types.h
index 89ac1fb..4cd87a0 100644
--- a/types.h
+++ b/types.h
@@ -68,6 +68,7 @@ struct batadv_hard_iface_bat_iv {
  * @soft_iface: the batman-adv interface which uses this network interface
  * @rcu: struct used for freeing in an RCU-safe manner
  * @bat_iv: BATMAN IV specific per hard interface data
+ * @cleanup_work: work queue callback item for hard interface deinit
  */
 struct batadv_hard_iface {
struct list_head list;
@@ -81,6 +82,7 @@ struct batadv_hard_iface {
struct net_device *soft_iface;
struct rcu_head rcu;
struct batadv_hard_iface_bat_iv bat_iv;
+   struct work_struct cleanup_work;
 };
 

Re: [B.A.T.M.A.N.] [PATCHv2] batman-adv: postpone sysfs removal when unregistering

2013-01-10 Thread Marek Lindner
On Friday, January 11, 2013 01:09:21 Simon Wunderlich wrote:
 +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);
 +}

Kernel doc ?


 +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();
 +}

Kernel doc ?

The rest looks good.  :)

Cheers,
Marek