Repository : ssh://git@open-mesh.org/batman-adv
On branch : master
>---------------------------------------------------------------
commit 6b0485c758be31ff3d466644975eaedc54db6d17
Author: Andrew Lunn <andrew(a)lunn.ch>
Date: Tue May 17 15:39:27 2016 +0200
batman-adv: Fix hardif remove/add race
A hard interface can be removed and then added back in quick
succession. This is particularly true for hdlc interface when changing
the protocol.
It is not possible it synchronously remove the sysfs and debugfs
entries for the hard interface when it is removed because the files
may be open. Thus removal is deferred. The files may thus already
exist in sysfs and debugfs when the hard interface is re-added, and
the operations fail.
To fix this race, synchronously rename the debugfs and sysfs files to
a unique temporary name, thus making the name available when the
interface comes back, yet keeps open files still available.
Signed-off-by: Andrew Lunn <andrew(a)lunn.ch>
Signed-off-by: Marek Lindner <mareklindner(a)neomailbox.ch>
>---------------------------------------------------------------
6b0485c758be31ff3d466644975eaedc54db6d17
net/batman-adv/debugfs.c | 23 +++++++++++++++++++++++
net/batman-adv/debugfs.h | 1 +
net/batman-adv/hard-interface.c | 19 +++++++++++++++++++
net/batman-adv/sysfs.c | 17 +++++++++++++++++
net/batman-adv/sysfs.h | 2 ++
5 files changed, 62 insertions(+)
diff --git a/net/batman-adv/debugfs.c b/net/batman-adv/debugfs.c
index 1d68b6e..aea4133 100644
--- a/net/batman-adv/debugfs.c
+++ b/net/batman-adv/debugfs.c
@@ -44,6 +44,7 @@
#include "translation-table.h"
static struct dentry *batadv_debugfs;
+static atomic_t batadv_rename = ATOMIC_INIT(0);
static int batadv_algorithms_open(struct inode *inode, struct file *file)
{
@@ -347,6 +348,28 @@ void batadv_debugfs_del_hardif(struct batadv_hard_iface *hard_iface)
}
}
+/**
+ * batadv_debugfs_rename_hardif - rename the base directory
+ * @hard_iface: hard interface which is renamed.
+ *
+ * The interface may be removed and then quickly added back
+ * again. Rename the old instance to something temporary and unique,
+ * so avoiding a name space clash if it does reappear before the deferred
+ * work completes the removal.
+ */
+void batadv_debugfs_rename_hardif(struct batadv_hard_iface *hard_iface)
+{
+ char new_name[32];
+
+ snprintf(new_name, sizeof(*new_name) - 1, "tmp-%d",
+ atomic_inc_return(&batadv_rename));
+
+ if (batadv_debugfs && hard_iface->debug_dir) {
+ debugfs_rename(batadv_debugfs, hard_iface->debug_dir,
+ batadv_debugfs, new_name);
+ }
+}
+
int batadv_debugfs_add_meshif(struct net_device *dev)
{
struct batadv_priv *bat_priv = netdev_priv(dev);
diff --git a/net/batman-adv/debugfs.h b/net/batman-adv/debugfs.h
index 1ab4e2e..e7d19c1 100644
--- a/net/batman-adv/debugfs.h
+++ b/net/batman-adv/debugfs.h
@@ -34,6 +34,7 @@ int batadv_debugfs_add_meshif(struct net_device *dev);
void batadv_debugfs_del_meshif(struct net_device *dev);
int batadv_debugfs_add_hardif(struct batadv_hard_iface *hard_iface);
void batadv_debugfs_del_hardif(struct batadv_hard_iface *hard_iface);
+void batadv_debugfs_rename_hardif(struct batadv_hard_iface *hard_iface);
#else
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index 1f90808..ccc7641 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -706,6 +706,23 @@ out:
return NULL;
}
+/**
+ * batadv_hardif_rename - rename the sysfs and debugfs
+ * @hard_iface: The hard interface to rename
+ *
+ * The sysfs and debugfs files cannot be removed until all users close
+ * them. So the removal is deferred into a work queue. This however
+ * means if the interface comes back before the work queue runs, the
+ * files are still there, and so the create gives an EEXISTS error. To
+ * avoid this, rename them to a tempory name.
+ */
+static void batadv_hardif_rename(struct batadv_hard_iface *hard_iface)
+{
+ batadv_sysfs_rename_hardif(&hard_iface->hardif_obj,
+ hard_iface->net_dev);
+ batadv_debugfs_rename_hardif(hard_iface);
+}
+
static void batadv_hardif_remove_interface(struct batadv_hard_iface *hard_iface)
{
ASSERT_RTNL();
@@ -719,6 +736,8 @@ static void batadv_hardif_remove_interface(struct batadv_hard_iface *hard_iface)
return;
hard_iface->if_status = BATADV_IF_TO_BE_REMOVED;
+ batadv_hardif_rename(hard_iface);
+
queue_work(batadv_event_workqueue, &hard_iface->cleanup_work);
}
diff --git a/net/batman-adv/sysfs.c b/net/batman-adv/sysfs.c
index fe9ca94..9b2c28f 100644
--- a/net/batman-adv/sysfs.c
+++ b/net/batman-adv/sysfs.c
@@ -48,6 +48,8 @@
#include "packet.h"
#include "soft-interface.h"
+static atomic_t batadv_rename = ATOMIC_INIT(0);
+
static struct net_device *batadv_kobj_to_netdev(struct kobject *obj)
{
struct device *dev = container_of(obj->parent, struct device, kobj);
@@ -1046,6 +1048,21 @@ out:
return -ENOMEM;
}
+void batadv_sysfs_rename_hardif(struct kobject **hardif_obj,
+ struct net_device *dev)
+{
+ char new_name[32];
+ int err;
+
+ snprintf(new_name, sizeof(*new_name) - 1, "tmp-%d",
+ atomic_inc_return(&batadv_rename));
+
+ err = kobject_rename(*hardif_obj, new_name);
+ if (err)
+ batadv_err(dev, "Can't rename sysfs dir: %s/%s: %d\n",
+ dev->name, new_name, err);
+}
+
void batadv_sysfs_del_hardif(struct kobject **hardif_obj)
{
kobject_put(*hardif_obj);
diff --git a/net/batman-adv/sysfs.h b/net/batman-adv/sysfs.h
index c76021b..64d3722 100644
--- a/net/batman-adv/sysfs.h
+++ b/net/batman-adv/sysfs.h
@@ -48,6 +48,8 @@ void batadv_sysfs_del_meshif(struct net_device *dev);
int batadv_sysfs_add_hardif(struct kobject **hardif_obj,
struct net_device *dev);
void batadv_sysfs_del_hardif(struct kobject **hardif_obj);
+void batadv_sysfs_rename_hardif(struct kobject **hardif_obj,
+ struct net_device *dev);
int batadv_sysfs_add_vlan(struct net_device *dev,
struct batadv_softif_vlan *vlan);
void batadv_sysfs_del_vlan(struct batadv_priv *bat_priv,