aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/mesh_plink.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2008-02-25 10:27:46 -0500
committerJohn W. Linville <linville@tuxdriver.com>2008-03-06 15:30:46 -0500
commitd0709a65181beb787ef3f58cfe45536a2bb254c8 (patch)
tree29e5f36583b0e0a3f11b291347e57672eab41dad /net/mac80211/mesh_plink.c
parent5cf121c3cdb955583bf0c5d28c992b7968a4aa1a (diff)
mac80211: RCU-ify STA info structure access
This makes access to the STA hash table/list use RCU to protect against freeing of items. However, it's not a true RCU, the copy step is missing: whenever somebody changes a STA item it is simply updated. This is an existing race condition that is now somewhat understandable. This patch also fixes the race key freeing vs. STA destruction by making sure that sta_info_destroy() is always called under RTNL and frees the key. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/mesh_plink.c')
-rw-r--r--net/mac80211/mesh_plink.c101
1 files changed, 52 insertions, 49 deletions
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index b5fbe970e48f..c2b80500ae72 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -65,14 +65,14 @@ static inline
65void mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata) 65void mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata)
66{ 66{
67 atomic_inc(&sdata->u.sta.mshstats.estab_plinks); 67 atomic_inc(&sdata->u.sta.mshstats.estab_plinks);
68 mesh_accept_plinks_update(sdata->dev); 68 mesh_accept_plinks_update(sdata);
69} 69}
70 70
71static inline 71static inline
72void mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata) 72void mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata)
73{ 73{
74 atomic_dec(&sdata->u.sta.mshstats.estab_plinks); 74 atomic_dec(&sdata->u.sta.mshstats.estab_plinks);
75 mesh_accept_plinks_update(sdata->dev); 75 mesh_accept_plinks_update(sdata);
76} 76}
77 77
78/** 78/**
@@ -99,12 +99,13 @@ static inline void mesh_plink_fsm_restart(struct sta_info *sta)
99 * 99 *
100 * Returns: non-NULL on success, ERR_PTR() on error. 100 * Returns: non-NULL on success, ERR_PTR() on error.
101 */ 101 */
102struct sta_info *mesh_plink_add(u8 *hw_addr, u64 rates, struct net_device *dev) 102struct sta_info *mesh_plink_add(u8 *hw_addr, u64 rates,
103 struct ieee80211_sub_if_data *sdata)
103{ 104{
104 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); 105 struct ieee80211_local *local = sdata->local;
105 struct sta_info *sta; 106 struct sta_info *sta;
106 107
107 if (memcmp(hw_addr, dev->dev_addr, ETH_ALEN) == 0) 108 if (compare_ether_addr(hw_addr, sdata->dev->dev_addr) == 0)
108 /* never add ourselves as neighbours */ 109 /* never add ourselves as neighbours */
109 return ERR_PTR(-EINVAL); 110 return ERR_PTR(-EINVAL);
110 111
@@ -114,7 +115,7 @@ struct sta_info *mesh_plink_add(u8 *hw_addr, u64 rates, struct net_device *dev)
114 if (local->num_sta >= MESH_MAX_PLINKS) 115 if (local->num_sta >= MESH_MAX_PLINKS)
115 return ERR_PTR(-ENOSPC); 116 return ERR_PTR(-ENOSPC);
116 117
117 sta = sta_info_add(local, dev, hw_addr, GFP_KERNEL); 118 sta = sta_info_add(sdata, hw_addr);
118 if (IS_ERR(sta)) 119 if (IS_ERR(sta))
119 return sta; 120 return sta;
120 121
@@ -125,7 +126,7 @@ struct sta_info *mesh_plink_add(u8 *hw_addr, u64 rates, struct net_device *dev)
125 sta->supp_rates[local->hw.conf.channel->band] = rates; 126 sta->supp_rates[local->hw.conf.channel->band] = rates;
126 rate_control_rate_init(sta, local); 127 rate_control_rate_init(sta, local);
127 128
128 mesh_accept_plinks_update(dev); 129 mesh_accept_plinks_update(sdata);
129 130
130 return sta; 131 return sta;
131} 132}
@@ -141,7 +142,8 @@ struct sta_info *mesh_plink_add(u8 *hw_addr, u64 rates, struct net_device *dev)
141 */ 142 */
142static void __mesh_plink_deactivate(struct sta_info *sta) 143static void __mesh_plink_deactivate(struct sta_info *sta)
143{ 144{
144 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); 145 struct ieee80211_sub_if_data *sdata = sta->sdata;
146
145 if (sta->plink_state == ESTAB) 147 if (sta->plink_state == ESTAB)
146 mesh_plink_dec_estab_count(sdata); 148 mesh_plink_dec_estab_count(sdata);
147 sta->plink_state = BLOCKED; 149 sta->plink_state = BLOCKED;
@@ -246,11 +248,15 @@ void mesh_neighbour_update(u8 *hw_addr, u64 rates, struct net_device *dev,
246 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); 248 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
247 struct sta_info *sta; 249 struct sta_info *sta;
248 250
251 rcu_read_lock();
252
249 sta = sta_info_get(local, hw_addr); 253 sta = sta_info_get(local, hw_addr);
250 if (!sta) { 254 if (!sta) {
251 sta = mesh_plink_add(hw_addr, rates, dev); 255 sta = mesh_plink_add(hw_addr, rates, sdata);
252 if (IS_ERR(sta)) 256 if (IS_ERR(sta)) {
257 rcu_read_unlock();
253 return; 258 return;
259 }
254 } 260 }
255 261
256 sta->last_rx = jiffies; 262 sta->last_rx = jiffies;
@@ -260,7 +266,7 @@ void mesh_neighbour_update(u8 *hw_addr, u64 rates, struct net_device *dev,
260 sdata->u.sta.mshcfg.auto_open_plinks) 266 sdata->u.sta.mshcfg.auto_open_plinks)
261 mesh_plink_open(sta); 267 mesh_plink_open(sta);
262 268
263 sta_info_put(sta); 269 rcu_read_unlock();
264} 270}
265 271
266static void mesh_plink_timer(unsigned long data) 272static void mesh_plink_timer(unsigned long data)
@@ -273,6 +279,11 @@ static void mesh_plink_timer(unsigned long data)
273 DECLARE_MAC_BUF(mac); 279 DECLARE_MAC_BUF(mac);
274#endif 280#endif
275 281
282 /*
283 * This STA is valid because sta_info_destroy() will
284 * del_timer_sync() this timer after having made sure
285 * it cannot be readded (by deleting the plink.)
286 */
276 sta = (struct sta_info *) data; 287 sta = (struct sta_info *) data;
277 288
278 spin_lock_bh(&sta->plink_lock); 289 spin_lock_bh(&sta->plink_lock);
@@ -286,8 +297,8 @@ static void mesh_plink_timer(unsigned long data)
286 reason = 0; 297 reason = 0;
287 llid = sta->llid; 298 llid = sta->llid;
288 plid = sta->plid; 299 plid = sta->plid;
289 dev = sta->dev; 300 sdata = sta->sdata;
290 sdata = IEEE80211_DEV_TO_SUB_IF(dev); 301 dev = sdata->dev;
291 302
292 switch (sta->plink_state) { 303 switch (sta->plink_state) {
293 case OPN_RCVD: 304 case OPN_RCVD:
@@ -302,8 +313,7 @@ static void mesh_plink_timer(unsigned long data)
302 sta->plink_timeout = sta->plink_timeout + 313 sta->plink_timeout = sta->plink_timeout +
303 rand % sta->plink_timeout; 314 rand % sta->plink_timeout;
304 ++sta->plink_retries; 315 ++sta->plink_retries;
305 if (!mod_plink_timer(sta, sta->plink_timeout)) 316 mod_plink_timer(sta, sta->plink_timeout);
306 __sta_info_get(sta);
307 spin_unlock_bh(&sta->plink_lock); 317 spin_unlock_bh(&sta->plink_lock);
308 mesh_plink_frame_tx(dev, PLINK_OPEN, sta->addr, llid, 318 mesh_plink_frame_tx(dev, PLINK_OPEN, sta->addr, llid,
309 0, 0); 319 0, 0);
@@ -316,16 +326,14 @@ static void mesh_plink_timer(unsigned long data)
316 if (!reason) 326 if (!reason)
317 reason = cpu_to_le16(MESH_CONFIRM_TIMEOUT); 327 reason = cpu_to_le16(MESH_CONFIRM_TIMEOUT);
318 sta->plink_state = HOLDING; 328 sta->plink_state = HOLDING;
319 if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) 329 mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
320 __sta_info_get(sta);
321 spin_unlock_bh(&sta->plink_lock); 330 spin_unlock_bh(&sta->plink_lock);
322 mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid, plid, 331 mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid, plid,
323 reason); 332 reason);
324 break; 333 break;
325 case HOLDING: 334 case HOLDING:
326 /* holding timer */ 335 /* holding timer */
327 if (del_timer(&sta->plink_timer)) 336 del_timer(&sta->plink_timer);
328 sta_info_put(sta);
329 mesh_plink_fsm_restart(sta); 337 mesh_plink_fsm_restart(sta);
330 spin_unlock_bh(&sta->plink_lock); 338 spin_unlock_bh(&sta->plink_lock);
331 break; 339 break;
@@ -333,8 +341,6 @@ static void mesh_plink_timer(unsigned long data)
333 spin_unlock_bh(&sta->plink_lock); 341 spin_unlock_bh(&sta->plink_lock);
334 break; 342 break;
335 } 343 }
336
337 sta_info_put(sta);
338} 344}
339 345
340static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout) 346static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)
@@ -343,14 +349,13 @@ static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)
343 sta->plink_timer.data = (unsigned long) sta; 349 sta->plink_timer.data = (unsigned long) sta;
344 sta->plink_timer.function = mesh_plink_timer; 350 sta->plink_timer.function = mesh_plink_timer;
345 sta->plink_timeout = timeout; 351 sta->plink_timeout = timeout;
346 __sta_info_get(sta);
347 add_timer(&sta->plink_timer); 352 add_timer(&sta->plink_timer);
348} 353}
349 354
350int mesh_plink_open(struct sta_info *sta) 355int mesh_plink_open(struct sta_info *sta)
351{ 356{
352 __le16 llid; 357 __le16 llid;
353 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); 358 struct ieee80211_sub_if_data *sdata = sta->sdata;
354#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG 359#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
355 DECLARE_MAC_BUF(mac); 360 DECLARE_MAC_BUF(mac);
356#endif 361#endif
@@ -360,7 +365,6 @@ int mesh_plink_open(struct sta_info *sta)
360 sta->llid = llid; 365 sta->llid = llid;
361 if (sta->plink_state != LISTEN) { 366 if (sta->plink_state != LISTEN) {
362 spin_unlock_bh(&sta->plink_lock); 367 spin_unlock_bh(&sta->plink_lock);
363 sta_info_put(sta);
364 return -EBUSY; 368 return -EBUSY;
365 } 369 }
366 sta->plink_state = OPN_SNT; 370 sta->plink_state = OPN_SNT;
@@ -369,7 +373,8 @@ int mesh_plink_open(struct sta_info *sta)
369 mpl_dbg("Mesh plink: starting establishment with %s\n", 373 mpl_dbg("Mesh plink: starting establishment with %s\n",
370 print_mac(mac, sta->addr)); 374 print_mac(mac, sta->addr));
371 375
372 return mesh_plink_frame_tx(sta->dev, PLINK_OPEN, sta->addr, llid, 0, 0); 376 return mesh_plink_frame_tx(sdata->dev, PLINK_OPEN,
377 sta->addr, llid, 0, 0);
373} 378}
374 379
375void mesh_plink_block(struct sta_info *sta) 380void mesh_plink_block(struct sta_info *sta)
@@ -386,7 +391,7 @@ void mesh_plink_block(struct sta_info *sta)
386 391
387int mesh_plink_close(struct sta_info *sta) 392int mesh_plink_close(struct sta_info *sta)
388{ 393{
389 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); 394 struct ieee80211_sub_if_data *sdata = sta->sdata;
390 int llid, plid, reason; 395 int llid, plid, reason;
391#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG 396#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
392 DECLARE_MAC_BUF(mac); 397 DECLARE_MAC_BUF(mac);
@@ -401,13 +406,11 @@ int mesh_plink_close(struct sta_info *sta)
401 if (sta->plink_state == LISTEN || sta->plink_state == BLOCKED) { 406 if (sta->plink_state == LISTEN || sta->plink_state == BLOCKED) {
402 mesh_plink_fsm_restart(sta); 407 mesh_plink_fsm_restart(sta);
403 spin_unlock_bh(&sta->plink_lock); 408 spin_unlock_bh(&sta->plink_lock);
404 sta_info_put(sta);
405 return 0; 409 return 0;
406 } else if (sta->plink_state == ESTAB) { 410 } else if (sta->plink_state == ESTAB) {
407 __mesh_plink_deactivate(sta); 411 __mesh_plink_deactivate(sta);
408 /* The timer should not be running */ 412 /* The timer should not be running */
409 if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) 413 mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
410 __sta_info_get(sta);
411 } else if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) 414 } else if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)))
412 sta->ignore_plink_timer = true; 415 sta->ignore_plink_timer = true;
413 416
@@ -415,15 +418,16 @@ int mesh_plink_close(struct sta_info *sta)
415 llid = sta->llid; 418 llid = sta->llid;
416 plid = sta->plid; 419 plid = sta->plid;
417 spin_unlock_bh(&sta->plink_lock); 420 spin_unlock_bh(&sta->plink_lock);
418 mesh_plink_frame_tx(sta->dev, PLINK_CLOSE, sta->addr, llid, plid, 421 mesh_plink_frame_tx(sta->sdata->dev, PLINK_CLOSE, sta->addr, llid,
419 reason); 422 plid, reason);
420 return 0; 423 return 0;
421} 424}
422 425
423void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, 426void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
424 size_t len, struct ieee80211_rx_status *rx_status) 427 size_t len, struct ieee80211_rx_status *rx_status)
425{ 428{
426 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); 429 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
430 struct ieee80211_local *local = sdata->local;
427 struct ieee802_11_elems elems; 431 struct ieee802_11_elems elems;
428 struct sta_info *sta; 432 struct sta_info *sta;
429 enum plink_event event; 433 enum plink_event event;
@@ -435,7 +439,6 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
435#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG 439#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
436 DECLARE_MAC_BUF(mac); 440 DECLARE_MAC_BUF(mac);
437#endif 441#endif
438 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
439 442
440 if (is_multicast_ether_addr(mgmt->da)) { 443 if (is_multicast_ether_addr(mgmt->da)) {
441 mpl_dbg("Mesh plink: ignore frame from multicast address"); 444 mpl_dbg("Mesh plink: ignore frame from multicast address");
@@ -474,14 +477,17 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
474 if (ftype == PLINK_CONFIRM || (ftype == PLINK_CLOSE && ie_len == 7)) 477 if (ftype == PLINK_CONFIRM || (ftype == PLINK_CLOSE && ie_len == 7))
475 memcpy(&llid, PLINK_GET_PLID(elems.peer_link), 2); 478 memcpy(&llid, PLINK_GET_PLID(elems.peer_link), 2);
476 479
480 rcu_read_lock();
481
477 sta = sta_info_get(local, mgmt->sa); 482 sta = sta_info_get(local, mgmt->sa);
478 if (!sta && ftype != PLINK_OPEN) { 483 if (!sta && ftype != PLINK_OPEN) {
479 mpl_dbg("Mesh plink: cls or cnf from unknown peer\n"); 484 mpl_dbg("Mesh plink: cls or cnf from unknown peer\n");
485 rcu_read_unlock();
480 return; 486 return;
481 } 487 }
482 488
483 if (sta && sta->plink_state == BLOCKED) { 489 if (sta && sta->plink_state == BLOCKED) {
484 sta_info_put(sta); 490 rcu_read_unlock();
485 return; 491 return;
486 } 492 }
487 493
@@ -505,13 +511,15 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
505 u64 rates; 511 u64 rates;
506 if (!mesh_plink_free_count(sdata)) { 512 if (!mesh_plink_free_count(sdata)) {
507 mpl_dbg("Mesh plink error: no more free plinks\n"); 513 mpl_dbg("Mesh plink error: no more free plinks\n");
514 rcu_read_unlock();
508 return; 515 return;
509 } 516 }
510 517
511 rates = ieee80211_sta_get_rates(local, &elems, rx_status->band); 518 rates = ieee80211_sta_get_rates(local, &elems, rx_status->band);
512 sta = mesh_plink_add(mgmt->sa, rates, dev); 519 sta = mesh_plink_add(mgmt->sa, rates, sdata);
513 if (IS_ERR(sta)) { 520 if (IS_ERR(sta)) {
514 mpl_dbg("Mesh plink error: plink table full\n"); 521 mpl_dbg("Mesh plink error: plink table full\n");
522 rcu_read_unlock();
515 return; 523 return;
516 } 524 }
517 event = OPN_ACPT; 525 event = OPN_ACPT;
@@ -521,14 +529,14 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
521 switch (ftype) { 529 switch (ftype) {
522 case PLINK_OPEN: 530 case PLINK_OPEN:
523 if (!mesh_plink_free_count(sdata) || 531 if (!mesh_plink_free_count(sdata) ||
524 (sta->plid && sta->plid != plid)) 532 (sta->plid && sta->plid != plid))
525 event = OPN_IGNR; 533 event = OPN_IGNR;
526 else 534 else
527 event = OPN_ACPT; 535 event = OPN_ACPT;
528 break; 536 break;
529 case PLINK_CONFIRM: 537 case PLINK_CONFIRM:
530 if (!mesh_plink_free_count(sdata) || 538 if (!mesh_plink_free_count(sdata) ||
531 (sta->llid != llid || sta->plid != plid)) 539 (sta->llid != llid || sta->plid != plid))
532 event = CNF_IGNR; 540 event = CNF_IGNR;
533 else 541 else
534 event = CNF_ACPT; 542 event = CNF_ACPT;
@@ -555,7 +563,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
555 default: 563 default:
556 mpl_dbg("Mesh plink: unknown frame subtype\n"); 564 mpl_dbg("Mesh plink: unknown frame subtype\n");
557 spin_unlock_bh(&sta->plink_lock); 565 spin_unlock_bh(&sta->plink_lock);
558 sta_info_put(sta); 566 rcu_read_unlock();
559 return; 567 return;
560 } 568 }
561 } 569 }
@@ -659,8 +667,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
659 plid, 0); 667 plid, 0);
660 break; 668 break;
661 case CNF_ACPT: 669 case CNF_ACPT:
662 if (del_timer(&sta->plink_timer)) 670 del_timer(&sta->plink_timer);
663 sta_info_put(sta);
664 sta->plink_state = ESTAB; 671 sta->plink_state = ESTAB;
665 mesh_plink_inc_estab_count(sdata); 672 mesh_plink_inc_estab_count(sdata);
666 spin_unlock_bh(&sta->plink_lock); 673 spin_unlock_bh(&sta->plink_lock);
@@ -693,8 +700,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
693 plid, reason); 700 plid, reason);
694 break; 701 break;
695 case OPN_ACPT: 702 case OPN_ACPT:
696 if (del_timer(&sta->plink_timer)) 703 del_timer(&sta->plink_timer);
697 sta_info_put(sta);
698 sta->plink_state = ESTAB; 704 sta->plink_state = ESTAB;
699 mesh_plink_inc_estab_count(sdata); 705 mesh_plink_inc_estab_count(sdata);
700 spin_unlock_bh(&sta->plink_lock); 706 spin_unlock_bh(&sta->plink_lock);
@@ -717,9 +723,7 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
717 __mesh_plink_deactivate(sta); 723 __mesh_plink_deactivate(sta);
718 sta->plink_state = HOLDING; 724 sta->plink_state = HOLDING;
719 llid = sta->llid; 725 llid = sta->llid;
720 if (!mod_plink_timer(sta, 726 mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
721 dot11MeshHoldingTimeout(sdata)))
722 __sta_info_get(sta);
723 spin_unlock_bh(&sta->plink_lock); 727 spin_unlock_bh(&sta->plink_lock);
724 mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid, 728 mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid,
725 plid, reason); 729 plid, reason);
@@ -738,10 +742,8 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
738 case HOLDING: 742 case HOLDING:
739 switch (event) { 743 switch (event) {
740 case CLS_ACPT: 744 case CLS_ACPT:
741 if (del_timer(&sta->plink_timer)) { 745 if (del_timer(&sta->plink_timer))
742 sta->ignore_plink_timer = 1; 746 sta->ignore_plink_timer = 1;
743 sta_info_put(sta);
744 }
745 mesh_plink_fsm_restart(sta); 747 mesh_plink_fsm_restart(sta);
746 spin_unlock_bh(&sta->plink_lock); 748 spin_unlock_bh(&sta->plink_lock);
747 break; 749 break;
@@ -766,5 +768,6 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
766 spin_unlock_bh(&sta->plink_lock); 768 spin_unlock_bh(&sta->plink_lock);
767 break; 769 break;
768 } 770 }
769 sta_info_put(sta); 771
772 rcu_read_unlock();
770} 773}