aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/sta_info.c
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/mac80211/sta_info.c
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/mac80211/sta_info.c')
-rw-r--r--net/mac80211/sta_info.c184
1 files changed, 74 insertions, 110 deletions
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}