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

Commit 54ab1ffb authored by Thomas Pedersen's avatar Thomas Pedersen Committed by John W. Linville
Browse files

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: default avatarThomas Pedersen <thomas@cozybit.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 75acd5a8
Loading
Loading
Loading
Loading
+72 −54
Original line number Diff line number Diff line
@@ -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
 *       on it in the lifecycle management section!
 * 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, u32 rates,
					 struct ieee802_11_elems *elems)
					 u8 *hw_addr)
{
	struct ieee80211_local *local = sdata->local;
	struct ieee80211_supported_band *sband;
	struct sta_info *sta;

	sband = local->hw.wiphy->bands[local->oper_channel->band];

	if (local->num_sta >= MESH_MAX_PLINKS)
	if (sdata->local->num_sta >= MESH_MAX_PLINKS)
		return NULL;

	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);

	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);
	rate_control_rate_init(sta);
	if (sta_info_insert(sta))
		return NULL;

	return sta;
}
@@ -274,36 +264,68 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
	return 0;
}

void mesh_neighbour_update(u8 *hw_addr, u32 rates,
		struct ieee80211_sub_if_data *sdata,
/* mesh_peer_init - initialize new mesh peer and return resulting sta_info
 *
 * @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_supported_band *sband;
	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) {
		rcu_read_unlock();
		/* 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);
		sta = mesh_plink_alloc(sdata, addr);
		if (!sta)
			return;
		if (sta_info_insert_rcu(sta)) {
			rcu_read_unlock();
			return;
		}
			return NULL;
	}

	spin_lock_bh(&sta->lock);
	sta->last_rx = jiffies;
	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) &&
	    sta->plink_state == NL80211_PLINK_LISTEN &&
	    sdata->u.mesh.accepting_plinks &&
@@ -311,6 +333,7 @@ void mesh_neighbour_update(u8 *hw_addr, u32 rates,
	    rssi_threshold_check(sta, sdata))
		mesh_plink_open(sta);

out:
	rcu_read_unlock();
}

@@ -587,26 +610,13 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
		return;
	} else if (!sta) {
		/* ftype == WLAN_SP_MESH_PEERING_OPEN */

		rcu_read_unlock();

		if (!mesh_plink_free_count(sdata)) {
			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();
			return;
		}
		event = OPN_ACPT;
		spin_lock_bh(&sta->lock);
	} else if (matches_local) {
		spin_lock_bh(&sta->lock);
		switch (ftype) {
		case WLAN_SP_MESH_PEERING_OPEN:
			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;
		default:
			mpl_dbg("Mesh plink: unknown frame subtype\n");
			spin_unlock_bh(&sta->lock);
			rcu_read_unlock();
			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",
@@ -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),
		event);
	reason = 0;
	spin_lock_bh(&sta->lock);
	switch (sta->plink_state) {
		/* spin_unlock as soon as state is updated at each case */
	case NL80211_PLINK_LISTEN: