wlan-ap-Telecominfraproject/feeds/ipq95xx/mac80211/patches/qca/723-mac80211-add-mlo-monitor-support.patch
John Crispin 144c5d00f4 ipq95xx/mac80211: update to ATH12.3-CS
Signed-off-by: John Crispin <john@phrozen.org>
2024-02-28 18:56:21 +01:00

913 lines
28 KiB
Diff

From f09803a38e857400355e005681b68a7303202303 Mon Sep 17 00:00:00 2001
From: Karthikeyan Kathirvel <quic_kathirve@quicinc.com>
Date: Sat, 25 Mar 2023 22:53:27 +0530
Subject: [PATCH 1/2] mac80211: add mlo monitor support
Add support to capture packets from multiple channels concurrently,
this will be capturing all the packets in a single netdev
To capture multiple channels in a single netdev execute following cmd
iw phy0 interface add mon0 type monitor
Either the channel can be set before IFUP or default channel will be
configured.
The below command helps to add multiple channels to monitor interface
iw dev mon0 add channel 11
iw dev mon0 add channel 36
iw dev mon0 add channel 49 6G
To stop capturing on a channel in mlo mode,
Channel can be deleted from the monitor interface using below command
iw dev mon0 del channel 36
Signed-off-by: Karthikeyan Kathirvel <quic_kathirve@quicinc.com>
---
net/mac80211/cfg.c | 169 ++++++++++++++++++++++++++++++-------
net/mac80211/chan.c | 22 ++++-
net/mac80211/ieee80211_i.h | 5 +-
net/mac80211/iface.c | 158 +++++++++++++++++++++-------------
net/mac80211/main.c | 88 +++++++++++++------
net/mac80211/rx.c | 16 +++-
net/mac80211/tx.c | 9 +-
net/mac80211/util.c | 55 +++++++-----
8 files changed, 372 insertions(+), 150 deletions(-)
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 6caf3a7a3f06..bfc8f2110c86 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -86,6 +86,7 @@ static int ieee80211_set_mon_options(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_sub_if_data *monitor_sdata;
+ int i;
/* check flags first */
if (params->flags && ieee80211_sdata_running(sdata)) {
@@ -104,18 +105,21 @@ static int ieee80211_set_mon_options(struct ieee80211_sub_if_data *sdata,
return -EBUSY;
}
- /* also validate MU-MIMO change */
- monitor_sdata = wiphy_dereference(local->hw.wiphy,
- local->monitor_sdata);
+ i = 0;
+ do {
+ /* also validate MU-MIMO change */
+ monitor_sdata = wiphy_dereference(local->hw.wiphy,
+ local->monitor_sdata[i]);
- if (!monitor_sdata &&
- (params->vht_mumimo_groups || params->vht_mumimo_follow_addr))
- return -EOPNOTSUPP;
+ if (!monitor_sdata &&
+ (params->vht_mumimo_groups || params->vht_mumimo_follow_addr))
+ return -EOPNOTSUPP;
- /* apply all changes now - no failures allowed */
+ /* apply all changes now - no failures allowed */
- if (monitor_sdata)
- ieee80211_set_mu_mimo_follow(monitor_sdata, params);
+ if (monitor_sdata)
+ ieee80211_set_mu_mimo_follow(monitor_sdata, params);
+ } while (++i < local->hw.wiphy->num_hw);
if (params->flags) {
if (ieee80211_sdata_running(sdata)) {
@@ -917,15 +921,66 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
{
struct ieee80211_local *local = wiphy_priv(wiphy);
struct ieee80211_sub_if_data *sdata;
- int ret = 0;
+ int ret = 0, i, def_idx = 0, hw_idx;
- if (cfg80211_chandef_identical(&local->monitor_chandef, chandef))
+ if (wiphy->num_hw) {
+ hw_idx = cfg80211_get_hw_idx_by_chan(local->hw.wiphy, chandef->chan);
+ if (hw_idx >= 0)
+ def_idx = hw_idx;
+ } else if (cfg80211_chandef_identical(&local->monitor_chandef[def_idx], chandef))
return 0;
mutex_lock(&local->mtx);
if (local->use_chanctx) {
+ /* Clear all chanctx set only the user chandef */
+ i = 0;
+ do {
+ sdata = wiphy_dereference(local->hw.wiphy,
+ local->monitor_sdata[i]);
+ if (sdata)
+ ieee80211_link_release_channel(&sdata->deflink);
+ memset(&local->monitor_chandef[i], 0, sizeof(struct cfg80211_chan_def));
+ } while (++i < local->hw.wiphy->num_hw);
sdata = wiphy_dereference(local->hw.wiphy,
- local->monitor_sdata);
+ local->monitor_sdata[def_idx]);
+ if (sdata)
+ ret = ieee80211_link_use_channel(&sdata->deflink,
+ chandef,
+ IEEE80211_CHANCTX_EXCLUSIVE);
+ } else if (local->open_count == local->monitors) {
+ local->_oper_chandef = *chandef;
+ ieee80211_hw_config(local, 0);
+ }
+
+ if (ret == 0)
+ local->monitor_chandef[def_idx] = *chandef;
+ mutex_unlock(&local->mtx);
+
+ return ret;
+}
+
+static int ieee80211_add_monitor_channel(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct ieee80211_sub_if_data *sdata;
+ int ret = 0, hw_idx = 0;
+
+ if (wiphy->num_hw) {
+ hw_idx = cfg80211_get_hw_idx_by_chan(local->hw.wiphy, chandef->chan);
+ if (hw_idx < 0)
+ return -EINVAL;
+ } else {
+ return -ENOTSUPP;
+ }
+
+ if (cfg80211_chandef_identical(&local->monitor_chandef[hw_idx], chandef))
+ return 0;
+
+ mutex_lock(&local->mtx);
+ if (local->use_chanctx) {
+ sdata = wiphy_dereference(local->hw.wiphy,
+ local->monitor_sdata[hw_idx]);
if (sdata) {
ieee80211_link_release_channel(&sdata->deflink);
ret = ieee80211_link_use_channel(&sdata->deflink,
@@ -938,12 +993,49 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
}
if (ret == 0)
- local->monitor_chandef = *chandef;
+ local->monitor_chandef[hw_idx] = *chandef;
mutex_unlock(&local->mtx);
return ret;
}
+static int ieee80211_del_monitor_channel(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct ieee80211_sub_if_data *sdata;
+ int hw_idx = 0;
+
+ if (wiphy->num_hw) {
+ hw_idx = cfg80211_get_hw_idx_by_chan(local->hw.wiphy, chandef->chan);
+ if (hw_idx < 0)
+ return -EINVAL;
+ } else {
+ return -ENOTSUPP;
+ }
+
+ /* check only channel and delete it, unlike
+ * add channel checks for chandef
+ */
+ if (!cfg80211_channel_identical(&local->monitor_chandef[hw_idx].chan, chandef->chan))
+ return -EINVAL;
+
+ mutex_lock(&local->mtx);
+ if (local->use_chanctx) {
+ sdata = wiphy_dereference(local->hw.wiphy,
+ local->monitor_sdata[hw_idx]);
+ if (sdata) {
+ ieee80211_link_release_channel(&sdata->deflink);
+ memset(&local->monitor_chandef[hw_idx], 0, sizeof(struct cfg80211_chan_def));
+ mutex_unlock(&local->mtx);
+ return 0;
+ }
+ }
+ mutex_unlock(&local->mtx);
+
+ return 0;
+}
+
static int
ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
const u8 *resp, size_t resp_len,
@@ -3112,6 +3204,7 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
bool update_txp_type = false;
bool has_monitor = false;
struct ieee80211_link_data *link_data;
+ int i, hw_idx = 0;
if (wdev) {
sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
@@ -3121,8 +3214,14 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
return -ENOLINK;
if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
+ if (wiphy->num_hw) {
+ hw_idx = cfg80211_get_hw_idx_by_chan(wiphy, link_data->conf->chandef.chan);
+ if (hw_idx < 0)
+ return -EINVAL;
+ }
+
sdata = wiphy_dereference(local->hw.wiphy,
- local->monitor_sdata);
+ local->monitor_sdata[hw_idx]);
if (!sdata)
return -EOPNOTSUPP;
}
@@ -3164,7 +3263,6 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
break;
}
- /*TODO: monitor mode for MLO */
mutex_lock(&local->iflist_mtx);
list_for_each_entry(sdata, &local->interfaces, list) {
if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
@@ -3184,16 +3282,19 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
mutex_unlock(&local->iflist_mtx);
if (has_monitor) {
- sdata = wiphy_dereference(local->hw.wiphy,
- local->monitor_sdata);
- if (sdata) {
- sdata->deflink.user_power_level = local->user_power_level;
- if (txp_type != sdata->vif.bss_conf.txpower_type)
- update_txp_type = true;
- sdata->vif.bss_conf.txpower_type = txp_type;
-
- ieee80211_recalc_txpower(sdata, update_txp_type, 0);
- }
+ i = 0;
+ do {
+ sdata = wiphy_dereference(local->hw.wiphy,
+ local->monitor_sdata[i]);
+ if (sdata) {
+ sdata->deflink.user_power_level = local->user_power_level;
+ if (txp_type != sdata->vif.bss_conf.txpower_type)
+ update_txp_type = true;
+ sdata->vif.bss_conf.txpower_type = txp_type;
+
+ ieee80211_recalc_txpower(sdata, update_txp_type, link_id);
+ }
+ } while (++i < local->hw.wiphy->num_hw);
}
return 0;
@@ -4402,7 +4503,7 @@ static int ieee80211_cfg_get_channel(struct wiphy *wiphy,
struct ieee80211_local *local = wiphy_priv(wiphy);
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_link_data *link;
- int ret = -ENODATA;
+ int ret = -ENODATA, i;
rcu_read_lock();
link = rcu_dereference(sdata->link[link_id]);
@@ -4418,10 +4519,18 @@ static int ieee80211_cfg_get_channel(struct wiphy *wiphy,
} else if (local->open_count > 0 &&
local->open_count == local->monitors &&
sdata->vif.type == NL80211_IFTYPE_MONITOR) {
- if (local->use_chanctx)
- *chandef = local->monitor_chandef;
- else
+ if (local->use_chanctx) {
+ i = 0;
+ do {
+ if (local->monitor_chandef[i].chan) {
+ /* Get a valid chan and return */
+ *chandef = local->monitor_chandef[i];
+ break;
+ }
+ } while (++i < local->hw.wiphy->num_hw);
+ } else {
*chandef = local->_oper_chandef;
+ }
ret = 0;
}
out:
@@ -5316,6 +5425,8 @@ const struct cfg80211_ops mac80211_config_ops = {
.change_bss = ieee80211_change_bss,
.set_txq_params = ieee80211_set_txq_params,
.set_monitor_channel = ieee80211_set_monitor_channel,
+ .add_monitor_channel = ieee80211_add_monitor_channel,
+ .del_monitor_channel = ieee80211_del_monitor_channel,
.suspend = ieee80211_suspend,
.resume = ieee80211_resume,
.scan = ieee80211_scan,
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 7d2631354a92..2354d38c1228 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -337,6 +337,7 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
{
struct ieee80211_sub_if_data *sdata;
enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT;
+ int hw_idx = 0;
rcu_read_lock();
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
@@ -350,8 +351,16 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
max_bw = max(max_bw, width);
}
+ if (local->hw.wiphy->num_hw) {
+ hw_idx = cfg80211_get_hw_idx_by_chan(local->hw.wiphy, conf->def.chan);
+ if (hw_idx < 0) {
+ rcu_read_unlock();
+ return max_bw;
+ }
+ }
+
/* use the configured bandwidth in case of monitor interface */
- sdata = rcu_dereference(local->monitor_sdata);
+ sdata = rcu_dereference(local->monitor_sdata[hw_idx]);
if (sdata &&
rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) == conf)
max_bw = max(max_bw, conf->def.width);
@@ -927,6 +936,7 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
{
struct ieee80211_sub_if_data *sdata;
u8 rx_chains_static, rx_chains_dynamic;
+ int hw_idx = 0;
lockdep_assert_held(&local->chanctx_mtx);
@@ -990,8 +1000,16 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
}
}
+ if (local->hw.wiphy->num_hw) {
+ hw_idx = cfg80211_get_hw_idx_by_chan(local->hw.wiphy, chanctx->conf.def.chan);
+ if (hw_idx < 0) {
+ rcu_read_unlock();
+ return;
+ }
+ }
+
/* Disable SMPS for the monitor interface */
- sdata = rcu_dereference(local->monitor_sdata);
+ sdata = rcu_dereference(local->monitor_sdata[hw_idx]);
if (sdata &&
rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) == &chanctx->conf)
rx_chains_dynamic = rx_chains_static = local->rx_chains;
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index c525789f808a..1400946c8e08 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1588,8 +1588,9 @@ struct ieee80211_local {
struct ieee80211_sub_if_data __rcu *p2p_sdata;
/* virtual monitor interface */
- struct ieee80211_sub_if_data __rcu *monitor_sdata;
- struct cfg80211_chan_def monitor_chandef;
+ struct ieee80211_sub_if_data __rcu **monitor_sdata;
+ struct cfg80211_chan_def *monitor_chandef;
+ bool monitor_sdata_created;
struct mac80211_memory_stats memory_stats;
struct work_struct awgn_detected_work;
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 90fad651777d..1a342fb5a154 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -400,7 +400,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
unsigned long flags;
struct sk_buff *skb, *tmp;
u32 hw_reconf_flags = 0;
- int i, flushed;
+ int i, flushed, hw_idx = 0;
struct ps_data *ps;
struct cfg80211_chan_def chandef;
bool cancel_scan;
@@ -623,7 +623,14 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
*/
if (local->suspended) {
WARN_ON(local->wowlan);
- WARN_ON(rcu_access_pointer(local->monitor_sdata));
+
+ if (local->hw.wiphy->num_hw) {
+ hw_idx = cfg80211_get_hw_idx_by_chan(local->hw.wiphy, sdata->deflink.conf->chandef.chan);
+ if (hw_idx < 0)
+ return;
+ }
+
+ WARN_ON(rcu_access_pointer(local->monitor_sdata[hw_idx]));
return;
}
@@ -1065,7 +1072,7 @@ static void ieee80211_sdata_init(struct ieee80211_local *local,
int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
{
struct ieee80211_sub_if_data *sdata;
- int ret;
+ int ret, i = 0;
if (!ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF))
return 0;
@@ -1073,99 +1080,128 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
ASSERT_RTNL();
lockdep_assert_wiphy(local->hw.wiphy);
- if (local->monitor_sdata)
+ if (local->monitor_sdata_created)
return 0;
- sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL);
- if (!sdata)
- return -ENOMEM;
+ do {
+ mutex_lock(&local->mtx);
+ if (!local->monitor_chandef[i].chan) {
+ if (i < local->hw.wiphy->num_hw) {
+ mutex_unlock(&local->mtx);
+ continue;
+ }
+ }
+ mutex_unlock(&local->mtx);
- /* set up data */
- sdata->vif.type = NL80211_IFTYPE_MONITOR;
- snprintf(sdata->name, IFNAMSIZ, "%s-monitor",
- wiphy_name(local->hw.wiphy));
- sdata->wdev.iftype = NL80211_IFTYPE_MONITOR;
+ sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL);
+ if (!sdata)
+ return -ENOMEM;
- ieee80211_sdata_init(local, sdata);
+ /* set up data */
+ sdata->vif.type = NL80211_IFTYPE_MONITOR;
+ snprintf(sdata->name, IFNAMSIZ, "%s-monitor",
+ wiphy_name(local->hw.wiphy));
+ sdata->wdev.iftype = NL80211_IFTYPE_MONITOR;
- ieee80211_set_default_queues(sdata);
+ ieee80211_sdata_init(local, sdata);
- ret = drv_add_interface(local, sdata);
- if (WARN_ON(ret)) {
- /* ok .. stupid driver, it asked for this! */
- kfree(sdata);
- return ret;
- }
+ ieee80211_set_default_queues(sdata);
- set_bit(SDATA_STATE_RUNNING, &sdata->state);
+ ret = drv_add_interface(local, sdata);
+ if (WARN_ON(ret))
+ /* ok .. stupid driver, it asked for this! */
+ goto free;
- ret = ieee80211_check_queues(sdata, NL80211_IFTYPE_MONITOR);
- if (ret) {
- kfree(sdata);
- return ret;
- }
+ set_bit(SDATA_STATE_RUNNING, &sdata->state);
- mutex_lock(&local->iflist_mtx);
- rcu_assign_pointer(local->monitor_sdata, sdata);
- mutex_unlock(&local->iflist_mtx);
+ ret = ieee80211_check_queues(sdata, NL80211_IFTYPE_MONITOR);
+ if (ret)
+ goto free;
- mutex_lock(&local->mtx);
- ret = ieee80211_link_use_channel(&sdata->deflink, &local->monitor_chandef,
- IEEE80211_CHANCTX_EXCLUSIVE);
- mutex_unlock(&local->mtx);
- if (ret) {
mutex_lock(&local->iflist_mtx);
- RCU_INIT_POINTER(local->monitor_sdata, NULL);
+ rcu_assign_pointer(local->monitor_sdata[i], sdata);
+ local->monitor_sdata_created = true;
mutex_unlock(&local->iflist_mtx);
- synchronize_net();
- drv_remove_interface(local, sdata);
- kfree(sdata);
- return ret;
- }
- atomic_add(sizeof(*sdata) + local->hw.vif_data_size,
- &local->memory_stats.malloc_size);
+ mutex_lock(&local->mtx);
+ ret = ieee80211_link_use_channel(&sdata->deflink, &local->monitor_chandef[i],
+ IEEE80211_CHANCTX_EXCLUSIVE);
+ mutex_unlock(&local->mtx);
+ if (ret)
+ goto free;
- skb_queue_head_init(&sdata->skb_queue);
- skb_queue_head_init(&sdata->status_queue);
- INIT_WORK(&sdata->work, ieee80211_iface_work);
+ atomic_add(sizeof(*sdata) + local->hw.vif_data_size,
+ &local->memory_stats.malloc_size);
+
+ skb_queue_head_init(&sdata->skb_queue);
+ skb_queue_head_init(&sdata->status_queue);
+ INIT_WORK(&sdata->work, ieee80211_iface_work);
+ } while (++i < local->hw.wiphy->num_hw);
return 0;
+
+free:
+ mutex_lock(&local->iflist_mtx);
+ do {
+ RCU_INIT_POINTER(local->monitor_sdata[i], NULL);
+ synchronize_net();
+
+
+ sdata = rcu_dereference_protected(local->monitor_sdata[i],
+ lockdep_is_held(&local->iflist_mtx));
+ if (!sdata)
+ continue;
+
+ drv_remove_interface(local, sdata);
+ kfree(sdata);
+ } while (--i >= 0);
+ mutex_unlock(&local->iflist_mtx);
+ local->monitor_sdata_created = false;
+ return ret;
}
void ieee80211_del_virtual_monitor(struct ieee80211_local *local)
{
struct ieee80211_sub_if_data *sdata;
+ int i;
if (!ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF))
return;
+ if (!local->monitor_sdata_created)
+ return;
+
ASSERT_RTNL();
lockdep_assert_wiphy(local->hw.wiphy);
- mutex_lock(&local->iflist_mtx);
- sdata = rcu_dereference_protected(local->monitor_sdata,
- lockdep_is_held(&local->iflist_mtx));
- if (!sdata) {
+ i = 0;
+ do {
+ mutex_lock(&local->iflist_mtx);
+ sdata = rcu_dereference_protected(local->monitor_sdata[i],
+ lockdep_is_held(&local->iflist_mtx));
+ if (!sdata) {
+ mutex_unlock(&local->iflist_mtx);
+ continue;
+ }
+
+ RCU_INIT_POINTER(local->monitor_sdata[i], NULL);
mutex_unlock(&local->iflist_mtx);
- return;
- }
- RCU_INIT_POINTER(local->monitor_sdata, NULL);
- mutex_unlock(&local->iflist_mtx);
+ synchronize_net();
- synchronize_net();
+ mutex_lock(&local->mtx);
+ ieee80211_link_release_channel(&sdata->deflink);
+ mutex_unlock(&local->mtx);
- mutex_lock(&local->mtx);
- ieee80211_link_release_channel(&sdata->deflink);
- mutex_unlock(&local->mtx);
+ drv_remove_interface(local, sdata);
- drv_remove_interface(local, sdata);
+ atomic_sub(sizeof(*sdata) + local->hw.vif_data_size,
+ &local->memory_stats.malloc_size);
+ kfree(sdata);
+ } while (++i < local->hw.wiphy->num_hw);
- atomic_sub(sizeof(*sdata) + local->hw.vif_data_size,
- &local->memory_stats.malloc_size);
- kfree(sdata);
+ local->monitor_sdata_created = false;
}
#ifdef CPTCFG_MAC80211_NSS_SUPPORT
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index bf8802fe60f1..8a12504fbfce 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -987,7 +987,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
enum nl80211_band band;
int channels, max_bitrates;
bool supp_ht, supp_vht, supp_he, supp_eht;
- struct cfg80211_chan_def dflt_chandef = {};
+ size_t size = 0;
+ int hw_idx = 0, num_hw;
if (ieee80211_hw_check(hw, QUEUE_CONTROL) &&
(local->hw.offchannel_tx_hw_queue == IEEE80211_INVAL_HW_QUEUE ||
@@ -1133,35 +1134,56 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
supp_vht = false;
supp_he = false;
supp_eht = false;
+
+ if (local->hw.wiphy->num_hw)
+ num_hw = local->hw.wiphy->num_hw;
+ else
+ num_hw = 1;
+ size = sizeof(*local->monitor_chandef) * num_hw;
+ local->monitor_chandef = kzalloc(size, GFP_KERNEL);
+ if (!local->monitor_chandef)
+ return -ENOMEM;
+
+ size = sizeof(**local->monitor_sdata) * num_hw;
+ local->monitor_sdata = kzalloc(size, GFP_KERNEL);
+ if (!local->monitor_sdata) {
+ kfree(local->monitor_chandef);
+ return -ENOMEM;
+ }
+
for (band = 0; band < NUM_NL80211_BANDS; band++) {
struct ieee80211_supported_band *sband;
+ struct cfg80211_chan_def dflt_chandef = {};
sband = local->hw.wiphy->bands[band];
if (!sband)
continue;
- if (!dflt_chandef.chan) {
- /*
- * Assign the first enabled channel to dflt_chandef
- * from the list of channels
- */
- for (i = 0; i < sband->n_channels; i++)
- if (!(sband->channels[i].flags &
+ /*
+ * Assign the first enabled channel to dflt_chandef
+ * from the list of channels
+ */
+ for (i = 0; i < sband->n_channels; i++)
+ if (!(sband->channels[i].flags &
IEEE80211_CHAN_DISABLED))
- break;
- /* if none found then use the first anyway */
- if (i == sband->n_channels)
- i = 0;
- cfg80211_chandef_create(&dflt_chandef,
- &sband->channels[i],
- NL80211_CHAN_NO_HT);
- /* init channel we're on */
- if (!local->use_chanctx && !local->_oper_chandef.chan) {
- local->hw.conf.chandef = dflt_chandef;
- local->_oper_chandef = dflt_chandef;
- }
- local->monitor_chandef = dflt_chandef;
+ break;
+ /* if none found then use the first anyway */
+ if (i == sband->n_channels)
+ i = 0;
+ cfg80211_chandef_create(&dflt_chandef,
+ &sband->channels[i],
+ NL80211_CHAN_NO_HT);
+ /* init channel we're on */
+ if (!local->use_chanctx && !local->_oper_chandef.chan) {
+ local->hw.conf.chandef = dflt_chandef;
+ local->_oper_chandef = dflt_chandef;
}
+ hw_idx = cfg80211_get_hw_idx_by_chan(local->hw.wiphy, dflt_chandef.chan);
+ if (hw_idx < 0)
+ /* Resetting to use first index for non-MLO case */
+ hw_idx = 0;
+
+ local->monitor_chandef[hw_idx] = dflt_chandef;
channels += sband->n_channels;
@@ -1181,12 +1203,16 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
/* HT, VHT, HE, EHT require QoS, thus >= 4 queues */
if (WARN_ON(local->hw.queues < IEEE80211_NUM_ACS &&
- (supp_ht || supp_vht || supp_he || supp_eht)))
- return -EINVAL;
+ (supp_ht || supp_vht || supp_he || supp_eht))) {
+ result = -EINVAL;
+ goto fail_monitor_chan;
+ }
/* EHT requires HE support */
- if (WARN_ON(supp_eht && !supp_he))
- return -EINVAL;
+ if (WARN_ON(supp_eht && !supp_he)) {
+ result = -EINVAL;
+ goto fail_monitor_chan;
+ }
if (!sband->ht_cap.ht_supported)
continue;
@@ -1220,8 +1246,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
for (j = 0; j < c->n_limits; j++)
if ((c->limits[j].types & BIT(NL80211_IFTYPE_ADHOC)) &&
- c->limits[j].max > 1)
- return -EINVAL;
+ c->limits[j].max > 1) {
+ result = -EINVAL;
+ goto fail_monitor_chan;
+ }
}
local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) +
@@ -1516,6 +1544,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if (local->wiphy_ciphers_allocated)
kfree(local->hw.wiphy->cipher_suites);
kfree(local->int_scan_req);
+fail_monitor_chan:
+ kfree(local->monitor_chandef);
+ kfree(local->monitor_sdata);
return result;
}
EXPORT_SYMBOL(ieee80211_register_hw);
@@ -1565,6 +1596,9 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
destroy_workqueue(local->workqueue);
ieee80211_led_exit(local);
kfree(local->int_scan_req);
+
+ kfree(local->monitor_chandef);
+ kfree(local->monitor_sdata);
}
EXPORT_SYMBOL(ieee80211_unregister_hw);
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 3053db03f6e5..227c323f663b 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -866,10 +866,9 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(origskb);
struct ieee80211_sub_if_data *sdata;
struct sk_buff *monskb = NULL;
- int present_fcs_len = 0;
+ int present_fcs_len = 0, hw_idx = 0;
unsigned int rtap_space = 0;
- struct ieee80211_sub_if_data *monitor_sdata =
- rcu_dereference(local->monitor_sdata);
+ struct ieee80211_sub_if_data *monitor_sdata;
bool only_monitor = false;
unsigned int min_head_len;
@@ -941,6 +940,17 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
rtap_space);
}
+ if (local->hw.wiphy->num_hw) {
+ hw_idx = cfg80211_get_hw_idx_by_freq(local->hw.wiphy, status->freq);
+ if (hw_idx < 0) {
+ WARN_ON_ONCE(1);
+ dev_kfree_skb(origskb);
+ return NULL;
+ }
+ }
+
+ monitor_sdata = rcu_dereference(local->monitor_sdata[hw_idx]);
+
ieee80211_handle_mu_mimo_mon(monitor_sdata, origskb, rtap_space);
list_for_each_entry_rcu(sdata, &local->mon_list, u.mntr.list) {
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 94a8785c9976..0e8988fa9768 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1823,7 +1823,10 @@ static bool __ieee80211_tx(struct ieee80211_local *local,
vif = &sdata->vif;
break;
}
- sdata = rcu_dereference(local->monitor_sdata);
+ /* Here we wont know the chan to tx except the band info
+ * so tx in first hw_idx
+ */
+ sdata = rcu_dereference(local->monitor_sdata[0]);
if (sdata) {
vif = &sdata->vif;
info->hw_queue =
@@ -2427,7 +2430,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf);
if (!chanctx_conf) {
- tmp_sdata = rcu_dereference(local->monitor_sdata);
+ tmp_sdata = rcu_dereference(local->monitor_sdata[0]);
if (tmp_sdata)
chanctx_conf =
rcu_dereference(tmp_sdata->vif.bss_conf.chanctx_conf);
@@ -3984,7 +3987,7 @@ begin:
vif = &tx.sdata->vif;
break;
}
- tx.sdata = rcu_dereference(local->monitor_sdata);
+ tx.sdata = rcu_dereference(local->monitor_sdata[0]);
if (tx.sdata) {
vif = &tx.sdata->vif;
info->hw_queue =
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 0902c8319ed0..e5095f8153ab 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -759,6 +759,7 @@ static void __iterate_interfaces(struct ieee80211_local *local,
{
struct ieee80211_sub_if_data *sdata;
bool active_only = iter_flags & IEEE80211_IFACE_ITER_ACTIVE;
+ int i;
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
switch (sdata->vif.type) {
@@ -782,13 +783,16 @@ static void __iterate_interfaces(struct ieee80211_local *local,
&sdata->vif);
}
- sdata = rcu_dereference_check(local->monitor_sdata,
- lockdep_is_held(&local->iflist_mtx) ||
- lockdep_is_held(&local->hw.wiphy->mtx));
- if (sdata &&
- (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || !active_only ||
- sdata->flags & IEEE80211_SDATA_IN_DRIVER))
- iterator(data, sdata->vif.addr, &sdata->vif);
+ i = 0;
+ do {
+ sdata = rcu_dereference_check(local->monitor_sdata[i],
+ lockdep_is_held(&local->iflist_mtx) ||
+ lockdep_is_held(&local->hw.wiphy->mtx));
+ if (sdata &&
+ (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || !active_only ||
+ sdata->flags & IEEE80211_SDATA_IN_DRIVER))
+ iterator(data, sdata->vif.addr, &sdata->vif);
+ } while (++i < local->hw.wiphy->num_hw);
}
void ieee80211_iterate_interfaces(
@@ -2488,7 +2492,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
struct ieee80211_sub_if_data *sdata;
struct ieee80211_chanctx *ctx;
struct sta_info *sta;
- int res;
+ int res, i;
bool reconfig_due_to_wowlan = false;
struct ieee80211_sub_if_data *sched_scan_sdata;
struct cfg80211_sched_scan_request *sched_scan_req;
@@ -2572,18 +2576,21 @@ int ieee80211_reconfig(struct ieee80211_local *local)
ieee80211_mod_tpt_led_trig(local,
IEEE80211_TPT_LEDTRIG_FL_RADIO, 0);
- /* add interfaces */
- sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata);
- if (sdata) {
- /* in HW restart it exists already */
- WARN_ON(local->resuming);
- res = drv_add_interface(local, sdata);
- if (WARN_ON(res)) {
- RCU_INIT_POINTER(local->monitor_sdata, NULL);
- synchronize_net();
- kfree(sdata);
+ i = 0;
+ do {
+ /* add interfaces */
+ sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata[i]);
+ if (sdata) {
+ /* in HW restart it exists already */
+ WARN_ON(local->resuming);
+ res = drv_add_interface(local, sdata);
+ if (WARN_ON(res)) {
+ RCU_INIT_POINTER(local->monitor_sdata[i], NULL);
+ synchronize_net();
+ kfree(sdata);
+ }
}
- }
+ } while (++i < local->hw.wiphy->num_hw);;
list_for_each_entry(sdata, &local->interfaces, list) {
if ((sdata->vif.type != NL80211_IFTYPE_AP_VLAN ||
@@ -2620,11 +2627,13 @@ int ieee80211_reconfig(struct ieee80211_local *local)
WARN_ON(drv_add_chanctx(local, ctx));
mutex_unlock(&local->chanctx_mtx);
- sdata = wiphy_dereference(local->hw.wiphy,
- local->monitor_sdata);
+ do {
+ sdata = wiphy_dereference(local->hw.wiphy,
+ local->monitor_sdata[i]);
- if (sdata && ieee80211_sdata_running(sdata))
- ieee80211_assign_chanctx(local, sdata, &sdata->deflink);
+ if (sdata && ieee80211_sdata_running(sdata))
+ ieee80211_assign_chanctx(local, sdata, &sdata->deflink);
+ } while (++i < local->hw.wiphy->num_hw);
}
/* reconfigure hardware */
--
2.38.0