xref: /linux/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c (revision 8be4d31cb8aaeea27bde4b7ddb26e28a89062ebf)
1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3  * KUnit tests for link selection functions
4  *
5  * Copyright (C) 2025 Intel Corporation
6  */
7 #include <kunit/static_stub.h>
8 
9 #include "utils.h"
10 #include "mld.h"
11 #include "link.h"
12 #include "iface.h"
13 #include "phy.h"
14 #include "mlo.h"
15 
16 static const struct link_grading_test_case {
17 	const char *desc;
18 	struct {
19 		struct {
20 			u8 link_id;
21 			const struct cfg80211_chan_def *chandef;
22 			bool active;
23 			s32 signal;
24 			bool has_chan_util_elem;
25 			u8 chan_util; /* 0-255 , used only if has_chan_util_elem is true */
26 			u8 chan_load_by_us; /* 0-100, used only if active is true */;
27 		} link;
28 	} input;
29 	unsigned int expected_grade;
30 } link_grading_cases[] = {
31 	{
32 		.desc = "channel util of 128 (50%)",
33 		.input.link = {
34 			.link_id = 0,
35 			.chandef = &chandef_2ghz_20mhz,
36 			.active = false,
37 			.has_chan_util_elem = true,
38 			.chan_util = 128,
39 		},
40 		.expected_grade = 86,
41 	},
42 	{
43 		.desc = "channel util of 180 (70%)",
44 		.input.link = {
45 			.link_id = 0,
46 			.chandef = &chandef_2ghz_20mhz,
47 			.active = false,
48 			.has_chan_util_elem = true,
49 			.chan_util = 180,
50 		},
51 		.expected_grade = 51,
52 	},
53 	{
54 		.desc = "channel util of 180 (70%), channel load by us of 10%",
55 		.input.link = {
56 			.link_id = 0,
57 			.chandef = &chandef_2ghz_20mhz,
58 			.has_chan_util_elem = true,
59 			.chan_util = 180,
60 			.active = true,
61 			.chan_load_by_us = 10,
62 		},
63 		.expected_grade = 67,
64 	},
65 		{
66 		.desc = "no channel util element",
67 		.input.link = {
68 			.link_id = 0,
69 			.chandef = &chandef_2ghz_20mhz,
70 			.active = true,
71 		},
72 		.expected_grade = 120,
73 	},
74 };
75 
76 KUNIT_ARRAY_PARAM_DESC(link_grading, link_grading_cases, desc);
77 
setup_link(struct ieee80211_bss_conf * link)78 static void setup_link(struct ieee80211_bss_conf *link)
79 {
80 	struct kunit *test = kunit_get_current_test();
81 	struct iwl_mld *mld = test->priv;
82 	const struct link_grading_test_case *test_param =
83 		(const void *)(test->param_value);
84 
85 	KUNIT_ALLOC_AND_ASSERT(test, link->bss);
86 
87 	link->bss->signal = DBM_TO_MBM(test_param->input.link.signal);
88 
89 	link->chanreq.oper = *test_param->input.link.chandef;
90 
91 	if (test_param->input.link.has_chan_util_elem) {
92 		struct cfg80211_bss_ies *ies;
93 		struct ieee80211_bss_load_elem bss_load = {
94 			.channel_util = test_param->input.link.chan_util,
95 		};
96 		struct element *elem =
97 			iwlmld_kunit_gen_element(WLAN_EID_QBSS_LOAD,
98 						 &bss_load,
99 						 sizeof(bss_load));
100 		unsigned int elem_len = sizeof(*elem) + sizeof(bss_load);
101 
102 		KUNIT_ALLOC_AND_ASSERT_SIZE(test, ies, sizeof(*ies) + elem_len);
103 		memcpy(ies->data, elem, elem_len);
104 		ies->len = elem_len;
105 		rcu_assign_pointer(link->bss->beacon_ies, ies);
106 		rcu_assign_pointer(link->bss->ies, ies);
107 	}
108 
109 	if (test_param->input.link.active) {
110 		struct ieee80211_chanctx_conf *chan_ctx =
111 			wiphy_dereference(mld->wiphy, link->chanctx_conf);
112 		struct iwl_mld_phy *phy;
113 
114 		KUNIT_ASSERT_NOT_NULL(test, chan_ctx);
115 
116 		phy = iwl_mld_phy_from_mac80211(chan_ctx);
117 
118 		phy->channel_load_by_us = test_param->input.link.chan_load_by_us;
119 	}
120 }
121 
test_link_grading(struct kunit * test)122 static void test_link_grading(struct kunit *test)
123 {
124 	struct iwl_mld *mld = test->priv;
125 	const struct link_grading_test_case *test_param =
126 		(const void *)(test->param_value);
127 	struct ieee80211_vif *vif;
128 	struct ieee80211_bss_conf *link;
129 	unsigned int actual_grade;
130 	/* Extract test case parameters */
131 	u8 link_id = test_param->input.link.link_id;
132 	bool active = test_param->input.link.active;
133 	u16 valid_links;
134 	struct iwl_mld_kunit_link assoc_link = {
135 		.chandef = test_param->input.link.chandef,
136 	};
137 
138 	/* If the link is not active, use a different link as the assoc link */
139 	if (active) {
140 		assoc_link.id = link_id;
141 		valid_links = BIT(link_id);
142 	} else {
143 		assoc_link.id = BIT(ffz(BIT(link_id)));
144 		valid_links = BIT(assoc_link.id) | BIT(link_id);
145 	}
146 
147 	vif = iwlmld_kunit_setup_mlo_assoc(valid_links, &assoc_link);
148 
149 	wiphy_lock(mld->wiphy);
150 	link = wiphy_dereference(mld->wiphy, vif->link_conf[link_id]);
151 	KUNIT_ASSERT_NOT_NULL(test, link);
152 
153 	setup_link(link);
154 
155 	actual_grade = iwl_mld_get_link_grade(mld, link);
156 	wiphy_unlock(mld->wiphy);
157 
158 	/* Assert that the returned grade matches the expected grade */
159 	KUNIT_EXPECT_EQ(test, actual_grade, test_param->expected_grade);
160 }
161 
162 static struct kunit_case link_selection_cases[] = {
163 	KUNIT_CASE_PARAM(test_link_grading, link_grading_gen_params),
164 	{},
165 };
166 
167 static struct kunit_suite link_selection = {
168 	.name = "iwlmld-link-selection-tests",
169 	.test_cases = link_selection_cases,
170 	.init = iwlmld_kunit_test_init,
171 };
172 
173 kunit_test_suite(link_selection);
174 
175 static const struct link_pair_case {
176 	const char *desc;
177 	const struct cfg80211_chan_def *chandef_a, *chandef_b;
178 	bool low_latency_vif;
179 	u32 chan_load_not_by_us;
180 	bool primary_link_active;
181 	u32 expected_result;
182 } link_pair_cases[] = {
183 	{
184 		.desc = "Unequal bandwidth, primary link inactive, EMLSR not allowed",
185 		.low_latency_vif = false,
186 		.primary_link_active = false,
187 		.chandef_a = &chandef_5ghz_40mhz,
188 		.chandef_b = &chandef_6ghz_20mhz,
189 		.expected_result = IWL_MLD_EMLSR_EXIT_CHAN_LOAD,
190 	},
191 	{
192 		.desc = "Equal bandwidths, sufficient channel load, EMLSR allowed",
193 		.low_latency_vif = false,
194 		.primary_link_active = true,
195 		.chan_load_not_by_us = 11,
196 		.chandef_a = &chandef_5ghz_40mhz,
197 		.chandef_b = &chandef_6ghz_40mhz,
198 		.expected_result = 0,
199 	},
200 	{
201 		.desc = "Equal bandwidths, insufficient channel load, EMLSR not allowed",
202 		.low_latency_vif = false,
203 		.primary_link_active = true,
204 		.chan_load_not_by_us = 6,
205 		.chandef_a = &chandef_5ghz_80mhz,
206 		.chandef_b = &chandef_6ghz_80mhz,
207 		.expected_result = IWL_MLD_EMLSR_EXIT_CHAN_LOAD,
208 	},
209 	{
210 		.desc = "Low latency VIF, sufficient channel load, EMLSR allowed",
211 		.low_latency_vif = true,
212 		.primary_link_active = true,
213 		.chan_load_not_by_us = 6,
214 		.chandef_a = &chandef_5ghz_160mhz,
215 		.chandef_b = &chandef_6ghz_160mhz,
216 		.expected_result = 0,
217 	},
218 	{
219 		.desc = "Different bandwidths (2x ratio), primary link load permits EMLSR",
220 		.low_latency_vif = false,
221 		.primary_link_active = true,
222 		.chan_load_not_by_us = 30,
223 		.chandef_a = &chandef_5ghz_40mhz,
224 		.chandef_b = &chandef_6ghz_20mhz,
225 		.expected_result = 0,
226 	},
227 	{
228 		.desc = "Different bandwidths (4x ratio), primary link load permits EMLSR",
229 		.low_latency_vif = false,
230 		.primary_link_active = true,
231 		.chan_load_not_by_us = 45,
232 		.chandef_a = &chandef_5ghz_80mhz,
233 		.chandef_b = &chandef_6ghz_20mhz,
234 		.expected_result = 0,
235 	},
236 	{
237 		.desc = "Different bandwidths (16x ratio), primary link load insufficient",
238 		.low_latency_vif = false,
239 		.primary_link_active = true,
240 		.chan_load_not_by_us = 45,
241 		.chandef_a = &chandef_6ghz_320mhz,
242 		.chandef_b = &chandef_5ghz_20mhz,
243 		.expected_result = IWL_MLD_EMLSR_EXIT_CHAN_LOAD,
244 	},
245 	{
246 		.desc = "Same band not allowed (2.4 GHz)",
247 		.low_latency_vif = false,
248 		.primary_link_active = true,
249 		.chan_load_not_by_us = 30,
250 		.chandef_a = &chandef_2ghz_20mhz,
251 		.chandef_b = &chandef_2ghz_11_20mhz,
252 		.expected_result = IWL_MLD_EMLSR_EXIT_EQUAL_BAND,
253 	},
254 	{
255 		.desc = "Same band not allowed (5 GHz)",
256 		.low_latency_vif = false,
257 		.primary_link_active = true,
258 		.chan_load_not_by_us = 30,
259 		.chandef_a = &chandef_5ghz_40mhz,
260 		.chandef_b = &chandef_5ghz_40mhz,
261 		.expected_result = IWL_MLD_EMLSR_EXIT_EQUAL_BAND,
262 	},
263 	{
264 		.desc = "Same band allowed (5 GHz separated)",
265 		.low_latency_vif = false,
266 		.primary_link_active = true,
267 		.chan_load_not_by_us = 30,
268 		.chandef_a = &chandef_5ghz_40mhz,
269 		.chandef_b = &chandef_5ghz_120_40mhz,
270 		.expected_result = 0,
271 	},
272 	{
273 		.desc = "Same band not allowed (6 GHz)",
274 		.low_latency_vif = false,
275 		.primary_link_active = true,
276 		.chan_load_not_by_us = 30,
277 		.chandef_a = &chandef_6ghz_160mhz,
278 		.chandef_b = &chandef_6ghz_221_160mhz,
279 		.expected_result = IWL_MLD_EMLSR_EXIT_EQUAL_BAND,
280 	},
281 };
282 
283 KUNIT_ARRAY_PARAM_DESC(link_pair, link_pair_cases, desc);
284 
test_iwl_mld_link_pair_allows_emlsr(struct kunit * test)285 static void test_iwl_mld_link_pair_allows_emlsr(struct kunit *test)
286 {
287 	const struct link_pair_case *params = test->param_value;
288 	struct iwl_mld *mld = test->priv;
289 	struct ieee80211_vif *vif;
290 	/* link A is the primary and link B is the secondary */
291 	struct iwl_mld_link_sel_data a = {
292 		.chandef = params->chandef_a,
293 		.link_id = 4,
294 	};
295 	struct iwl_mld_link_sel_data b = {
296 		.chandef = params->chandef_b,
297 		.link_id = 5,
298 	};
299 	struct iwl_mld_kunit_link assoc_link = {
300 		.chandef = params->primary_link_active ? a.chandef : b.chandef,
301 		.id = params->primary_link_active ? a.link_id : b.link_id,
302 	};
303 	u32 result;
304 
305 	vif = iwlmld_kunit_setup_mlo_assoc(BIT(a.link_id) | BIT(b.link_id),
306 					   &assoc_link);
307 
308 	if (params->low_latency_vif)
309 		iwl_mld_vif_from_mac80211(vif)->low_latency_causes = 1;
310 
311 	wiphy_lock(mld->wiphy);
312 
313 	/* Simulate channel load */
314 	if (params->primary_link_active) {
315 		struct iwl_mld_phy *phy =
316 			iwlmld_kunit_get_phy_of_link(vif, a.link_id);
317 
318 		phy->avg_channel_load_not_by_us = params->chan_load_not_by_us;
319 	}
320 
321 	result = iwl_mld_emlsr_pair_state(vif, &a, &b);
322 
323 	wiphy_unlock(mld->wiphy);
324 
325 	KUNIT_EXPECT_EQ(test, result, params->expected_result);
326 }
327 
328 static struct kunit_case link_pair_criteria_test_cases[] = {
329 	KUNIT_CASE_PARAM(test_iwl_mld_link_pair_allows_emlsr, link_pair_gen_params),
330 	{}
331 };
332 
333 static struct kunit_suite link_pair_criteria_tests = {
334 	.name = "iwlmld_link_pair_allows_emlsr",
335 	.test_cases = link_pair_criteria_test_cases,
336 	.init = iwlmld_kunit_test_init,
337 };
338 
339 kunit_test_suite(link_pair_criteria_tests);
340