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, 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, 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, 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, 70 .active = true, 71 }, 72 .expected_grade = 120, 73 }, 74 }; 75 76 KUNIT_ARRAY_PARAM_DESC(link_grading, link_grading_cases, desc); 77 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 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 .band = test_param->input.link.chandef->chan->band, 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 channel_load_case { 176 const char *desc; 177 bool low_latency_vif; 178 u32 chan_load_not_by_us; 179 enum nl80211_chan_width bw_a; 180 enum nl80211_chan_width bw_b; 181 bool primary_link_active; 182 bool expected_result; 183 } channel_load_cases[] = { 184 { 185 .desc = "Unequal bandwidth, primary link inactive, EMLSR not allowed", 186 .low_latency_vif = false, 187 .primary_link_active = false, 188 .bw_a = NL80211_CHAN_WIDTH_40, 189 .bw_b = NL80211_CHAN_WIDTH_20, 190 .expected_result = false, 191 }, 192 { 193 .desc = "Equal bandwidths, sufficient channel load, EMLSR allowed", 194 .low_latency_vif = false, 195 .primary_link_active = true, 196 .chan_load_not_by_us = 11, 197 .bw_a = NL80211_CHAN_WIDTH_40, 198 .bw_b = NL80211_CHAN_WIDTH_40, 199 .expected_result = true, 200 }, 201 { 202 .desc = "Equal bandwidths, insufficient channel load, EMLSR not allowed", 203 .low_latency_vif = false, 204 .primary_link_active = true, 205 .chan_load_not_by_us = 6, 206 .bw_a = NL80211_CHAN_WIDTH_80, 207 .bw_b = NL80211_CHAN_WIDTH_80, 208 .expected_result = false, 209 }, 210 { 211 .desc = "Low latency VIF, sufficient channel load, EMLSR allowed", 212 .low_latency_vif = true, 213 .primary_link_active = true, 214 .chan_load_not_by_us = 6, 215 .bw_a = NL80211_CHAN_WIDTH_160, 216 .bw_b = NL80211_CHAN_WIDTH_160, 217 .expected_result = true, 218 }, 219 { 220 .desc = "Different bandwidths (2x ratio), primary link load permits EMLSR", 221 .low_latency_vif = false, 222 .primary_link_active = true, 223 .chan_load_not_by_us = 30, 224 .bw_a = NL80211_CHAN_WIDTH_40, 225 .bw_b = NL80211_CHAN_WIDTH_20, 226 .expected_result = true, 227 }, 228 { 229 .desc = "Different bandwidths (4x ratio), primary link load permits EMLSR", 230 .low_latency_vif = false, 231 .primary_link_active = true, 232 .chan_load_not_by_us = 45, 233 .bw_a = NL80211_CHAN_WIDTH_80, 234 .bw_b = NL80211_CHAN_WIDTH_20, 235 .expected_result = true, 236 }, 237 { 238 .desc = "Different bandwidths (16x ratio), primary link load insufficient", 239 .low_latency_vif = false, 240 .primary_link_active = true, 241 .chan_load_not_by_us = 45, 242 .bw_a = NL80211_CHAN_WIDTH_320, 243 .bw_b = NL80211_CHAN_WIDTH_20, 244 .expected_result = false, 245 }, 246 }; 247 248 KUNIT_ARRAY_PARAM_DESC(channel_load, channel_load_cases, desc); 249 250 static void test_iwl_mld_channel_load_allows_emlsr(struct kunit *test) 251 { 252 const struct channel_load_case *params = test->param_value; 253 struct iwl_mld *mld = test->priv; 254 struct ieee80211_vif *vif; 255 struct cfg80211_chan_def chandef_a, chandef_b; 256 struct iwl_mld_link_sel_data a = {.chandef = &chandef_a, 257 .link_id = 4}; 258 struct iwl_mld_link_sel_data b = {.chandef = &chandef_b, 259 .link_id = 5}; 260 struct iwl_mld_kunit_link assoc_link = { 261 .id = params->primary_link_active ? a.link_id : b.link_id, 262 .bandwidth = params->primary_link_active ? params->bw_a : params->bw_b, 263 }; 264 bool result; 265 266 vif = iwlmld_kunit_setup_mlo_assoc(BIT(a.link_id) | BIT(b.link_id), 267 &assoc_link); 268 269 chandef_a.width = params->bw_a; 270 chandef_b.width = params->bw_b; 271 272 if (params->low_latency_vif) 273 iwl_mld_vif_from_mac80211(vif)->low_latency_causes = 1; 274 275 wiphy_lock(mld->wiphy); 276 277 /* Simulate channel load */ 278 if (params->primary_link_active) { 279 struct iwl_mld_phy *phy = 280 iwlmld_kunit_get_phy_of_link(vif, a.link_id); 281 282 phy->avg_channel_load_not_by_us = params->chan_load_not_by_us; 283 } 284 285 result = iwl_mld_channel_load_allows_emlsr(mld, vif, &a, &b); 286 287 wiphy_unlock(mld->wiphy); 288 289 KUNIT_EXPECT_EQ(test, result, params->expected_result); 290 } 291 292 static struct kunit_case channel_load_criteria_test_cases[] = { 293 KUNIT_CASE_PARAM(test_iwl_mld_channel_load_allows_emlsr, channel_load_gen_params), 294 {} 295 }; 296 297 static struct kunit_suite channel_load_criteria_tests = { 298 .name = "iwlmld_channel_load_allows_emlsr", 299 .test_cases = channel_load_criteria_test_cases, 300 .init = iwlmld_kunit_test_init, 301 }; 302 303 kunit_test_suite(channel_load_criteria_tests); 304