mac80211: refactor mesh peer initialization

This patch unifies the previous two paths toward mesh peer creation a
bit. It also fixes a bug where a peer's changing rates or HT mode
wouldn't register on leaving and then returning to the mesh with a sta
entry still present.

Also clean up locking and clear possibly stale ht cap.

Signed-off-by: Thomas Pedersen <thomas@cozybit.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Thomas Pedersen 2012-04-18 19:23:42 -07:00 committed by John W. Linville
parent 75acd5a82a
commit 54ab1ffb6c
1 changed files with 72 additions and 54 deletions

View File

@ -82,20 +82,14 @@ static inline void mesh_plink_fsm_restart(struct sta_info *sta)
} }
/* /*
* NOTE: This is just an alias for sta_info_alloc(), see notes * Allocate mesh sta entry and insert into station table
* on it in the lifecycle management section!
*/ */
static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
u8 *hw_addr, u32 rates, u8 *hw_addr)
struct ieee802_11_elems *elems)
{ {
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
struct sta_info *sta; struct sta_info *sta;
sband = local->hw.wiphy->bands[local->oper_channel->band]; if (sdata->local->num_sta >= MESH_MAX_PLINKS)
if (local->num_sta >= MESH_MAX_PLINKS)
return NULL; return NULL;
sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL); sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL);
@ -108,12 +102,8 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
set_sta_flag(sta, WLAN_STA_WME); set_sta_flag(sta, WLAN_STA_WME);
sta->sta.supp_rates[local->hw.conf.channel->band] = rates; if (sta_info_insert(sta))
if (elems->ht_cap_elem) return NULL;
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
elems->ht_cap_elem,
&sta->sta.ht_cap);
rate_control_rate_init(sta);
return sta; return sta;
} }
@ -274,43 +264,76 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
return 0; return 0;
} }
void mesh_neighbour_update(u8 *hw_addr, u32 rates, /* mesh_peer_init - initialize new mesh peer and return resulting sta_info
struct ieee80211_sub_if_data *sdata, *
struct ieee802_11_elems *elems) * @sdata: local meshif
* @addr: peer's address
* @rates: station's supported rates
* @elems: IEs from beacon or mesh peering frame
*
* call under RCU
*/
static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata,
u8 *addr, u32 rates,
struct ieee802_11_elems *elems)
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
struct sta_info *sta; struct sta_info *sta;
rcu_read_lock(); sband = local->hw.wiphy->bands[local->oper_channel->band];
sta = sta_info_get(sdata, hw_addr); sta = sta_info_get(sdata, addr);
if (!sta) { if (!sta) {
rcu_read_unlock(); sta = mesh_plink_alloc(sdata, addr);
/* Userspace handles peer allocation when security is enabled
* */
if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED)
cfg80211_notify_new_peer_candidate(sdata->dev, hw_addr,
elems->ie_start, elems->total_len,
GFP_KERNEL);
else
sta = mesh_plink_alloc(sdata, hw_addr, rates, elems);
if (!sta) if (!sta)
return; return NULL;
if (sta_info_insert_rcu(sta)) {
rcu_read_unlock();
return;
}
} }
spin_lock_bh(&sta->lock);
sta->last_rx = jiffies; sta->last_rx = jiffies;
sta->sta.supp_rates[local->hw.conf.channel->band] = rates; sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
if (elems->ht_cap_elem)
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
elems->ht_cap_elem,
&sta->sta.ht_cap);
else
memset(&sta->sta.ht_cap, 0, sizeof(sta->sta.ht_cap));
rate_control_rate_init(sta);
spin_unlock_bh(&sta->lock);
return sta;
}
void mesh_neighbour_update(u8 *hw_addr, u32 rates,
struct ieee80211_sub_if_data *sdata,
struct ieee802_11_elems *elems)
{
struct sta_info *sta;
/* Userspace handles peer allocation when security is enabled */
if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) {
cfg80211_notify_new_peer_candidate(sdata->dev, hw_addr,
elems->ie_start,
elems->total_len,
GFP_KERNEL);
return;
}
rcu_read_lock();
sta = mesh_peer_init(sdata, hw_addr, rates, elems);
if (!sta)
goto out;
if (mesh_peer_accepts_plinks(elems) && if (mesh_peer_accepts_plinks(elems) &&
sta->plink_state == NL80211_PLINK_LISTEN && sta->plink_state == NL80211_PLINK_LISTEN &&
sdata->u.mesh.accepting_plinks && sdata->u.mesh.accepting_plinks &&
sdata->u.mesh.mshcfg.auto_open_plinks && sdata->u.mesh.mshcfg.auto_open_plinks &&
rssi_threshold_check(sta, sdata)) rssi_threshold_check(sta, sdata))
mesh_plink_open(sta); mesh_plink_open(sta);
out:
rcu_read_unlock(); rcu_read_unlock();
} }
@ -587,26 +610,13 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
return; return;
} else if (!sta) { } else if (!sta) {
/* ftype == WLAN_SP_MESH_PEERING_OPEN */ /* ftype == WLAN_SP_MESH_PEERING_OPEN */
rcu_read_unlock();
if (!mesh_plink_free_count(sdata)) { if (!mesh_plink_free_count(sdata)) {
mpl_dbg("Mesh plink error: no more free plinks\n"); mpl_dbg("Mesh plink error: no more free plinks\n");
return;
}
sta = mesh_plink_alloc(sdata, mgmt->sa, rates, &elems);
if (!sta) {
mpl_dbg("Mesh plink error: plink table full\n");
return;
}
if (sta_info_insert_rcu(sta)) {
rcu_read_unlock(); rcu_read_unlock();
return; return;
} }
event = OPN_ACPT; event = OPN_ACPT;
spin_lock_bh(&sta->lock);
} else if (matches_local) { } else if (matches_local) {
spin_lock_bh(&sta->lock);
switch (ftype) { switch (ftype) {
case WLAN_SP_MESH_PEERING_OPEN: case WLAN_SP_MESH_PEERING_OPEN:
if (!mesh_plink_free_count(sdata) || if (!mesh_plink_free_count(sdata) ||
@ -643,12 +653,19 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
break; break;
default: default:
mpl_dbg("Mesh plink: unknown frame subtype\n"); mpl_dbg("Mesh plink: unknown frame subtype\n");
spin_unlock_bh(&sta->lock);
rcu_read_unlock(); rcu_read_unlock();
return; return;
} }
} else { }
spin_lock_bh(&sta->lock);
if (event == OPN_ACPT) {
/* allocate sta entry if necessary and update info */
sta = mesh_peer_init(sdata, mgmt->sa, rates, &elems);
if (!sta) {
mpl_dbg("Mesh plink: failed to init peer!\n");
rcu_read_unlock();
return;
}
} }
mpl_dbg("Mesh plink (peer, state, llid, plid, event): %pM %s %d %d %d\n", mpl_dbg("Mesh plink (peer, state, llid, plid, event): %pM %s %d %d %d\n",
@ -656,6 +673,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
le16_to_cpu(sta->llid), le16_to_cpu(sta->plid), le16_to_cpu(sta->llid), le16_to_cpu(sta->plid),
event); event);
reason = 0; reason = 0;
spin_lock_bh(&sta->lock);
switch (sta->plink_state) { switch (sta->plink_state) {
/* spin_unlock as soon as state is updated at each case */ /* spin_unlock as soon as state is updated at each case */
case NL80211_PLINK_LISTEN: case NL80211_PLINK_LISTEN: