Initial commit
Some checks failed
Build Kernel / Build all affected Kernels (push) Has been cancelled
Build all core packages / Build all core packages for selected target (push) Has been cancelled
Build and Push prebuilt tools container / Build and Push all prebuilt containers (push) Has been cancelled
Build Toolchains / Build Toolchains for each target (push) Has been cancelled
Build host tools / Build host tools for linux and macos based systems (push) Has been cancelled
Coverity scan build / Coverity x86/64 build (push) Has been cancelled
Some checks failed
Build Kernel / Build all affected Kernels (push) Has been cancelled
Build all core packages / Build all core packages for selected target (push) Has been cancelled
Build and Push prebuilt tools container / Build and Push all prebuilt containers (push) Has been cancelled
Build Toolchains / Build Toolchains for each target (push) Has been cancelled
Build host tools / Build host tools for linux and macos based systems (push) Has been cancelled
Coverity scan build / Coverity x86/64 build (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Mon, 27 Oct 2014 00:00:00 +0100
|
||||
Subject: [PATCH] mac80211: preseve AP mode keys across STA reconnect
|
||||
|
||||
Used for AP+STA support in OpenWrt - preserve AP mode keys across STA reconnect
|
||||
---
|
||||
net/mac80211/cfg.c | 1 -
|
||||
1 file changed, 1 deletion(-)
|
||||
|
||||
--- a/net/mac80211/cfg.c
|
||||
+++ b/net/mac80211/cfg.c
|
||||
@@ -1647,12 +1647,6 @@ static int ieee80211_stop_ap(struct wiph
|
||||
|
||||
__sta_info_flush(sdata, true, link_id);
|
||||
|
||||
- ieee80211_remove_link_keys(link, &keys);
|
||||
- if (!list_empty(&keys)) {
|
||||
- synchronize_net();
|
||||
- ieee80211_free_key_list(local, &keys);
|
||||
- }
|
||||
-
|
||||
link_conf->enable_beacon = false;
|
||||
sdata->beacon_rate_set = false;
|
||||
sdata->vif.cfg.ssid_len = 0;
|
||||
@@ -0,0 +1,52 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Thu, 11 Dec 2014 00:00:00 +0100
|
||||
Subject: [PATCH] cfg80211: add support for changing the device mac address via
|
||||
sysfs
|
||||
|
||||
---
|
||||
net/wireless/sysfs.c | 27 ++++++++++++++++++++++-----
|
||||
1 file changed, 22 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/net/wireless/sysfs.c
|
||||
+++ b/net/wireless/sysfs.c
|
||||
@@ -24,18 +24,35 @@ static inline struct cfg80211_registered
|
||||
return container_of(dev, struct cfg80211_registered_device, wiphy.dev);
|
||||
}
|
||||
|
||||
-#define SHOW_FMT(name, fmt, member) \
|
||||
+#define SHOW_FMT(name, fmt, member, mode) \
|
||||
static ssize_t name ## _show(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member); \
|
||||
} \
|
||||
-static DEVICE_ATTR_RO(name)
|
||||
+static DEVICE_ATTR_##mode(name)
|
||||
|
||||
-SHOW_FMT(index, "%d", wiphy_idx);
|
||||
-SHOW_FMT(macaddress, "%pM", wiphy.perm_addr);
|
||||
-SHOW_FMT(address_mask, "%pM", wiphy.addr_mask);
|
||||
+static ssize_t macaddress_store(struct device *dev,
|
||||
+ struct device_attribute *attr,
|
||||
+ const char *buf, size_t len)
|
||||
+{
|
||||
+ u8 mac[ETH_ALEN];
|
||||
+
|
||||
+ if (!mac_pton(buf, mac))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (buf[3 * ETH_ALEN - 1] && buf[3 * ETH_ALEN - 1] != '\n')
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ memcpy(dev_to_rdev(dev)->wiphy.perm_addr, mac, ETH_ALEN);
|
||||
+
|
||||
+ return strnlen(buf, len);
|
||||
+}
|
||||
+
|
||||
+SHOW_FMT(index, "%d", wiphy_idx, RO);
|
||||
+SHOW_FMT(macaddress, "%pM", wiphy.perm_addr, RW);
|
||||
+SHOW_FMT(address_mask, "%pM", wiphy.addr_mask, RO);
|
||||
|
||||
static ssize_t name_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
@@ -0,0 +1,27 @@
|
||||
--- a/net/mac80211/main.c
|
||||
+++ b/net/mac80211/main.c
|
||||
@@ -1564,24 +1564,6 @@ int ieee80211_register_hw(struct ieee802
|
||||
|
||||
ieee80211_check_wbrf_support(local);
|
||||
|
||||
- rtnl_lock();
|
||||
- wiphy_lock(hw->wiphy);
|
||||
-
|
||||
- /* add one default STA interface if supported */
|
||||
- if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION) &&
|
||||
- !ieee80211_hw_check(hw, NO_AUTO_VIF)) {
|
||||
- struct vif_params params = {0};
|
||||
-
|
||||
- result = ieee80211_if_add(local, "wlan%d", NET_NAME_ENUM, NULL,
|
||||
- NL80211_IFTYPE_STATION, ¶ms);
|
||||
- if (result)
|
||||
- wiphy_warn(local->hw.wiphy,
|
||||
- "Failed to add default virtual iface\n");
|
||||
- }
|
||||
-
|
||||
- wiphy_unlock(hw->wiphy);
|
||||
- rtnl_unlock();
|
||||
-
|
||||
#ifdef CONFIG_INET
|
||||
local->ifa_notifier.notifier_call = ieee80211_ifa_changed;
|
||||
result = register_inetaddr_notifier(&local->ifa_notifier);
|
||||
19
package/kernel/mac80211/patches/subsys/210-ap_scan.patch
Normal file
19
package/kernel/mac80211/patches/subsys/210-ap_scan.patch
Normal file
@@ -0,0 +1,19 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Wed, 3 Oct 2012 00:00:00 +0200
|
||||
Subject: [PATCH] mac80211: allow scans in access point mode (for site survey)
|
||||
|
||||
---
|
||||
net/mac80211/cfg.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/net/mac80211/cfg.c
|
||||
+++ b/net/mac80211/cfg.c
|
||||
@@ -2844,6 +2844,8 @@ static int ieee80211_scan(struct wiphy *
|
||||
*/
|
||||
fallthrough;
|
||||
case NL80211_IFTYPE_AP:
|
||||
+ /* skip check */
|
||||
+ break;
|
||||
/*
|
||||
* If the scan has been forced (and the driver supports
|
||||
* forcing), don't care about being beaconing already.
|
||||
@@ -0,0 +1,42 @@
|
||||
From b478e06a16a8baa00c5ecc87c1d636981f2206d5 Mon Sep 17 00:00:00 2001
|
||||
From: Johannes Berg <johannes.berg@intel.com>
|
||||
Date: Tue, 29 Oct 2019 10:25:25 +0100
|
||||
Subject: [PATCH] mac80211: sta: randomize BA session dialog token allocator
|
||||
|
||||
We currently always start the dialog token generator at zero,
|
||||
so the first dialog token we use is always 1. This would be
|
||||
OK if we had a perfect guarantee that we always do a proper
|
||||
deauth/re-auth handshake, but in IBSS mode this doesn't always
|
||||
happen properly.
|
||||
|
||||
To make problems with block ack (aggregation) sessions getting
|
||||
stuck less likely, randomize the dialog token so if we start a
|
||||
new session but the peer still has old state for us, it can
|
||||
better detect this.
|
||||
|
||||
This is really just a workaround to make things a bit more
|
||||
robust than they are now - a better fix would be to do a full
|
||||
authentication handshake in IBSS mode upon having discovered a
|
||||
new station, and on the receiver resetting the state (removing
|
||||
and re-adding the station) on receiving the authentication
|
||||
packet.
|
||||
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
net/mac80211/sta_info.c | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
--- a/net/mac80211/sta_info.c
|
||||
+++ b/net/mac80211/sta_info.c
|
||||
@@ -565,6 +565,11 @@ __sta_info_alloc(struct ieee80211_sub_if
|
||||
spin_lock_init(&sta->ps_lock);
|
||||
INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames);
|
||||
wiphy_work_init(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
|
||||
+#if LINUX_VERSION_IS_LESS(6,2,0)
|
||||
+ sta->ampdu_mlme.dialog_token_allocator = prandom_u32_max(U8_MAX);
|
||||
+#else
|
||||
+ sta->ampdu_mlme.dialog_token_allocator = get_random_u32_below(U8_MAX);
|
||||
+#endif
|
||||
#ifdef CPTCFG_MAC80211_MESH
|
||||
if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
||||
sta->mesh = kzalloc(sizeof(*sta->mesh), gfp);
|
||||
@@ -0,0 +1,21 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Wed, 28 Apr 2021 21:03:13 +0200
|
||||
Subject: [PATCH] mac80211: minstrel_ht: fix MINSTREL_FRAC macro
|
||||
|
||||
Add missing braces to avoid issues with e.g. using additions in the
|
||||
div expression
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/rc80211_minstrel_ht.h
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht.h
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
/* scaled fraction values */
|
||||
#define MINSTREL_SCALE 12
|
||||
-#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div)
|
||||
+#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / (div))
|
||||
#define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE)
|
||||
|
||||
#define EWMA_LEVEL 96 /* ewma weighting factor [/EWMA_DIV] */
|
||||
@@ -0,0 +1,30 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sat, 6 Feb 2021 16:08:01 +0100
|
||||
Subject: [PATCH] mac80211: minstrel_ht: reduce fluctuations in rate
|
||||
probability stats
|
||||
|
||||
In some scenarios when there is a lot of fluctuation in packet error rates,
|
||||
rate switching can be amplified when the statistics get skewed by time slots
|
||||
with very few tries.
|
||||
Make the input data to the moving average more smooth by adding the
|
||||
success/attempts count from the last stats window as well. This has the
|
||||
advantage of smoothing the data without introducing any extra lag to sampling
|
||||
rates.
|
||||
This significantly improves rate stability on a strong test link subjected to
|
||||
periodic noise bursts generated with a SDR
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/rc80211_minstrel_ht.c
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht.c
|
||||
@@ -769,7 +769,8 @@ minstrel_ht_calc_rate_stats(struct minst
|
||||
unsigned int cur_prob;
|
||||
|
||||
if (unlikely(mrs->attempts > 0)) {
|
||||
- cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts);
|
||||
+ cur_prob = MINSTREL_FRAC(mrs->success + mrs->last_success,
|
||||
+ mrs->attempts + mrs->last_attempts);
|
||||
minstrel_filter_avg_add(&mrs->prob_avg,
|
||||
&mrs->prob_avg_1, cur_prob);
|
||||
mrs->att_hist += mrs->attempts;
|
||||
@@ -0,0 +1,151 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sat, 6 Feb 2021 16:33:14 +0100
|
||||
Subject: [PATCH] mac80211: minstrel_ht: rework rate downgrade code and
|
||||
max_prob rate selection
|
||||
|
||||
The current fallback code for fast rate switching on potentially failing rates
|
||||
is triggering too often if there is some strong noise on the channel. This can
|
||||
lead to wild fluctuations in the rate selection.
|
||||
Additionally, switching down to max_prob_rate can create a significant gap down
|
||||
in throughput, especially when using only 2 spatial streams, because max_prob_rate
|
||||
is limited to using fewer streams than the max_tp rates.
|
||||
In order to improve throughput without reducing reliability too much, use the
|
||||
rate downgrade code for the max_prob_rate only, and allow the non-downgraded
|
||||
max_prob_rate to use as many spatial streams as the max_tp rates
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/rc80211_minstrel_ht.c
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht.c
|
||||
@@ -580,6 +580,14 @@ minstrel_ht_set_best_prob_rate(struct mi
|
||||
int cur_tp_avg, cur_group, cur_idx;
|
||||
int max_gpr_group, max_gpr_idx;
|
||||
int max_gpr_tp_avg, max_gpr_prob;
|
||||
+ int min_dur;
|
||||
+
|
||||
+ min_dur = max(minstrel_get_duration(mi->max_tp_rate[0]),
|
||||
+ minstrel_get_duration(mi->max_tp_rate[1]));
|
||||
+
|
||||
+ /* make the rate at least 18% slower than max tp rates */
|
||||
+ if (minstrel_get_duration(index) <= min_dur * 19 / 16)
|
||||
+ return;
|
||||
|
||||
cur_group = MI_RATE_GROUP(index);
|
||||
cur_idx = MI_RATE_IDX(index);
|
||||
@@ -601,11 +609,6 @@ minstrel_ht_set_best_prob_rate(struct mi
|
||||
!minstrel_ht_is_legacy_group(max_tp_group))
|
||||
return;
|
||||
|
||||
- /* skip rates faster than max tp rate with lower prob */
|
||||
- if (minstrel_get_duration(mi->max_tp_rate[0]) > minstrel_get_duration(index) &&
|
||||
- mrs->prob_avg < max_tp_prob)
|
||||
- return;
|
||||
-
|
||||
max_gpr_group = MI_RATE_GROUP(mg->max_group_prob_rate);
|
||||
max_gpr_idx = MI_RATE_IDX(mg->max_group_prob_rate);
|
||||
max_gpr_prob = mi->groups[max_gpr_group].rates[max_gpr_idx].prob_avg;
|
||||
@@ -663,40 +666,6 @@ minstrel_ht_assign_best_tp_rates(struct
|
||||
|
||||
}
|
||||
|
||||
-/*
|
||||
- * Try to increase robustness of max_prob rate by decrease number of
|
||||
- * streams if possible.
|
||||
- */
|
||||
-static inline void
|
||||
-minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi)
|
||||
-{
|
||||
- struct minstrel_mcs_group_data *mg;
|
||||
- int tmp_max_streams, group, tmp_idx, tmp_prob;
|
||||
- int tmp_tp = 0;
|
||||
-
|
||||
- if (!mi->sta->deflink.ht_cap.ht_supported)
|
||||
- return;
|
||||
-
|
||||
- group = MI_RATE_GROUP(mi->max_tp_rate[0]);
|
||||
- tmp_max_streams = minstrel_mcs_groups[group].streams;
|
||||
- for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) {
|
||||
- mg = &mi->groups[group];
|
||||
- if (!mi->supported[group] || group == MINSTREL_CCK_GROUP)
|
||||
- continue;
|
||||
-
|
||||
- tmp_idx = MI_RATE_IDX(mg->max_group_prob_rate);
|
||||
- tmp_prob = mi->groups[group].rates[tmp_idx].prob_avg;
|
||||
-
|
||||
- if (tmp_tp < minstrel_ht_get_tp_avg(mi, group, tmp_idx, tmp_prob) &&
|
||||
- (minstrel_mcs_groups[group].streams < tmp_max_streams)) {
|
||||
- mi->max_prob_rate = mg->max_group_prob_rate;
|
||||
- tmp_tp = minstrel_ht_get_tp_avg(mi, group,
|
||||
- tmp_idx,
|
||||
- tmp_prob);
|
||||
- }
|
||||
- }
|
||||
-}
|
||||
-
|
||||
static u16
|
||||
__minstrel_ht_get_sample_rate(struct minstrel_ht_sta *mi,
|
||||
enum minstrel_sample_type type)
|
||||
@@ -1176,8 +1145,6 @@ minstrel_ht_update_stats(struct minstrel
|
||||
|
||||
mi->max_prob_rate = tmp_max_prob_rate;
|
||||
|
||||
- /* Try to increase robustness of max_prob_rate*/
|
||||
- minstrel_ht_prob_rate_reduce_streams(mi);
|
||||
minstrel_ht_refill_sample_rates(mi);
|
||||
|
||||
#ifdef CPTCFG_MAC80211_DEBUGFS
|
||||
@@ -1256,7 +1223,7 @@ minstrel_ht_ri_txstat_valid(struct minst
|
||||
}
|
||||
|
||||
static void
|
||||
-minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary)
|
||||
+minstrel_downgrade_prob_rate(struct minstrel_ht_sta *mi, u16 *idx)
|
||||
{
|
||||
int group, orig_group;
|
||||
|
||||
@@ -1271,11 +1238,7 @@ minstrel_downgrade_rate(struct minstrel_
|
||||
minstrel_mcs_groups[orig_group].streams)
|
||||
continue;
|
||||
|
||||
- if (primary)
|
||||
- *idx = mi->groups[group].max_group_tp_rate[0];
|
||||
- else
|
||||
- *idx = mi->groups[group].max_group_tp_rate[1];
|
||||
- break;
|
||||
+ *idx = mi->groups[group].max_group_prob_rate;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1286,7 +1249,7 @@ minstrel_ht_tx_status(void *priv, struct
|
||||
struct ieee80211_tx_info *info = st->info;
|
||||
struct minstrel_ht_sta *mi = priv_sta;
|
||||
struct ieee80211_tx_rate *ar = info->status.rates;
|
||||
- struct minstrel_rate_stats *rate, *rate2;
|
||||
+ struct minstrel_rate_stats *rate;
|
||||
struct minstrel_priv *mp = priv;
|
||||
u32 update_interval = mp->update_interval;
|
||||
bool last, update = false;
|
||||
@@ -1354,18 +1317,13 @@ minstrel_ht_tx_status(void *priv, struct
|
||||
/*
|
||||
* check for sudden death of spatial multiplexing,
|
||||
* downgrade to a lower number of streams if necessary.
|
||||
+ * only do this for the max_prob_rate to prevent spurious
|
||||
+ * rate fluctuations when the link changes suddenly
|
||||
*/
|
||||
- rate = minstrel_get_ratestats(mi, mi->max_tp_rate[0]);
|
||||
+ rate = minstrel_get_ratestats(mi, mi->max_prob_rate);
|
||||
if (rate->attempts > 30 &&
|
||||
rate->success < rate->attempts / 4) {
|
||||
- minstrel_downgrade_rate(mi, &mi->max_tp_rate[0], true);
|
||||
- update = true;
|
||||
- }
|
||||
-
|
||||
- rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate[1]);
|
||||
- if (rate2->attempts > 30 &&
|
||||
- rate2->success < rate2->attempts / 4) {
|
||||
- minstrel_downgrade_rate(mi, &mi->max_tp_rate[1], false);
|
||||
+ minstrel_downgrade_prob_rate(mi, &mi->max_prob_rate);
|
||||
update = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sun, 26 Jun 2022 11:43:25 +0200
|
||||
Subject: [PATCH] mac80211: increase quantum for airtime scheduler
|
||||
|
||||
Given the typical AQL budget and queue length, a quantum of 256 with the
|
||||
default station weight often requires iterating over all queues frequently,
|
||||
until one of them becomes eligible.
|
||||
Improve performance by using 8 times station weight as scheduler quantum
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/ieee80211_i.h
|
||||
+++ b/net/mac80211/ieee80211_i.h
|
||||
@@ -101,6 +101,8 @@ ieee80211_sta_keep_active(struct sta_inf
|
||||
return time_before_eq(jiffies, sta->airtime[ac].last_active + HZ / 10);
|
||||
}
|
||||
|
||||
+#define AIRTIME_QUANTUM_SHIFT 3
|
||||
+
|
||||
struct ieee80211_bss {
|
||||
u32 device_ts_beacon, device_ts_presp;
|
||||
|
||||
--- a/net/mac80211/tx.c
|
||||
+++ b/net/mac80211/tx.c
|
||||
@@ -4082,7 +4082,7 @@ struct ieee80211_txq *ieee80211_next_txq
|
||||
|
||||
if (deficit < 0)
|
||||
sta->airtime[txqi->txq.ac].deficit +=
|
||||
- sta->airtime_weight;
|
||||
+ sta->airtime_weight << AIRTIME_QUANTUM_SHIFT;
|
||||
|
||||
if (deficit < 0 || !aql_check) {
|
||||
list_move_tail(&txqi->schedule_order,
|
||||
@@ -4225,7 +4225,8 @@ bool ieee80211_txq_may_transmit(struct i
|
||||
}
|
||||
sta = container_of(iter->txq.sta, struct sta_info, sta);
|
||||
if (ieee80211_sta_deficit(sta, ac) < 0)
|
||||
- sta->airtime[ac].deficit += sta->airtime_weight;
|
||||
+ sta->airtime[ac].deficit += sta->airtime_weight <<
|
||||
+ AIRTIME_QUANTUM_SHIFT;
|
||||
list_move_tail(&iter->schedule_order, &local->active_txqs[ac]);
|
||||
}
|
||||
|
||||
@@ -4233,7 +4234,7 @@ bool ieee80211_txq_may_transmit(struct i
|
||||
if (sta->airtime[ac].deficit >= 0)
|
||||
goto out;
|
||||
|
||||
- sta->airtime[ac].deficit += sta->airtime_weight;
|
||||
+ sta->airtime[ac].deficit += sta->airtime_weight << AIRTIME_QUANTUM_SHIFT;
|
||||
list_move_tail(&txqi->schedule_order, &local->active_txqs[ac]);
|
||||
spin_unlock_bh(&local->active_txq_lock[ac]);
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Wed, 3 Jul 2024 12:10:01 +0200
|
||||
Subject: [PATCH] wifi: mac80211: clear vif drv_priv after calling
|
||||
remove_interface
|
||||
|
||||
Avoid reusing stale driver data when an interface is brought down and up
|
||||
again. In order to avoid having to duplicate the memset in every single
|
||||
driver, do it here.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/iface.c
|
||||
+++ b/net/mac80211/iface.c
|
||||
@@ -693,8 +693,12 @@ static void ieee80211_do_stop(struct iee
|
||||
|
||||
fallthrough;
|
||||
default:
|
||||
- if (going_down)
|
||||
- drv_remove_interface(local, sdata);
|
||||
+ if (!going_down)
|
||||
+ break;
|
||||
+ drv_remove_interface(local, sdata);
|
||||
+
|
||||
+ /* Clear private driver data to prevent reuse */
|
||||
+ memset(sdata->vif.drv_priv, 0, local->hw.vif_data_size);
|
||||
}
|
||||
|
||||
ieee80211_recalc_ps(local);
|
||||
@@ -0,0 +1,149 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Thu, 14 Sep 2023 13:17:16 +0200
|
||||
Subject: [PATCH] cfg80211: allow grace period for DFS available after beacon
|
||||
shutdown
|
||||
|
||||
Fixes reconfiguring an AP on a DFS channel in non-ETSI regdomain
|
||||
|
||||
Fixes: b35a51c7dd25 ("cfg80211: Make pre-CAC results valid only for ETSI domain")
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/include/net/cfg80211.h
|
||||
+++ b/include/net/cfg80211.h
|
||||
@@ -189,6 +189,8 @@ enum ieee80211_channel_flags {
|
||||
* @dfs_state: current state of this channel. Only relevant if radar is required
|
||||
* on this channel.
|
||||
* @dfs_state_entered: timestamp (jiffies) when the dfs state was entered.
|
||||
+ * @dfs_state_last_available: timestamp (jiffies) of the last time when the
|
||||
+ * channel was available.
|
||||
* @dfs_cac_ms: DFS CAC time in milliseconds, this is valid for DFS channels.
|
||||
* @psd: power spectral density (in dBm)
|
||||
*/
|
||||
@@ -206,6 +208,7 @@ struct ieee80211_channel {
|
||||
int orig_mag, orig_mpwr;
|
||||
enum nl80211_dfs_state dfs_state;
|
||||
unsigned long dfs_state_entered;
|
||||
+ unsigned long dfs_state_last_available;
|
||||
unsigned int dfs_cac_ms;
|
||||
s8 psd;
|
||||
};
|
||||
--- a/net/wireless/ap.c
|
||||
+++ b/net/wireless/ap.c
|
||||
@@ -30,6 +30,9 @@ static int ___cfg80211_stop_ap(struct cf
|
||||
if (!wdev->links[link_id].ap.beacon_interval)
|
||||
return -ENOENT;
|
||||
|
||||
+ cfg80211_update_last_available(wdev->wiphy,
|
||||
+ &wdev->links[link_id].ap.chandef);
|
||||
+
|
||||
err = rdev_stop_ap(rdev, dev, link_id);
|
||||
if (!err) {
|
||||
wdev->conn_owner_nlportid = 0;
|
||||
@@ -41,9 +44,6 @@ static int ___cfg80211_stop_ap(struct cf
|
||||
if (notify)
|
||||
nl80211_send_ap_stopped(wdev, link_id);
|
||||
|
||||
- /* Should we apply the grace period during beaconing interface
|
||||
- * shutdown also?
|
||||
- */
|
||||
cfg80211_sched_dfs_chan_update(rdev);
|
||||
}
|
||||
|
||||
--- a/net/wireless/chan.c
|
||||
+++ b/net/wireless/chan.c
|
||||
@@ -560,6 +560,8 @@ static void cfg80211_set_chans_dfs_state
|
||||
|
||||
c->dfs_state = dfs_state;
|
||||
c->dfs_state_entered = jiffies;
|
||||
+ if (dfs_state == NL80211_DFS_AVAILABLE)
|
||||
+ c->dfs_state_last_available = jiffies;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1049,6 +1051,49 @@ static bool cfg80211_get_chans_dfs_avail
|
||||
return true;
|
||||
}
|
||||
|
||||
+static void
|
||||
+__cfg80211_update_last_available(struct wiphy *wiphy,
|
||||
+ u32 center_freq,
|
||||
+ u32 bandwidth)
|
||||
+{
|
||||
+ struct ieee80211_channel *c;
|
||||
+ u32 freq, start_freq, end_freq;
|
||||
+
|
||||
+ start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
|
||||
+ end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
|
||||
+
|
||||
+ /*
|
||||
+ * Check entire range of channels for the bandwidth.
|
||||
+ * If any channel in between is disabled or has not
|
||||
+ * had gone through CAC return false
|
||||
+ */
|
||||
+ for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
|
||||
+ c = ieee80211_get_channel_khz(wiphy, freq);
|
||||
+ if (!c)
|
||||
+ return;
|
||||
+
|
||||
+ c->dfs_state_last_available = jiffies;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void cfg80211_update_last_available(struct wiphy *wiphy,
|
||||
+ const struct cfg80211_chan_def *chandef)
|
||||
+{
|
||||
+ int width;
|
||||
+
|
||||
+ width = cfg80211_chandef_get_width(chandef);
|
||||
+ if (width < 0)
|
||||
+ return;
|
||||
+
|
||||
+ __cfg80211_update_last_available(wiphy, MHZ_TO_KHZ(chandef->center_freq1),
|
||||
+ width);
|
||||
+ if (chandef->width != NL80211_CHAN_WIDTH_80P80)
|
||||
+ return;
|
||||
+
|
||||
+ __cfg80211_update_last_available(wiphy, MHZ_TO_KHZ(chandef->center_freq2),
|
||||
+ width);
|
||||
+}
|
||||
+
|
||||
static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
--- a/net/wireless/core.h
|
||||
+++ b/net/wireless/core.h
|
||||
@@ -467,6 +467,8 @@ void cfg80211_set_dfs_state(struct wiphy
|
||||
enum nl80211_dfs_state dfs_state);
|
||||
|
||||
void cfg80211_dfs_channels_update_work(struct work_struct *work);
|
||||
+void cfg80211_update_last_available(struct wiphy *wiphy,
|
||||
+ const struct cfg80211_chan_def *chandef);
|
||||
|
||||
void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev);
|
||||
|
||||
--- a/net/wireless/mlme.c
|
||||
+++ b/net/wireless/mlme.c
|
||||
@@ -1037,6 +1037,8 @@ void cfg80211_dfs_channels_update_work(s
|
||||
if (c->dfs_state == NL80211_DFS_UNAVAILABLE) {
|
||||
time_dfs_update = IEEE80211_DFS_MIN_NOP_TIME_MS;
|
||||
radar_event = NL80211_RADAR_NOP_FINISHED;
|
||||
+ timeout = c->dfs_state_entered +
|
||||
+ msecs_to_jiffies(time_dfs_update);
|
||||
} else {
|
||||
if (regulatory_pre_cac_allowed(wiphy) ||
|
||||
cfg80211_any_wiphy_oper_chan(wiphy, c))
|
||||
@@ -1044,11 +1046,10 @@ void cfg80211_dfs_channels_update_work(s
|
||||
|
||||
time_dfs_update = REG_PRE_CAC_EXPIRY_GRACE_MS;
|
||||
radar_event = NL80211_RADAR_PRE_CAC_EXPIRED;
|
||||
+ timeout = c->dfs_state_last_available +
|
||||
+ msecs_to_jiffies(time_dfs_update);
|
||||
}
|
||||
|
||||
- timeout = c->dfs_state_entered +
|
||||
- msecs_to_jiffies(time_dfs_update);
|
||||
-
|
||||
if (time_after_eq(jiffies, timeout)) {
|
||||
c->dfs_state = NL80211_DFS_USABLE;
|
||||
c->dfs_state_entered = jiffies;
|
||||
@@ -0,0 +1,293 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Fri, 9 Feb 2024 19:43:40 +0100
|
||||
Subject: [PATCH] mac80211: add AQL support for broadcast packets
|
||||
|
||||
Excessive broadcast traffic with little competing unicast traffic can easily
|
||||
flood hardware queues, leading to throughput issues. Additionally, filling
|
||||
the hardware queues with too many packets breaks FQ for broadcast data.
|
||||
Fix this by enabling AQL for broadcast packets.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/include/net/cfg80211.h
|
||||
+++ b/include/net/cfg80211.h
|
||||
@@ -3416,6 +3416,7 @@ enum wiphy_params_flags {
|
||||
/* The per TXQ device queue limit in airtime */
|
||||
#define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L 5000
|
||||
#define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H 12000
|
||||
+#define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_BC 50000
|
||||
|
||||
/* The per interface airtime threshold to switch to lower queue limit */
|
||||
#define IEEE80211_AQL_THRESHOLD 24000
|
||||
--- a/net/mac80211/debugfs.c
|
||||
+++ b/net/mac80211/debugfs.c
|
||||
@@ -215,11 +215,13 @@ static ssize_t aql_pending_read(struct f
|
||||
"VI %u us\n"
|
||||
"BE %u us\n"
|
||||
"BK %u us\n"
|
||||
+ "BC/MC %u us\n"
|
||||
"total %u us\n",
|
||||
atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VO]),
|
||||
atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VI]),
|
||||
atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BE]),
|
||||
atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BK]),
|
||||
+ atomic_read(&local->aql_bc_pending_airtime),
|
||||
atomic_read(&local->aql_total_pending_airtime));
|
||||
return simple_read_from_buffer(user_buf, count, ppos,
|
||||
buf, len);
|
||||
@@ -245,7 +247,8 @@ static ssize_t aql_txq_limit_read(struct
|
||||
"VO %u %u\n"
|
||||
"VI %u %u\n"
|
||||
"BE %u %u\n"
|
||||
- "BK %u %u\n",
|
||||
+ "BK %u %u\n"
|
||||
+ "BC/MC %u\n",
|
||||
local->aql_txq_limit_low[IEEE80211_AC_VO],
|
||||
local->aql_txq_limit_high[IEEE80211_AC_VO],
|
||||
local->aql_txq_limit_low[IEEE80211_AC_VI],
|
||||
@@ -253,7 +256,8 @@ static ssize_t aql_txq_limit_read(struct
|
||||
local->aql_txq_limit_low[IEEE80211_AC_BE],
|
||||
local->aql_txq_limit_high[IEEE80211_AC_BE],
|
||||
local->aql_txq_limit_low[IEEE80211_AC_BK],
|
||||
- local->aql_txq_limit_high[IEEE80211_AC_BK]);
|
||||
+ local->aql_txq_limit_high[IEEE80211_AC_BK],
|
||||
+ local->aql_txq_limit_bc);
|
||||
return simple_read_from_buffer(user_buf, count, ppos,
|
||||
buf, len);
|
||||
}
|
||||
@@ -279,6 +283,11 @@ static ssize_t aql_txq_limit_write(struc
|
||||
else
|
||||
buf[count] = '\0';
|
||||
|
||||
+ if (sscanf(buf, "mcast %u", &q_limit_low) == 1) {
|
||||
+ local->aql_txq_limit_bc = q_limit_low;
|
||||
+ return count;
|
||||
+ }
|
||||
+
|
||||
if (sscanf(buf, "%u %u %u", &ac, &q_limit_low, &q_limit_high) != 3)
|
||||
return -EINVAL;
|
||||
|
||||
--- a/net/mac80211/ieee80211_i.h
|
||||
+++ b/net/mac80211/ieee80211_i.h
|
||||
@@ -1338,10 +1338,12 @@ struct ieee80211_local {
|
||||
spinlock_t handle_wake_tx_queue_lock;
|
||||
|
||||
u16 airtime_flags;
|
||||
+ u32 aql_txq_limit_bc;
|
||||
u32 aql_txq_limit_low[IEEE80211_NUM_ACS];
|
||||
u32 aql_txq_limit_high[IEEE80211_NUM_ACS];
|
||||
u32 aql_threshold;
|
||||
atomic_t aql_total_pending_airtime;
|
||||
+ atomic_t aql_bc_pending_airtime;
|
||||
atomic_t aql_ac_pending_airtime[IEEE80211_NUM_ACS];
|
||||
|
||||
const struct ieee80211_ops *ops;
|
||||
--- a/net/mac80211/main.c
|
||||
+++ b/net/mac80211/main.c
|
||||
@@ -944,6 +944,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_
|
||||
spin_lock_init(&local->rx_path_lock);
|
||||
spin_lock_init(&local->queue_stop_reason_lock);
|
||||
|
||||
+ local->aql_txq_limit_bc = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_BC;
|
||||
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
|
||||
INIT_LIST_HEAD(&local->active_txqs[i]);
|
||||
spin_lock_init(&local->active_txq_lock[i]);
|
||||
--- a/net/mac80211/sta_info.c
|
||||
+++ b/net/mac80211/sta_info.c
|
||||
@@ -2357,13 +2357,28 @@ EXPORT_SYMBOL(ieee80211_sta_recalc_aggre
|
||||
|
||||
void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
|
||||
struct sta_info *sta, u8 ac,
|
||||
- u16 tx_airtime, bool tx_completed)
|
||||
+ u16 tx_airtime, bool tx_completed,
|
||||
+ bool mcast)
|
||||
{
|
||||
int tx_pending;
|
||||
|
||||
if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL))
|
||||
return;
|
||||
|
||||
+ if (mcast) {
|
||||
+ if (!tx_completed) {
|
||||
+ atomic_add(tx_airtime, &local->aql_bc_pending_airtime);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ tx_pending = atomic_sub_return(tx_airtime,
|
||||
+ &local->aql_bc_pending_airtime);
|
||||
+ if (tx_pending < 0)
|
||||
+ atomic_cmpxchg(&local->aql_bc_pending_airtime,
|
||||
+ tx_pending, 0);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
if (!tx_completed) {
|
||||
if (sta)
|
||||
atomic_add(tx_airtime,
|
||||
--- a/net/mac80211/tx.c
|
||||
+++ b/net/mac80211/tx.c
|
||||
@@ -2554,7 +2554,7 @@ static u16 ieee80211_store_ack_skb(struc
|
||||
|
||||
spin_lock_irqsave(&local->ack_status_lock, flags);
|
||||
id = idr_alloc(&local->ack_status_frames, ack_skb,
|
||||
- 1, 0x2000, GFP_ATOMIC);
|
||||
+ 1, 0x1000, GFP_ATOMIC);
|
||||
spin_unlock_irqrestore(&local->ack_status_lock, flags);
|
||||
|
||||
if (id >= 0) {
|
||||
@@ -3983,20 +3983,20 @@ begin:
|
||||
encap_out:
|
||||
info->control.vif = vif;
|
||||
|
||||
- if (tx.sta &&
|
||||
- wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) {
|
||||
- bool ampdu = txq->ac != IEEE80211_AC_VO;
|
||||
+ if (wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) {
|
||||
+ bool ampdu = txq->sta && txq->ac != IEEE80211_AC_VO;
|
||||
u32 airtime;
|
||||
|
||||
airtime = ieee80211_calc_expected_tx_airtime(hw, vif, txq->sta,
|
||||
skb->len, ampdu);
|
||||
- if (airtime) {
|
||||
- airtime = ieee80211_info_set_tx_time_est(info, airtime);
|
||||
- ieee80211_sta_update_pending_airtime(local, tx.sta,
|
||||
- txq->ac,
|
||||
- airtime,
|
||||
- false);
|
||||
- }
|
||||
+ if (!airtime)
|
||||
+ return skb;
|
||||
+
|
||||
+ airtime = ieee80211_info_set_tx_time_est(info, airtime);
|
||||
+ info->tx_time_mc = !tx.sta;
|
||||
+ ieee80211_sta_update_pending_airtime(local, tx.sta, txq->ac,
|
||||
+ airtime, false,
|
||||
+ info->tx_time_mc);
|
||||
}
|
||||
|
||||
return skb;
|
||||
@@ -4048,6 +4048,7 @@ struct ieee80211_txq *ieee80211_next_txq
|
||||
struct ieee80211_txq *ret = NULL;
|
||||
struct txq_info *txqi = NULL, *head = NULL;
|
||||
bool found_eligible_txq = false;
|
||||
+ bool aql_check;
|
||||
|
||||
spin_lock_bh(&local->active_txq_lock[ac]);
|
||||
|
||||
@@ -4071,26 +4072,26 @@ struct ieee80211_txq *ieee80211_next_txq
|
||||
if (!head)
|
||||
head = txqi;
|
||||
|
||||
+ aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq);
|
||||
+ if (aql_check)
|
||||
+ found_eligible_txq = true;
|
||||
+
|
||||
if (txqi->txq.sta) {
|
||||
struct sta_info *sta = container_of(txqi->txq.sta,
|
||||
struct sta_info, sta);
|
||||
- bool aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq);
|
||||
- s32 deficit = ieee80211_sta_deficit(sta, txqi->txq.ac);
|
||||
-
|
||||
- if (aql_check)
|
||||
- found_eligible_txq = true;
|
||||
-
|
||||
- if (deficit < 0)
|
||||
+ if (ieee80211_sta_deficit(sta, txqi->txq.ac) < 0) {
|
||||
sta->airtime[txqi->txq.ac].deficit +=
|
||||
sta->airtime_weight << AIRTIME_QUANTUM_SHIFT;
|
||||
-
|
||||
- if (deficit < 0 || !aql_check) {
|
||||
- list_move_tail(&txqi->schedule_order,
|
||||
- &local->active_txqs[txqi->txq.ac]);
|
||||
- goto begin;
|
||||
+ aql_check = false;
|
||||
}
|
||||
}
|
||||
|
||||
+ if (!aql_check) {
|
||||
+ list_move_tail(&txqi->schedule_order,
|
||||
+ &local->active_txqs[txqi->txq.ac]);
|
||||
+ goto begin;
|
||||
+ }
|
||||
+
|
||||
if (txqi->schedule_round == local->schedule_round[ac])
|
||||
goto out;
|
||||
|
||||
@@ -4155,7 +4156,8 @@ bool ieee80211_txq_airtime_check(struct
|
||||
return true;
|
||||
|
||||
if (!txq->sta)
|
||||
- return true;
|
||||
+ return atomic_read(&local->aql_bc_pending_airtime) <
|
||||
+ local->aql_txq_limit_bc;
|
||||
|
||||
if (unlikely(txq->tid == IEEE80211_NUM_TIDS))
|
||||
return true;
|
||||
@@ -4204,15 +4206,15 @@ bool ieee80211_txq_may_transmit(struct i
|
||||
|
||||
spin_lock_bh(&local->active_txq_lock[ac]);
|
||||
|
||||
- if (!txqi->txq.sta)
|
||||
- goto out;
|
||||
-
|
||||
if (list_empty(&txqi->schedule_order))
|
||||
goto out;
|
||||
|
||||
if (!ieee80211_txq_schedule_airtime_check(local, ac))
|
||||
goto out;
|
||||
|
||||
+ if (!txqi->txq.sta)
|
||||
+ goto out;
|
||||
+
|
||||
list_for_each_entry_safe(iter, tmp, &local->active_txqs[ac],
|
||||
schedule_order) {
|
||||
if (iter == txqi)
|
||||
--- a/include/net/mac80211.h
|
||||
+++ b/include/net/mac80211.h
|
||||
@@ -1180,8 +1180,8 @@ struct ieee80211_tx_info {
|
||||
status_data_idr:1,
|
||||
status_data:13,
|
||||
hw_queue:4,
|
||||
+ tx_time_mc:1,
|
||||
tx_time_est:10;
|
||||
- /* 1 free bit */
|
||||
|
||||
union {
|
||||
struct {
|
||||
--- a/net/mac80211/sta_info.h
|
||||
+++ b/net/mac80211/sta_info.h
|
||||
@@ -147,7 +147,8 @@ struct airtime_info {
|
||||
|
||||
void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
|
||||
struct sta_info *sta, u8 ac,
|
||||
- u16 tx_airtime, bool tx_completed);
|
||||
+ u16 tx_airtime, bool tx_completed,
|
||||
+ bool mcast);
|
||||
|
||||
struct sta_info;
|
||||
|
||||
--- a/net/mac80211/status.c
|
||||
+++ b/net/mac80211/status.c
|
||||
@@ -717,7 +717,7 @@ static void ieee80211_report_used_skb(st
|
||||
ieee80211_sta_update_pending_airtime(local, sta,
|
||||
skb_get_queue_mapping(skb),
|
||||
tx_time_est,
|
||||
- true);
|
||||
+ true, info->tx_time_mc);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
@@ -1138,10 +1138,11 @@ void ieee80211_tx_status_ext(struct ieee
|
||||
/* Do this here to avoid the expensive lookup of the sta
|
||||
* in ieee80211_report_used_skb().
|
||||
*/
|
||||
+ bool mcast = IEEE80211_SKB_CB(skb)->tx_time_mc;
|
||||
ieee80211_sta_update_pending_airtime(local, sta,
|
||||
skb_get_queue_mapping(skb),
|
||||
tx_time_est,
|
||||
- true);
|
||||
+ true, mcast);
|
||||
ieee80211_info_set_tx_time_est(IEEE80211_SKB_CB(skb), 0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
From: Michael-CY Lee <michael-cy.lee@mediatek.com>
|
||||
Date: Tue, 26 Mar 2024 08:30:36 +0800
|
||||
Subject: [PATCH] wifi: mac80211: extend IEEE80211_KEY_FLAG_GENERATE_MMIE to
|
||||
other ciphers
|
||||
|
||||
Extend the flag IEEE80211_KEY_FLAG_GENERATE_MMIE to BIP-CMAC-256,
|
||||
BIP-GMAC-128 and BIP-GMAC-256 for the same reason and in the same
|
||||
way that the flag was added originally in commit a0b4496a4368
|
||||
("mac80211: add IEEE80211_KEY_FLAG_GENERATE_MMIE to ieee80211_key_flags").
|
||||
|
||||
Signed-off-by: Michael-CY Lee <michael-cy.lee@mediatek.com>
|
||||
Link: https://msgid.link/20240326003036.15215-1-michael-cy.lee@mediatek.com
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/include/net/mac80211.h
|
||||
+++ b/include/net/mac80211.h
|
||||
@@ -2123,8 +2123,8 @@ static inline bool lockdep_vif_wiphy_mut
|
||||
* @IEEE80211_KEY_FLAG_GENERATE_MMIC on the same key.
|
||||
* @IEEE80211_KEY_FLAG_NO_AUTO_TX: Key needs explicit Tx activation.
|
||||
* @IEEE80211_KEY_FLAG_GENERATE_MMIE: This flag should be set by the driver
|
||||
- * for a AES_CMAC key to indicate that it requires sequence number
|
||||
- * generation only
|
||||
+ * for a AES_CMAC or a AES_GMAC key to indicate that it requires sequence
|
||||
+ * number generation only
|
||||
* @IEEE80211_KEY_FLAG_SPP_AMSDU: SPP A-MSDUs can be used with this key
|
||||
* (set by mac80211 from the sta->spp_amsdu flag)
|
||||
*/
|
||||
--- a/net/mac80211/wpa.c
|
||||
+++ b/net/mac80211/wpa.c
|
||||
@@ -895,7 +895,8 @@ ieee80211_crypto_aes_cmac_256_encrypt(st
|
||||
|
||||
info = IEEE80211_SKB_CB(skb);
|
||||
|
||||
- if (info->control.hw_key)
|
||||
+ if (info->control.hw_key &&
|
||||
+ !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIE))
|
||||
return TX_CONTINUE;
|
||||
|
||||
if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie)))
|
||||
@@ -911,6 +912,9 @@ ieee80211_crypto_aes_cmac_256_encrypt(st
|
||||
|
||||
bip_ipn_set64(mmie->sequence_number, pn64);
|
||||
|
||||
+ if (info->control.hw_key)
|
||||
+ return TX_CONTINUE;
|
||||
+
|
||||
bip_aad(skb, aad);
|
||||
|
||||
/* MIC = AES-256-CMAC(IGTK, AAD || Management Frame Body || MMIE, 128)
|
||||
@@ -1040,7 +1044,8 @@ ieee80211_crypto_aes_gmac_encrypt(struct
|
||||
|
||||
info = IEEE80211_SKB_CB(skb);
|
||||
|
||||
- if (info->control.hw_key)
|
||||
+ if (info->control.hw_key &&
|
||||
+ !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIE))
|
||||
return TX_CONTINUE;
|
||||
|
||||
if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie)))
|
||||
@@ -1056,6 +1061,9 @@ ieee80211_crypto_aes_gmac_encrypt(struct
|
||||
|
||||
bip_ipn_set64(mmie->sequence_number, pn64);
|
||||
|
||||
+ if (info->control.hw_key)
|
||||
+ return TX_CONTINUE;
|
||||
+
|
||||
bip_aad(skb, aad);
|
||||
|
||||
hdr = (struct ieee80211_hdr *)skb->data;
|
||||
@@ -0,0 +1,145 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Tue, 4 Jun 2024 14:31:09 +0200
|
||||
Subject: [PATCH] wifi: nl80211: split helper function from
|
||||
nl80211_put_iface_combinations
|
||||
|
||||
Create a helper function that puts the data from struct
|
||||
ieee80211_iface_combination to a nl80211 message.
|
||||
This will be used for adding per-radio interface combination data.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/wireless/nl80211.c
|
||||
+++ b/net/wireless/nl80211.c
|
||||
@@ -1620,71 +1620,78 @@ nla_put_failure:
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
-static int nl80211_put_iface_combinations(struct wiphy *wiphy,
|
||||
- struct sk_buff *msg,
|
||||
- bool large)
|
||||
+static int nl80211_put_ifcomb_data(struct sk_buff *msg, bool large, int idx,
|
||||
+ const struct ieee80211_iface_combination *c)
|
||||
{
|
||||
- struct nlattr *nl_combis;
|
||||
- int i, j;
|
||||
+ struct nlattr *nl_combi, *nl_limits;
|
||||
+ int i;
|
||||
|
||||
- nl_combis = nla_nest_start_noflag(msg,
|
||||
- NL80211_ATTR_INTERFACE_COMBINATIONS);
|
||||
- if (!nl_combis)
|
||||
+ nl_combi = nla_nest_start_noflag(msg, idx);
|
||||
+ if (!nl_combi)
|
||||
goto nla_put_failure;
|
||||
|
||||
- for (i = 0; i < wiphy->n_iface_combinations; i++) {
|
||||
- const struct ieee80211_iface_combination *c;
|
||||
- struct nlattr *nl_combi, *nl_limits;
|
||||
+ nl_limits = nla_nest_start_noflag(msg, NL80211_IFACE_COMB_LIMITS);
|
||||
+ if (!nl_limits)
|
||||
+ goto nla_put_failure;
|
||||
|
||||
- c = &wiphy->iface_combinations[i];
|
||||
+ for (i = 0; i < c->n_limits; i++) {
|
||||
+ struct nlattr *nl_limit;
|
||||
|
||||
- nl_combi = nla_nest_start_noflag(msg, i + 1);
|
||||
- if (!nl_combi)
|
||||
+ nl_limit = nla_nest_start_noflag(msg, i + 1);
|
||||
+ if (!nl_limit)
|
||||
goto nla_put_failure;
|
||||
-
|
||||
- nl_limits = nla_nest_start_noflag(msg,
|
||||
- NL80211_IFACE_COMB_LIMITS);
|
||||
- if (!nl_limits)
|
||||
+ if (nla_put_u32(msg, NL80211_IFACE_LIMIT_MAX, c->limits[i].max))
|
||||
goto nla_put_failure;
|
||||
+ if (nl80211_put_iftypes(msg, NL80211_IFACE_LIMIT_TYPES,
|
||||
+ c->limits[i].types))
|
||||
+ goto nla_put_failure;
|
||||
+ nla_nest_end(msg, nl_limit);
|
||||
+ }
|
||||
|
||||
- for (j = 0; j < c->n_limits; j++) {
|
||||
- struct nlattr *nl_limit;
|
||||
+ nla_nest_end(msg, nl_limits);
|
||||
|
||||
- nl_limit = nla_nest_start_noflag(msg, j + 1);
|
||||
- if (!nl_limit)
|
||||
- goto nla_put_failure;
|
||||
- if (nla_put_u32(msg, NL80211_IFACE_LIMIT_MAX,
|
||||
- c->limits[j].max))
|
||||
- goto nla_put_failure;
|
||||
- if (nl80211_put_iftypes(msg, NL80211_IFACE_LIMIT_TYPES,
|
||||
- c->limits[j].types))
|
||||
- goto nla_put_failure;
|
||||
- nla_nest_end(msg, nl_limit);
|
||||
- }
|
||||
+ if (c->beacon_int_infra_match &&
|
||||
+ nla_put_flag(msg, NL80211_IFACE_COMB_STA_AP_BI_MATCH))
|
||||
+ goto nla_put_failure;
|
||||
+ if (nla_put_u32(msg, NL80211_IFACE_COMB_NUM_CHANNELS,
|
||||
+ c->num_different_channels) ||
|
||||
+ nla_put_u32(msg, NL80211_IFACE_COMB_MAXNUM,
|
||||
+ c->max_interfaces))
|
||||
+ goto nla_put_failure;
|
||||
+ if (large &&
|
||||
+ (nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
|
||||
+ c->radar_detect_widths) ||
|
||||
+ nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_REGIONS,
|
||||
+ c->radar_detect_regions)))
|
||||
+ goto nla_put_failure;
|
||||
+ if (c->beacon_int_min_gcd &&
|
||||
+ nla_put_u32(msg, NL80211_IFACE_COMB_BI_MIN_GCD,
|
||||
+ c->beacon_int_min_gcd))
|
||||
+ goto nla_put_failure;
|
||||
|
||||
- nla_nest_end(msg, nl_limits);
|
||||
+ nla_nest_end(msg, nl_combi);
|
||||
|
||||
- if (c->beacon_int_infra_match &&
|
||||
- nla_put_flag(msg, NL80211_IFACE_COMB_STA_AP_BI_MATCH))
|
||||
- goto nla_put_failure;
|
||||
- if (nla_put_u32(msg, NL80211_IFACE_COMB_NUM_CHANNELS,
|
||||
- c->num_different_channels) ||
|
||||
- nla_put_u32(msg, NL80211_IFACE_COMB_MAXNUM,
|
||||
- c->max_interfaces))
|
||||
- goto nla_put_failure;
|
||||
- if (large &&
|
||||
- (nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
|
||||
- c->radar_detect_widths) ||
|
||||
- nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_REGIONS,
|
||||
- c->radar_detect_regions)))
|
||||
- goto nla_put_failure;
|
||||
- if (c->beacon_int_min_gcd &&
|
||||
- nla_put_u32(msg, NL80211_IFACE_COMB_BI_MIN_GCD,
|
||||
- c->beacon_int_min_gcd))
|
||||
- goto nla_put_failure;
|
||||
+ return 0;
|
||||
+nla_put_failure:
|
||||
+ return -ENOBUFS;
|
||||
+}
|
||||
|
||||
- nla_nest_end(msg, nl_combi);
|
||||
- }
|
||||
+static int nl80211_put_iface_combinations(struct wiphy *wiphy,
|
||||
+ struct sk_buff *msg,
|
||||
+ bool large)
|
||||
+{
|
||||
+ struct nlattr *nl_combis;
|
||||
+ int i;
|
||||
+
|
||||
+ nl_combis = nla_nest_start_noflag(msg,
|
||||
+ NL80211_ATTR_INTERFACE_COMBINATIONS);
|
||||
+ if (!nl_combis)
|
||||
+ goto nla_put_failure;
|
||||
+
|
||||
+ for (i = 0; i < wiphy->n_iface_combinations; i++)
|
||||
+ if (nl80211_put_ifcomb_data(msg, large, i + 1,
|
||||
+ &wiphy->iface_combinations[i]))
|
||||
+ goto nla_put_failure;
|
||||
|
||||
nla_nest_end(msg, nl_combis);
|
||||
|
||||
@@ -0,0 +1,348 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Wed, 22 May 2024 11:42:57 +0200
|
||||
Subject: [PATCH] wifi: cfg80211: add support for advertising multiple
|
||||
radios belonging to a wiphy
|
||||
|
||||
The prerequisite for MLO support in cfg80211/mac80211 is that all the links
|
||||
participating in MLO must be from the same wiphy/ieee80211_hw. To meet this
|
||||
expectation, some drivers may need to group multiple discrete hardware each
|
||||
acting as a link in MLO under single wiphy.
|
||||
|
||||
With this change, supported frequencies and interface combinations of each
|
||||
individual radio are reported to user space. This allows user space to figure
|
||||
out the limitations of what combination of channels can be used concurrently.
|
||||
|
||||
Even for non-MLO devices, this improves support for devices capable of
|
||||
running on multiple channels at the same time.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/include/net/cfg80211.h
|
||||
+++ b/include/net/cfg80211.h
|
||||
@@ -5045,7 +5045,9 @@ struct ieee80211_iface_limit {
|
||||
* struct ieee80211_iface_combination - possible interface combination
|
||||
*
|
||||
* With this structure the driver can describe which interface
|
||||
- * combinations it supports concurrently.
|
||||
+ * combinations it supports concurrently. When set in a struct wiphy_radio,
|
||||
+ * the combinations refer to combinations of interfaces currently active on
|
||||
+ * that radio.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
@@ -5403,6 +5405,38 @@ struct wiphy_iftype_akm_suites {
|
||||
int n_akm_suites;
|
||||
};
|
||||
|
||||
+/**
|
||||
+ * struct wiphy_radio_freq_range - wiphy frequency range
|
||||
+ * @start_freq: start range edge frequency (kHz)
|
||||
+ * @end_freq: end range edge frequency (kHz)
|
||||
+ */
|
||||
+struct wiphy_radio_freq_range {
|
||||
+ u32 start_freq;
|
||||
+ u32 end_freq;
|
||||
+};
|
||||
+
|
||||
+
|
||||
+/**
|
||||
+ * struct wiphy_radio - physical radio of a wiphy
|
||||
+ * This structure describes a physical radio belonging to a wiphy.
|
||||
+ * It is used to describe concurrent-channel capabilities. Only one channel
|
||||
+ * can be active on the radio described by struct wiphy_radio.
|
||||
+ *
|
||||
+ * @freq_range: frequency range that the radio can operate on.
|
||||
+ * @n_freq_range: number of elements in @freq_range
|
||||
+ *
|
||||
+ * @iface_combinations: Valid interface combinations array, should not
|
||||
+ * list single interface types.
|
||||
+ * @n_iface_combinations: number of entries in @iface_combinations array.
|
||||
+ */
|
||||
+struct wiphy_radio {
|
||||
+ const struct wiphy_radio_freq_range *freq_range;
|
||||
+ int n_freq_range;
|
||||
+
|
||||
+ const struct ieee80211_iface_combination *iface_combinations;
|
||||
+ int n_iface_combinations;
|
||||
+};
|
||||
+
|
||||
#define CFG80211_HW_TIMESTAMP_ALL_PEERS 0xffff
|
||||
|
||||
/**
|
||||
@@ -5621,6 +5655,9 @@ struct wiphy_iftype_akm_suites {
|
||||
* A value of %CFG80211_HW_TIMESTAMP_ALL_PEERS indicates the driver
|
||||
* supports enabling HW timestamping for all peers (i.e. no need to
|
||||
* specify a mac address).
|
||||
+ *
|
||||
+ * @radio: radios belonging to this wiphy
|
||||
+ * @n_radio: number of radios
|
||||
*/
|
||||
struct wiphy {
|
||||
struct mutex mtx;
|
||||
@@ -5771,6 +5808,9 @@ struct wiphy {
|
||||
|
||||
u16 hw_timestamp_max_peers;
|
||||
|
||||
+ int n_radio;
|
||||
+ const struct wiphy_radio *radio;
|
||||
+
|
||||
char priv[] __aligned(NETDEV_ALIGN);
|
||||
};
|
||||
|
||||
--- a/include/uapi/linux/nl80211.h
|
||||
+++ b/include/uapi/linux/nl80211.h
|
||||
@@ -2061,6 +2061,10 @@ enum nl80211_commands {
|
||||
* @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported
|
||||
* interface combinations. In each nested item, it contains attributes
|
||||
* defined in &enum nl80211_if_combination_attrs.
|
||||
+ * If the wiphy uses multiple radios (@NL80211_ATTR_WIPHY_RADIOS is set),
|
||||
+ * this attribute contains the interface combinations of the first radio.
|
||||
+ * See @NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS for the global wiphy
|
||||
+ * combinations for the sum of all radios.
|
||||
* @NL80211_ATTR_SOFTWARE_IFTYPES: Nested attribute (just like
|
||||
* %NL80211_ATTR_SUPPORTED_IFTYPES) containing the interface types that
|
||||
* are managed in software: interfaces of these types aren't subject to
|
||||
@@ -2856,6 +2860,14 @@ enum nl80211_commands {
|
||||
* %NL80211_CMD_ASSOCIATE indicating the SPP A-MSDUs
|
||||
* are used on this connection
|
||||
*
|
||||
+ * @NL80211_ATTR_WIPHY_RADIOS: Nested attribute describing physical radios
|
||||
+ * belonging to this wiphy. See &enum nl80211_wiphy_radio_attrs.
|
||||
+ *
|
||||
+ * @NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS: Nested attribute listing the
|
||||
+ * supported interface combinations for all radios combined. In each
|
||||
+ * nested item, it contains attributes defined in
|
||||
+ * &enum nl80211_if_combination_attrs.
|
||||
+ *
|
||||
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
|
||||
* @NL80211_ATTR_MAX: highest attribute number currently defined
|
||||
* @__NL80211_ATTR_AFTER_LAST: internal use
|
||||
@@ -3401,6 +3413,9 @@ enum nl80211_attrs {
|
||||
|
||||
NL80211_ATTR_ASSOC_SPP_AMSDU,
|
||||
|
||||
+ NL80211_ATTR_WIPHY_RADIOS,
|
||||
+ NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS,
|
||||
+
|
||||
/* add attributes here, update the policy in nl80211.c */
|
||||
|
||||
__NL80211_ATTR_AFTER_LAST,
|
||||
@@ -7987,4 +8002,54 @@ enum nl80211_ap_settings_flags {
|
||||
NL80211_AP_SETTINGS_SA_QUERY_OFFLOAD_SUPPORT = 1 << 1,
|
||||
};
|
||||
|
||||
+/**
|
||||
+ * enum nl80211_wiphy_radio_attrs - wiphy radio attributes
|
||||
+ *
|
||||
+ * @__NL80211_WIPHY_RADIO_ATTR_INVALID: Invalid
|
||||
+ *
|
||||
+ * @NL80211_WIPHY_RADIO_ATTR_INDEX: Index of this radio (u32)
|
||||
+ * @NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE: Frequency range supported by this
|
||||
+ * radio. Attribute may be present multiple times.
|
||||
+ * @NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION: Supported interface
|
||||
+ * combination for this radio. Attribute may be present multiple times
|
||||
+ * and contains attributes defined in &enum nl80211_if_combination_attrs.
|
||||
+ *
|
||||
+ * @__NL80211_WIPHY_RADIO_ATTR_LAST: Internal
|
||||
+ * @NL80211_WIPHY_RADIO_ATTR_MAX: Highest attribute
|
||||
+ */
|
||||
+enum nl80211_wiphy_radio_attrs {
|
||||
+ __NL80211_WIPHY_RADIO_ATTR_INVALID,
|
||||
+
|
||||
+ NL80211_WIPHY_RADIO_ATTR_INDEX,
|
||||
+ NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE,
|
||||
+ NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION,
|
||||
+
|
||||
+ /* keep last */
|
||||
+ __NL80211_WIPHY_RADIO_ATTR_LAST,
|
||||
+ NL80211_WIPHY_RADIO_ATTR_MAX = __NL80211_WIPHY_RADIO_ATTR_LAST - 1,
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * enum nl80211_wiphy_radio_freq_range - wiphy radio frequency range
|
||||
+ *
|
||||
+ * @__NL80211_WIPHY_RADIO_FREQ_ATTR_INVALID: Invalid
|
||||
+ *
|
||||
+ * @NL80211_WIPHY_RADIO_FREQ_ATTR_START: Frequency range start (u32).
|
||||
+ * The unit is kHz.
|
||||
+ * @NL80211_WIPHY_RADIO_FREQ_ATTR_END: Frequency range end (u32).
|
||||
+ * The unit is kHz.
|
||||
+ *
|
||||
+ * @__NL80211_WIPHY_RADIO_FREQ_ATTR_LAST: Internal
|
||||
+ * @NL80211_WIPHY_RADIO_FREQ_ATTR_MAX: Highest attribute
|
||||
+ */
|
||||
+enum nl80211_wiphy_radio_freq_range {
|
||||
+ __NL80211_WIPHY_RADIO_FREQ_ATTR_INVALID,
|
||||
+
|
||||
+ NL80211_WIPHY_RADIO_FREQ_ATTR_START,
|
||||
+ NL80211_WIPHY_RADIO_FREQ_ATTR_END,
|
||||
+
|
||||
+ __NL80211_WIPHY_RADIO_FREQ_ATTR_LAST,
|
||||
+ NL80211_WIPHY_RADIO_FREQ_ATTR_MAX = __NL80211_WIPHY_RADIO_FREQ_ATTR_LAST - 1,
|
||||
+};
|
||||
+
|
||||
#endif /* __LINUX_NL80211_H */
|
||||
--- a/net/wireless/nl80211.c
|
||||
+++ b/net/wireless/nl80211.c
|
||||
@@ -1621,16 +1621,18 @@ nla_put_failure:
|
||||
}
|
||||
|
||||
static int nl80211_put_ifcomb_data(struct sk_buff *msg, bool large, int idx,
|
||||
- const struct ieee80211_iface_combination *c)
|
||||
+ const struct ieee80211_iface_combination *c,
|
||||
+ u16 nested)
|
||||
{
|
||||
struct nlattr *nl_combi, *nl_limits;
|
||||
int i;
|
||||
|
||||
- nl_combi = nla_nest_start_noflag(msg, idx);
|
||||
+ nl_combi = nla_nest_start_noflag(msg, idx | nested);
|
||||
if (!nl_combi)
|
||||
goto nla_put_failure;
|
||||
|
||||
- nl_limits = nla_nest_start_noflag(msg, NL80211_IFACE_COMB_LIMITS);
|
||||
+ nl_limits = nla_nest_start_noflag(msg, NL80211_IFACE_COMB_LIMITS |
|
||||
+ nested);
|
||||
if (!nl_limits)
|
||||
goto nla_put_failure;
|
||||
|
||||
@@ -1678,19 +1680,26 @@ nla_put_failure:
|
||||
|
||||
static int nl80211_put_iface_combinations(struct wiphy *wiphy,
|
||||
struct sk_buff *msg,
|
||||
- bool large)
|
||||
+ int attr, int radio,
|
||||
+ bool large, u16 nested)
|
||||
{
|
||||
+ const struct ieee80211_iface_combination *c;
|
||||
struct nlattr *nl_combis;
|
||||
- int i;
|
||||
+ int i, n;
|
||||
|
||||
- nl_combis = nla_nest_start_noflag(msg,
|
||||
- NL80211_ATTR_INTERFACE_COMBINATIONS);
|
||||
+ nl_combis = nla_nest_start_noflag(msg, attr | nested);
|
||||
if (!nl_combis)
|
||||
goto nla_put_failure;
|
||||
|
||||
- for (i = 0; i < wiphy->n_iface_combinations; i++)
|
||||
- if (nl80211_put_ifcomb_data(msg, large, i + 1,
|
||||
- &wiphy->iface_combinations[i]))
|
||||
+ if (radio >= 0) {
|
||||
+ c = wiphy->radio[0].iface_combinations;
|
||||
+ n = wiphy->radio[0].n_iface_combinations;
|
||||
+ } else {
|
||||
+ c = wiphy->iface_combinations;
|
||||
+ n = wiphy->n_iface_combinations;
|
||||
+ }
|
||||
+ for (i = 0; i < n; i++)
|
||||
+ if (nl80211_put_ifcomb_data(msg, large, i + 1, &c[i], nested))
|
||||
goto nla_put_failure;
|
||||
|
||||
nla_nest_end(msg, nl_combis);
|
||||
@@ -2397,6 +2406,80 @@ fail:
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
+static int nl80211_put_radio(struct wiphy *wiphy, struct sk_buff *msg, int idx)
|
||||
+{
|
||||
+ const struct wiphy_radio *r = &wiphy->radio[idx];
|
||||
+ struct nlattr *radio, *freq;
|
||||
+ int i;
|
||||
+
|
||||
+ radio = nla_nest_start(msg, idx);
|
||||
+ if (!radio)
|
||||
+ return -ENOBUFS;
|
||||
+
|
||||
+ if (nla_put_u32(msg, NL80211_WIPHY_RADIO_ATTR_INDEX, idx))
|
||||
+ goto nla_put_failure;
|
||||
+
|
||||
+ for (i = 0; i < r->n_freq_range; i++) {
|
||||
+ const struct wiphy_radio_freq_range *range = &r->freq_range[i];
|
||||
+
|
||||
+ freq = nla_nest_start(msg, NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE);
|
||||
+ if (!freq)
|
||||
+ goto nla_put_failure;
|
||||
+
|
||||
+ if (nla_put_u32(msg, NL80211_WIPHY_RADIO_FREQ_ATTR_START,
|
||||
+ range->start_freq) ||
|
||||
+ nla_put_u32(msg, NL80211_WIPHY_RADIO_FREQ_ATTR_END,
|
||||
+ range->end_freq))
|
||||
+ goto nla_put_failure;
|
||||
+
|
||||
+ nla_nest_end(msg, freq);
|
||||
+ }
|
||||
+
|
||||
+ for (i = 0; i < r->n_iface_combinations; i++)
|
||||
+ if (nl80211_put_ifcomb_data(msg, true,
|
||||
+ NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION,
|
||||
+ &r->iface_combinations[i],
|
||||
+ NLA_F_NESTED))
|
||||
+ goto nla_put_failure;
|
||||
+
|
||||
+ nla_nest_end(msg, radio);
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+nla_put_failure:
|
||||
+ return -ENOBUFS;
|
||||
+}
|
||||
+
|
||||
+static int nl80211_put_radios(struct wiphy *wiphy, struct sk_buff *msg)
|
||||
+{
|
||||
+ struct nlattr *radios;
|
||||
+ int i;
|
||||
+
|
||||
+ if (!wiphy->n_radio)
|
||||
+ return 0;
|
||||
+
|
||||
+ radios = nla_nest_start(msg, NL80211_ATTR_WIPHY_RADIOS);
|
||||
+ if (!radios)
|
||||
+ return -ENOBUFS;
|
||||
+
|
||||
+ for (i = 0; i < wiphy->n_radio; i++)
|
||||
+ if (nl80211_put_radio(wiphy, msg, i))
|
||||
+ goto fail;
|
||||
+
|
||||
+ nla_nest_end(msg, radios);
|
||||
+
|
||||
+ if (nl80211_put_iface_combinations(wiphy, msg,
|
||||
+ NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS,
|
||||
+ -1, true, NLA_F_NESTED))
|
||||
+ return -ENOBUFS;
|
||||
+
|
||||
+ return 0;
|
||||
+
|
||||
+fail:
|
||||
+ nla_nest_cancel(msg, radios);
|
||||
+ return -ENOBUFS;
|
||||
+}
|
||||
+
|
||||
struct nl80211_dump_wiphy_state {
|
||||
s64 filter_wiphy;
|
||||
long start;
|
||||
@@ -2692,7 +2775,9 @@ static int nl80211_send_wiphy(struct cfg
|
||||
goto nla_put_failure;
|
||||
|
||||
if (nl80211_put_iface_combinations(&rdev->wiphy, msg,
|
||||
- state->split))
|
||||
+ NL80211_ATTR_INTERFACE_COMBINATIONS,
|
||||
+ rdev->wiphy.n_radio ? 0 : -1,
|
||||
+ state->split, 0))
|
||||
goto nla_put_failure;
|
||||
|
||||
state->split_start++;
|
||||
@@ -3006,6 +3091,12 @@ static int nl80211_send_wiphy(struct cfg
|
||||
rdev->wiphy.hw_timestamp_max_peers))
|
||||
goto nla_put_failure;
|
||||
|
||||
+ state->split_start++;
|
||||
+ break;
|
||||
+ case 17:
|
||||
+ if (nl80211_put_radios(&rdev->wiphy, msg))
|
||||
+ goto nla_put_failure;
|
||||
+
|
||||
/* done */
|
||||
state->split_start = 0;
|
||||
break;
|
||||
@@ -0,0 +1,172 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Tue, 4 Jun 2024 21:01:50 +0200
|
||||
Subject: [PATCH] wifi: cfg80211: extend interface combination check for
|
||||
multi-radio
|
||||
|
||||
Add a field in struct iface_combination_params to check per-radio
|
||||
interface combinations instead of per-wiphy ones.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/include/net/cfg80211.h
|
||||
+++ b/include/net/cfg80211.h
|
||||
@@ -1598,6 +1598,7 @@ struct cfg80211_color_change_settings {
|
||||
*
|
||||
* Used to pass interface combination parameters
|
||||
*
|
||||
+ * @radio_idx: wiphy radio index or -1 for global
|
||||
* @num_different_channels: the number of different channels we want
|
||||
* to use for verification
|
||||
* @radar_detect: a bitmap where each bit corresponds to a channel
|
||||
@@ -1611,6 +1612,7 @@ struct cfg80211_color_change_settings {
|
||||
* the verification
|
||||
*/
|
||||
struct iface_combination_params {
|
||||
+ int radio_idx;
|
||||
int num_different_channels;
|
||||
u8 radar_detect;
|
||||
int iftype_num[NUM_NL80211_IFTYPES];
|
||||
@@ -4579,6 +4581,8 @@ struct mgmt_frame_regs {
|
||||
*
|
||||
* @set_hw_timestamp: Enable/disable HW timestamping of TM/FTM frames.
|
||||
* @set_ttlm: set the TID to link mapping.
|
||||
+ * @get_radio_mask: get bitmask of radios in use.
|
||||
+ * (invoked with the wiphy mutex held)
|
||||
*/
|
||||
struct cfg80211_ops {
|
||||
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
|
||||
@@ -4940,6 +4944,7 @@ struct cfg80211_ops {
|
||||
struct cfg80211_set_hw_timestamp *hwts);
|
||||
int (*set_ttlm)(struct wiphy *wiphy, struct net_device *dev,
|
||||
struct cfg80211_ttlm_params *params);
|
||||
+ u32 (*get_radio_mask)(struct wiphy *wiphy, struct net_device *dev);
|
||||
};
|
||||
|
||||
/*
|
||||
--- a/net/mac80211/util.c
|
||||
+++ b/net/mac80211/util.c
|
||||
@@ -3930,6 +3930,7 @@ int ieee80211_check_combinations(struct
|
||||
int total = 1;
|
||||
struct iface_combination_params params = {
|
||||
.radar_detect = radar_detect,
|
||||
+ .radio_idx = -1,
|
||||
};
|
||||
|
||||
lockdep_assert_wiphy(local->hw.wiphy);
|
||||
@@ -4020,7 +4021,9 @@ int ieee80211_max_num_channels(struct ie
|
||||
struct ieee80211_chanctx *ctx;
|
||||
u32 max_num_different_channels = 1;
|
||||
int err;
|
||||
- struct iface_combination_params params = {0};
|
||||
+ struct iface_combination_params params = {
|
||||
+ .radio_idx = -1,
|
||||
+ };
|
||||
|
||||
lockdep_assert_wiphy(local->hw.wiphy);
|
||||
|
||||
--- a/net/wireless/rdev-ops.h
|
||||
+++ b/net/wireless/rdev-ops.h
|
||||
@@ -1542,4 +1542,16 @@ rdev_set_ttlm(struct cfg80211_registered
|
||||
|
||||
return ret;
|
||||
}
|
||||
+
|
||||
+static inline u32
|
||||
+rdev_get_radio_mask(struct cfg80211_registered_device *rdev,
|
||||
+ struct net_device *dev)
|
||||
+{
|
||||
+ struct wiphy *wiphy = &rdev->wiphy;
|
||||
+
|
||||
+ if (!rdev->ops->get_radio_mask)
|
||||
+ return 0;
|
||||
+
|
||||
+ return rdev->ops->get_radio_mask(wiphy, dev);
|
||||
+}
|
||||
#endif /* __CFG80211_RDEV_OPS */
|
||||
--- a/net/wireless/util.c
|
||||
+++ b/net/wireless/util.c
|
||||
@@ -2305,13 +2305,16 @@ static int cfg80211_wdev_bi(struct wirel
|
||||
|
||||
static void cfg80211_calculate_bi_data(struct wiphy *wiphy, u32 new_beacon_int,
|
||||
u32 *beacon_int_gcd,
|
||||
- bool *beacon_int_different)
|
||||
+ bool *beacon_int_different,
|
||||
+ int radio_idx)
|
||||
{
|
||||
+ struct cfg80211_registered_device *rdev;
|
||||
struct wireless_dev *wdev;
|
||||
|
||||
*beacon_int_gcd = 0;
|
||||
*beacon_int_different = false;
|
||||
|
||||
+ rdev = wiphy_to_rdev(wiphy);
|
||||
list_for_each_entry(wdev, &wiphy->wdev_list, list) {
|
||||
int wdev_bi;
|
||||
|
||||
@@ -2319,6 +2322,11 @@ static void cfg80211_calculate_bi_data(s
|
||||
if (wdev->valid_links)
|
||||
continue;
|
||||
|
||||
+ /* skip wdevs not active on the given wiphy radio */
|
||||
+ if (radio_idx >= 0 &&
|
||||
+ !(rdev_get_radio_mask(rdev, wdev->netdev) & BIT(radio_idx)))
|
||||
+ continue;
|
||||
+
|
||||
wdev_bi = cfg80211_wdev_bi(wdev);
|
||||
|
||||
if (!wdev_bi)
|
||||
@@ -2366,14 +2374,19 @@ int cfg80211_iter_combinations(struct wi
|
||||
void *data),
|
||||
void *data)
|
||||
{
|
||||
+ const struct wiphy_radio *radio = NULL;
|
||||
+ const struct ieee80211_iface_combination *c, *cs;
|
||||
const struct ieee80211_regdomain *regdom;
|
||||
enum nl80211_dfs_regions region = 0;
|
||||
- int i, j, iftype;
|
||||
+ int i, j, n, iftype;
|
||||
int num_interfaces = 0;
|
||||
u32 used_iftypes = 0;
|
||||
u32 beacon_int_gcd;
|
||||
bool beacon_int_different;
|
||||
|
||||
+ if (params->radio_idx >= 0)
|
||||
+ radio = &wiphy->radio[params->radio_idx];
|
||||
+
|
||||
/*
|
||||
* This is a bit strange, since the iteration used to rely only on
|
||||
* the data given by the driver, but here it now relies on context,
|
||||
@@ -2385,7 +2398,8 @@ int cfg80211_iter_combinations(struct wi
|
||||
* interfaces (while being brought up) and channel/radar data.
|
||||
*/
|
||||
cfg80211_calculate_bi_data(wiphy, params->new_beacon_int,
|
||||
- &beacon_int_gcd, &beacon_int_different);
|
||||
+ &beacon_int_gcd, &beacon_int_different,
|
||||
+ params->radio_idx);
|
||||
|
||||
if (params->radar_detect) {
|
||||
rcu_read_lock();
|
||||
@@ -2402,13 +2416,18 @@ int cfg80211_iter_combinations(struct wi
|
||||
used_iftypes |= BIT(iftype);
|
||||
}
|
||||
|
||||
- for (i = 0; i < wiphy->n_iface_combinations; i++) {
|
||||
- const struct ieee80211_iface_combination *c;
|
||||
+ if (radio) {
|
||||
+ cs = radio->iface_combinations;
|
||||
+ n = radio->n_iface_combinations;
|
||||
+ } else {
|
||||
+ cs = wiphy->iface_combinations;
|
||||
+ n = wiphy->n_iface_combinations;
|
||||
+ }
|
||||
+ for (i = 0; i < n; i++) {
|
||||
struct ieee80211_iface_limit *limits;
|
||||
u32 all_iftypes = 0;
|
||||
|
||||
- c = &wiphy->iface_combinations[i];
|
||||
-
|
||||
+ c = &cs[i];
|
||||
if (num_interfaces > c->max_interfaces)
|
||||
continue;
|
||||
if (params->num_different_channels > c->num_different_channels)
|
||||
@@ -0,0 +1,69 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Fri, 7 Jun 2024 17:58:54 +0200
|
||||
Subject: [PATCH] wifi: cfg80211: add helper for checking if a chandef is
|
||||
valid on a radio
|
||||
|
||||
Check if the full channel width is in the radio's frequency range.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/include/net/cfg80211.h
|
||||
+++ b/include/net/cfg80211.h
|
||||
@@ -6485,6 +6485,15 @@ static inline bool cfg80211_channel_is_p
|
||||
}
|
||||
|
||||
/**
|
||||
+ * cfg80211_radio_chandef_valid - Check if the radio supports the chandef
|
||||
+ *
|
||||
+ * @radio: wiphy radio
|
||||
+ * @chandef: chandef for current channel
|
||||
+ */
|
||||
+bool cfg80211_radio_chandef_valid(const struct wiphy_radio *radio,
|
||||
+ const struct cfg80211_chan_def *chandef);
|
||||
+
|
||||
+/**
|
||||
* ieee80211_get_response_rate - get basic rate for a given rate
|
||||
*
|
||||
* @sband: the band to look for rates in
|
||||
--- a/net/wireless/util.c
|
||||
+++ b/net/wireless/util.c
|
||||
@@ -2884,3 +2884,38 @@ cfg80211_get_iftype_ext_capa(struct wiph
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_get_iftype_ext_capa);
|
||||
+
|
||||
+static bool
|
||||
+ieee80211_radio_freq_range_valid(const struct wiphy_radio *radio,
|
||||
+ u32 freq, u32 width)
|
||||
+{
|
||||
+ const struct wiphy_radio_freq_range *r;
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < radio->n_freq_range; i++) {
|
||||
+ r = &radio->freq_range[i];
|
||||
+ if (freq - width / 2 >= r->start_freq &&
|
||||
+ freq + width / 2 <= r->end_freq)
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
+bool cfg80211_radio_chandef_valid(const struct wiphy_radio *radio,
|
||||
+ const struct cfg80211_chan_def *chandef)
|
||||
+{
|
||||
+ u32 freq, width;
|
||||
+
|
||||
+ freq = ieee80211_chandef_to_khz(chandef);
|
||||
+ width = nl80211_chan_width_to_mhz(chandef->width);
|
||||
+ if (!ieee80211_radio_freq_range_valid(radio, freq, width))
|
||||
+ return false;
|
||||
+
|
||||
+ freq = MHZ_TO_KHZ(chandef->center_freq2);
|
||||
+ if (freq && !ieee80211_radio_freq_range_valid(radio, freq, width))
|
||||
+ return false;
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+EXPORT_SYMBOL(cfg80211_radio_chandef_valid);
|
||||
@@ -0,0 +1,88 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Thu, 6 Jun 2024 12:19:08 +0200
|
||||
Subject: [PATCH] wifi: mac80211: add support for DFS with multiple
|
||||
radios
|
||||
|
||||
DFS can be supported with multi-channel combinations, as long as each DFS
|
||||
capable radio only supports one channel.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/main.c
|
||||
+++ b/net/mac80211/main.c
|
||||
@@ -1084,6 +1084,27 @@ static int ieee80211_init_cipher_suites(
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static bool
|
||||
+ieee80211_ifcomb_check(const struct ieee80211_iface_combination *c, int n_comb)
|
||||
+{
|
||||
+ int i, j;
|
||||
+
|
||||
+ for (i = 0; i < n_comb; i++, c++) {
|
||||
+ /* DFS is not supported with multi-channel combinations yet */
|
||||
+ if (c->radar_detect_widths &&
|
||||
+ c->num_different_channels > 1)
|
||||
+ return false;
|
||||
+
|
||||
+ /* mac80211 doesn't support more than one IBSS interface */
|
||||
+ for (j = 0; j < c->n_limits; j++)
|
||||
+ if ((c->limits[j].types & BIT(NL80211_IFTYPE_ADHOC)) &&
|
||||
+ c->limits[j].max > 1)
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
int ieee80211_register_hw(struct ieee80211_hw *hw)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
@@ -1173,17 +1194,20 @@ int ieee80211_register_hw(struct ieee802
|
||||
if (comb->num_different_channels > 1)
|
||||
return -EINVAL;
|
||||
}
|
||||
- } else {
|
||||
- /* DFS is not supported with multi-channel combinations yet */
|
||||
- for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) {
|
||||
- const struct ieee80211_iface_combination *comb;
|
||||
+ }
|
||||
|
||||
- comb = &local->hw.wiphy->iface_combinations[i];
|
||||
+ if (hw->wiphy->n_radio) {
|
||||
+ for (i = 0; i < hw->wiphy->n_radio; i++) {
|
||||
+ const struct wiphy_radio *radio = &hw->wiphy->radio[i];
|
||||
|
||||
- if (comb->radar_detect_widths &&
|
||||
- comb->num_different_channels > 1)
|
||||
+ if (!ieee80211_ifcomb_check(radio->iface_combinations,
|
||||
+ radio->n_iface_combinations))
|
||||
return -EINVAL;
|
||||
}
|
||||
+ } else {
|
||||
+ if (!ieee80211_ifcomb_check(hw->wiphy->iface_combinations,
|
||||
+ hw->wiphy->n_iface_combinations))
|
||||
+ return -EINVAL;
|
||||
}
|
||||
|
||||
/* Only HW csum features are currently compatible with mac80211 */
|
||||
@@ -1313,18 +1337,6 @@ int ieee80211_register_hw(struct ieee802
|
||||
hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
|
||||
hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR);
|
||||
|
||||
- /* mac80211 doesn't support more than one IBSS interface right now */
|
||||
- for (i = 0; i < hw->wiphy->n_iface_combinations; i++) {
|
||||
- const struct ieee80211_iface_combination *c;
|
||||
- int j;
|
||||
-
|
||||
- c = &hw->wiphy->iface_combinations[i];
|
||||
-
|
||||
- for (j = 0; j < c->n_limits; j++)
|
||||
- if ((c->limits[j].types & BIT(NL80211_IFTYPE_ADHOC)) &&
|
||||
- c->limits[j].max > 1)
|
||||
- return -EINVAL;
|
||||
- }
|
||||
|
||||
local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) +
|
||||
sizeof(void *) * channels, GFP_KERNEL);
|
||||
@@ -0,0 +1,66 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Wed, 5 Jun 2024 10:41:34 +0200
|
||||
Subject: [PATCH] wifi: mac80211: add radio index to
|
||||
ieee80211_chanctx_conf
|
||||
|
||||
Will be used to explicitly assign a channel context to a wiphy radio.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/include/net/mac80211.h
|
||||
+++ b/include/net/mac80211.h
|
||||
@@ -250,6 +250,7 @@ struct ieee80211_chan_req {
|
||||
* @min_def: the minimum channel definition currently required.
|
||||
* @ap: the channel definition the AP actually is operating as,
|
||||
* for use with (wider bandwidth) OFDMA
|
||||
+ * @radio_idx: index of the wiphy radio used used for this channel
|
||||
* @rx_chains_static: The number of RX chains that must always be
|
||||
* active on the channel to receive MIMO transmissions
|
||||
* @rx_chains_dynamic: The number of RX chains that must be enabled
|
||||
@@ -264,6 +265,7 @@ struct ieee80211_chanctx_conf {
|
||||
struct cfg80211_chan_def min_def;
|
||||
struct cfg80211_chan_def ap;
|
||||
|
||||
+ int radio_idx;
|
||||
u8 rx_chains_static, rx_chains_dynamic;
|
||||
|
||||
bool radar_enabled;
|
||||
--- a/net/mac80211/chan.c
|
||||
+++ b/net/mac80211/chan.c
|
||||
@@ -623,7 +623,8 @@ ieee80211_chanctx_radar_required(struct
|
||||
static struct ieee80211_chanctx *
|
||||
ieee80211_alloc_chanctx(struct ieee80211_local *local,
|
||||
const struct ieee80211_chan_req *chanreq,
|
||||
- enum ieee80211_chanctx_mode mode)
|
||||
+ enum ieee80211_chanctx_mode mode,
|
||||
+ int radio_idx)
|
||||
{
|
||||
struct ieee80211_chanctx *ctx;
|
||||
|
||||
@@ -641,6 +642,7 @@ ieee80211_alloc_chanctx(struct ieee80211
|
||||
ctx->conf.rx_chains_dynamic = 1;
|
||||
ctx->mode = mode;
|
||||
ctx->conf.radar_enabled = false;
|
||||
+ ctx->conf.radio_idx = radio_idx;
|
||||
_ieee80211_recalc_chanctx_min_def(local, ctx, NULL);
|
||||
|
||||
return ctx;
|
||||
@@ -680,7 +682,7 @@ ieee80211_new_chanctx(struct ieee80211_l
|
||||
|
||||
lockdep_assert_wiphy(local->hw.wiphy);
|
||||
|
||||
- ctx = ieee80211_alloc_chanctx(local, chanreq, mode);
|
||||
+ ctx = ieee80211_alloc_chanctx(local, chanreq, mode, -1);
|
||||
if (!ctx)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
@@ -1098,7 +1100,7 @@ int ieee80211_link_reserve_chanctx(struc
|
||||
!list_empty(&curr_ctx->reserved_links))
|
||||
return -EBUSY;
|
||||
|
||||
- new_ctx = ieee80211_alloc_chanctx(local, chanreq, mode);
|
||||
+ new_ctx = ieee80211_alloc_chanctx(local, chanreq, mode, -1);
|
||||
if (!new_ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -0,0 +1,322 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Tue, 4 Jun 2024 21:48:48 +0200
|
||||
Subject: [PATCH] wifi: mac80211: extend ifcomb check functions for
|
||||
multi-radio
|
||||
|
||||
Add support for counting global and per-radio max/current number of
|
||||
channels, as well as checking radio-specific interface combinations.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/cfg.c
|
||||
+++ b/net/mac80211/cfg.c
|
||||
@@ -263,7 +263,7 @@ static int ieee80211_start_p2p_device(st
|
||||
|
||||
lockdep_assert_wiphy(sdata->local->hw.wiphy);
|
||||
|
||||
- ret = ieee80211_check_combinations(sdata, NULL, 0, 0);
|
||||
+ ret = ieee80211_check_combinations(sdata, NULL, 0, 0, -1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -285,7 +285,7 @@ static int ieee80211_start_nan(struct wi
|
||||
|
||||
lockdep_assert_wiphy(sdata->local->hw.wiphy);
|
||||
|
||||
- ret = ieee80211_check_combinations(sdata, NULL, 0, 0);
|
||||
+ ret = ieee80211_check_combinations(sdata, NULL, 0, 0, -1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -3992,7 +3992,7 @@ __ieee80211_channel_switch(struct wiphy
|
||||
goto out;
|
||||
|
||||
/* if reservation is invalid then this will fail */
|
||||
- err = ieee80211_check_combinations(sdata, NULL, chanctx->mode, 0);
|
||||
+ err = ieee80211_check_combinations(sdata, NULL, chanctx->mode, 0, -1);
|
||||
if (err) {
|
||||
ieee80211_link_unreserve_chanctx(link_data);
|
||||
goto out;
|
||||
@@ -5161,4 +5161,5 @@ const struct cfg80211_ops mac80211_confi
|
||||
.del_link_station = ieee80211_del_link_station,
|
||||
.set_hw_timestamp = ieee80211_set_hw_timestamp,
|
||||
.set_ttlm = ieee80211_set_ttlm,
|
||||
+ .get_radio_mask = ieee80211_get_radio_mask,
|
||||
};
|
||||
--- a/net/mac80211/chan.c
|
||||
+++ b/net/mac80211/chan.c
|
||||
@@ -47,24 +47,29 @@ int ieee80211_chanctx_refcount(struct ie
|
||||
ieee80211_chanctx_num_reserved(local, ctx);
|
||||
}
|
||||
|
||||
-static int ieee80211_num_chanctx(struct ieee80211_local *local)
|
||||
+static int ieee80211_num_chanctx(struct ieee80211_local *local, int radio_idx)
|
||||
{
|
||||
struct ieee80211_chanctx *ctx;
|
||||
int num = 0;
|
||||
|
||||
lockdep_assert_wiphy(local->hw.wiphy);
|
||||
|
||||
- list_for_each_entry(ctx, &local->chanctx_list, list)
|
||||
+ list_for_each_entry(ctx, &local->chanctx_list, list) {
|
||||
+ if (radio_idx >= 0 && ctx->conf.radio_idx != radio_idx)
|
||||
+ continue;
|
||||
num++;
|
||||
+ }
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
-static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local)
|
||||
+static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local,
|
||||
+ int radio_idx)
|
||||
{
|
||||
lockdep_assert_wiphy(local->hw.wiphy);
|
||||
|
||||
- return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local);
|
||||
+ return ieee80211_num_chanctx(local, radio_idx) <
|
||||
+ ieee80211_max_num_channels(local, radio_idx);
|
||||
}
|
||||
|
||||
static struct ieee80211_chanctx *
|
||||
@@ -1045,7 +1050,7 @@ int ieee80211_link_reserve_chanctx(struc
|
||||
|
||||
new_ctx = ieee80211_find_reservation_chanctx(local, chanreq, mode);
|
||||
if (!new_ctx) {
|
||||
- if (ieee80211_can_create_new_chanctx(local)) {
|
||||
+ if (ieee80211_can_create_new_chanctx(local, -1)) {
|
||||
new_ctx = ieee80211_new_chanctx(local, chanreq, mode);
|
||||
if (IS_ERR(new_ctx))
|
||||
return PTR_ERR(new_ctx);
|
||||
@@ -1736,7 +1741,7 @@ int ieee80211_link_use_channel(struct ie
|
||||
link->radar_required = ret;
|
||||
|
||||
ret = ieee80211_check_combinations(sdata, &chanreq->oper, mode,
|
||||
- radar_detect_width);
|
||||
+ radar_detect_width, -1);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
--- a/net/mac80211/ibss.c
|
||||
+++ b/net/mac80211/ibss.c
|
||||
@@ -1745,7 +1745,7 @@ int ieee80211_ibss_join(struct ieee80211
|
||||
IEEE80211_CHANCTX_SHARED : IEEE80211_CHANCTX_EXCLUSIVE;
|
||||
|
||||
ret = ieee80211_check_combinations(sdata, ¶ms->chandef, chanmode,
|
||||
- radar_detect_width);
|
||||
+ radar_detect_width, -1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
--- a/net/mac80211/ieee80211_i.h
|
||||
+++ b/net/mac80211/ieee80211_i.h
|
||||
@@ -2596,8 +2596,9 @@ void ieee80211_recalc_dtim(struct ieee80
|
||||
int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
enum ieee80211_chanctx_mode chanmode,
|
||||
- u8 radar_detect);
|
||||
-int ieee80211_max_num_channels(struct ieee80211_local *local);
|
||||
+ u8 radar_detect, int radio_idx);
|
||||
+int ieee80211_max_num_channels(struct ieee80211_local *local, int radio_idx);
|
||||
+u32 ieee80211_get_radio_mask(struct wiphy *wiphy, struct net_device *dev);
|
||||
void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
|
||||
struct ieee80211_chanctx *ctx);
|
||||
|
||||
--- a/net/mac80211/iface.c
|
||||
+++ b/net/mac80211/iface.c
|
||||
@@ -397,7 +397,7 @@ static int ieee80211_check_concurrent_if
|
||||
}
|
||||
}
|
||||
|
||||
- return ieee80211_check_combinations(sdata, NULL, 0, 0);
|
||||
+ return ieee80211_check_combinations(sdata, NULL, 0, 0, -1);
|
||||
}
|
||||
|
||||
static int ieee80211_check_queues(struct ieee80211_sub_if_data *sdata,
|
||||
--- a/net/mac80211/util.c
|
||||
+++ b/net/mac80211/util.c
|
||||
@@ -3918,20 +3918,103 @@ static u8 ieee80211_chanctx_radar_detect
|
||||
return radar_detect;
|
||||
}
|
||||
|
||||
+static u32
|
||||
+__ieee80211_get_radio_mask(struct ieee80211_sub_if_data *sdata)
|
||||
+{
|
||||
+ struct ieee80211_bss_conf *link_conf;
|
||||
+ struct ieee80211_chanctx_conf *conf;
|
||||
+ unsigned int link_id;
|
||||
+ u32 mask = 0;
|
||||
+
|
||||
+ for_each_vif_active_link(&sdata->vif, link_conf, link_id) {
|
||||
+ conf = sdata_dereference(link_conf->chanctx_conf, sdata);
|
||||
+ if (!conf || conf->radio_idx < 0)
|
||||
+ continue;
|
||||
+
|
||||
+ mask |= BIT(conf->radio_idx);
|
||||
+ }
|
||||
+
|
||||
+ return mask;
|
||||
+}
|
||||
+
|
||||
+u32 ieee80211_get_radio_mask(struct wiphy *wiphy, struct net_device *dev)
|
||||
+{
|
||||
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
+
|
||||
+ return __ieee80211_get_radio_mask(sdata);
|
||||
+}
|
||||
+
|
||||
+static bool
|
||||
+ieee80211_sdata_uses_radio(struct ieee80211_sub_if_data *sdata, int radio_idx)
|
||||
+{
|
||||
+ if (radio_idx < 0)
|
||||
+ return true;
|
||||
+
|
||||
+ return __ieee80211_get_radio_mask(sdata) & BIT(radio_idx);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+ieee80211_fill_ifcomb_params(struct ieee80211_local *local,
|
||||
+ struct iface_combination_params *params,
|
||||
+ const struct cfg80211_chan_def *chandef,
|
||||
+ struct ieee80211_sub_if_data *sdata)
|
||||
+{
|
||||
+ struct ieee80211_sub_if_data *sdata_iter;
|
||||
+ struct ieee80211_chanctx *ctx;
|
||||
+ int total = !!sdata;
|
||||
+
|
||||
+ list_for_each_entry(ctx, &local->chanctx_list, list) {
|
||||
+ if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
|
||||
+ continue;
|
||||
+
|
||||
+ if (params->radio_idx >= 0 &&
|
||||
+ ctx->conf.radio_idx != params->radio_idx)
|
||||
+ continue;
|
||||
+
|
||||
+ params->radar_detect |=
|
||||
+ ieee80211_chanctx_radar_detect(local, ctx);
|
||||
+
|
||||
+ if (chandef && ctx->mode != IEEE80211_CHANCTX_EXCLUSIVE &&
|
||||
+ cfg80211_chandef_compatible(chandef, &ctx->conf.def))
|
||||
+ continue;
|
||||
+
|
||||
+ params->num_different_channels++;
|
||||
+ }
|
||||
+
|
||||
+ list_for_each_entry(sdata_iter, &local->interfaces, list) {
|
||||
+ struct wireless_dev *wdev_iter;
|
||||
+
|
||||
+ wdev_iter = &sdata_iter->wdev;
|
||||
+
|
||||
+ if (sdata_iter == sdata ||
|
||||
+ !ieee80211_sdata_running(sdata_iter) ||
|
||||
+ cfg80211_iftype_allowed(local->hw.wiphy,
|
||||
+ wdev_iter->iftype, 0, 1))
|
||||
+ continue;
|
||||
+
|
||||
+ if (!ieee80211_sdata_uses_radio(sdata_iter, params->radio_idx))
|
||||
+ continue;
|
||||
+
|
||||
+ params->iftype_num[wdev_iter->iftype]++;
|
||||
+ total++;
|
||||
+ }
|
||||
+
|
||||
+ return total;
|
||||
+}
|
||||
+
|
||||
int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
enum ieee80211_chanctx_mode chanmode,
|
||||
- u8 radar_detect)
|
||||
+ u8 radar_detect, int radio_idx)
|
||||
{
|
||||
+ bool shared = chanmode == IEEE80211_CHANCTX_SHARED;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
- struct ieee80211_sub_if_data *sdata_iter;
|
||||
enum nl80211_iftype iftype = sdata->wdev.iftype;
|
||||
- struct ieee80211_chanctx *ctx;
|
||||
- int total = 1;
|
||||
struct iface_combination_params params = {
|
||||
.radar_detect = radar_detect,
|
||||
- .radio_idx = -1,
|
||||
+ .radio_idx = radio_idx,
|
||||
};
|
||||
+ int total;
|
||||
|
||||
lockdep_assert_wiphy(local->hw.wiphy);
|
||||
|
||||
@@ -3968,37 +4051,9 @@ int ieee80211_check_combinations(struct
|
||||
if (iftype != NL80211_IFTYPE_UNSPECIFIED)
|
||||
params.iftype_num[iftype] = 1;
|
||||
|
||||
- list_for_each_entry(ctx, &local->chanctx_list, list) {
|
||||
- if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
|
||||
- continue;
|
||||
- params.radar_detect |=
|
||||
- ieee80211_chanctx_radar_detect(local, ctx);
|
||||
- if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) {
|
||||
- params.num_different_channels++;
|
||||
- continue;
|
||||
- }
|
||||
- if (chandef && chanmode == IEEE80211_CHANCTX_SHARED &&
|
||||
- cfg80211_chandef_compatible(chandef,
|
||||
- &ctx->conf.def))
|
||||
- continue;
|
||||
- params.num_different_channels++;
|
||||
- }
|
||||
-
|
||||
- list_for_each_entry_rcu(sdata_iter, &local->interfaces, list) {
|
||||
- struct wireless_dev *wdev_iter;
|
||||
-
|
||||
- wdev_iter = &sdata_iter->wdev;
|
||||
-
|
||||
- if (sdata_iter == sdata ||
|
||||
- !ieee80211_sdata_running(sdata_iter) ||
|
||||
- cfg80211_iftype_allowed(local->hw.wiphy,
|
||||
- wdev_iter->iftype, 0, 1))
|
||||
- continue;
|
||||
-
|
||||
- params.iftype_num[wdev_iter->iftype]++;
|
||||
- total++;
|
||||
- }
|
||||
-
|
||||
+ total = ieee80211_fill_ifcomb_params(local, ¶ms,
|
||||
+ shared ? chandef : NULL,
|
||||
+ sdata);
|
||||
if (total == 1 && !params.radar_detect)
|
||||
return 0;
|
||||
|
||||
@@ -4015,30 +4070,17 @@ ieee80211_iter_max_chans(const struct ie
|
||||
c->num_different_channels);
|
||||
}
|
||||
|
||||
-int ieee80211_max_num_channels(struct ieee80211_local *local)
|
||||
+int ieee80211_max_num_channels(struct ieee80211_local *local, int radio_idx)
|
||||
{
|
||||
- struct ieee80211_sub_if_data *sdata;
|
||||
- struct ieee80211_chanctx *ctx;
|
||||
u32 max_num_different_channels = 1;
|
||||
int err;
|
||||
struct iface_combination_params params = {
|
||||
- .radio_idx = -1,
|
||||
+ .radio_idx = radio_idx,
|
||||
};
|
||||
|
||||
lockdep_assert_wiphy(local->hw.wiphy);
|
||||
|
||||
- list_for_each_entry(ctx, &local->chanctx_list, list) {
|
||||
- if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)
|
||||
- continue;
|
||||
-
|
||||
- params.num_different_channels++;
|
||||
-
|
||||
- params.radar_detect |=
|
||||
- ieee80211_chanctx_radar_detect(local, ctx);
|
||||
- }
|
||||
-
|
||||
- list_for_each_entry_rcu(sdata, &local->interfaces, list)
|
||||
- params.iftype_num[sdata->wdev.iftype]++;
|
||||
+ ieee80211_fill_ifcomb_params(local, ¶ms, NULL, NULL);
|
||||
|
||||
err = cfg80211_iter_combinations(local->hw.wiphy, ¶ms,
|
||||
ieee80211_iter_max_chans,
|
||||
@@ -0,0 +1,175 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Wed, 5 Jun 2024 10:49:22 +0200
|
||||
Subject: [PATCH] wifi: mac80211: move code in
|
||||
ieee80211_link_reserve_chanctx to a helper
|
||||
|
||||
Reduces indentation in preparation for further changes
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/chan.c
|
||||
+++ b/net/mac80211/chan.c
|
||||
@@ -1033,6 +1033,71 @@ int ieee80211_link_unreserve_chanctx(str
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static struct ieee80211_chanctx *
|
||||
+ieee80211_replace_chanctx(struct ieee80211_local *local,
|
||||
+ const struct ieee80211_chan_req *chanreq,
|
||||
+ enum ieee80211_chanctx_mode mode,
|
||||
+ struct ieee80211_chanctx *curr_ctx)
|
||||
+{
|
||||
+ struct ieee80211_chanctx *new_ctx, *ctx;
|
||||
+
|
||||
+ if (!curr_ctx || (curr_ctx->replace_state ==
|
||||
+ IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
|
||||
+ !list_empty(&curr_ctx->reserved_links)) {
|
||||
+ /*
|
||||
+ * Another link already requested this context for a
|
||||
+ * reservation. Find another one hoping all links assigned
|
||||
+ * to it will also switch soon enough.
|
||||
+ *
|
||||
+ * TODO: This needs a little more work as some cases
|
||||
+ * (more than 2 chanctx capable devices) may fail which could
|
||||
+ * otherwise succeed provided some channel context juggling was
|
||||
+ * performed.
|
||||
+ *
|
||||
+ * Consider ctx1..3, link1..6, each ctx has 2 links. link1 and
|
||||
+ * link2 from ctx1 request new different chandefs starting 2
|
||||
+ * in-place reserations with ctx4 and ctx5 replacing ctx1 and
|
||||
+ * ctx2 respectively. Next link5 and link6 from ctx3 reserve
|
||||
+ * ctx4. If link3 and link4 remain on ctx2 as they are then this
|
||||
+ * fails unless `replace_ctx` from ctx5 is replaced with ctx3.
|
||||
+ */
|
||||
+ list_for_each_entry(ctx, &local->chanctx_list, list) {
|
||||
+ if (ctx->replace_state !=
|
||||
+ IEEE80211_CHANCTX_REPLACE_NONE)
|
||||
+ continue;
|
||||
+
|
||||
+ if (!list_empty(&ctx->reserved_links))
|
||||
+ continue;
|
||||
+
|
||||
+ curr_ctx = ctx;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * If that's true then all available contexts already have reservations
|
||||
+ * and cannot be used.
|
||||
+ */
|
||||
+ if (!curr_ctx || (curr_ctx->replace_state ==
|
||||
+ IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
|
||||
+ !list_empty(&curr_ctx->reserved_links))
|
||||
+ return ERR_PTR(-EBUSY);
|
||||
+
|
||||
+ new_ctx = ieee80211_alloc_chanctx(local, chanreq, mode, -1);
|
||||
+ if (!new_ctx)
|
||||
+ return ERR_PTR(-ENOMEM);
|
||||
+
|
||||
+ new_ctx->replace_ctx = curr_ctx;
|
||||
+ new_ctx->replace_state = IEEE80211_CHANCTX_REPLACES_OTHER;
|
||||
+
|
||||
+ curr_ctx->replace_ctx = new_ctx;
|
||||
+ curr_ctx->replace_state = IEEE80211_CHANCTX_WILL_BE_REPLACED;
|
||||
+
|
||||
+ list_add_rcu(&new_ctx->list, &local->chanctx_list);
|
||||
+
|
||||
+ return new_ctx;
|
||||
+}
|
||||
+
|
||||
int ieee80211_link_reserve_chanctx(struct ieee80211_link_data *link,
|
||||
const struct ieee80211_chan_req *chanreq,
|
||||
enum ieee80211_chanctx_mode mode,
|
||||
@@ -1040,7 +1105,7 @@ int ieee80211_link_reserve_chanctx(struc
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = link->sdata;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
- struct ieee80211_chanctx *new_ctx, *curr_ctx, *ctx;
|
||||
+ struct ieee80211_chanctx *new_ctx, *curr_ctx;
|
||||
|
||||
lockdep_assert_wiphy(local->hw.wiphy);
|
||||
|
||||
@@ -1050,75 +1115,13 @@ int ieee80211_link_reserve_chanctx(struc
|
||||
|
||||
new_ctx = ieee80211_find_reservation_chanctx(local, chanreq, mode);
|
||||
if (!new_ctx) {
|
||||
- if (ieee80211_can_create_new_chanctx(local, -1)) {
|
||||
+ if (ieee80211_can_create_new_chanctx(local, -1))
|
||||
new_ctx = ieee80211_new_chanctx(local, chanreq, mode);
|
||||
- if (IS_ERR(new_ctx))
|
||||
- return PTR_ERR(new_ctx);
|
||||
- } else {
|
||||
- if (!curr_ctx ||
|
||||
- (curr_ctx->replace_state ==
|
||||
- IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
|
||||
- !list_empty(&curr_ctx->reserved_links)) {
|
||||
- /*
|
||||
- * Another link already requested this context
|
||||
- * for a reservation. Find another one hoping
|
||||
- * all links assigned to it will also switch
|
||||
- * soon enough.
|
||||
- *
|
||||
- * TODO: This needs a little more work as some
|
||||
- * cases (more than 2 chanctx capable devices)
|
||||
- * may fail which could otherwise succeed
|
||||
- * provided some channel context juggling was
|
||||
- * performed.
|
||||
- *
|
||||
- * Consider ctx1..3, link1..6, each ctx has 2
|
||||
- * links. link1 and link2 from ctx1 request new
|
||||
- * different chandefs starting 2 in-place
|
||||
- * reserations with ctx4 and ctx5 replacing
|
||||
- * ctx1 and ctx2 respectively. Next link5 and
|
||||
- * link6 from ctx3 reserve ctx4. If link3 and
|
||||
- * link4 remain on ctx2 as they are then this
|
||||
- * fails unless `replace_ctx` from ctx5 is
|
||||
- * replaced with ctx3.
|
||||
- */
|
||||
- list_for_each_entry(ctx, &local->chanctx_list,
|
||||
- list) {
|
||||
- if (ctx->replace_state !=
|
||||
- IEEE80211_CHANCTX_REPLACE_NONE)
|
||||
- continue;
|
||||
-
|
||||
- if (!list_empty(&ctx->reserved_links))
|
||||
- continue;
|
||||
-
|
||||
- curr_ctx = ctx;
|
||||
- break;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- /*
|
||||
- * If that's true then all available contexts already
|
||||
- * have reservations and cannot be used.
|
||||
- */
|
||||
- if (!curr_ctx ||
|
||||
- (curr_ctx->replace_state ==
|
||||
- IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
|
||||
- !list_empty(&curr_ctx->reserved_links))
|
||||
- return -EBUSY;
|
||||
-
|
||||
- new_ctx = ieee80211_alloc_chanctx(local, chanreq, mode, -1);
|
||||
- if (!new_ctx)
|
||||
- return -ENOMEM;
|
||||
-
|
||||
- new_ctx->replace_ctx = curr_ctx;
|
||||
- new_ctx->replace_state =
|
||||
- IEEE80211_CHANCTX_REPLACES_OTHER;
|
||||
-
|
||||
- curr_ctx->replace_ctx = new_ctx;
|
||||
- curr_ctx->replace_state =
|
||||
- IEEE80211_CHANCTX_WILL_BE_REPLACED;
|
||||
-
|
||||
- list_add_rcu(&new_ctx->list, &local->chanctx_list);
|
||||
- }
|
||||
+ else
|
||||
+ new_ctx = ieee80211_replace_chanctx(local, chanreq,
|
||||
+ mode, curr_ctx);
|
||||
+ if (IS_ERR(new_ctx))
|
||||
+ return PTR_ERR(new_ctx);
|
||||
}
|
||||
|
||||
list_add(&link->reserved_chanctx_list, &new_ctx->reserved_links);
|
||||
@@ -0,0 +1,132 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Wed, 5 Jun 2024 18:39:55 +0200
|
||||
Subject: [PATCH] wifi: mac80211: add wiphy radio assignment and
|
||||
validation
|
||||
|
||||
Validate number of channels and interface combinations per radio.
|
||||
Assign each channel context to a radio.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/chan.c
|
||||
+++ b/net/mac80211/chan.c
|
||||
@@ -680,14 +680,15 @@ static int ieee80211_add_chanctx(struct
|
||||
static struct ieee80211_chanctx *
|
||||
ieee80211_new_chanctx(struct ieee80211_local *local,
|
||||
const struct ieee80211_chan_req *chanreq,
|
||||
- enum ieee80211_chanctx_mode mode)
|
||||
+ enum ieee80211_chanctx_mode mode,
|
||||
+ int radio_idx)
|
||||
{
|
||||
struct ieee80211_chanctx *ctx;
|
||||
int err;
|
||||
|
||||
lockdep_assert_wiphy(local->hw.wiphy);
|
||||
|
||||
- ctx = ieee80211_alloc_chanctx(local, chanreq, mode, -1);
|
||||
+ ctx = ieee80211_alloc_chanctx(local, chanreq, mode, radio_idx);
|
||||
if (!ctx)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
@@ -1040,6 +1041,8 @@ ieee80211_replace_chanctx(struct ieee802
|
||||
struct ieee80211_chanctx *curr_ctx)
|
||||
{
|
||||
struct ieee80211_chanctx *new_ctx, *ctx;
|
||||
+ struct wiphy *wiphy = local->hw.wiphy;
|
||||
+ const struct wiphy_radio *radio;
|
||||
|
||||
if (!curr_ctx || (curr_ctx->replace_state ==
|
||||
IEEE80211_CHANCTX_WILL_BE_REPLACED) ||
|
||||
@@ -1069,6 +1072,12 @@ ieee80211_replace_chanctx(struct ieee802
|
||||
if (!list_empty(&ctx->reserved_links))
|
||||
continue;
|
||||
|
||||
+ if (ctx->conf.radio_idx >= 0) {
|
||||
+ radio = &wiphy->radio[ctx->conf.radio_idx];
|
||||
+ if (!cfg80211_radio_chandef_valid(radio, &chanreq->oper))
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
curr_ctx = ctx;
|
||||
break;
|
||||
}
|
||||
@@ -1098,6 +1107,34 @@ ieee80211_replace_chanctx(struct ieee802
|
||||
return new_ctx;
|
||||
}
|
||||
|
||||
+static bool
|
||||
+ieee80211_find_available_radio(struct ieee80211_local *local,
|
||||
+ const struct ieee80211_chan_req *chanreq,
|
||||
+ int *radio_idx)
|
||||
+{
|
||||
+ struct wiphy *wiphy = local->hw.wiphy;
|
||||
+ const struct wiphy_radio *radio;
|
||||
+ int i;
|
||||
+
|
||||
+ *radio_idx = -1;
|
||||
+ if (!wiphy->n_radio)
|
||||
+ return true;
|
||||
+
|
||||
+ for (i = 0; i < wiphy->n_radio; i++) {
|
||||
+ radio = &wiphy->radio[i];
|
||||
+ if (!cfg80211_radio_chandef_valid(radio, &chanreq->oper))
|
||||
+ continue;
|
||||
+
|
||||
+ if (!ieee80211_can_create_new_chanctx(local, i))
|
||||
+ continue;
|
||||
+
|
||||
+ *radio_idx = i;
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
int ieee80211_link_reserve_chanctx(struct ieee80211_link_data *link,
|
||||
const struct ieee80211_chan_req *chanreq,
|
||||
enum ieee80211_chanctx_mode mode,
|
||||
@@ -1106,6 +1143,7 @@ int ieee80211_link_reserve_chanctx(struc
|
||||
struct ieee80211_sub_if_data *sdata = link->sdata;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_chanctx *new_ctx, *curr_ctx;
|
||||
+ int radio_idx;
|
||||
|
||||
lockdep_assert_wiphy(local->hw.wiphy);
|
||||
|
||||
@@ -1115,8 +1153,10 @@ int ieee80211_link_reserve_chanctx(struc
|
||||
|
||||
new_ctx = ieee80211_find_reservation_chanctx(local, chanreq, mode);
|
||||
if (!new_ctx) {
|
||||
- if (ieee80211_can_create_new_chanctx(local, -1))
|
||||
- new_ctx = ieee80211_new_chanctx(local, chanreq, mode);
|
||||
+ if (ieee80211_can_create_new_chanctx(local, -1) &&
|
||||
+ ieee80211_find_available_radio(local, chanreq, &radio_idx))
|
||||
+ new_ctx = ieee80211_new_chanctx(local, chanreq, mode,
|
||||
+ radio_idx);
|
||||
else
|
||||
new_ctx = ieee80211_replace_chanctx(local, chanreq,
|
||||
mode, curr_ctx);
|
||||
@@ -1724,6 +1764,7 @@ int ieee80211_link_use_channel(struct ie
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_chanctx *ctx;
|
||||
u8 radar_detect_width = 0;
|
||||
+ int radio_idx;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_wiphy(local->hw.wiphy);
|
||||
@@ -1751,8 +1792,12 @@ int ieee80211_link_use_channel(struct ie
|
||||
__ieee80211_link_release_channel(link);
|
||||
|
||||
ctx = ieee80211_find_chanctx(local, chanreq, mode);
|
||||
- if (!ctx)
|
||||
- ctx = ieee80211_new_chanctx(local, chanreq, mode);
|
||||
+ if (!ctx) {
|
||||
+ if (!ieee80211_find_available_radio(local, chanreq, &radio_idx))
|
||||
+ ctx = ERR_PTR(-EBUSY);
|
||||
+ else
|
||||
+ ctx = ieee80211_new_chanctx(local, chanreq, mode, radio_idx);
|
||||
+ }
|
||||
if (IS_ERR(ctx)) {
|
||||
ret = PTR_ERR(ctx);
|
||||
goto out;
|
||||
@@ -0,0 +1,199 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Tue, 11 Jun 2024 09:02:55 +0200
|
||||
Subject: [PATCH] wifi: mac80211_hwsim: add support for multi-radio wiphy
|
||||
|
||||
This registers one wiphy radio per supported band. Number of different
|
||||
channels is set per radio.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/drivers/net/wireless/virtual/mac80211_hwsim.c
|
||||
+++ b/drivers/net/wireless/virtual/mac80211_hwsim.c
|
||||
@@ -69,6 +69,10 @@ static bool mlo;
|
||||
module_param(mlo, bool, 0444);
|
||||
MODULE_PARM_DESC(mlo, "Support MLO");
|
||||
|
||||
+static bool multi_radio;
|
||||
+module_param(multi_radio, bool, 0444);
|
||||
+MODULE_PARM_DESC(mlo, "Support Multiple Radios per wiphy");
|
||||
+
|
||||
/**
|
||||
* enum hwsim_regtest - the type of regulatory tests we offer
|
||||
*
|
||||
@@ -669,6 +673,10 @@ struct mac80211_hwsim_data {
|
||||
struct ieee80211_iface_limit if_limits[3];
|
||||
int n_if_limits;
|
||||
|
||||
+ struct ieee80211_iface_combination if_combination_radio;
|
||||
+ struct wiphy_radio_freq_range radio_range[NUM_NL80211_BANDS];
|
||||
+ struct wiphy_radio radio[NUM_NL80211_BANDS];
|
||||
+
|
||||
u32 ciphers[ARRAY_SIZE(hwsim_ciphers)];
|
||||
|
||||
struct mac_address addresses[2];
|
||||
@@ -917,6 +925,7 @@ static const struct nla_policy hwsim_gen
|
||||
[HWSIM_ATTR_MLO_SUPPORT] = { .type = NLA_FLAG },
|
||||
[HWSIM_ATTR_PMSR_SUPPORT] = NLA_POLICY_NESTED(hwsim_pmsr_capa_policy),
|
||||
[HWSIM_ATTR_PMSR_RESULT] = NLA_POLICY_NESTED(hwsim_pmsr_peers_result_policy),
|
||||
+ [HWSIM_ATTR_MULTI_RADIO] = { .type = NLA_FLAG },
|
||||
};
|
||||
|
||||
#if IS_REACHABLE(CONFIG_VIRTIO)
|
||||
@@ -4007,6 +4016,7 @@ struct hwsim_new_radio_params {
|
||||
bool reg_strict;
|
||||
bool p2p_device;
|
||||
bool use_chanctx;
|
||||
+ bool multi_radio;
|
||||
bool destroy_on_close;
|
||||
const char *hwname;
|
||||
bool no_vif;
|
||||
@@ -4083,6 +4093,12 @@ static int append_radio_msg(struct sk_bu
|
||||
return ret;
|
||||
}
|
||||
|
||||
+ if (param->multi_radio) {
|
||||
+ ret = nla_put_flag(skb, HWSIM_ATTR_MULTI_RADIO);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
if (param->hwname) {
|
||||
ret = nla_put(skb, HWSIM_ATTR_RADIO_NAME,
|
||||
strlen(param->hwname), param->hwname);
|
||||
@@ -5099,6 +5115,7 @@ static int mac80211_hwsim_new_radio(stru
|
||||
struct net *net;
|
||||
int idx, i;
|
||||
int n_limits = 0;
|
||||
+ int n_bands = 0;
|
||||
|
||||
if (WARN_ON(param->channels > 1 && !param->use_chanctx))
|
||||
return -EINVAL;
|
||||
@@ -5202,22 +5219,22 @@ static int mac80211_hwsim_new_radio(stru
|
||||
n_limits++;
|
||||
}
|
||||
|
||||
+ data->if_combination.radar_detect_widths =
|
||||
+ BIT(NL80211_CHAN_WIDTH_5) |
|
||||
+ BIT(NL80211_CHAN_WIDTH_10) |
|
||||
+ BIT(NL80211_CHAN_WIDTH_20_NOHT) |
|
||||
+ BIT(NL80211_CHAN_WIDTH_20) |
|
||||
+ BIT(NL80211_CHAN_WIDTH_40) |
|
||||
+ BIT(NL80211_CHAN_WIDTH_80) |
|
||||
+ BIT(NL80211_CHAN_WIDTH_160);
|
||||
+
|
||||
if (data->use_chanctx) {
|
||||
hw->wiphy->max_scan_ssids = 255;
|
||||
hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
|
||||
hw->wiphy->max_remain_on_channel_duration = 1000;
|
||||
- data->if_combination.radar_detect_widths = 0;
|
||||
data->if_combination.num_different_channels = data->channels;
|
||||
} else {
|
||||
data->if_combination.num_different_channels = 1;
|
||||
- data->if_combination.radar_detect_widths =
|
||||
- BIT(NL80211_CHAN_WIDTH_5) |
|
||||
- BIT(NL80211_CHAN_WIDTH_10) |
|
||||
- BIT(NL80211_CHAN_WIDTH_20_NOHT) |
|
||||
- BIT(NL80211_CHAN_WIDTH_20) |
|
||||
- BIT(NL80211_CHAN_WIDTH_40) |
|
||||
- BIT(NL80211_CHAN_WIDTH_80) |
|
||||
- BIT(NL80211_CHAN_WIDTH_160);
|
||||
}
|
||||
|
||||
if (!n_limits) {
|
||||
@@ -5333,6 +5350,9 @@ static int mac80211_hwsim_new_radio(stru
|
||||
|
||||
for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) {
|
||||
struct ieee80211_supported_band *sband = &data->bands[band];
|
||||
+ struct wiphy_radio_freq_range *radio_range;
|
||||
+ const struct ieee80211_channel *c;
|
||||
+ struct wiphy_radio *radio;
|
||||
|
||||
sband->band = band;
|
||||
|
||||
@@ -5406,8 +5426,36 @@ static int mac80211_hwsim_new_radio(stru
|
||||
mac80211_hwsim_sband_capab(sband);
|
||||
|
||||
hw->wiphy->bands[band] = sband;
|
||||
+
|
||||
+ if (!param->multi_radio)
|
||||
+ continue;
|
||||
+
|
||||
+ c = sband->channels;
|
||||
+ radio_range = &data->radio_range[n_bands];
|
||||
+ radio_range->start_freq = ieee80211_channel_to_khz(c) - 10000;
|
||||
+
|
||||
+ c += sband->n_channels - 1;
|
||||
+ radio_range->end_freq = ieee80211_channel_to_khz(c) + 10000;
|
||||
+
|
||||
+ radio = &data->radio[n_bands++];
|
||||
+ radio->freq_range = radio_range;
|
||||
+ radio->n_freq_range = 1;
|
||||
+ radio->iface_combinations = &data->if_combination_radio;
|
||||
+ radio->n_iface_combinations = 1;
|
||||
}
|
||||
|
||||
+ if (param->multi_radio) {
|
||||
+ hw->wiphy->radio = data->radio;
|
||||
+ hw->wiphy->n_radio = n_bands;
|
||||
+
|
||||
+ memcpy(&data->if_combination_radio, &data->if_combination,
|
||||
+ sizeof(data->if_combination));
|
||||
+ data->if_combination.num_different_channels *= n_bands;
|
||||
+ }
|
||||
+
|
||||
+ if (data->use_chanctx)
|
||||
+ data->if_combination.radar_detect_widths = 0;
|
||||
+
|
||||
/* By default all radios belong to the first group */
|
||||
data->group = 1;
|
||||
mutex_init(&data->mutex);
|
||||
@@ -6025,6 +6073,9 @@ static int hwsim_new_radio_nl(struct sk_
|
||||
else
|
||||
param.use_chanctx = (param.channels > 1);
|
||||
|
||||
+ if (info->attrs[HWSIM_ATTR_MULTI_RADIO])
|
||||
+ param.multi_radio = true;
|
||||
+
|
||||
if (info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2])
|
||||
param.reg_alpha2 =
|
||||
nla_data(info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]);
|
||||
@@ -6105,7 +6156,7 @@ static int hwsim_new_radio_nl(struct sk_
|
||||
|
||||
param.mlo = info->attrs[HWSIM_ATTR_MLO_SUPPORT];
|
||||
|
||||
- if (param.mlo)
|
||||
+ if (param.mlo || param.multi_radio)
|
||||
param.use_chanctx = true;
|
||||
|
||||
if (info->attrs[HWSIM_ATTR_RADIO_NAME]) {
|
||||
@@ -6802,7 +6853,8 @@ static int __init init_mac80211_hwsim(vo
|
||||
|
||||
param.p2p_device = support_p2p_device;
|
||||
param.mlo = mlo;
|
||||
- param.use_chanctx = channels > 1 || mlo;
|
||||
+ param.multi_radio = multi_radio;
|
||||
+ param.use_chanctx = channels > 1 || mlo || multi_radio;
|
||||
param.iftypes = HWSIM_IFTYPE_SUPPORT_MASK;
|
||||
if (param.p2p_device)
|
||||
param.iftypes |= BIT(NL80211_IFTYPE_P2P_DEVICE);
|
||||
--- a/drivers/net/wireless/virtual/mac80211_hwsim.h
|
||||
+++ b/drivers/net/wireless/virtual/mac80211_hwsim.h
|
||||
@@ -157,6 +157,9 @@ enum hwsim_commands {
|
||||
* to provide details about peer measurement request (nl80211_peer_measurement_attrs)
|
||||
* @HWSIM_ATTR_PMSR_RESULT: nested attributed used with %HWSIM_CMD_REPORT_PMSR
|
||||
* to provide peer measurement result (nl80211_peer_measurement_attrs)
|
||||
+ * @HWSIM_ATTR_MULTI_RADIO: Register multiple wiphy radios (flag).
|
||||
+ * Adds one radio for each band. Number of supported channels will be set for
|
||||
+ * each radio instead of for the wiphy.
|
||||
* @__HWSIM_ATTR_MAX: enum limit
|
||||
*/
|
||||
enum hwsim_attrs {
|
||||
@@ -189,6 +192,7 @@ enum hwsim_attrs {
|
||||
HWSIM_ATTR_PMSR_SUPPORT,
|
||||
HWSIM_ATTR_PMSR_REQUEST,
|
||||
HWSIM_ATTR_PMSR_RESULT,
|
||||
+ HWSIM_ATTR_MULTI_RADIO,
|
||||
__HWSIM_ATTR_MAX,
|
||||
};
|
||||
#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
|
||||
@@ -0,0 +1,40 @@
|
||||
From: Hauke Mehrtens <hauke@hauke-m.de>
|
||||
Date: Mon, 24 Feb 2020 00:00:00 +0100
|
||||
Subject: [PATCH] mac80211: Allow IBSS mode and different beacon intervals
|
||||
|
||||
ath10k-ct supports the combination to select IBSS (ADHOC) mode and
|
||||
different beacon intervals together. mac80211 does not like this
|
||||
combination, but Ben says this is ok, so remove this check.
|
||||
|
||||
ath10k-ct starting with version 5.2 allows the combination of
|
||||
NL80211_IFTYPE_ADHOC and beacon_int_min_gcd in ath10k_10x_ct_if_comb
|
||||
which triggers this warning. Ben told me that this is not a big problem
|
||||
and we should ignore this.
|
||||
---
|
||||
net/wireless/core.c | 15 ---------------
|
||||
1 file changed, 15 deletions(-)
|
||||
|
||||
--- a/net/wireless/core.c
|
||||
+++ b/net/wireless/core.c
|
||||
@@ -651,21 +651,6 @@ static int wiphy_verify_combinations(str
|
||||
c->limits[j].max > 1))
|
||||
return -EINVAL;
|
||||
|
||||
- /*
|
||||
- * This isn't well-defined right now. If you have an
|
||||
- * IBSS interface, then its beacon interval may change
|
||||
- * by joining other networks, and nothing prevents it
|
||||
- * from doing that.
|
||||
- * So technically we probably shouldn't even allow AP
|
||||
- * and IBSS in the same interface, but it seems that
|
||||
- * some drivers support that, possibly only with fixed
|
||||
- * beacon intervals for IBSS.
|
||||
- */
|
||||
- if (WARN_ON(types & BIT(NL80211_IFTYPE_ADHOC) &&
|
||||
- c->beacon_int_min_gcd)) {
|
||||
- return -EINVAL;
|
||||
- }
|
||||
-
|
||||
cnt += c->limits[j].max;
|
||||
/*
|
||||
* Don't advertise an unsupported type
|
||||
@@ -0,0 +1,34 @@
|
||||
From: David Bauer <mail@david-bauer.net>
|
||||
Date: Thu, 30 Nov 2023 07:32:52 +0100
|
||||
Subject: [PATCH] mac80211: avoid crashing on invalid band info
|
||||
|
||||
Frequent crashes have been observed on MT7916 based platforms. While the
|
||||
root of these crashes are currently unknown, they happen when decoding
|
||||
rate information of connected STAs in AP mode. The rate-information is
|
||||
associated with a band which is not available on the PHY.
|
||||
|
||||
Check for this condition in order to avoid crashing the whole system.
|
||||
This patch should be removed once the roout cause has been found and
|
||||
fixed.
|
||||
|
||||
Link: https://github.com/freifunk-gluon/gluon/issues/2980
|
||||
|
||||
Signed-off-by: David Bauer <mail@david-bauer.net>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/sta_info.c
|
||||
+++ b/net/mac80211/sta_info.c
|
||||
@@ -2459,6 +2459,13 @@ static void sta_stats_decode_rate(struct
|
||||
|
||||
sband = local->hw.wiphy->bands[band];
|
||||
|
||||
+ if (!sband) {
|
||||
+ wiphy_warn(local->hw.wiphy,
|
||||
+ "Invalid band %d\n",
|
||||
+ band);
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
if (WARN_ON_ONCE(!sband->bitrates))
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
--- a/backport-include/linux/of_net.h
|
||||
+++ /dev/null
|
||||
@@ -1,26 +0,0 @@
|
||||
-#ifndef _BP_OF_NET_H
|
||||
-#define _BP_OF_NET_H
|
||||
-#include_next <linux/of_net.h>
|
||||
-#include <linux/version.h>
|
||||
-#include <linux/etherdevice.h>
|
||||
-
|
||||
-/* The behavior of of_get_mac_address() changed in kernel 5.2, it now
|
||||
- * returns an error code and not NULL in case of an error.
|
||||
- */
|
||||
-#if LINUX_VERSION_IS_LESS(5,13,0)
|
||||
-static inline int backport_of_get_mac_address(struct device_node *np, u8 *mac_out)
|
||||
-{
|
||||
- const void *mac = of_get_mac_address(np);
|
||||
-
|
||||
- if (!mac)
|
||||
- return -ENODEV;
|
||||
- if (IS_ERR(mac))
|
||||
- return PTR_ERR(mac);
|
||||
- ether_addr_copy(mac_out, mac);
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-#define of_get_mac_address LINUX_BACKPORT(of_get_mac_address)
|
||||
-#endif /* < 5.2 */
|
||||
-
|
||||
-#endif /* _BP_OF_NET_H */
|
||||
Reference in New Issue
Block a user