aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJuuso Oikarinen <juuso.oikarinen@nokia.com>2010-06-09 06:43:26 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-06-14 15:42:31 -0400
commit685429623f88d84f98bd5daffc3c427c408740d4 (patch)
treed50f119094dd3fb7668f837dbe50857d11bb4fa3 /net
parent5ea096c0c85e80335889539899af9a4717976e0b (diff)
mac80211: Fix circular locking dependency in ARP filter handling
There is a circular locking dependency when configuring the hardware ARP filters on association, occurring when flushing the mac80211 workqueue. This is what happens: [ 92.026800] ======================================================= [ 92.030507] [ INFO: possible circular locking dependency detected ] [ 92.030507] 2.6.34-04781-g2b2c009 #85 [ 92.030507] ------------------------------------------------------- [ 92.030507] modprobe/5225 is trying to acquire lock: [ 92.030507] ((wiphy_name(local->hw.wiphy))){+.+.+.}, at: [<ffffffff8105b5c0>] flush_workq ueue+0x0/0xb0 [ 92.030507] [ 92.030507] but task is already holding lock: [ 92.030507] (rtnl_mutex){+.+.+.}, at: [<ffffffff812b9ce2>] rtnl_lock+0x12/0x20 [ 92.030507] [ 92.030507] which lock already depends on the new lock. [ 92.030507] [ 92.030507] [ 92.030507] the existing dependency chain (in reverse order) is: [ 92.030507] [ 92.030507] -> #2 (rtnl_mutex){+.+.+.}: [ 92.030507] [<ffffffff810761fb>] lock_acquire+0xdb/0x110 [ 92.030507] [<ffffffff81341754>] mutex_lock_nested+0x44/0x300 [ 92.030507] [<ffffffff812b9ce2>] rtnl_lock+0x12/0x20 [ 92.030507] [<ffffffffa022d47c>] ieee80211_assoc_done+0x6c/0xe0 [mac80211] [ 92.030507] [<ffffffffa022f2ad>] ieee80211_work_work+0x31d/0x1280 [mac80211] [ 92.030507] -> #1 ((&local->work_work)){+.+.+.}: [ 92.030507] [<ffffffff810761fb>] lock_acquire+0xdb/0x110 [ 92.030507] [<ffffffff8105a51a>] worker_thread+0x22a/0x370 [ 92.030507] [<ffffffff8105ecc6>] kthread+0x96/0xb0 [ 92.030507] [<ffffffff81003a94>] kernel_thread_helper+0x4/0x10 [ 92.030507] [ 92.030507] -> #0 ((wiphy_name(local->hw.wiphy))){+.+.+.}: [ 92.030507] [<ffffffff81075fdc>] __lock_acquire+0x1c0c/0x1d50 [ 92.030507] [<ffffffff810761fb>] lock_acquire+0xdb/0x110 [ 92.030507] [<ffffffff8105b60e>] flush_workqueue+0x4e/0xb0 [ 92.030507] [<ffffffffa023ff7b>] ieee80211_stop_device+0x2b/0xb0 [mac80211] [ 92.030507] [<ffffffffa0231635>] ieee80211_stop+0x3e5/0x680 [mac80211] The locking in this case is quite complex. Fix the problem by rewriting the way the hardware ARP filter list is handled - i.e. make a copy of the address list to the bss_conf struct, and provide that list to the hardware driver when needed. The current patch will enable filtering also in promiscuous mode. This may need to be changed in the future. Reported-by: Reinette Chatre <reinette.chatre@intel.com> Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/driver-ops.h17
-rw-r--r--net/mac80211/driver-trace.h22
-rw-r--r--net/mac80211/ieee80211_i.h2
-rw-r--r--net/mac80211/iface.c3
-rw-r--r--net/mac80211/main.c54
-rw-r--r--net/mac80211/mlme.c34
6 files changed, 58 insertions, 74 deletions
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 965d64f68567..c33317320eee 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -89,23 +89,6 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local,
89 trace_drv_return_void(local); 89 trace_drv_return_void(local);
90} 90}
91 91
92struct in_ifaddr;
93static inline int drv_configure_arp_filter(struct ieee80211_local *local,
94 struct ieee80211_vif *vif,
95 struct in_ifaddr *ifa_list)
96{
97 int ret = 0;
98
99 might_sleep();
100
101 trace_drv_configure_arp_filter(local, vif_to_sdata(vif));
102 if (local->ops->configure_arp_filter)
103 ret = local->ops->configure_arp_filter(&local->hw, vif,
104 ifa_list);
105 trace_drv_return_int(local, ret);
106 return ret;
107}
108
109static inline u64 drv_prepare_multicast(struct ieee80211_local *local, 92static inline u64 drv_prepare_multicast(struct ieee80211_local *local,
110 struct netdev_hw_addr_list *mc_list) 93 struct netdev_hw_addr_list *mc_list)
111{ 94{
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h
index 06444ea67bc4..8da31caff931 100644
--- a/net/mac80211/driver-trace.h
+++ b/net/mac80211/driver-trace.h
@@ -251,28 +251,6 @@ TRACE_EVENT(drv_bss_info_changed,
251 ) 251 )
252); 252);
253 253
254TRACE_EVENT(drv_configure_arp_filter,
255 TP_PROTO(struct ieee80211_local *local,
256 struct ieee80211_sub_if_data *sdata),
257
258 TP_ARGS(local, sdata),
259
260 TP_STRUCT__entry(
261 LOCAL_ENTRY
262 VIF_ENTRY
263 ),
264
265 TP_fast_assign(
266 LOCAL_ASSIGN;
267 VIF_ASSIGN;
268 ),
269
270 TP_printk(
271 VIF_PR_FMT LOCAL_PR_FMT,
272 VIF_PR_ARG, LOCAL_PR_ARG
273 )
274);
275
276TRACE_EVENT(drv_prepare_multicast, 254TRACE_EVENT(drv_prepare_multicast,
277 TP_PROTO(struct ieee80211_local *local, int mc_count), 255 TP_PROTO(struct ieee80211_local *local, int mc_count),
278 256
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index c3c2be3f8a2c..9b3c3f971d28 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -514,6 +514,8 @@ struct ieee80211_sub_if_data {
514 struct work_struct work; 514 struct work_struct work;
515 struct sk_buff_head skb_queue; 515 struct sk_buff_head skb_queue;
516 516
517 bool arp_filter_state;
518
517 /* 519 /*
518 * AP this belongs to: self in AP mode and 520 * AP this belongs to: self in AP mode and
519 * corresponding AP in VLAN mode, NULL for 521 * corresponding AP in VLAN mode, NULL for
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 490be2f3af27..910729fc18cd 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1076,6 +1076,9 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
1076 sdata->wdev.wiphy = local->hw.wiphy; 1076 sdata->wdev.wiphy = local->hw.wiphy;
1077 sdata->local = local; 1077 sdata->local = local;
1078 sdata->dev = ndev; 1078 sdata->dev = ndev;
1079#ifdef CONFIG_INET
1080 sdata->arp_filter_state = true;
1081#endif
1079 1082
1080 for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) 1083 for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
1081 skb_queue_head_init(&sdata->fragments[i].skb_list); 1084 skb_queue_head_init(&sdata->fragments[i].skb_list);
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index c2e46e88f3c9..a1bf46c64b93 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -20,6 +20,7 @@
20#include <linux/rtnetlink.h> 20#include <linux/rtnetlink.h>
21#include <linux/bitmap.h> 21#include <linux/bitmap.h>
22#include <linux/pm_qos_params.h> 22#include <linux/pm_qos_params.h>
23#include <linux/inetdevice.h>
23#include <net/net_namespace.h> 24#include <net/net_namespace.h>
24#include <net/cfg80211.h> 25#include <net/cfg80211.h>
25 26
@@ -317,23 +318,6 @@ static void ieee80211_recalc_smps_work(struct work_struct *work)
317} 318}
318 319
319#ifdef CONFIG_INET 320#ifdef CONFIG_INET
320int ieee80211_set_arp_filter(struct ieee80211_sub_if_data *sdata)
321{
322 struct in_device *idev;
323 int ret = 0;
324
325 BUG_ON(!sdata);
326 ASSERT_RTNL();
327
328 idev = sdata->dev->ip_ptr;
329 if (!idev)
330 return 0;
331
332 ret = drv_configure_arp_filter(sdata->local, &sdata->vif,
333 idev->ifa_list);
334 return ret;
335}
336
337static int ieee80211_ifa_changed(struct notifier_block *nb, 321static int ieee80211_ifa_changed(struct notifier_block *nb,
338 unsigned long data, void *arg) 322 unsigned long data, void *arg)
339{ 323{
@@ -343,8 +327,11 @@ static int ieee80211_ifa_changed(struct notifier_block *nb,
343 ifa_notifier); 327 ifa_notifier);
344 struct net_device *ndev = ifa->ifa_dev->dev; 328 struct net_device *ndev = ifa->ifa_dev->dev;
345 struct wireless_dev *wdev = ndev->ieee80211_ptr; 329 struct wireless_dev *wdev = ndev->ieee80211_ptr;
330 struct in_device *idev;
346 struct ieee80211_sub_if_data *sdata; 331 struct ieee80211_sub_if_data *sdata;
332 struct ieee80211_bss_conf *bss_conf;
347 struct ieee80211_if_managed *ifmgd; 333 struct ieee80211_if_managed *ifmgd;
334 int c = 0;
348 335
349 if (!netif_running(ndev)) 336 if (!netif_running(ndev))
350 return NOTIFY_DONE; 337 return NOTIFY_DONE;
@@ -356,17 +343,44 @@ static int ieee80211_ifa_changed(struct notifier_block *nb,
356 if (wdev->wiphy != local->hw.wiphy) 343 if (wdev->wiphy != local->hw.wiphy)
357 return NOTIFY_DONE; 344 return NOTIFY_DONE;
358 345
359 /* We are concerned about IP addresses only when associated */
360 sdata = IEEE80211_DEV_TO_SUB_IF(ndev); 346 sdata = IEEE80211_DEV_TO_SUB_IF(ndev);
347 bss_conf = &sdata->vif.bss_conf;
361 348
362 /* ARP filtering is only supported in managed mode */ 349 /* ARP filtering is only supported in managed mode */
363 if (sdata->vif.type != NL80211_IFTYPE_STATION) 350 if (sdata->vif.type != NL80211_IFTYPE_STATION)
364 return NOTIFY_DONE; 351 return NOTIFY_DONE;
365 352
353 idev = sdata->dev->ip_ptr;
354 if (!idev)
355 return NOTIFY_DONE;
356
366 ifmgd = &sdata->u.mgd; 357 ifmgd = &sdata->u.mgd;
367 mutex_lock(&ifmgd->mtx); 358 mutex_lock(&ifmgd->mtx);
368 if (ifmgd->associated) 359
369 ieee80211_set_arp_filter(sdata); 360 /* Copy the addresses to the bss_conf list */
361 ifa = idev->ifa_list;
362 while (c < IEEE80211_BSS_ARP_ADDR_LIST_LEN && ifa) {
363 bss_conf->arp_addr_list[c] = ifa->ifa_address;
364 ifa = ifa->ifa_next;
365 c++;
366 }
367
368 /* If not all addresses fit the list, disable filtering */
369 if (ifa) {
370 sdata->arp_filter_state = false;
371 c = 0;
372 } else {
373 sdata->arp_filter_state = true;
374 }
375 bss_conf->arp_addr_cnt = c;
376
377 /* Configure driver only if associated */
378 if (ifmgd->associated) {
379 bss_conf->arp_filter_enabled = sdata->arp_filter_state;
380 ieee80211_bss_info_change_notify(sdata,
381 BSS_CHANGED_ARP_FILTER);
382 }
383
370 mutex_unlock(&ifmgd->mtx); 384 mutex_unlock(&ifmgd->mtx);
371 385
372 return NOTIFY_DONE; 386 return NOTIFY_DONE;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 583b34686a26..74479c2d12d4 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -806,11 +806,12 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
806{ 806{
807 struct ieee80211_bss *bss = (void *)cbss->priv; 807 struct ieee80211_bss *bss = (void *)cbss->priv;
808 struct ieee80211_local *local = sdata->local; 808 struct ieee80211_local *local = sdata->local;
809 struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
809 810
810 bss_info_changed |= BSS_CHANGED_ASSOC; 811 bss_info_changed |= BSS_CHANGED_ASSOC;
811 /* set timing information */ 812 /* set timing information */
812 sdata->vif.bss_conf.beacon_int = cbss->beacon_interval; 813 bss_conf->beacon_int = cbss->beacon_interval;
813 sdata->vif.bss_conf.timestamp = cbss->tsf; 814 bss_conf->timestamp = cbss->tsf;
814 815
815 bss_info_changed |= BSS_CHANGED_BEACON_INT; 816 bss_info_changed |= BSS_CHANGED_BEACON_INT;
816 bss_info_changed |= ieee80211_handle_bss_capability(sdata, 817 bss_info_changed |= ieee80211_handle_bss_capability(sdata,
@@ -835,7 +836,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
835 836
836 ieee80211_led_assoc(local, 1); 837 ieee80211_led_assoc(local, 1);
837 838
838 sdata->vif.bss_conf.assoc = 1; 839 bss_conf->assoc = 1;
839 /* 840 /*
840 * For now just always ask the driver to update the basic rateset 841 * For now just always ask the driver to update the basic rateset
841 * when we have associated, we aren't checking whether it actually 842 * when we have associated, we aren't checking whether it actually
@@ -848,9 +849,15 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
848 849
849 /* Tell the driver to monitor connection quality (if supported) */ 850 /* Tell the driver to monitor connection quality (if supported) */
850 if ((local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI) && 851 if ((local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI) &&
851 sdata->vif.bss_conf.cqm_rssi_thold) 852 bss_conf->cqm_rssi_thold)
852 bss_info_changed |= BSS_CHANGED_CQM; 853 bss_info_changed |= BSS_CHANGED_CQM;
853 854
855 /* Enable ARP filtering */
856 if (bss_conf->arp_filter_enabled != sdata->arp_filter_state) {
857 bss_conf->arp_filter_enabled = sdata->arp_filter_state;
858 bss_info_changed |= BSS_CHANGED_ARP_FILTER;
859 }
860
854 ieee80211_bss_info_change_notify(sdata, bss_info_changed); 861 ieee80211_bss_info_change_notify(sdata, bss_info_changed);
855 862
856 mutex_lock(&local->iflist_mtx); 863 mutex_lock(&local->iflist_mtx);
@@ -932,6 +939,12 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
932 939
933 ieee80211_hw_config(local, config_changed); 940 ieee80211_hw_config(local, config_changed);
934 941
942 /* Disable ARP filtering */
943 if (sdata->vif.bss_conf.arp_filter_enabled) {
944 sdata->vif.bss_conf.arp_filter_enabled = false;
945 changed |= BSS_CHANGED_ARP_FILTER;
946 }
947
935 /* The BSSID (not really interesting) and HT changed */ 948 /* The BSSID (not really interesting) and HT changed */
936 changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT; 949 changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT;
937 ieee80211_bss_info_change_notify(sdata, changed); 950 ieee80211_bss_info_change_notify(sdata, changed);
@@ -2018,18 +2031,9 @@ static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk,
2018 cfg80211_send_assoc_timeout(wk->sdata->dev, 2031 cfg80211_send_assoc_timeout(wk->sdata->dev,
2019 wk->filter_ta); 2032 wk->filter_ta);
2020 return WORK_DONE_DESTROY; 2033 return WORK_DONE_DESTROY;
2021 } else {
2022 mutex_unlock(&wk->sdata->u.mgd.mtx);
2023#ifdef CONFIG_INET
2024 /*
2025 * configure ARP filter IP addresses to the driver,
2026 * intentionally outside the mgd mutex.
2027 */
2028 rtnl_lock();
2029 ieee80211_set_arp_filter(wk->sdata);
2030 rtnl_unlock();
2031#endif
2032 } 2034 }
2035
2036 mutex_unlock(&wk->sdata->u.mgd.mtx);
2033 } 2037 }
2034 2038
2035 cfg80211_send_rx_assoc(wk->sdata->dev, skb->data, skb->len); 2039 cfg80211_send_rx_assoc(wk->sdata->dev, skb->data, skb->len);