summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--quicktun.c59
1 files changed, 35 insertions, 24 deletions
diff --git a/quicktun.c b/quicktun.c
index 50429a4..1aa426b 100644
--- a/quicktun.c
+++ b/quicktun.c
@@ -40,6 +40,7 @@ struct quicktun_struct {
struct quicktun_list_entry {
struct list_head list;
+ struct rcu_head rcu;
struct quicktun_struct *quicktun;
};
@@ -366,14 +367,34 @@ static int quicktun_cmd_create_device(struct sk_buff *skb, struct genl_info *inf
return err;
}
+static void quicktun_free_list_entry(struct rcu_head *rcu) {
+ struct quicktun_list_entry *entry = container_of(rcu, struct quicktun_list_entry, rcu);
+ kfree(entry);
+}
+
+static void __quicktun_delete_device(struct net_device *dev) {
+ struct quicktun_struct *tun = netdev_priv(dev);
+ struct socket *sock = tun->sock;
+ struct quicktun_list_entry *entry = NULL;
+
+ list_for_each_entry(entry, &quicktun_list, list) {
+ if (entry->quicktun->dev == dev) {
+ break;
+ }
+ }
+
+ list_del_rcu(&entry->list);
+ call_rcu(&entry->rcu, quicktun_free_list_entry);
+
+ unregister_netdevice(dev);
+ sock_release(sock);
+}
+
static int quicktun_cmd_delete_device(struct sk_buff *skb, struct genl_info *info)
{
int err;
struct net *net = genl_info_net(info);
struct net_device *dev;
- struct quicktun_struct *tun;
- struct quicktun_list_entry *entry = NULL;
- struct socket *sock;
char *name;
if (!capable(CAP_NET_ADMIN))
@@ -397,30 +418,10 @@ static int quicktun_cmd_delete_device(struct sk_buff *skb, struct genl_info *inf
goto err_unlock;
}
- tun = netdev_priv(dev);
- sock = tun->sock;
-
- rcu_read_lock();
-
- list_for_each_entry_rcu(entry, &quicktun_list, list) {
- if (entry->quicktun->dev == dev) {
- break;
- }
- }
-
- list_del_rcu(&entry->list);
-
- rcu_read_unlock();
-
- unregister_netdevice(dev);
+ __quicktun_delete_device(dev);
rtnl_unlock();
- sock_release(sock);
-
- synchronize_rcu();
- kfree(entry);
-
return 0;
err_unlock:
@@ -474,7 +475,17 @@ static int __init quicktun_init(void)
static void quicktun_exit(void)
{
+ struct quicktun_list_entry *entry, *next;
+
genl_unregister_family(&quicktun_nl_family);
+
+ rtnl_lock();
+
+ list_for_each_entry_safe(entry, next, &quicktun_list, list) {
+ __quicktun_delete_device(entry->quicktun->dev);
+ }
+
+ rtnl_unlock();
}