From: Johannes Berg Implement the mgmt_tx_unlocked() method and accept frames that are transmitted on the operating channel with it, as they don't need further handling and can go out directly. For off-channel etc. just return -EAGAIN to fall back to the locked version. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 1 + net/mac80211/ieee80211_i.h | 3 + net/mac80211/offchannel.c | 109 +++++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 30fe29f25928..daabac9fc6e6 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -6316,6 +6316,7 @@ const struct cfg80211_ops mac80211_config_ops = { .remain_on_channel = ieee80211_remain_on_channel, .cancel_remain_on_channel = ieee80211_cancel_remain_on_channel, .mgmt_tx = ieee80211_mgmt_tx, + .mgmt_tx_unlocked = ieee80211_mgmt_tx_unlocked, .mgmt_tx_cancel_wait = ieee80211_mgmt_tx_cancel_wait, .set_cqm_rssi_config = ieee80211_set_cqm_rssi_config, .set_cqm_rssi_range_config = ieee80211_set_cqm_rssi_range_config, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 16c1e0337f3a..bf9f26234662 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2192,6 +2192,9 @@ int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie); int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_mgmt_tx_params *params, u64 cookie); +int ieee80211_mgmt_tx_unlocked(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_mgmt_tx_params *params, + u64 cookie); int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie); diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index 27ca2984826f..44808120ff51 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -1074,6 +1074,115 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, return ret; } +int ieee80211_mgmt_tx_unlocked(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_mgmt_tx_params *params, + u64 cookie) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); + const struct ieee80211_mgmt *mgmt = (void *)params->buf; + struct ieee80211_local *local = sdata->local; + struct sta_info *sta = NULL; + enum nl80211_band band; + struct sk_buff *skb; + int link_id = -1, i; + + if (READ_ONCE(local->in_reconfig) || READ_ONCE(local->quiescing)) + return -EAGAIN; + + guard(rcu)(); + + /* do_stop() synchronizes RCU before the interface type can change */ + if (!ieee80211_sdata_running(sdata)) + return -EAGAIN; + + switch (sdata->vif.type) { + case NL80211_IFTYPE_NAN_DATA: + if (is_multicast_ether_addr(mgmt->da)) + return -EOPNOTSUPP; + fallthrough; + case NL80211_IFTYPE_NAN: + /* Frames can be sent during NAN schedule */ + skb = ieee80211_mgmt_tx_skb(sdata, params, cookie, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + __ieee80211_tx_skb_tid_band(sdata, skb, + params->no_sta ? ERR_PTR(-ENOENT) : + NULL, + 7, -1, NUM_NL80211_BANDS); + return 0; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + if (sdata->bss->active) + break; + fallthrough; + default: + return -EAGAIN; + } + + if (!params->no_sta) + sta = sta_info_get_bss(sdata, mgmt->da); + + if (ieee80211_is_action(mgmt->frame_control) && + mgmt->u.action.category != WLAN_CATEGORY_PUBLIC && + mgmt->u.action.category != WLAN_CATEGORY_SELF_PROTECTED && + mgmt->u.action.category != WLAN_CATEGORY_SPECTRUM_MGMT) { + if (!sta) + return -ENOLINK; + if (params->link_id >= 0 && + !(sta->sta.valid_links & BIT(params->link_id))) + return -ENOLINK; + link_id = params->link_id; + } + + if ((params->chan || !sta || !sta->sta.mlo) && + ieee80211_mgmt_tx_need_offchan(sdata, params, mgmt, + sta && sta->sta.mlo, &link_id)) + return -EAGAIN; + + /* things may have changed, avoid WARNs in ieee80211_tx_skb_tid() */ + if (ieee80211_vif_is_mld(&sdata->vif)) { + band = 0; + + if (link_id < 0 && + !ether_addr_equal(sdata->vif.addr, mgmt->sa)) { + for (i = 0; i < ARRAY_SIZE(sdata->vif.link_conf); i++) { + struct ieee80211_bss_conf *conf; + + conf = rcu_dereference(sdata->vif.link_conf[i]); + if (conf && + ether_addr_equal(conf->addr, mgmt->sa)) { + link_id = i; + break; + } + } + if (link_id < 0) + return -EAGAIN; + } + + if (link_id >= 0 && + !(READ_ONCE(sdata->vif.active_links) & BIT(link_id))) + return -EAGAIN; + } else { + struct ieee80211_chanctx_conf *chanctx_conf; + + chanctx_conf = + rcu_dereference(sdata->vif.bss_conf.chanctx_conf); + if (!chanctx_conf) + return -EAGAIN; + band = chanctx_conf->def.chan->band; + } + + skb = ieee80211_mgmt_tx_skb(sdata, params, cookie, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + __ieee80211_tx_skb_tid_band(sdata, skb, + params->no_sta ? ERR_PTR(-ENOENT) : sta, + 7, link_id, band); + return 0; +} + int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie) { -- 2.56.0