diff options
Diffstat (limited to 'net/mac80211/mesh.c')
| -rw-r--r-- | net/mac80211/mesh.c | 107 | 
1 files changed, 69 insertions, 38 deletions
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 6952760881c8..447f41bbe744 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -271,8 +271,7 @@ int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata,  	*pos++ = ifmsh->mesh_auth_id;  	/* Mesh Formation Info - number of neighbors */  	neighbors = atomic_read(&ifmsh->estab_plinks); -	/* Number of neighbor mesh STAs or 15 whichever is smaller */ -	neighbors = (neighbors > 15) ? 15 : neighbors; +	neighbors = min_t(int, neighbors, IEEE80211_MAX_MESH_PEERINGS);  	*pos++ = neighbors << 1;  	/* Mesh capability */  	*pos = IEEE80211_MESHCONF_CAPAB_FORWARDING; @@ -417,7 +416,9 @@ int mesh_add_ht_cap_ie(struct ieee80211_sub_if_data *sdata,  	sband = local->hw.wiphy->bands[band];  	if (!sband->ht_cap.ht_supported || -	    sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) +	    sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT || +	    sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_5 || +	    sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_10)  		return 0;  	if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap)) @@ -573,7 +574,7 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata)  	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;  	u32 changed; -	ieee80211_sta_expire(sdata, IEEE80211_MESH_PEER_INACTIVITY_LIMIT); +	ieee80211_sta_expire(sdata, ifmsh->mshcfg.plink_timeout * HZ);  	mesh_path_expire(sdata);  	changed = mesh_accept_plinks_update(sdata); @@ -697,38 +698,38 @@ out_free:  }  static int -ieee80211_mesh_rebuild_beacon(struct ieee80211_if_mesh *ifmsh) +ieee80211_mesh_rebuild_beacon(struct ieee80211_sub_if_data *sdata)  {  	struct beacon_data *old_bcn;  	int ret; -	mutex_lock(&ifmsh->mtx); - -	old_bcn = rcu_dereference_protected(ifmsh->beacon, -					    lockdep_is_held(&ifmsh->mtx)); -	ret = ieee80211_mesh_build_beacon(ifmsh); +	old_bcn = rcu_dereference_protected(sdata->u.mesh.beacon, +					    lockdep_is_held(&sdata->wdev.mtx)); +	ret = ieee80211_mesh_build_beacon(&sdata->u.mesh);  	if (ret)  		/* just reuse old beacon */ -		goto out; +		return ret;  	if (old_bcn)  		kfree_rcu(old_bcn, rcu_head); -out: -	mutex_unlock(&ifmsh->mtx); -	return ret; +	return 0;  }  void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata,  				       u32 changed)  { -	if (sdata->vif.bss_conf.enable_beacon && -	    (changed & (BSS_CHANGED_BEACON | -			BSS_CHANGED_HT | -			BSS_CHANGED_BASIC_RATES | -			BSS_CHANGED_BEACON_INT))) -		if (ieee80211_mesh_rebuild_beacon(&sdata->u.mesh)) -			return; -	ieee80211_bss_info_change_notify(sdata, changed); +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	unsigned long bits = changed; +	u32 bit; + +	if (!bits) +		return; + +	/* if we race with running work, worst case this work becomes a noop */ +	for_each_set_bit(bit, &bits, sizeof(changed) * BITS_PER_BYTE) +		set_bit(bit, &ifmsh->mbss_changed); +	set_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags); +	ieee80211_queue_work(&sdata->local->hw, &sdata->work);  }  int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) @@ -740,7 +741,6 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)  		      BSS_CHANGED_HT |  		      BSS_CHANGED_BASIC_RATES |  		      BSS_CHANGED_BEACON_INT; -	enum ieee80211_band band = ieee80211_get_sdata_band(sdata);  	local->fif_other_bss++;  	/* mesh ifaces must set allmulti to forward mcast traffic */ @@ -748,7 +748,6 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)  	ieee80211_configure_filter(local);  	ifmsh->mesh_cc_id = 0;	/* Disabled */ -	ifmsh->mesh_auth_id = 0;	/* Disabled */  	/* register sync ops from extensible synchronization framework */  	ifmsh->sync_ops = ieee80211_mesh_sync_ops_get(ifmsh->mesh_sp_id);  	ifmsh->adjusting_tbtt = false; @@ -759,8 +758,6 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)  	sdata->vif.bss_conf.ht_operation_mode =  				ifmsh->mshcfg.ht_opmode;  	sdata->vif.bss_conf.enable_beacon = true; -	sdata->vif.bss_conf.basic_rates = -		ieee80211_mandatory_rates(local, band);  	changed |= ieee80211_mps_local_status_update(sdata); @@ -788,12 +785,10 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)  	sdata->vif.bss_conf.enable_beacon = false;  	clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);  	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); -	mutex_lock(&ifmsh->mtx);  	bcn = rcu_dereference_protected(ifmsh->beacon, -					lockdep_is_held(&ifmsh->mtx)); +					lockdep_is_held(&sdata->wdev.mtx));  	rcu_assign_pointer(ifmsh->beacon, NULL);  	kfree_rcu(bcn, rcu_head); -	mutex_unlock(&ifmsh->mtx);  	/* flush STAs and mpaths on this iface */  	sta_info_flush(sdata); @@ -806,14 +801,10 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)  	del_timer_sync(&sdata->u.mesh.housekeeping_timer);  	del_timer_sync(&sdata->u.mesh.mesh_path_root_timer);  	del_timer_sync(&sdata->u.mesh.mesh_path_timer); -	/* -	 * If the timer fired while we waited for it, it will have -	 * requeued the work. Now the work will be running again -	 * but will not rearm the timer again because it checks -	 * whether the interface is running, which, at this point, -	 * it no longer is. -	 */ -	cancel_work_sync(&sdata->work); + +	/* clear any mesh work (for next join) we may have accrued */ +	ifmsh->wrkq_flags = 0; +	ifmsh->mbss_changed = 0;  	local->fif_other_bss--;  	atomic_dec(&local->iff_allmultis); @@ -954,6 +945,12 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,  	struct ieee80211_mgmt *mgmt;  	u16 stype; +	sdata_lock(sdata); + +	/* mesh already went down */ +	if (!sdata->wdev.mesh_id_len) +		goto out; +  	rx_status = IEEE80211_SKB_RXCB(skb);  	mgmt = (struct ieee80211_mgmt *) skb->data;  	stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE; @@ -971,12 +968,42 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,  		ieee80211_mesh_rx_mgmt_action(sdata, mgmt, skb->len, rx_status);  		break;  	} +out: +	sdata_unlock(sdata); +} + +static void mesh_bss_info_changed(struct ieee80211_sub_if_data *sdata) +{ +	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	u32 bit, changed = 0; + +	for_each_set_bit(bit, &ifmsh->mbss_changed, +			 sizeof(changed) * BITS_PER_BYTE) { +		clear_bit(bit, &ifmsh->mbss_changed); +		changed |= BIT(bit); +	} + +	if (sdata->vif.bss_conf.enable_beacon && +	    (changed & (BSS_CHANGED_BEACON | +			BSS_CHANGED_HT | +			BSS_CHANGED_BASIC_RATES | +			BSS_CHANGED_BEACON_INT))) +		if (ieee80211_mesh_rebuild_beacon(sdata)) +			return; + +	ieee80211_bss_info_change_notify(sdata, changed);  }  void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)  {  	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; +	sdata_lock(sdata); + +	/* mesh already went down */ +	if (!sdata->wdev.mesh_id_len) +		goto out; +  	if (ifmsh->preq_queue_len &&  	    time_after(jiffies,  		       ifmsh->last_preq + msecs_to_jiffies(ifmsh->mshcfg.dot11MeshHWMPpreqMinInterval))) @@ -996,6 +1023,11 @@ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata)  	if (test_and_clear_bit(MESH_WORK_DRIFT_ADJUST, &ifmsh->wrkq_flags))  		mesh_sync_adjust_tbtt(sdata); + +	if (test_and_clear_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags)) +		mesh_bss_info_changed(sdata); +out: +	sdata_unlock(sdata);  }  void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local) @@ -1041,7 +1073,6 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)  	spin_lock_init(&ifmsh->mesh_preq_queue_lock);  	spin_lock_init(&ifmsh->sync_offset_lock);  	RCU_INIT_POINTER(ifmsh->beacon, NULL); -	mutex_init(&ifmsh->mtx);  	sdata->vif.bss_conf.bssid = zero_addr;  }  | 
