wireless extensions: make netns aware
authorJohannes Berg <johannes@sipsolutions.net>
Wed, 24 Jun 2009 01:34:48 +0000 (01:34 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 15 Jul 2009 15:53:32 +0000 (08:53 -0700)
This makes wireless extensions netns aware. The
tasklet sending the events is converted to a work
struct so that we can rtnl_lock() in it.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/net_namespace.h
net/wireless/wext.c

index 3882db1e263a502faeb40fd4d0c9adf523235d8a..5c5136fceea8f5d4bea4ba9c8bc0d7505192e4bd 100644 (file)
@@ -79,6 +79,9 @@ struct net {
 #endif
 #ifdef CONFIG_XFRM
        struct netns_xfrm       xfrm;
+#endif
+#ifdef CONFIG_WIRELESS_EXT
+       struct sk_buff_head     wext_nlevents;
 #endif
        struct net_generic      *gen;
 };
index 425f7d58b9612c1961b7b3ea8e386891d001113e..db8351a5a87d949eb16b343537c7daac10cf1a4a 100644 (file)
@@ -1257,48 +1257,48 @@ int compat_wext_handle_ioctl(struct net *net, unsigned int cmd,
 }
 #endif
 
-/************************* EVENT PROCESSING *************************/
-/*
- * Process events generated by the wireless layer or the driver.
- * Most often, the event will be propagated through rtnetlink
- */
+static int __net_init wext_pernet_init(struct net *net)
+{
+       skb_queue_head_init(&net->wext_nlevents);
+       return 0;
+}
 
-/* ---------------------------------------------------------------- */
-/*
- * Locking...
- * ----------
- *
- * Thanks to Herbert Xu <herbert@gondor.apana.org.au> for fixing
- * the locking issue in here and implementing this code !
- *
- * The issue : wireless_send_event() is often called in interrupt context,
- * while the Netlink layer can never be called in interrupt context.
- * The fully formed RtNetlink events are queued, and then a tasklet is run
- * to feed those to Netlink.
- * The skb_queue is interrupt safe, and its lock is not held while calling
- * Netlink, so there is no possibility of dealock.
- * Jean II
- */
+static void __net_exit wext_pernet_exit(struct net *net)
+{
+       skb_queue_purge(&net->wext_nlevents);
+}
 
-static struct sk_buff_head wireless_nlevent_queue;
+static struct pernet_operations wext_pernet_ops = {
+       .init = wext_pernet_init,
+       .exit = wext_pernet_exit,
+};
 
 static int __init wireless_nlevent_init(void)
 {
-       skb_queue_head_init(&wireless_nlevent_queue);
+       return register_pernet_subsys(&wext_pernet_ops);
        return 0;
 }
 
 subsys_initcall(wireless_nlevent_init);
 
-static void wireless_nlevent_process(unsigned long data)
+/* Process events generated by the wireless layer or the driver. */
+static void wireless_nlevent_process(struct work_struct *work)
 {
        struct sk_buff *skb;
+       struct net *net;
+
+       rtnl_lock();
 
-       while ((skb = skb_dequeue(&wireless_nlevent_queue)))
-               rtnl_notify(skb, &init_net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
+       for_each_net(net) {
+               while ((skb = skb_dequeue(&net->wext_nlevents)))
+                       rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL,
+                                   GFP_KERNEL);
+       }
+
+       rtnl_unlock();
 }
 
-static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0);
+static DECLARE_WORK(wireless_nlevent_work, wireless_nlevent_process);
 
 /* ---------------------------------------------------------------- */
 /*
@@ -1348,9 +1348,6 @@ static void rtmsg_iwinfo(struct net_device *dev, char *event, int event_len)
        struct sk_buff *skb;
        int err;
 
-       if (!net_eq(dev_net(dev), &init_net))
-               return;
-
        skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
        if (!skb)
                return;
@@ -1363,8 +1360,8 @@ static void rtmsg_iwinfo(struct net_device *dev, char *event, int event_len)
        }
 
        NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
-       skb_queue_tail(&wireless_nlevent_queue, skb);
-       tasklet_schedule(&wireless_nlevent_tasklet);
+       skb_queue_tail(&dev_net(dev)->wext_nlevents, skb);
+       schedule_work(&wireless_nlevent_work);
 }
 
 /* ---------------------------------------------------------------- */