aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--include/net/mac80211.h35
-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
7 files changed, 79 insertions, 88 deletions
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index bbae3d9b1176..3a47877f4965 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -19,7 +19,6 @@
19#include <linux/wireless.h> 19#include <linux/wireless.h>
20#include <linux/device.h> 20#include <linux/device.h>
21#include <linux/ieee80211.h> 21#include <linux/ieee80211.h>
22#include <linux/inetdevice.h>
23#include <net/cfg80211.h> 22#include <net/cfg80211.h>
24 23
25/** 24/**
@@ -147,6 +146,7 @@ struct ieee80211_low_level_stats {
147 * enabled/disabled (beaconing modes) 146 * enabled/disabled (beaconing modes)
148 * @BSS_CHANGED_CQM: Connection quality monitor config changed 147 * @BSS_CHANGED_CQM: Connection quality monitor config changed
149 * @BSS_CHANGED_IBSS: IBSS join status changed 148 * @BSS_CHANGED_IBSS: IBSS join status changed
149 * @BSS_CHANGED_ARP_FILTER: Hardware ARP filter address list or state changed.
150 */ 150 */
151enum ieee80211_bss_change { 151enum ieee80211_bss_change {
152 BSS_CHANGED_ASSOC = 1<<0, 152 BSS_CHANGED_ASSOC = 1<<0,
@@ -161,10 +161,18 @@ enum ieee80211_bss_change {
161 BSS_CHANGED_BEACON_ENABLED = 1<<9, 161 BSS_CHANGED_BEACON_ENABLED = 1<<9,
162 BSS_CHANGED_CQM = 1<<10, 162 BSS_CHANGED_CQM = 1<<10,
163 BSS_CHANGED_IBSS = 1<<11, 163 BSS_CHANGED_IBSS = 1<<11,
164 BSS_CHANGED_ARP_FILTER = 1<<12,
164 165
165 /* when adding here, make sure to change ieee80211_reconfig */ 166 /* when adding here, make sure to change ieee80211_reconfig */
166}; 167};
167 168
169/*
170 * The maximum number of IPv4 addresses listed for ARP filtering. If the number
171 * of addresses for an interface increase beyond this value, hardware ARP
172 * filtering will be disabled.
173 */
174#define IEEE80211_BSS_ARP_ADDR_LIST_LEN 4
175
168/** 176/**
169 * struct ieee80211_bss_conf - holds the BSS's changing parameters 177 * struct ieee80211_bss_conf - holds the BSS's changing parameters
170 * 178 *
@@ -200,6 +208,15 @@ enum ieee80211_bss_change {
200 * @cqm_rssi_thold: Connection quality monitor RSSI threshold, a zero value 208 * @cqm_rssi_thold: Connection quality monitor RSSI threshold, a zero value
201 * implies disabled 209 * implies disabled
202 * @cqm_rssi_hyst: Connection quality monitor RSSI hysteresis 210 * @cqm_rssi_hyst: Connection quality monitor RSSI hysteresis
211 * @arp_addr_list: List of IPv4 addresses for hardware ARP filtering. The
212 * may filter ARP queries targeted for other addresses than listed here.
213 * The driver must allow ARP queries targeted for all address listed here
214 * to pass through. An empty list implies no ARP queries need to pass.
215 * @arp_addr_cnt: Number of addresses currently on the list.
216 * @arp_filter_enabled: Enable ARP filtering - if enabled, the hardware may
217 * filter ARP queries based on the @arp_addr_list, if disabled, the
218 * hardware must not perform any ARP filtering. Note, that the filter will
219 * be enabled also in promiscuous mode.
203 */ 220 */
204struct ieee80211_bss_conf { 221struct ieee80211_bss_conf {
205 const u8 *bssid; 222 const u8 *bssid;
@@ -220,6 +237,9 @@ struct ieee80211_bss_conf {
220 s32 cqm_rssi_thold; 237 s32 cqm_rssi_thold;
221 u32 cqm_rssi_hyst; 238 u32 cqm_rssi_hyst;
222 enum nl80211_channel_type channel_type; 239 enum nl80211_channel_type channel_type;
240 __be32 arp_addr_list[IEEE80211_BSS_ARP_ADDR_LIST_LEN];
241 u8 arp_addr_cnt;
242 bool arp_filter_enabled;
223}; 243};
224 244
225/** 245/**
@@ -1529,16 +1549,6 @@ enum ieee80211_ampdu_mlme_action {
1529 * of the bss parameters has changed when a call is made. The callback 1549 * of the bss parameters has changed when a call is made. The callback
1530 * can sleep. 1550 * can sleep.
1531 * 1551 *
1532 * @configure_arp_filter: Configuration function for hardware ARP query filter.
1533 * This function is called with all the IP addresses configured to the
1534 * interface as argument - all ARP queries targeted to any of these
1535 * addresses must pass through. If the hardware filter does not support
1536 * enought addresses, hardware filtering must be disabled. The ifa_list
1537 * argument may be NULL, indicating that filtering must be disabled.
1538 * This function is called upon association complete with current
1539 * address(es), and while associated whenever the IP address(es) change.
1540 * The callback can sleep.
1541 *
1542 * @prepare_multicast: Prepare for multicast filter configuration. 1552 * @prepare_multicast: Prepare for multicast filter configuration.
1543 * This callback is optional, and its return value is passed 1553 * This callback is optional, and its return value is passed
1544 * to configure_filter(). This callback must be atomic. 1554 * to configure_filter(). This callback must be atomic.
@@ -1678,9 +1688,6 @@ struct ieee80211_ops {
1678 struct ieee80211_vif *vif, 1688 struct ieee80211_vif *vif,
1679 struct ieee80211_bss_conf *info, 1689 struct ieee80211_bss_conf *info,
1680 u32 changed); 1690 u32 changed);
1681 int (*configure_arp_filter)(struct ieee80211_hw *hw,
1682 struct ieee80211_vif *vif,
1683 struct in_ifaddr *ifa_list);
1684 u64 (*prepare_multicast)(struct ieee80211_hw *hw, 1691 u64 (*prepare_multicast)(struct ieee80211_hw *hw,
1685 struct netdev_hw_addr_list *mc_list); 1692 struct netdev_hw_addr_list *mc_list);
1686 void (*configure_filter)(struct ieee80211_hw *hw, 1693 void (*configure_filter)(struct ieee80211_hw *hw,
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);