wifi: mac80211: update NAN data path state on schedule changes

A carrier of an NDI interface is turned on when there is at least one NDI
station that: (1) correlates to this interface (2) is authorized (3) the
NAN peer to which this station belongs has at least one common slot with
the local schedule. Otherwise, it is turned off.
(common slots are slots where both schedules are active on compatible
 channels.)

Implement the calculation of the carrier state and trigger it when
needed.

Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20260326121156.98ff4115406f.Ie796487ab9eb23cda819b0afac57e7267b134911@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Miri Korenblit 2026-03-26 12:14:40 +02:00 committed by Johannes Berg
parent 840492bf33
commit e1d5c95456
4 changed files with 122 additions and 1 deletions

View File

@ -2502,7 +2502,15 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct wireless_dev *wdev,
test_sta_flag(sta, WLAN_STA_ASSOC))
rate_control_rate_init_all_links(sta);
return sta_info_insert(sta);
err = sta_info_insert(sta);
/*
* ieee80211_nan_update_ndi_carrier was called from sta_apply_parameters,
* but then we did not have the STA in the list.
*/
if (!err && sdata->vif.type == NL80211_IFTYPE_NAN_DATA)
ieee80211_nan_update_ndi_carrier(sta->sdata);
return err;
}
static int ieee80211_del_station(struct wiphy *wiphy, struct wireless_dev *wdev,

View File

@ -2045,6 +2045,7 @@ int ieee80211_nan_set_local_sched(struct ieee80211_sub_if_data *sdata,
int ieee80211_nan_set_peer_sched(struct ieee80211_sub_if_data *sdata,
struct cfg80211_nan_peer_sched *sched);
void ieee80211_nan_free_peer_sched(struct ieee80211_nan_peer_sched *sched);
void ieee80211_nan_update_ndi_carrier(struct ieee80211_sub_if_data *ndi_sdata);
/* scan/BSS handling */
void ieee80211_scan_work(struct wiphy *wiphy, struct wiphy_work *work);

View File

@ -220,6 +220,23 @@ ieee80211_nan_remove_channel(struct ieee80211_sub_if_data *sdata,
ieee80211_free_chanctx(sdata->local, ctx, false);
}
static void
ieee80211_nan_update_all_ndi_carriers(struct ieee80211_local *local)
{
struct ieee80211_sub_if_data *sdata;
lockdep_assert_wiphy(local->hw.wiphy);
/* Iterate all interfaces and update carrier for NDI interfaces */
list_for_each_entry(sdata, &local->interfaces, list) {
if (!ieee80211_sdata_running(sdata) ||
sdata->vif.type != NL80211_IFTYPE_NAN_DATA)
continue;
ieee80211_nan_update_ndi_carrier(sdata);
}
}
static struct ieee80211_nan_channel *
ieee80211_nan_find_free_channel(struct ieee80211_nan_sched_cfg *sched_cfg)
{
@ -347,6 +364,7 @@ int ieee80211_nan_set_local_sched(struct ieee80211_sub_if_data *sdata,
if (sched_cfg->deferred)
return 0;
ieee80211_nan_update_all_ndi_carriers(sdata->local);
bitmap_zero(sdata->u.nan.removed_channels, IEEE80211_NAN_MAX_CHANNELS);
return 0;
@ -409,6 +427,7 @@ int ieee80211_nan_set_local_sched(struct ieee80211_sub_if_data *sdata,
bitmap_zero(sdata->u.nan.removed_channels, IEEE80211_NAN_MAX_CHANNELS);
drv_vif_cfg_changed(sdata->local, sdata, BSS_CHANGED_NAN_LOCAL_SCHED);
ieee80211_nan_update_all_ndi_carriers(sdata->local);
return ret;
}
@ -423,6 +442,8 @@ void ieee80211_nan_sched_update_done(struct ieee80211_vif *vif)
if (WARN_ON(!sched_cfg->deferred))
return;
ieee80211_nan_update_all_ndi_carriers(sdata->local);
/*
* Clear the deferred flag before removing channels. Removing channels
* will trigger another schedule update to the driver, and there is no
@ -531,6 +552,91 @@ ieee80211_nan_init_peer_map(struct ieee80211_nan_peer_sched *peer_sched,
}
}
/*
* Check if the local schedule and a peer schedule have at least one common
* slot - a slot where both schedules are active on compatible channels.
*/
static bool
ieee80211_nan_has_common_slots(struct ieee80211_sub_if_data *sdata,
struct ieee80211_nan_peer_sched *peer_sched)
{
for (int slot = 0; slot < CFG80211_NAN_SCHED_NUM_TIME_SLOTS; slot++) {
struct ieee80211_nan_channel *local_chan =
sdata->vif.cfg.nan_sched.schedule[slot];
if (!local_chan || !local_chan->chanctx_conf)
continue;
/* Check all peer maps for this slot */
for (int m = 0; m < CFG80211_NAN_MAX_PEER_MAPS; m++) {
struct ieee80211_nan_peer_map *map = &peer_sched->maps[m];
struct ieee80211_nan_channel *peer_chan;
if (map->map_id == CFG80211_NAN_INVALID_MAP_ID)
continue;
peer_chan = map->slots[slot];
if (!peer_chan)
continue;
if (local_chan->chanctx_conf == peer_chan->chanctx_conf)
return true;
}
}
return false;
}
void ieee80211_nan_update_ndi_carrier(struct ieee80211_sub_if_data *ndi_sdata)
{
struct ieee80211_local *local = ndi_sdata->local;
struct ieee80211_sub_if_data *nmi_sdata;
struct sta_info *sta;
lockdep_assert_wiphy(local->hw.wiphy);
if (WARN_ON(ndi_sdata->vif.type != NL80211_IFTYPE_NAN_DATA ||
!ndi_sdata->dev) || !ieee80211_sdata_running(ndi_sdata))
return;
nmi_sdata = wiphy_dereference(local->hw.wiphy, ndi_sdata->u.nan_data.nmi);
if (WARN_ON(!nmi_sdata))
return;
list_for_each_entry(sta, &local->sta_list, list) {
struct ieee80211_sta *nmi_sta;
if (sta->sdata != ndi_sdata ||
!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
continue;
nmi_sta = wiphy_dereference(local->hw.wiphy, sta->sta.nmi);
if (WARN_ON(!nmi_sta) || !nmi_sta->nan_sched)
continue;
if (ieee80211_nan_has_common_slots(nmi_sdata, nmi_sta->nan_sched)) {
netif_carrier_on(ndi_sdata->dev);
return;
}
}
netif_carrier_off(ndi_sdata->dev);
}
static void
ieee80211_nan_update_peer_ndis_carrier(struct ieee80211_local *local,
struct sta_info *nmi_sta)
{
struct sta_info *sta;
lockdep_assert_wiphy(local->hw.wiphy);
list_for_each_entry(sta, &local->sta_list, list) {
if (rcu_access_pointer(sta->sta.nmi) == &nmi_sta->sta)
ieee80211_nan_update_ndi_carrier(sta->sdata);
}
}
int ieee80211_nan_set_peer_sched(struct ieee80211_sub_if_data *sdata,
struct cfg80211_nan_peer_sched *sched)
{
@ -592,6 +698,8 @@ int ieee80211_nan_set_peer_sched(struct ieee80211_sub_if_data *sdata,
goto out;
}
ieee80211_nan_update_peer_ndis_carrier(sdata->local, sta);
/* Success - free old schedule */
to_free = old_sched;
ret = 0;

View File

@ -1454,6 +1454,8 @@ static int _sta_info_move_state(struct sta_info *sta,
} else if (sta->sta_state == IEEE80211_STA_AUTHORIZED) {
ieee80211_vif_dec_num_mcast(sta->sdata);
clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
if (sta->sdata->vif.type == NL80211_IFTYPE_NAN_DATA)
ieee80211_nan_update_ndi_carrier(sta->sdata);
/*
* If we have encryption offload, flush (station) queues
@ -1482,6 +1484,8 @@ static int _sta_info_move_state(struct sta_info *sta,
set_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
ieee80211_check_fast_xmit(sta);
ieee80211_check_fast_rx(sta);
if (sta->sdata->vif.type == NL80211_IFTYPE_NAN_DATA)
ieee80211_nan_update_ndi_carrier(sta->sdata);
}
if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
sta->sdata->vif.type == NL80211_IFTYPE_AP)