2024-06-05 18:48:52

by Felix Fietkau

[permalink] [raw]
Subject: [RFC v2 0/7] cfg80211/mac80211: support defining multiple radios per 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 series, the bands and supported frequencies 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.

Each mac80211 channel context is assigned to a radio based on radio specific
frequency ranges and interface combinations.

This is loosely based on Karthikeyan Periyasamy's series
"[PATCH 00/13] wifi: Add multi physical hardware iface combination support"
with some differences:

- a struct wiphy_radio is defined, which holds the frequency ranges and
a full struct ieee80211_iface_combination array
- a channel context is explicitly assigned to a radio when created
- both global and per-radio interface combination limits are checked
and enforced on channel context assignment

Changes since RFC:
- replace static per-radio number of channels limit with full ifcomb
checks
- remove band bitmask in favor of only using freq ranges

Felix Fietkau (7):
wifi: nl80211: split helper function from nl80211_put_iface_combinations
wifi: cfg80211: add support for advertising multiple radios belonging to a wiphy
wifi: cfg80211: extend interface combination check for multi-radio
wifi: mac80211: add radio index to ieee80211_chanctx_conf
wifi: mac80211: extend ifcomb check functions for multi-radio
wifi: mac80211: move code in ieee80211_link_reserve_chanctx to a helper
wifi: mac80211: add wiphy radio assignment and validation

include/net/cfg80211.h | 40 ++++++-
include/net/mac80211.h | 2 +-
include/uapi/linux/nl80211.h | 48 ++++++++-
net/mac80211/cfg.c | 6 +-
net/mac80211/chan.c | 228 +++++++++++++++++++++++-------------
net/mac80211/ibss.c | 2 +-
net/mac80211/ieee80211_i.h | 4 +-
net/mac80211/iface.c | 2 +-
net/mac80211/util.c | 114 ++++++++++++------
net/wireless/nl80211.c | 190 +++++++++++++++++++++---------
net/wireless/util.c | 29 ++---
11 files changed, 481 insertions(+), 184 deletions(-)

base-commit: 5bcd9a0a59953b5ff55ac226f903397319bf7f40
--
git-series 0.9.1


2024-06-05 18:48:58

by Felix Fietkau

[permalink] [raw]
Subject: [RFC v2 2/7] 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.

Signed-off-by: Felix Fietkau <[email protected]>
---
include/net/cfg80211.h | 38 ++++++++++++++++++-
include/uapi/linux/nl80211.h | 48 ++++++++++++++++++++++-
net/wireless/nl80211.c | 79 +++++++++++++++++++++++++++++++++++++-
3 files changed, 165 insertions(+)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 5da9bb0ac6a4..27355e08ae52 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -5403,6 +5403,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 - This structure describes a physical radio belonging
+ * to a wiphy. It is used to describe concurrent-channel capabilities of the
+ * phy. 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 +5653,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 +5806,9 @@ struct wiphy {

u16 hw_timestamp_max_peers;

+ const struct wiphy_radio *radio;
+ int n_radio;
+
char priv[] __aligned(NETDEV_ALIGN);
};

diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index f917bc6c9b6f..784bf7501d97 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -3401,6 +3401,8 @@ enum nl80211_attrs {

NL80211_ATTR_ASSOC_SPP_AMSDU,

+ NL80211_ATTR_RADIOS,
+
/* add attributes here, update the policy in nl80211.c */

__NL80211_ATTR_AFTER_LAST,
@@ -7999,4 +8001,50 @@ 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_FREQ_RANGES: Nested array of frequency ranges
+ * supported by this radio.
+ * @NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing
+ * the supported interface combinations for this radio. In each nested item,
+ * it 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_FREQ_RANGES,
+ NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATIONS,
+
+ /* 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
+ * @NL80211_WIPHY_RADIO_FREQ_ATTR_END: Frequency range end
+ *
+ * @__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 */
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 7b0ba0fff082..a8e3a08e908d 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -2399,6 +2399,79 @@ static int nl80211_put_mbssid_support(struct wiphy *wiphy, struct sk_buff *msg)
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, *freqs, *freq, *nl_combis;
+ int i;
+
+ radio = nla_nest_start(msg, idx);
+ if (!radio)
+ return -ENOBUFS;
+
+ freqs = nla_nest_start_noflag(msg, NL80211_WIPHY_RADIO_ATTR_FREQ_RANGES);
+ if (!freqs)
+ goto nla_put_failure;
+
+ for (i = 0; i < r->n_freq_range; i++) {
+ const struct wiphy_radio_freq_range *range = &r->freq_range[i];
+ int ret;
+
+ freq = nla_nest_start(msg, i);
+ ret = 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);
+ nla_nest_end(msg, freq);
+
+ if (ret)
+ goto nla_put_failure;
+ }
+
+ nla_nest_end(msg, freqs);
+
+ nl_combis = nla_nest_start_noflag(msg,
+ NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATIONS);
+ if (!nl_combis)
+ goto nla_put_failure;
+
+ for (i = 0; i < r->n_iface_combinations; i++)
+ if (nl80211_put_ifcomb_data(msg, true, i + 1,
+ &r->iface_combinations[i]))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, nl_combis);
+ 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_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);
+ return 0;
+
+fail:
+ nla_nest_cancel(msg, radios);
+ return -ENOBUFS;
+}
+
struct nl80211_dump_wiphy_state {
s64 filter_wiphy;
long start;
@@ -3008,6 +3081,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
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;
--
git-series 0.9.1

2024-06-05 18:58:26

by Felix Fietkau

[permalink] [raw]
Subject: [RFC v2 4/7] 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 <[email protected]>
---
include/net/mac80211.h | 2 ++
net/mac80211/chan.c | 8 +++++---
2 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index ecfa65ade226..98394970c991 100644
--- 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;
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index ec16d7676088..574ae961a7cf 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -638,7 +638,8 @@ ieee80211_chanctx_radar_required(struct ieee80211_local *local,
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;

@@ -656,6 +657,7 @@ ieee80211_alloc_chanctx(struct ieee80211_local *local,
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;
@@ -696,7 +698,7 @@ ieee80211_new_chanctx(struct ieee80211_local *local,

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);

@@ -1126,7 +1128,7 @@ int ieee80211_link_reserve_chanctx(struct ieee80211_link_data *link,
!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;

--
git-series 0.9.1

2024-06-05 18:58:57

by Felix Fietkau

[permalink] [raw]
Subject: [RFC v2 1/7] 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 <[email protected]>
---
net/wireless/nl80211.c | 111 ++++++++++++++++++++++--------------------
1 file changed, 59 insertions(+), 52 deletions(-)

diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 8ff5f79d446a..7b0ba0fff082 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1622,71 +1622,78 @@ static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes)
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);

--
git-series 0.9.1

2024-06-06 10:00:24

by Aditya Kumar Singh

[permalink] [raw]
Subject: Re: [RFC v2 0/7] cfg80211/mac80211: support defining multiple radios per wiphy

On 6/6/24 00:01, Felix Fietkau wrote:
> 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 series, the bands and supported frequencies 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.
>
> Each mac80211 channel context is assigned to a radio based on radio specific
> frequency ranges and interface combinations.
>
> This is loosely based on Karthikeyan Periyasamy's series
> "[PATCH 00/13] wifi: Add multi physical hardware iface combination support"
> with some differences:
>
> - a struct wiphy_radio is defined, which holds the frequency ranges and
> a full struct ieee80211_iface_combination array
> - a channel context is explicitly assigned to a radio when created
> - both global and per-radio interface combination limits are checked
> and enforced on channel context assignment
>
> Changes since RFC:
> - replace static per-radio number of channels limit with full ifcomb
> checks
> - remove band bitmask in favor of only using freq ranges

What about handling 2 GHz + 5 GHz issue we discussed in v1 related to
radar detection width and num chan ctx? Is that taken care?

2024-06-06 10:10:33

by Felix Fietkau

[permalink] [raw]
Subject: Re: [RFC v2 0/7] cfg80211/mac80211: support defining multiple radios per wiphy

On 06.06.24 12:00, Aditya Kumar Singh wrote:
> On 6/6/24 00:01, Felix Fietkau wrote:
>> 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 series, the bands and supported frequencies 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.
>>
>> Each mac80211 channel context is assigned to a radio based on radio specific
>> frequency ranges and interface combinations.
>>
>> This is loosely based on Karthikeyan Periyasamy's series
>> "[PATCH 00/13] wifi: Add multi physical hardware iface combination support"
>> with some differences:
>>
>> - a struct wiphy_radio is defined, which holds the frequency ranges and
>> a full struct ieee80211_iface_combination array
>> - a channel context is explicitly assigned to a radio when created
>> - both global and per-radio interface combination limits are checked
>> and enforced on channel context assignment
>>
>> Changes since RFC:
>> - replace static per-radio number of channels limit with full ifcomb
>> checks
>> - remove band bitmask in favor of only using freq ranges
>
> What about handling 2 GHz + 5 GHz issue we discussed in v1 related to
> radar detection width and num chan ctx? Is that taken care?

Seems that I missed that one. I will take care of it in the next version.

Thanks,

- Felix