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 { |