diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2009-06-23 21:34:48 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-07-15 11:53:32 -0400 |
commit | b333b3d22822cf9b295990866798e9239c9dee72 (patch) | |
tree | 307298699e475eb93a3326384fff6f28b1e28b0f | |
parent | 97fd5bc7f2e442482a7a6cc4bc2a286cbb5f4754 (diff) |
wireless extensions: make netns aware
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>
-rw-r--r-- | include/net/net_namespace.h | 3 | ||||
-rw-r--r-- | net/wireless/wext.c | 61 |
2 files changed, 32 insertions, 32 deletions
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 3882db1e263a..5c5136fceea8 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h | |||
@@ -80,6 +80,9 @@ struct net { | |||
80 | #ifdef CONFIG_XFRM | 80 | #ifdef CONFIG_XFRM |
81 | struct netns_xfrm xfrm; | 81 | struct netns_xfrm xfrm; |
82 | #endif | 82 | #endif |
83 | #ifdef CONFIG_WIRELESS_EXT | ||
84 | struct sk_buff_head wext_nlevents; | ||
85 | #endif | ||
83 | struct net_generic *gen; | 86 | struct net_generic *gen; |
84 | }; | 87 | }; |
85 | 88 | ||
diff --git a/net/wireless/wext.c b/net/wireless/wext.c index 425f7d58b961..db8351a5a87d 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c | |||
@@ -1257,48 +1257,48 @@ int compat_wext_handle_ioctl(struct net *net, unsigned int cmd, | |||
1257 | } | 1257 | } |
1258 | #endif | 1258 | #endif |
1259 | 1259 | ||
1260 | /************************* EVENT PROCESSING *************************/ | 1260 | static int __net_init wext_pernet_init(struct net *net) |
1261 | /* | 1261 | { |
1262 | * Process events generated by the wireless layer or the driver. | 1262 | skb_queue_head_init(&net->wext_nlevents); |
1263 | * Most often, the event will be propagated through rtnetlink | 1263 | return 0; |
1264 | */ | 1264 | } |
1265 | 1265 | ||
1266 | /* ---------------------------------------------------------------- */ | 1266 | static void __net_exit wext_pernet_exit(struct net *net) |
1267 | /* | 1267 | { |
1268 | * Locking... | 1268 | skb_queue_purge(&net->wext_nlevents); |
1269 | * ---------- | 1269 | } |
1270 | * | ||
1271 | * Thanks to Herbert Xu <herbert@gondor.apana.org.au> for fixing | ||
1272 | * the locking issue in here and implementing this code ! | ||
1273 | * | ||
1274 | * The issue : wireless_send_event() is often called in interrupt context, | ||
1275 | * while the Netlink layer can never be called in interrupt context. | ||
1276 | * The fully formed RtNetlink events are queued, and then a tasklet is run | ||
1277 | * to feed those to Netlink. | ||
1278 | * The skb_queue is interrupt safe, and its lock is not held while calling | ||
1279 | * Netlink, so there is no possibility of dealock. | ||
1280 | * Jean II | ||
1281 | */ | ||
1282 | 1270 | ||
1283 | static struct sk_buff_head wireless_nlevent_queue; | 1271 | static struct pernet_operations wext_pernet_ops = { |
1272 | .init = wext_pernet_init, | ||
1273 | .exit = wext_pernet_exit, | ||
1274 | }; | ||
1284 | 1275 | ||
1285 | static int __init wireless_nlevent_init(void) | 1276 | static int __init wireless_nlevent_init(void) |
1286 | { | 1277 | { |
1287 | skb_queue_head_init(&wireless_nlevent_queue); | 1278 | return register_pernet_subsys(&wext_pernet_ops); |
1288 | return 0; | 1279 | return 0; |
1289 | } | 1280 | } |
1290 | 1281 | ||
1291 | subsys_initcall(wireless_nlevent_init); | 1282 | subsys_initcall(wireless_nlevent_init); |
1292 | 1283 | ||
1293 | static void wireless_nlevent_process(unsigned long data) | 1284 | /* Process events generated by the wireless layer or the driver. */ |
1285 | static void wireless_nlevent_process(struct work_struct *work) | ||
1294 | { | 1286 | { |
1295 | struct sk_buff *skb; | 1287 | struct sk_buff *skb; |
1288 | struct net *net; | ||
1289 | |||
1290 | rtnl_lock(); | ||
1296 | 1291 | ||
1297 | while ((skb = skb_dequeue(&wireless_nlevent_queue))) | 1292 | for_each_net(net) { |
1298 | rtnl_notify(skb, &init_net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); | 1293 | while ((skb = skb_dequeue(&net->wext_nlevents))) |
1294 | rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, | ||
1295 | GFP_KERNEL); | ||
1296 | } | ||
1297 | |||
1298 | rtnl_unlock(); | ||
1299 | } | 1299 | } |
1300 | 1300 | ||
1301 | static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0); | 1301 | static DECLARE_WORK(wireless_nlevent_work, wireless_nlevent_process); |
1302 | 1302 | ||
1303 | /* ---------------------------------------------------------------- */ | 1303 | /* ---------------------------------------------------------------- */ |
1304 | /* | 1304 | /* |
@@ -1348,9 +1348,6 @@ static void rtmsg_iwinfo(struct net_device *dev, char *event, int event_len) | |||
1348 | struct sk_buff *skb; | 1348 | struct sk_buff *skb; |
1349 | int err; | 1349 | int err; |
1350 | 1350 | ||
1351 | if (!net_eq(dev_net(dev), &init_net)) | ||
1352 | return; | ||
1353 | |||
1354 | skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); | 1351 | skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); |
1355 | if (!skb) | 1352 | if (!skb) |
1356 | return; | 1353 | return; |
@@ -1363,8 +1360,8 @@ static void rtmsg_iwinfo(struct net_device *dev, char *event, int event_len) | |||
1363 | } | 1360 | } |
1364 | 1361 | ||
1365 | NETLINK_CB(skb).dst_group = RTNLGRP_LINK; | 1362 | NETLINK_CB(skb).dst_group = RTNLGRP_LINK; |
1366 | skb_queue_tail(&wireless_nlevent_queue, skb); | 1363 | skb_queue_tail(&dev_net(dev)->wext_nlevents, skb); |
1367 | tasklet_schedule(&wireless_nlevent_tasklet); | 1364 | schedule_work(&wireless_nlevent_work); |
1368 | } | 1365 | } |
1369 | 1366 | ||
1370 | /* ---------------------------------------------------------------- */ | 1367 | /* ---------------------------------------------------------------- */ |