aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorMichael Wu <flamingice@sourmilk.net>2007-07-27 09:43:23 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2007-10-10 19:47:37 -0400
commitbe8755e1804d6f60e6a96a46ac6bc46ce6dfca53 (patch)
treea3038a924114f49aa2372ab7c9417d4e072c4b20 /net
parentc2d1560ad8c2f6e0dd0d34102d022f3709325c26 (diff)
[MAC80211]: improve locking of sta_info related structures
The sta_info code has some awkward locking which prevents some driver callbacks from being allowed to sleep. This patch makes the locking more focused so code that calls driver callbacks are allowed to sleep. It also converts sta_lock to a rwlock. Signed-off-by: Michael Wu <flamingice@sourmilk.net> Signed-off-by: Jiri Benc <jbenc@suse.cz> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/ieee80211.c6
-rw-r--r--net/mac80211/ieee80211_i.h11
-rw-r--r--net/mac80211/ieee80211_iface.c2
-rw-r--r--net/mac80211/ieee80211_sta.c20
-rw-r--r--net/mac80211/sta_info.c184
-rw-r--r--net/mac80211/sta_info.h11
-rw-r--r--net/mac80211/tx.c8
7 files changed, 108 insertions, 134 deletions
diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c
index 1981058907b7..566bdca32b86 100644
--- a/net/mac80211/ieee80211.c
+++ b/net/mac80211/ieee80211.c
@@ -628,8 +628,8 @@ int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr)
628 /* Remove STA entry for the old peer */ 628 /* Remove STA entry for the old peer */
629 sta = sta_info_get(local, sdata->u.wds.remote_addr); 629 sta = sta_info_get(local, sdata->u.wds.remote_addr);
630 if (sta) { 630 if (sta) {
631 sta_info_free(sta);
631 sta_info_put(sta); 632 sta_info_put(sta);
632 sta_info_free(sta, 0);
633 } else { 633 } else {
634 printk(KERN_DEBUG "%s: could not find STA entry for WDS link " 634 printk(KERN_DEBUG "%s: could not find STA entry for WDS link "
635 "peer " MAC_FMT "\n", 635 "peer " MAC_FMT "\n",
@@ -776,13 +776,13 @@ static void ieee80211_stat_refresh(unsigned long data)
776 return; 776 return;
777 777
778 /* go through all stations */ 778 /* go through all stations */
779 spin_lock_bh(&local->sta_lock); 779 read_lock_bh(&local->sta_lock);
780 list_for_each_entry(sta, &local->sta_list, list) { 780 list_for_each_entry(sta, &local->sta_list, list) {
781 sta->channel_use = (sta->channel_use_raw / local->stat_time) / 781 sta->channel_use = (sta->channel_use_raw / local->stat_time) /
782 CHAN_UTIL_PER_10MS; 782 CHAN_UTIL_PER_10MS;
783 sta->channel_use_raw = 0; 783 sta->channel_use_raw = 0;
784 } 784 }
785 spin_unlock_bh(&local->sta_lock); 785 read_unlock_bh(&local->sta_lock);
786 786
787 /* go through all subinterfaces */ 787 /* go through all subinterfaces */
788 read_lock(&local->sub_if_lock); 788 read_lock(&local->sub_if_lock);
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index ed00552b3418..e76a58678959 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -417,10 +417,9 @@ struct ieee80211_local {
417 struct sk_buff_head skb_queue_unreliable; 417 struct sk_buff_head skb_queue_unreliable;
418 418
419 /* Station data structures */ 419 /* Station data structures */
420 spinlock_t sta_lock; /* mutex for STA data structures */ 420 rwlock_t sta_lock; /* protects STA data structures */
421 int num_sta; /* number of stations in sta_list */ 421 int num_sta; /* number of stations in sta_list */
422 struct list_head sta_list; 422 struct list_head sta_list;
423 struct list_head deleted_sta_list;
424 struct sta_info *sta_hash[STA_HASH_SIZE]; 423 struct sta_info *sta_hash[STA_HASH_SIZE];
425 struct timer_list sta_cleanup; 424 struct timer_list sta_cleanup;
426 425
@@ -669,9 +668,9 @@ static inline void __bss_tim_set(struct ieee80211_if_ap *bss, int aid)
669static inline void bss_tim_set(struct ieee80211_local *local, 668static inline void bss_tim_set(struct ieee80211_local *local,
670 struct ieee80211_if_ap *bss, int aid) 669 struct ieee80211_if_ap *bss, int aid)
671{ 670{
672 spin_lock_bh(&local->sta_lock); 671 read_lock_bh(&local->sta_lock);
673 __bss_tim_set(bss, aid); 672 __bss_tim_set(bss, aid);
674 spin_unlock_bh(&local->sta_lock); 673 read_unlock_bh(&local->sta_lock);
675} 674}
676 675
677static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, int aid) 676static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, int aid)
@@ -686,9 +685,9 @@ static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, int aid)
686static inline void bss_tim_clear(struct ieee80211_local *local, 685static inline void bss_tim_clear(struct ieee80211_local *local,
687 struct ieee80211_if_ap *bss, int aid) 686 struct ieee80211_if_ap *bss, int aid)
688{ 687{
689 spin_lock_bh(&local->sta_lock); 688 read_lock_bh(&local->sta_lock);
690 __bss_tim_clear(bss, aid); 689 __bss_tim_clear(bss, aid);
691 spin_unlock_bh(&local->sta_lock); 690 read_unlock_bh(&local->sta_lock);
692} 691}
693 692
694/** 693/**
diff --git a/net/mac80211/ieee80211_iface.c b/net/mac80211/ieee80211_iface.c
index 8532a5ccdd1e..6db67767801d 100644
--- a/net/mac80211/ieee80211_iface.c
+++ b/net/mac80211/ieee80211_iface.c
@@ -272,8 +272,8 @@ void ieee80211_if_reinit(struct net_device *dev)
272 case IEEE80211_IF_TYPE_WDS: 272 case IEEE80211_IF_TYPE_WDS:
273 sta = sta_info_get(local, sdata->u.wds.remote_addr); 273 sta = sta_info_get(local, sdata->u.wds.remote_addr);
274 if (sta) { 274 if (sta) {
275 sta_info_free(sta);
275 sta_info_put(sta); 276 sta_info_put(sta);
276 sta_info_free(sta, 0);
277 } else { 277 } else {
278#ifdef CONFIG_MAC80211_VERBOSE_DEBUG 278#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
279 printk(KERN_DEBUG "%s: Someone had deleted my STA " 279 printk(KERN_DEBUG "%s: Someone had deleted my STA "
diff --git a/net/mac80211/ieee80211_sta.c b/net/mac80211/ieee80211_sta.c
index 0d99b685df5f..9aee1abae127 100644
--- a/net/mac80211/ieee80211_sta.c
+++ b/net/mac80211/ieee80211_sta.c
@@ -773,7 +773,7 @@ static void ieee80211_associated(struct net_device *dev,
773 "range\n", 773 "range\n",
774 dev->name, MAC_ARG(ifsta->bssid)); 774 dev->name, MAC_ARG(ifsta->bssid));
775 disassoc = 1; 775 disassoc = 1;
776 sta_info_free(sta, 0); 776 sta_info_free(sta);
777 ifsta->probereq_poll = 0; 777 ifsta->probereq_poll = 0;
778 } else { 778 } else {
779 ieee80211_send_probe_req(dev, ifsta->bssid, 779 ieee80211_send_probe_req(dev, ifsta->bssid,
@@ -1890,7 +1890,7 @@ static int ieee80211_sta_active_ibss(struct net_device *dev)
1890 int active = 0; 1890 int active = 0;
1891 struct sta_info *sta; 1891 struct sta_info *sta;
1892 1892
1893 spin_lock_bh(&local->sta_lock); 1893 read_lock_bh(&local->sta_lock);
1894 list_for_each_entry(sta, &local->sta_list, list) { 1894 list_for_each_entry(sta, &local->sta_list, list) {
1895 if (sta->dev == dev && 1895 if (sta->dev == dev &&
1896 time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL, 1896 time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL,
@@ -1899,7 +1899,7 @@ static int ieee80211_sta_active_ibss(struct net_device *dev)
1899 break; 1899 break;
1900 } 1900 }
1901 } 1901 }
1902 spin_unlock_bh(&local->sta_lock); 1902 read_unlock_bh(&local->sta_lock);
1903 1903
1904 return active; 1904 return active;
1905} 1905}
@@ -1909,16 +1909,24 @@ static void ieee80211_sta_expire(struct net_device *dev)
1909{ 1909{
1910 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); 1910 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
1911 struct sta_info *sta, *tmp; 1911 struct sta_info *sta, *tmp;
1912 LIST_HEAD(tmp_list);
1912 1913
1913 spin_lock_bh(&local->sta_lock); 1914 write_lock_bh(&local->sta_lock);
1914 list_for_each_entry_safe(sta, tmp, &local->sta_list, list) 1915 list_for_each_entry_safe(sta, tmp, &local->sta_list, list)
1915 if (time_after(jiffies, sta->last_rx + 1916 if (time_after(jiffies, sta->last_rx +
1916 IEEE80211_IBSS_INACTIVITY_LIMIT)) { 1917 IEEE80211_IBSS_INACTIVITY_LIMIT)) {
1917 printk(KERN_DEBUG "%s: expiring inactive STA " MAC_FMT 1918 printk(KERN_DEBUG "%s: expiring inactive STA " MAC_FMT
1918 "\n", dev->name, MAC_ARG(sta->addr)); 1919 "\n", dev->name, MAC_ARG(sta->addr));
1919 sta_info_free(sta, 1); 1920 __sta_info_get(sta);
1921 sta_info_remove(sta);
1922 list_add(&sta->list, &tmp_list);
1920 } 1923 }
1921 spin_unlock_bh(&local->sta_lock); 1924 write_unlock_bh(&local->sta_lock);
1925
1926 list_for_each_entry_safe(sta, tmp, &tmp_list, list) {
1927 sta_info_free(sta);
1928 sta_info_put(sta);
1929 }
1922} 1930}
1923 1931
1924 1932
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index ab7b1f067c6e..34245b882c2b 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -32,38 +32,34 @@ static void sta_info_hash_add(struct ieee80211_local *local,
32 32
33 33
34/* Caller must hold local->sta_lock */ 34/* Caller must hold local->sta_lock */
35static void sta_info_hash_del(struct ieee80211_local *local, 35static int sta_info_hash_del(struct ieee80211_local *local,
36 struct sta_info *sta) 36 struct sta_info *sta)
37{ 37{
38 struct sta_info *s; 38 struct sta_info *s;
39 39
40 s = local->sta_hash[STA_HASH(sta->addr)]; 40 s = local->sta_hash[STA_HASH(sta->addr)];
41 if (!s) 41 if (!s)
42 return; 42 return -ENOENT;
43 if (memcmp(s->addr, sta->addr, ETH_ALEN) == 0) { 43 if (s == sta) {
44 local->sta_hash[STA_HASH(sta->addr)] = s->hnext; 44 local->sta_hash[STA_HASH(sta->addr)] = s->hnext;
45 return; 45 return 0;
46 } 46 }
47 47
48 while (s->hnext && memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0) 48 while (s->hnext && s->hnext != sta)
49 s = s->hnext; 49 s = s->hnext;
50 if (s->hnext) 50 if (s->hnext) {
51 s->hnext = s->hnext->hnext; 51 s->hnext = sta->hnext;
52 else 52 return 0;
53 printk(KERN_ERR "%s: could not remove STA " MAC_FMT " from " 53 }
54 "hash table\n", local->mdev->name, MAC_ARG(sta->addr));
55}
56 54
57static inline void __sta_info_get(struct sta_info *sta) 55 return -ENOENT;
58{
59 kref_get(&sta->kref);
60} 56}
61 57
62struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr) 58struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr)
63{ 59{
64 struct sta_info *sta; 60 struct sta_info *sta;
65 61
66 spin_lock_bh(&local->sta_lock); 62 read_lock_bh(&local->sta_lock);
67 sta = local->sta_hash[STA_HASH(addr)]; 63 sta = local->sta_hash[STA_HASH(addr)];
68 while (sta) { 64 while (sta) {
69 if (memcmp(sta->addr, addr, ETH_ALEN) == 0) { 65 if (memcmp(sta->addr, addr, ETH_ALEN) == 0) {
@@ -72,7 +68,7 @@ struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr)
72 } 68 }
73 sta = sta->hnext; 69 sta = sta->hnext;
74 } 70 }
75 spin_unlock_bh(&local->sta_lock); 71 read_unlock_bh(&local->sta_lock);
76 72
77 return sta; 73 return sta;
78} 74}
@@ -85,7 +81,7 @@ int sta_info_min_txrate_get(struct ieee80211_local *local)
85 int min_txrate = 9999999; 81 int min_txrate = 9999999;
86 int i; 82 int i;
87 83
88 spin_lock_bh(&local->sta_lock); 84 read_lock_bh(&local->sta_lock);
89 mode = local->oper_hw_mode; 85 mode = local->oper_hw_mode;
90 for (i = 0; i < STA_HASH_SIZE; i++) { 86 for (i = 0; i < STA_HASH_SIZE; i++) {
91 sta = local->sta_hash[i]; 87 sta = local->sta_hash[i];
@@ -95,7 +91,7 @@ int sta_info_min_txrate_get(struct ieee80211_local *local)
95 sta = sta->hnext; 91 sta = sta->hnext;
96 } 92 }
97 } 93 }
98 spin_unlock_bh(&local->sta_lock); 94 read_unlock_bh(&local->sta_lock);
99 if (min_txrate == 9999999) 95 if (min_txrate == 9999999)
100 min_txrate = 0; 96 min_txrate = 0;
101 97
@@ -150,7 +146,6 @@ struct sta_info * sta_info_add(struct ieee80211_local *local,
150 sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl, gfp); 146 sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl, gfp);
151 if (!sta->rate_ctrl_priv) { 147 if (!sta->rate_ctrl_priv) {
152 rate_control_put(sta->rate_ctrl); 148 rate_control_put(sta->rate_ctrl);
153 kref_put(&sta->kref, sta_info_release);
154 kfree(sta); 149 kfree(sta);
155 return NULL; 150 return NULL;
156 } 151 }
@@ -162,14 +157,14 @@ struct sta_info * sta_info_add(struct ieee80211_local *local,
162 skb_queue_head_init(&sta->tx_filtered); 157 skb_queue_head_init(&sta->tx_filtered);
163 __sta_info_get(sta); /* sta used by caller, decremented by 158 __sta_info_get(sta); /* sta used by caller, decremented by
164 * sta_info_put() */ 159 * sta_info_put() */
165 spin_lock_bh(&local->sta_lock); 160 write_lock_bh(&local->sta_lock);
166 list_add(&sta->list, &local->sta_list); 161 list_add(&sta->list, &local->sta_list);
167 local->num_sta++; 162 local->num_sta++;
168 sta_info_hash_add(local, sta); 163 sta_info_hash_add(local, sta);
169 spin_unlock_bh(&local->sta_lock);
170 if (local->ops->sta_table_notification) 164 if (local->ops->sta_table_notification)
171 local->ops->sta_table_notification(local_to_hw(local), 165 local->ops->sta_table_notification(local_to_hw(local),
172 local->num_sta); 166 local->num_sta);
167 write_unlock_bh(&local->sta_lock);
173 sta->key_idx_compression = HW_KEY_IDX_INVALID; 168 sta->key_idx_compression = HW_KEY_IDX_INVALID;
174 169
175#ifdef CONFIG_MAC80211_VERBOSE_DEBUG 170#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
@@ -178,47 +173,25 @@ struct sta_info * sta_info_add(struct ieee80211_local *local,
178#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ 173#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
179 174
180#ifdef CONFIG_MAC80211_DEBUGFS 175#ifdef CONFIG_MAC80211_DEBUGFS
181 if (!in_interrupt()) { 176 /* debugfs entry adding might sleep, so schedule process
182 sta->debugfs_registered = 1; 177 * context task for adding entry for STAs that do not yet
183 ieee80211_sta_debugfs_add(sta); 178 * have one. */
184 rate_control_add_sta_debugfs(sta); 179 queue_work(local->hw.workqueue, &local->sta_debugfs_add);
185 } else {
186 /* debugfs entry adding might sleep, so schedule process
187 * context task for adding entry for STAs that do not yet
188 * have one. */
189 queue_work(local->hw.workqueue, &local->sta_debugfs_add);
190 }
191#endif 180#endif
192 181
193 return sta; 182 return sta;
194} 183}
195 184
196static void finish_sta_info_free(struct ieee80211_local *local, 185/* Caller must hold local->sta_lock */
197 struct sta_info *sta) 186void sta_info_remove(struct sta_info *sta)
198{
199#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
200 printk(KERN_DEBUG "%s: Removed STA " MAC_FMT "\n",
201 local->mdev->name, MAC_ARG(sta->addr));
202#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
203
204 if (sta->key) {
205 ieee80211_debugfs_key_remove(sta->key);
206 ieee80211_key_free(sta->key);
207 sta->key = NULL;
208 }
209
210 rate_control_remove_sta_debugfs(sta);
211 ieee80211_sta_debugfs_remove(sta);
212
213 sta_info_put(sta);
214}
215
216static void sta_info_remove(struct sta_info *sta)
217{ 187{
218 struct ieee80211_local *local = sta->local; 188 struct ieee80211_local *local = sta->local;
219 struct ieee80211_sub_if_data *sdata; 189 struct ieee80211_sub_if_data *sdata;
220 190
221 sta_info_hash_del(local, sta); 191 /* don't do anything if we've been removed already */
192 if (sta_info_hash_del(local, sta))
193 return;
194
222 list_del(&sta->list); 195 list_del(&sta->list);
223 sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); 196 sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
224 if (sta->flags & WLAN_STA_PS) { 197 if (sta->flags & WLAN_STA_PS) {
@@ -228,30 +201,29 @@ static void sta_info_remove(struct sta_info *sta)
228 } 201 }
229 local->num_sta--; 202 local->num_sta--;
230 sta_info_remove_aid_ptr(sta); 203 sta_info_remove_aid_ptr(sta);
204
205 if (local->ops->sta_table_notification)
206 local->ops->sta_table_notification(local_to_hw(local),
207 local->num_sta);
231} 208}
232 209
233void sta_info_free(struct sta_info *sta, int locked) 210void sta_info_free(struct sta_info *sta)
234{ 211{
235 struct sk_buff *skb; 212 struct sk_buff *skb;
236 struct ieee80211_local *local = sta->local; 213 struct ieee80211_local *local = sta->local;
237 214
238 if (!locked) { 215 might_sleep();
239 spin_lock_bh(&local->sta_lock); 216
240 sta_info_remove(sta); 217 write_lock_bh(&local->sta_lock);
241 spin_unlock_bh(&local->sta_lock); 218 sta_info_remove(sta);
242 } else { 219 write_unlock_bh(&local->sta_lock);
243 sta_info_remove(sta);
244 }
245 if (local->ops->sta_table_notification)
246 local->ops->sta_table_notification(local_to_hw(local),
247 local->num_sta);
248 220
249 while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) { 221 while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
250 local->total_ps_buffered--; 222 local->total_ps_buffered--;
251 dev_kfree_skb_any(skb); 223 dev_kfree_skb(skb);
252 } 224 }
253 while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) { 225 while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) {
254 dev_kfree_skb_any(skb); 226 dev_kfree_skb(skb);
255 } 227 }
256 228
257 if (sta->key) { 229 if (sta->key) {
@@ -276,13 +248,21 @@ void sta_info_free(struct sta_info *sta, int locked)
276 sta->key_idx_compression = HW_KEY_IDX_INVALID; 248 sta->key_idx_compression = HW_KEY_IDX_INVALID;
277 } 249 }
278 250
279#ifdef CONFIG_MAC80211_DEBUGFS 251#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
280 if (in_atomic()) { 252 printk(KERN_DEBUG "%s: Removed STA " MAC_FMT "\n",
281 list_add(&sta->list, &local->deleted_sta_list); 253 local->mdev->name, MAC_ARG(sta->addr));
282 queue_work(local->hw.workqueue, &local->sta_debugfs_add); 254#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
283 } else 255
284#endif 256 if (sta->key) {
285 finish_sta_info_free(local, sta); 257 ieee80211_debugfs_key_remove(sta->key);
258 ieee80211_key_free(sta->key);
259 sta->key = NULL;
260 }
261
262 rate_control_remove_sta_debugfs(sta);
263 ieee80211_sta_debugfs_remove(sta);
264
265 sta_info_put(sta);
286} 266}
287 267
288 268
@@ -343,13 +323,13 @@ static void sta_info_cleanup(unsigned long data)
343 struct ieee80211_local *local = (struct ieee80211_local *) data; 323 struct ieee80211_local *local = (struct ieee80211_local *) data;
344 struct sta_info *sta; 324 struct sta_info *sta;
345 325
346 spin_lock_bh(&local->sta_lock); 326 read_lock_bh(&local->sta_lock);
347 list_for_each_entry(sta, &local->sta_list, list) { 327 list_for_each_entry(sta, &local->sta_list, list) {
348 __sta_info_get(sta); 328 __sta_info_get(sta);
349 sta_info_cleanup_expire_buffered(local, sta); 329 sta_info_cleanup_expire_buffered(local, sta);
350 sta_info_put(sta); 330 sta_info_put(sta);
351 } 331 }
352 spin_unlock_bh(&local->sta_lock); 332 read_unlock_bh(&local->sta_lock);
353 333
354 local->sta_cleanup.expires = jiffies + STA_INFO_CLEANUP_INTERVAL; 334 local->sta_cleanup.expires = jiffies + STA_INFO_CLEANUP_INTERVAL;
355 add_timer(&local->sta_cleanup); 335 add_timer(&local->sta_cleanup);
@@ -363,35 +343,20 @@ static void sta_info_debugfs_add_task(struct work_struct *work)
363 struct sta_info *sta, *tmp; 343 struct sta_info *sta, *tmp;
364 344
365 while (1) { 345 while (1) {
366 spin_lock_bh(&local->sta_lock);
367 if (!list_empty(&local->deleted_sta_list)) {
368 sta = list_entry(local->deleted_sta_list.next,
369 struct sta_info, list);
370 list_del(local->deleted_sta_list.next);
371 } else
372 sta = NULL;
373 spin_unlock_bh(&local->sta_lock);
374 if (!sta)
375 break;
376 finish_sta_info_free(local, sta);
377 }
378
379 while (1) {
380 sta = NULL; 346 sta = NULL;
381 spin_lock_bh(&local->sta_lock); 347 read_lock_bh(&local->sta_lock);
382 list_for_each_entry(tmp, &local->sta_list, list) { 348 list_for_each_entry(tmp, &local->sta_list, list) {
383 if (!tmp->debugfs_registered) { 349 if (!tmp->debugfs.dir) {
384 sta = tmp; 350 sta = tmp;
385 __sta_info_get(sta); 351 __sta_info_get(sta);
386 break; 352 break;
387 } 353 }
388 } 354 }
389 spin_unlock_bh(&local->sta_lock); 355 read_unlock_bh(&local->sta_lock);
390 356
391 if (!sta) 357 if (!sta)
392 break; 358 break;
393 359
394 sta->debugfs_registered = 1;
395 ieee80211_sta_debugfs_add(sta); 360 ieee80211_sta_debugfs_add(sta);
396 rate_control_add_sta_debugfs(sta); 361 rate_control_add_sta_debugfs(sta);
397 sta_info_put(sta); 362 sta_info_put(sta);
@@ -401,9 +366,8 @@ static void sta_info_debugfs_add_task(struct work_struct *work)
401 366
402void sta_info_init(struct ieee80211_local *local) 367void sta_info_init(struct ieee80211_local *local)
403{ 368{
404 spin_lock_init(&local->sta_lock); 369 rwlock_init(&local->sta_lock);
405 INIT_LIST_HEAD(&local->sta_list); 370 INIT_LIST_HEAD(&local->sta_list);
406 INIT_LIST_HEAD(&local->deleted_sta_list);
407 371
408 init_timer(&local->sta_cleanup); 372 init_timer(&local->sta_cleanup);
409 local->sta_cleanup.expires = jiffies + STA_INFO_CLEANUP_INTERVAL; 373 local->sta_cleanup.expires = jiffies + STA_INFO_CLEANUP_INTERVAL;
@@ -423,17 +387,8 @@ int sta_info_start(struct ieee80211_local *local)
423 387
424void sta_info_stop(struct ieee80211_local *local) 388void sta_info_stop(struct ieee80211_local *local)
425{ 389{
426 struct sta_info *sta, *tmp;
427
428 del_timer(&local->sta_cleanup); 390 del_timer(&local->sta_cleanup);
429 391 sta_info_flush(local, NULL);
430 list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
431 /* sta_info_free must be called with 0 as the last
432 * parameter to ensure all debugfs sta entries are
433 * unregistered. We don't need locking at this
434 * point. */
435 sta_info_free(sta, 0);
436 }
437} 392}
438 393
439void sta_info_remove_aid_ptr(struct sta_info *sta) 394void sta_info_remove_aid_ptr(struct sta_info *sta)
@@ -461,10 +416,19 @@ void sta_info_remove_aid_ptr(struct sta_info *sta)
461void sta_info_flush(struct ieee80211_local *local, struct net_device *dev) 416void sta_info_flush(struct ieee80211_local *local, struct net_device *dev)
462{ 417{
463 struct sta_info *sta, *tmp; 418 struct sta_info *sta, *tmp;
419 LIST_HEAD(tmp_list);
464 420
465 spin_lock_bh(&local->sta_lock); 421 write_lock_bh(&local->sta_lock);
466 list_for_each_entry_safe(sta, tmp, &local->sta_list, list) 422 list_for_each_entry_safe(sta, tmp, &local->sta_list, list)
467 if (!dev || dev == sta->dev) 423 if (!dev || dev == sta->dev) {
468 sta_info_free(sta, 1); 424 __sta_info_get(sta);
469 spin_unlock_bh(&local->sta_lock); 425 sta_info_remove(sta);
426 list_add_tail(&sta->list, &tmp_list);
427 }
428 write_unlock_bh(&local->sta_lock);
429
430 list_for_each_entry_safe(sta, tmp, &tmp_list, list) {
431 sta_info_free(sta);
432 sta_info_put(sta);
433 }
470} 434}
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index b5591d2f60a4..b5ef72379add 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -98,9 +98,6 @@ struct sta_info {
98 * filtering; used only if sta->key is not 98 * filtering; used only if sta->key is not
99 * set */ 99 * set */
100 100
101#ifdef CONFIG_MAC80211_DEBUGFS
102 int debugfs_registered;
103#endif
104 int assoc_ap; /* whether this is an AP that we are 101 int assoc_ap; /* whether this is an AP that we are
105 * associated with as a client */ 102 * associated with as a client */
106 103
@@ -149,12 +146,18 @@ struct sta_info {
149 */ 146 */
150#define STA_INFO_CLEANUP_INTERVAL (10 * HZ) 147#define STA_INFO_CLEANUP_INTERVAL (10 * HZ)
151 148
149static inline void __sta_info_get(struct sta_info *sta)
150{
151 kref_get(&sta->kref);
152}
153
152struct sta_info * sta_info_get(struct ieee80211_local *local, u8 *addr); 154struct sta_info * sta_info_get(struct ieee80211_local *local, u8 *addr);
153int sta_info_min_txrate_get(struct ieee80211_local *local); 155int sta_info_min_txrate_get(struct ieee80211_local *local);
154void sta_info_put(struct sta_info *sta); 156void sta_info_put(struct sta_info *sta);
155struct sta_info * sta_info_add(struct ieee80211_local *local, 157struct sta_info * sta_info_add(struct ieee80211_local *local,
156 struct net_device *dev, u8 *addr, gfp_t gfp); 158 struct net_device *dev, u8 *addr, gfp_t gfp);
157void sta_info_free(struct sta_info *sta, int locked); 159void sta_info_remove(struct sta_info *sta);
160void sta_info_free(struct sta_info *sta);
158void sta_info_init(struct ieee80211_local *local); 161void sta_info_init(struct ieee80211_local *local);
159int sta_info_start(struct ieee80211_local *local); 162int sta_info_start(struct ieee80211_local *local);
160void sta_info_stop(struct ieee80211_local *local); 163void sta_info_stop(struct ieee80211_local *local);
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index dc128b412eab..2a1a7d457136 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -306,7 +306,7 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)
306 } 306 }
307 read_unlock(&local->sub_if_lock); 307 read_unlock(&local->sub_if_lock);
308 308
309 spin_lock_bh(&local->sta_lock); 309 read_lock_bh(&local->sta_lock);
310 list_for_each_entry(sta, &local->sta_list, list) { 310 list_for_each_entry(sta, &local->sta_list, list) {
311 skb = skb_dequeue(&sta->ps_tx_buf); 311 skb = skb_dequeue(&sta->ps_tx_buf);
312 if (skb) { 312 if (skb) {
@@ -315,7 +315,7 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)
315 } 315 }
316 total += skb_queue_len(&sta->ps_tx_buf); 316 total += skb_queue_len(&sta->ps_tx_buf);
317 } 317 }
318 spin_unlock_bh(&local->sta_lock); 318 read_unlock_bh(&local->sta_lock);
319 319
320 local->total_ps_buffered = total; 320 local->total_ps_buffered = total;
321 printk(KERN_DEBUG "%s: PS buffers full - purged %d frames\n", 321 printk(KERN_DEBUG "%s: PS buffers full - purged %d frames\n",
@@ -1629,7 +1629,7 @@ static void ieee80211_beacon_add_tim(struct ieee80211_local *local,
1629 1629
1630 /* Generate bitmap for TIM only if there are any STAs in power save 1630 /* Generate bitmap for TIM only if there are any STAs in power save
1631 * mode. */ 1631 * mode. */
1632 spin_lock_bh(&local->sta_lock); 1632 read_lock_bh(&local->sta_lock);
1633 if (atomic_read(&bss->num_sta_ps) > 0) 1633 if (atomic_read(&bss->num_sta_ps) > 0)
1634 /* in the hope that this is faster than 1634 /* in the hope that this is faster than
1635 * checking byte-for-byte */ 1635 * checking byte-for-byte */
@@ -1680,7 +1680,7 @@ static void ieee80211_beacon_add_tim(struct ieee80211_local *local,
1680 *pos++ = aid0; /* Bitmap control */ 1680 *pos++ = aid0; /* Bitmap control */
1681 *pos++ = 0; /* Part Virt Bitmap */ 1681 *pos++ = 0; /* Part Virt Bitmap */
1682 } 1682 }
1683 spin_unlock_bh(&local->sta_lock); 1683 read_unlock_bh(&local->sta_lock);
1684} 1684}
1685 1685
1686struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, int if_id, 1686struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, int if_id,