Commit 296fcba3 authored by Thomas Pedersen's avatar Thomas Pedersen Committed by Johannes Berg

mac80211: clean up mesh sta allocation warning

This refactoring fixes a "scheduling while atomic" warning
when allocating a mesh station entry while holding the RCU
read lock. Fix this by creating a new function
mesh_sta_info_get(), which correctly handles the locking
and returns under RCU.

Also move some unnecessarily #ifdefed mesh station init
code from sta_info_alloc() to __mesh_sta_info_alloc().
Signed-off-by: default avatarThomas Pedersen <thomas@cozybit.com>
[change code flow to make sparse happy]
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent d437c86b
...@@ -55,30 +55,6 @@ static inline void mesh_plink_fsm_restart(struct sta_info *sta) ...@@ -55,30 +55,6 @@ static inline void mesh_plink_fsm_restart(struct sta_info *sta)
sta->plink_retries = 0; sta->plink_retries = 0;
} }
/*
* Allocate mesh sta entry and insert into station table
*/
static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
u8 *hw_addr)
{
struct sta_info *sta;
if (sdata->local->num_sta >= MESH_MAX_PLINKS)
return NULL;
sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL);
if (!sta)
return NULL;
sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED);
set_sta_flag(sta, WLAN_STA_WME);
return sta;
}
/** /**
* mesh_set_ht_prot_mode - set correct HT protection mode * mesh_set_ht_prot_mode - set correct HT protection mode
* *
...@@ -309,52 +285,24 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, ...@@ -309,52 +285,24 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
return err; return err;
} }
/** static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
* mesh_peer_init - initialize new mesh peer and return resulting sta_info struct sta_info *sta,
* struct ieee802_11_elems *elems, bool insert)
* @sdata: local meshif
* @addr: peer's address
* @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,
struct ieee802_11_elems *elems)
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
enum ieee80211_band band = ieee80211_get_sdata_band(sdata); enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
u32 rates, basic_rates = 0; u32 rates, basic_rates = 0;
struct sta_info *sta;
bool insert = false;
sband = local->hw.wiphy->bands[band]; sband = local->hw.wiphy->bands[band];
rates = ieee80211_sta_get_rates(local, elems, band, &basic_rates); rates = ieee80211_sta_get_rates(local, elems, band, &basic_rates);
sta = sta_info_get(sdata, addr);
if (!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, addr,
elems->ie_start,
elems->total_len,
GFP_ATOMIC);
return NULL;
}
sta = mesh_plink_alloc(sdata, addr);
if (!sta)
return NULL;
insert = true;
}
spin_lock_bh(&sta->lock); spin_lock_bh(&sta->lock);
sta->last_rx = jiffies; sta->last_rx = jiffies;
if (sta->plink_state == NL80211_PLINK_ESTAB) {
spin_unlock_bh(&sta->lock); /* rates and capabilities don't change during peering */
return sta; if (sta->plink_state == NL80211_PLINK_ESTAB)
} goto out;
sta->sta.supp_rates[band] = rates; sta->sta.supp_rates[band] = rates;
if (elems->ht_cap_elem && if (elems->ht_cap_elem &&
...@@ -379,22 +327,104 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata, ...@@ -379,22 +327,104 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata,
if (insert) if (insert)
rate_control_rate_init(sta); rate_control_rate_init(sta);
out:
spin_unlock_bh(&sta->lock); spin_unlock_bh(&sta->lock);
}
static struct sta_info *
__mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr)
{
struct sta_info *sta;
if (insert && sta_info_insert(sta)) if (sdata->local->num_sta >= MESH_MAX_PLINKS)
return NULL; return NULL;
sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL);
if (!sta)
return NULL;
sta->plink_state = NL80211_PLINK_LISTEN;
init_timer(&sta->plink_timer);
sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED);
set_sta_flag(sta, WLAN_STA_WME);
return sta;
}
static struct sta_info *
mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr,
struct ieee802_11_elems *elems)
{
struct sta_info *sta = NULL;
/* Userspace handles peer allocation when security is enabled */
if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED)
cfg80211_notify_new_peer_candidate(sdata->dev, addr,
elems->ie_start,
elems->total_len,
GFP_KERNEL);
else
sta = __mesh_sta_info_alloc(sdata, addr);
return sta; return sta;
} }
/*
* mesh_sta_info_get - return mesh sta info entry for @addr.
*
* @sdata: local meshif
* @addr: peer's address
* @elems: IEs from beacon or mesh peering frame.
*
* Return existing or newly allocated sta_info under RCU read lock.
* (re)initialize with given IEs.
*/
static struct sta_info *
mesh_sta_info_get(struct ieee80211_sub_if_data *sdata,
u8 *addr, struct ieee802_11_elems *elems) __acquires(RCU)
{
struct sta_info *sta = NULL;
rcu_read_lock();
sta = sta_info_get(sdata, addr);
if (sta) {
mesh_sta_info_init(sdata, sta, elems, false);
} else {
rcu_read_unlock();
/* can't run atomic */
sta = mesh_sta_info_alloc(sdata, addr, elems);
if (!sta) {
rcu_read_lock();
return NULL;
}
if (sta_info_insert_rcu(sta))
return NULL;
}
return sta;
}
/*
* mesh_neighbour_update - update or initialize new mesh neighbor.
*
* @sdata: local meshif
* @addr: peer's address
* @elems: IEs from beacon or mesh peering frame
*
* Initiates peering if appropriate.
*/
void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
u8 *hw_addr, u8 *hw_addr,
struct ieee802_11_elems *elems) struct ieee802_11_elems *elems)
{ {
struct sta_info *sta; struct sta_info *sta;
rcu_read_lock(); sta = mesh_sta_info_get(sdata, hw_addr, elems);
sta = mesh_peer_init(sdata, hw_addr, elems);
if (!sta) if (!sta)
goto out; goto out;
...@@ -632,6 +662,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m ...@@ -632,6 +662,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
(ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8)) (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8))
memcpy(&llid, PLINK_GET_PLID(elems.peering), 2); memcpy(&llid, PLINK_GET_PLID(elems.peering), 2);
/* WARNING: Only for sta pointer, is dropped & re-acquired */
rcu_read_lock(); rcu_read_lock();
sta = sta_info_get(sdata, mgmt->sa); sta = sta_info_get(sdata, mgmt->sa);
...@@ -735,8 +766,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m ...@@ -735,8 +766,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
} }
if (event == OPN_ACPT) { if (event == OPN_ACPT) {
rcu_read_unlock();
/* allocate sta entry if necessary and update info */ /* allocate sta entry if necessary and update info */
sta = mesh_peer_init(sdata, mgmt->sa, &elems); sta = mesh_sta_info_get(sdata, mgmt->sa, &elems);
if (!sta) { if (!sta) {
mpl_dbg(sdata, "Mesh plink: failed to init peer!\n"); mpl_dbg(sdata, "Mesh plink: failed to init peer!\n");
rcu_read_unlock(); rcu_read_unlock();
......
...@@ -380,11 +380,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, ...@@ -380,11 +380,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
#ifdef CONFIG_MAC80211_MESH
sta->plink_state = NL80211_PLINK_LISTEN;
init_timer(&sta->plink_timer);
#endif
return sta; return sta;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment