diff options
| author | Johannes Berg <johannes@sipsolutions.net> | 2008-02-25 10:27:47 -0500 |
|---|---|---|
| committer | John W. Linville <linville@tuxdriver.com> | 2008-03-06 15:30:47 -0500 |
| commit | 73651ee6396c499ccb59ebc84c9274db01ed026d (patch) | |
| tree | 1d59027cbdaec732f3e1378770cbf7b42b48cd70 /net/mac80211/mesh_plink.c | |
| parent | d0709a65181beb787ef3f58cfe45536a2bb254c8 (diff) | |
mac80211: split sta_info_add
sta_info_add() has two functions: allocating a station info
structure and inserting it into the hash table/list. Splitting
these two functions allows allocating with GFP_KERNEL in many
places instead of GFP_ATOMIC which is now required by the RCU
protection. Additionally, in many places RCU protection is now
no longer needed at all because between sta_info_alloc() and
sta_info_insert() the caller owns the structure.
This fixes a few race conditions with setting initial flags
and similar, but not all (see comments in ieee80211_sta.c and
cfg.c). More documentation on the existing races will be in
a follow-up patch.
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.c | 43 |
1 files changed, 25 insertions, 18 deletions
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index c2b80500ae72..85cb75d53c43 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c | |||
| @@ -89,44 +89,41 @@ static inline void mesh_plink_fsm_restart(struct sta_info *sta) | |||
| 89 | } | 89 | } |
| 90 | 90 | ||
| 91 | /** | 91 | /** |
| 92 | * mesh_plink_add - allocate and add a new mesh peer link | 92 | * mesh_plink_alloc - allocate a new mesh peer link |
| 93 | * | 93 | * |
| 94 | * @sdata: local mesh interface | ||
| 94 | * @hw_addr: hardware address (ETH_ALEN length) | 95 | * @hw_addr: hardware address (ETH_ALEN length) |
| 95 | * @rates: rates the mesh peer supports | 96 | * @rates: rates the mesh peer supports |
| 96 | * @dev: local mesh interface | ||
| 97 | * | 97 | * |
| 98 | * The initial state of the new plink is set to LISTEN | 98 | * The initial state of the new plink is set to LISTEN |
| 99 | * | 99 | * |
| 100 | * Returns: non-NULL on success, ERR_PTR() on error. | 100 | * Returns: NULL on error. |
| 101 | */ | 101 | */ |
| 102 | struct sta_info *mesh_plink_add(u8 *hw_addr, u64 rates, | 102 | struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, |
| 103 | struct ieee80211_sub_if_data *sdata) | 103 | u8 *hw_addr, u64 rates, gfp_t gfp) |
| 104 | { | 104 | { |
| 105 | struct ieee80211_local *local = sdata->local; | 105 | struct ieee80211_local *local = sdata->local; |
| 106 | struct sta_info *sta; | 106 | struct sta_info *sta; |
| 107 | 107 | ||
| 108 | if (compare_ether_addr(hw_addr, sdata->dev->dev_addr) == 0) | 108 | if (compare_ether_addr(hw_addr, sdata->dev->dev_addr) == 0) |
| 109 | /* never add ourselves as neighbours */ | 109 | /* never add ourselves as neighbours */ |
| 110 | return ERR_PTR(-EINVAL); | 110 | return NULL; |
| 111 | 111 | ||
| 112 | if (is_multicast_ether_addr(hw_addr)) | 112 | if (is_multicast_ether_addr(hw_addr)) |
| 113 | return ERR_PTR(-EINVAL); | 113 | return NULL; |
| 114 | 114 | ||
| 115 | if (local->num_sta >= MESH_MAX_PLINKS) | 115 | if (local->num_sta >= MESH_MAX_PLINKS) |
| 116 | return ERR_PTR(-ENOSPC); | 116 | return NULL; |
| 117 | 117 | ||
| 118 | sta = sta_info_add(sdata, hw_addr); | 118 | sta = sta_info_alloc(sdata, hw_addr, gfp); |
| 119 | if (IS_ERR(sta)) | 119 | if (!sta) |
| 120 | return sta; | 120 | return NULL; |
| 121 | 121 | ||
| 122 | sta->plink_state = LISTEN; | 122 | sta->plink_state = LISTEN; |
| 123 | spin_lock_init(&sta->plink_lock); | 123 | spin_lock_init(&sta->plink_lock); |
| 124 | init_timer(&sta->plink_timer); | 124 | init_timer(&sta->plink_timer); |
| 125 | sta->flags |= WLAN_STA_AUTHORIZED; | 125 | sta->flags |= WLAN_STA_AUTHORIZED; |
| 126 | sta->supp_rates[local->hw.conf.channel->band] = rates; | 126 | sta->supp_rates[local->hw.conf.channel->band] = rates; |
| 127 | rate_control_rate_init(sta, local); | ||
| 128 | |||
| 129 | mesh_accept_plinks_update(sdata); | ||
| 130 | 127 | ||
| 131 | return sta; | 128 | return sta; |
| 132 | } | 129 | } |
| @@ -252,8 +249,13 @@ void mesh_neighbour_update(u8 *hw_addr, u64 rates, struct net_device *dev, | |||
| 252 | 249 | ||
| 253 | sta = sta_info_get(local, hw_addr); | 250 | sta = sta_info_get(local, hw_addr); |
| 254 | if (!sta) { | 251 | if (!sta) { |
| 255 | sta = mesh_plink_add(hw_addr, rates, sdata); | 252 | sta = mesh_plink_alloc(sdata, hw_addr, rates, GFP_ATOMIC); |
| 256 | if (IS_ERR(sta)) { | 253 | if (!sta) { |
| 254 | rcu_read_unlock(); | ||
| 255 | return; | ||
| 256 | } | ||
| 257 | if (sta_info_insert(sta)) { | ||
| 258 | sta_info_destroy(sta); | ||
| 257 | rcu_read_unlock(); | 259 | rcu_read_unlock(); |
| 258 | return; | 260 | return; |
| 259 | } | 261 | } |
| @@ -516,12 +518,17 @@ void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt, | |||
| 516 | } | 518 | } |
| 517 | 519 | ||
| 518 | rates = ieee80211_sta_get_rates(local, &elems, rx_status->band); | 520 | rates = ieee80211_sta_get_rates(local, &elems, rx_status->band); |
| 519 | sta = mesh_plink_add(mgmt->sa, rates, sdata); | 521 | sta = mesh_plink_alloc(sdata, mgmt->sa, rates, GFP_ATOMIC); |
| 520 | if (IS_ERR(sta)) { | 522 | if (!sta) { |
| 521 | mpl_dbg("Mesh plink error: plink table full\n"); | 523 | mpl_dbg("Mesh plink error: plink table full\n"); |
| 522 | rcu_read_unlock(); | 524 | rcu_read_unlock(); |
| 523 | return; | 525 | return; |
| 524 | } | 526 | } |
| 527 | if (sta_info_insert(sta)) { | ||
| 528 | sta_info_destroy(sta); | ||
| 529 | rcu_read_unlock(); | ||
| 530 | return; | ||
| 531 | } | ||
| 525 | event = OPN_ACPT; | 532 | event = OPN_ACPT; |
| 526 | spin_lock_bh(&sta->plink_lock); | 533 | spin_lock_bh(&sta->plink_lock); |
| 527 | } else { | 534 | } else { |
