Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+95 −63
Original line number Diff line number Diff line
@@ -55,30 +55,6 @@ static inline void mesh_plink_fsm_restart(struct sta_info *sta)
	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
 *
@@ -309,52 +285,24 @@ free:
	return err;
}

/**
 * mesh_peer_init - initialize new mesh peer and return resulting sta_info
 *
 * @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)
static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
			       struct sta_info *sta,
			       struct ieee802_11_elems *elems, bool insert)
{
	struct ieee80211_local *local = sdata->local;
	enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
	struct ieee80211_supported_band *sband;
	u32 rates, basic_rates = 0;
	struct sta_info *sta;
	bool insert = false;

	sband = local->hw.wiphy->bands[band];
	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);
	sta->last_rx = jiffies;
	if (sta->plink_state == NL80211_PLINK_ESTAB) {
		spin_unlock_bh(&sta->lock);
		return sta;
	}

	/* rates and capabilities don't change during peering */
	if (sta->plink_state == NL80211_PLINK_ESTAB)
		goto out;

	sta->sta.supp_rates[band] = rates;
	if (elems->ht_cap_elem &&
@@ -379,22 +327,104 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata,

	if (insert)
		rate_control_rate_init(sta);
out:
	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 (sdata->local->num_sta >= MESH_MAX_PLINKS)
		return NULL;

	if (insert && sta_info_insert(sta))
	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;
}

/*
 * 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,
			   u8 *hw_addr,
			   struct ieee802_11_elems *elems)
{
	struct sta_info *sta;

	rcu_read_lock();
	sta = mesh_peer_init(sdata, hw_addr, elems);
	sta = mesh_sta_info_get(sdata, hw_addr, elems);
	if (!sta)
		goto out;

@@ -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))
		memcpy(&llid, PLINK_GET_PLID(elems.peering), 2);

	/* WARNING: Only for sta pointer, is dropped & re-acquired */
	rcu_read_lock();

	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
	}

	if (event == OPN_ACPT) {
		rcu_read_unlock();
		/* 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) {
			mpl_dbg(sdata, "Mesh plink: failed to init peer!\n");
			rcu_read_unlock();
+0 −5
Original line number Diff line number Diff line
@@ -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);

#ifdef CONFIG_MAC80211_MESH
	sta->plink_state = NL80211_PLINK_LISTEN;
	init_timer(&sta->plink_timer);
#endif

	return sta;
}