| b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame] | 1 | From: Felix Fietkau <nbd@nbd.name> |
| 2 | Date: Fri, 22 Jan 2021 23:57:50 +0100 |
| 3 | Subject: [PATCH] mac80211: minstrel_ht: significantly redesign the rate |
| 4 | probing strategy |
| 5 | |
| 6 | The biggest flaw in current minstrel_ht is the fact that it needs way too |
| 7 | many probing packets to be able to quickly find the best rate. |
| 8 | Depending on the wifi hardware and operating mode, this can significantly |
| 9 | reduce throughput when not operating at the highest available data rate. |
| 10 | |
| 11 | In order to be able to significantly reduce the amount of rate sampling, |
| 12 | we need a much smarter selection of probing rates. |
| 13 | |
| 14 | The new approach introduced by this patch maintains a limited set of |
| 15 | available rates to be tested during a statistics window. |
| 16 | |
| 17 | They are split into distinct categories: |
| 18 | - MINSTREL_SAMPLE_TYPE_INC - incremental rate upgrade: |
| 19 | Pick the next rate group and find the first rate that is faster than |
| 20 | the current max. throughput rate |
| 21 | - MINSTREL_SAMPLE_TYPE_JUMP - random testing of higher rates: |
| 22 | Pick a random rate from the next group that is faster than the current |
| 23 | max throughput rate. This allows faster adaptation when the link changes |
| 24 | significantly |
| 25 | - MINSTREL_SAMPLE_TYPE_SLOW - test a rate between max_prob, max_tp2 and |
| 26 | max_tp in order to reduce the gap between them |
| 27 | |
| 28 | In order to prioritize sampling, every 6 attempts are split into 3x INC, |
| 29 | 2x JUMP, 1x SLOW. |
| 30 | |
| 31 | Available rates are checked and refilled on every stats window update. |
| 32 | |
| 33 | With this approach, we finally get a very small delta in throughput when |
| 34 | comparing setting the optimal data rate as a fixed rate vs normal rate |
| 35 | control operation. |
| 36 | |
| 37 | Signed-off-by: Felix Fietkau <nbd@nbd.name> |
| 38 | --- |
| 39 | |
| 40 | --- a/net/mac80211/rc80211_minstrel_ht.c |
| 41 | +++ b/net/mac80211/rc80211_minstrel_ht.c |
| 42 | @@ -266,6 +266,14 @@ const struct mcs_group minstrel_mcs_grou |
| 43 | const s16 minstrel_cck_bitrates[4] = { 10, 20, 55, 110 }; |
| 44 | const s16 minstrel_ofdm_bitrates[8] = { 60, 90, 120, 180, 240, 360, 480, 540 }; |
| 45 | static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES] __read_mostly; |
| 46 | +static const u8 minstrel_sample_seq[] = { |
| 47 | + MINSTREL_SAMPLE_TYPE_INC, |
| 48 | + MINSTREL_SAMPLE_TYPE_JUMP, |
| 49 | + MINSTREL_SAMPLE_TYPE_INC, |
| 50 | + MINSTREL_SAMPLE_TYPE_JUMP, |
| 51 | + MINSTREL_SAMPLE_TYPE_INC, |
| 52 | + MINSTREL_SAMPLE_TYPE_SLOW, |
| 53 | +}; |
| 54 | |
| 55 | static void |
| 56 | minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi); |
| 57 | @@ -620,77 +628,31 @@ minstrel_ht_prob_rate_reduce_streams(str |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | -static bool |
| 62 | -minstrel_ht_probe_group(struct minstrel_ht_sta *mi, const struct mcs_group *tp_group, |
| 63 | - int tp_idx, const struct mcs_group *group) |
| 64 | -{ |
| 65 | - if (group->bw < tp_group->bw) |
| 66 | - return false; |
| 67 | - |
| 68 | - if (group->streams == tp_group->streams) |
| 69 | - return true; |
| 70 | - |
| 71 | - if (tp_idx < 4 && group->streams == tp_group->streams - 1) |
| 72 | - return true; |
| 73 | - |
| 74 | - return group->streams == tp_group->streams + 1; |
| 75 | -} |
| 76 | - |
| 77 | -static void |
| 78 | -minstrel_ht_find_probe_rates(struct minstrel_ht_sta *mi, u16 *rates, int *n_rates, |
| 79 | - bool faster_rate) |
| 80 | +static u16 |
| 81 | +__minstrel_ht_get_sample_rate(struct minstrel_ht_sta *mi, |
| 82 | + enum minstrel_sample_type type) |
| 83 | { |
| 84 | - const struct mcs_group *group, *tp_group; |
| 85 | - int i, g, max_dur; |
| 86 | - int tp_idx; |
| 87 | - |
| 88 | - tp_group = &minstrel_mcs_groups[MI_RATE_GROUP(mi->max_tp_rate[0])]; |
| 89 | - tp_idx = MI_RATE_IDX(mi->max_tp_rate[0]); |
| 90 | - |
| 91 | - max_dur = minstrel_get_duration(mi->max_tp_rate[0]); |
| 92 | - if (faster_rate) |
| 93 | - max_dur -= max_dur / 16; |
| 94 | - |
| 95 | - for (g = 0; g < MINSTREL_GROUPS_NB; g++) { |
| 96 | - u16 supported = mi->supported[g]; |
| 97 | - |
| 98 | - if (!supported) |
| 99 | - continue; |
| 100 | + u16 *rates = mi->sample[type].sample_rates; |
| 101 | + u16 cur; |
| 102 | + int i; |
| 103 | |
| 104 | - group = &minstrel_mcs_groups[g]; |
| 105 | - if (!minstrel_ht_probe_group(mi, tp_group, tp_idx, group)) |
| 106 | + for (i = 0; i < MINSTREL_SAMPLE_RATES; i++) { |
| 107 | + if (!rates[i]) |
| 108 | continue; |
| 109 | |
| 110 | - for (i = 0; supported; supported >>= 1, i++) { |
| 111 | - int idx; |
| 112 | - |
| 113 | - if (!(supported & 1)) |
| 114 | - continue; |
| 115 | - |
| 116 | - if ((group->duration[i] << group->shift) > max_dur) |
| 117 | - continue; |
| 118 | - |
| 119 | - idx = MI_RATE(g, i); |
| 120 | - if (idx == mi->max_tp_rate[0]) |
| 121 | - continue; |
| 122 | - |
| 123 | - rates[(*n_rates)++] = idx; |
| 124 | - break; |
| 125 | - } |
| 126 | + cur = rates[i]; |
| 127 | + rates[i] = 0; |
| 128 | + return cur; |
| 129 | } |
| 130 | + |
| 131 | + return 0; |
| 132 | } |
| 133 | |
| 134 | static void |
| 135 | minstrel_ht_rate_sample_switch(struct minstrel_priv *mp, |
| 136 | struct minstrel_ht_sta *mi) |
| 137 | { |
| 138 | - struct minstrel_rate_stats *mrs; |
| 139 | - u16 rates[MINSTREL_GROUPS_NB]; |
| 140 | - int n_rates = 0; |
| 141 | - int probe_rate = 0; |
| 142 | - bool faster_rate; |
| 143 | - int i; |
| 144 | - u8 random; |
| 145 | + u16 rate; |
| 146 | |
| 147 | /* |
| 148 | * Use rate switching instead of probing packets for devices with |
| 149 | @@ -699,43 +661,11 @@ minstrel_ht_rate_sample_switch(struct mi |
| 150 | if (mp->hw->max_rates > 1) |
| 151 | return; |
| 152 | |
| 153 | - /* |
| 154 | - * If the current EWMA prob is >75%, look for a rate that's 6.25% |
| 155 | - * faster than the max tp rate. |
| 156 | - * If that fails, look again for a rate that is at least as fast |
| 157 | - */ |
| 158 | - mrs = minstrel_get_ratestats(mi, mi->max_tp_rate[0]); |
| 159 | - faster_rate = mrs->prob_avg > MINSTREL_FRAC(75, 100); |
| 160 | - minstrel_ht_find_probe_rates(mi, rates, &n_rates, faster_rate); |
| 161 | - if (!n_rates && faster_rate) |
| 162 | - minstrel_ht_find_probe_rates(mi, rates, &n_rates, false); |
| 163 | - |
| 164 | - /* If no suitable rate was found, try to pick the next one in the group */ |
| 165 | - if (!n_rates) { |
| 166 | - int g_idx = MI_RATE_GROUP(mi->max_tp_rate[0]); |
| 167 | - u16 supported = mi->supported[g_idx]; |
| 168 | - |
| 169 | - supported >>= MI_RATE_IDX(mi->max_tp_rate[0]); |
| 170 | - for (i = 0; supported; supported >>= 1, i++) { |
| 171 | - if (!(supported & 1)) |
| 172 | - continue; |
| 173 | - |
| 174 | - probe_rate = mi->max_tp_rate[0] + i; |
| 175 | - goto out; |
| 176 | - } |
| 177 | - |
| 178 | + rate = __minstrel_ht_get_sample_rate(mi, MINSTREL_SAMPLE_TYPE_INC); |
| 179 | + if (!rate) |
| 180 | return; |
| 181 | - } |
| 182 | - |
| 183 | - i = 0; |
| 184 | - if (n_rates > 1) { |
| 185 | - random = prandom_u32(); |
| 186 | - i = random % n_rates; |
| 187 | - } |
| 188 | - probe_rate = rates[i]; |
| 189 | |
| 190 | -out: |
| 191 | - mi->sample_rate = probe_rate; |
| 192 | + mi->sample_rate = rate; |
| 193 | mi->sample_mode = MINSTREL_SAMPLE_ACTIVE; |
| 194 | } |
| 195 | |
| 196 | @@ -804,6 +734,274 @@ minstrel_ht_calc_rate_stats(struct minst |
| 197 | mrs->attempts = 0; |
| 198 | } |
| 199 | |
| 200 | +static bool |
| 201 | +minstrel_ht_find_sample_rate(struct minstrel_ht_sta *mi, int type, int idx) |
| 202 | +{ |
| 203 | + int i; |
| 204 | + |
| 205 | + for (i = 0; i < MINSTREL_SAMPLE_RATES; i++) { |
| 206 | + u16 cur = mi->sample[type].sample_rates[i]; |
| 207 | + |
| 208 | + if (cur == idx) |
| 209 | + return true; |
| 210 | + |
| 211 | + if (!cur) |
| 212 | + break; |
| 213 | + } |
| 214 | + |
| 215 | + return false; |
| 216 | +} |
| 217 | + |
| 218 | +static int |
| 219 | +minstrel_ht_move_sample_rates(struct minstrel_ht_sta *mi, int type, |
| 220 | + u32 fast_rate_dur, u32 slow_rate_dur) |
| 221 | +{ |
| 222 | + u16 *rates = mi->sample[type].sample_rates; |
| 223 | + int i, j; |
| 224 | + |
| 225 | + for (i = 0, j = 0; i < MINSTREL_SAMPLE_RATES; i++) { |
| 226 | + u32 duration; |
| 227 | + bool valid = false; |
| 228 | + u16 cur; |
| 229 | + |
| 230 | + cur = rates[i]; |
| 231 | + if (!cur) |
| 232 | + continue; |
| 233 | + |
| 234 | + duration = minstrel_get_duration(cur); |
| 235 | + switch (type) { |
| 236 | + case MINSTREL_SAMPLE_TYPE_SLOW: |
| 237 | + valid = duration > fast_rate_dur && |
| 238 | + duration < slow_rate_dur; |
| 239 | + break; |
| 240 | + case MINSTREL_SAMPLE_TYPE_INC: |
| 241 | + case MINSTREL_SAMPLE_TYPE_JUMP: |
| 242 | + valid = duration < fast_rate_dur; |
| 243 | + break; |
| 244 | + default: |
| 245 | + valid = false; |
| 246 | + break; |
| 247 | + } |
| 248 | + |
| 249 | + if (!valid) { |
| 250 | + rates[i] = 0; |
| 251 | + continue; |
| 252 | + } |
| 253 | + |
| 254 | + if (i == j) |
| 255 | + continue; |
| 256 | + |
| 257 | + rates[j++] = cur; |
| 258 | + rates[i] = 0; |
| 259 | + } |
| 260 | + |
| 261 | + return j; |
| 262 | +} |
| 263 | + |
| 264 | +static int |
| 265 | +minstrel_ht_group_min_rate_offset(struct minstrel_ht_sta *mi, int group, |
| 266 | + u32 max_duration) |
| 267 | +{ |
| 268 | + u16 supported = mi->supported[group]; |
| 269 | + int i; |
| 270 | + |
| 271 | + for (i = 0; i < MCS_GROUP_RATES && supported; i++, supported >>= 1) { |
| 272 | + if (!(supported & BIT(0))) |
| 273 | + continue; |
| 274 | + |
| 275 | + if (minstrel_get_duration(MI_RATE(group, i)) >= max_duration) |
| 276 | + continue; |
| 277 | + |
| 278 | + return i; |
| 279 | + } |
| 280 | + |
| 281 | + return -1; |
| 282 | +} |
| 283 | + |
| 284 | +/* |
| 285 | + * Incremental update rates: |
| 286 | + * Flip through groups and pick the first group rate that is faster than the |
| 287 | + * highest currently selected rate |
| 288 | + */ |
| 289 | +static u16 |
| 290 | +minstrel_ht_next_inc_rate(struct minstrel_ht_sta *mi, u32 fast_rate_dur) |
| 291 | +{ |
| 292 | + struct minstrel_mcs_group_data *mg; |
| 293 | + u8 type = MINSTREL_SAMPLE_TYPE_INC; |
| 294 | + int i, index = 0; |
| 295 | + u8 group; |
| 296 | + |
| 297 | + group = mi->sample[type].sample_group; |
| 298 | + for (i = 0; i < ARRAY_SIZE(minstrel_mcs_groups); i++) { |
| 299 | + group = (group + 1) % ARRAY_SIZE(minstrel_mcs_groups); |
| 300 | + mg = &mi->groups[group]; |
| 301 | + |
| 302 | + index = minstrel_ht_group_min_rate_offset(mi, group, |
| 303 | + fast_rate_dur); |
| 304 | + if (index < 0) |
| 305 | + continue; |
| 306 | + |
| 307 | + index = MI_RATE(group, index & 0xf); |
| 308 | + if (!minstrel_ht_find_sample_rate(mi, type, index)) |
| 309 | + goto out; |
| 310 | + } |
| 311 | + index = 0; |
| 312 | + |
| 313 | +out: |
| 314 | + mi->sample[type].sample_group = group; |
| 315 | + |
| 316 | + return index; |
| 317 | +} |
| 318 | + |
| 319 | +static int |
| 320 | +minstrel_ht_next_group_sample_rate(struct minstrel_ht_sta *mi, int group, |
| 321 | + u16 supported, int offset) |
| 322 | +{ |
| 323 | + struct minstrel_mcs_group_data *mg = &mi->groups[group]; |
| 324 | + u16 idx; |
| 325 | + int i; |
| 326 | + |
| 327 | + for (i = 0; i < MCS_GROUP_RATES; i++) { |
| 328 | + idx = sample_table[mg->column][mg->index]; |
| 329 | + if (++mg->index >= MCS_GROUP_RATES) { |
| 330 | + mg->index = 0; |
| 331 | + if (++mg->column >= ARRAY_SIZE(sample_table)) |
| 332 | + mg->column = 0; |
| 333 | + } |
| 334 | + |
| 335 | + if (idx < offset) |
| 336 | + continue; |
| 337 | + |
| 338 | + if (!(supported & BIT(idx))) |
| 339 | + continue; |
| 340 | + |
| 341 | + return MI_RATE(group, idx); |
| 342 | + } |
| 343 | + |
| 344 | + return -1; |
| 345 | +} |
| 346 | + |
| 347 | +/* |
| 348 | + * Jump rates: |
| 349 | + * Sample random rates, use those that are faster than the highest |
| 350 | + * currently selected rate. Rates between the fastest and the slowest |
| 351 | + * get sorted into the slow sample bucket, but only if it has room |
| 352 | + */ |
| 353 | +static u16 |
| 354 | +minstrel_ht_next_jump_rate(struct minstrel_ht_sta *mi, u32 fast_rate_dur, |
| 355 | + u32 slow_rate_dur, int *slow_rate_ofs) |
| 356 | +{ |
| 357 | + struct minstrel_mcs_group_data *mg; |
| 358 | + struct minstrel_rate_stats *mrs; |
| 359 | + u32 max_duration = slow_rate_dur; |
| 360 | + int i, index, offset; |
| 361 | + u16 *slow_rates; |
| 362 | + u16 supported; |
| 363 | + u32 duration; |
| 364 | + u8 group; |
| 365 | + |
| 366 | + if (*slow_rate_ofs >= MINSTREL_SAMPLE_RATES) |
| 367 | + max_duration = fast_rate_dur; |
| 368 | + |
| 369 | + slow_rates = mi->sample[MINSTREL_SAMPLE_TYPE_SLOW].sample_rates; |
| 370 | + group = mi->sample[MINSTREL_SAMPLE_TYPE_JUMP].sample_group; |
| 371 | + for (i = 0; i < ARRAY_SIZE(minstrel_mcs_groups); i++) { |
| 372 | + u8 type; |
| 373 | + |
| 374 | + group = (group + 1) % ARRAY_SIZE(minstrel_mcs_groups); |
| 375 | + mg = &mi->groups[group]; |
| 376 | + |
| 377 | + supported = mi->supported[group]; |
| 378 | + if (!supported) |
| 379 | + continue; |
| 380 | + |
| 381 | + offset = minstrel_ht_group_min_rate_offset(mi, group, |
| 382 | + max_duration); |
| 383 | + if (offset < 0) |
| 384 | + continue; |
| 385 | + |
| 386 | + index = minstrel_ht_next_group_sample_rate(mi, group, supported, |
| 387 | + offset); |
| 388 | + if (index < 0) |
| 389 | + continue; |
| 390 | + |
| 391 | + duration = minstrel_get_duration(index); |
| 392 | + if (duration < fast_rate_dur) |
| 393 | + type = MINSTREL_SAMPLE_TYPE_JUMP; |
| 394 | + else |
| 395 | + type = MINSTREL_SAMPLE_TYPE_SLOW; |
| 396 | + |
| 397 | + if (minstrel_ht_find_sample_rate(mi, type, index)) |
| 398 | + continue; |
| 399 | + |
| 400 | + if (type == MINSTREL_SAMPLE_TYPE_JUMP) |
| 401 | + goto found; |
| 402 | + |
| 403 | + if (*slow_rate_ofs >= MINSTREL_SAMPLE_RATES) |
| 404 | + continue; |
| 405 | + |
| 406 | + if (duration >= slow_rate_dur) |
| 407 | + continue; |
| 408 | + |
| 409 | + /* skip slow rates with high success probability */ |
| 410 | + mrs = minstrel_get_ratestats(mi, index); |
| 411 | + if (mrs->prob_avg > MINSTREL_FRAC(95, 100)) |
| 412 | + continue; |
| 413 | + |
| 414 | + slow_rates[(*slow_rate_ofs)++] = index; |
| 415 | + if (*slow_rate_ofs >= MINSTREL_SAMPLE_RATES) |
| 416 | + max_duration = fast_rate_dur; |
| 417 | + } |
| 418 | + index = 0; |
| 419 | + |
| 420 | +found: |
| 421 | + mi->sample[MINSTREL_SAMPLE_TYPE_JUMP].sample_group = group; |
| 422 | + |
| 423 | + return index; |
| 424 | +} |
| 425 | + |
| 426 | +static void |
| 427 | +minstrel_ht_refill_sample_rates(struct minstrel_ht_sta *mi) |
| 428 | +{ |
| 429 | + u32 prob_dur = minstrel_get_duration(mi->max_prob_rate); |
| 430 | + u32 tp_dur = minstrel_get_duration(mi->max_tp_rate[0]); |
| 431 | + u32 tp2_dur = minstrel_get_duration(mi->max_tp_rate[1]); |
| 432 | + u32 fast_rate_dur = min(min(tp_dur, tp2_dur), prob_dur); |
| 433 | + u32 slow_rate_dur = max(max(tp_dur, tp2_dur), prob_dur); |
| 434 | + u16 *rates; |
| 435 | + int i, j; |
| 436 | + |
| 437 | + rates = mi->sample[MINSTREL_SAMPLE_TYPE_INC].sample_rates; |
| 438 | + i = minstrel_ht_move_sample_rates(mi, MINSTREL_SAMPLE_TYPE_INC, |
| 439 | + fast_rate_dur, slow_rate_dur); |
| 440 | + while (i < MINSTREL_SAMPLE_RATES) { |
| 441 | + rates[i] = minstrel_ht_next_inc_rate(mi, tp_dur); |
| 442 | + if (!rates[i]) |
| 443 | + break; |
| 444 | + |
| 445 | + i++; |
| 446 | + } |
| 447 | + |
| 448 | + rates = mi->sample[MINSTREL_SAMPLE_TYPE_JUMP].sample_rates; |
| 449 | + i = minstrel_ht_move_sample_rates(mi, MINSTREL_SAMPLE_TYPE_JUMP, |
| 450 | + fast_rate_dur, slow_rate_dur); |
| 451 | + j = minstrel_ht_move_sample_rates(mi, MINSTREL_SAMPLE_TYPE_SLOW, |
| 452 | + fast_rate_dur, slow_rate_dur); |
| 453 | + while (i < MINSTREL_SAMPLE_RATES) { |
| 454 | + rates[i] = minstrel_ht_next_jump_rate(mi, fast_rate_dur, |
| 455 | + slow_rate_dur, &j); |
| 456 | + if (!rates[i]) |
| 457 | + break; |
| 458 | + |
| 459 | + i++; |
| 460 | + } |
| 461 | + |
| 462 | + for (i = 0; i < ARRAY_SIZE(mi->sample); i++) |
| 463 | + memcpy(mi->sample[i].cur_sample_rates, mi->sample[i].sample_rates, |
| 464 | + sizeof(mi->sample[i].cur_sample_rates)); |
| 465 | +} |
| 466 | + |
| 467 | + |
| 468 | /* |
| 469 | * Update rate statistics and select new primary rates |
| 470 | * |
| 471 | @@ -848,8 +1046,6 @@ minstrel_ht_update_stats(struct minstrel |
| 472 | mi->ampdu_packets = 0; |
| 473 | } |
| 474 | |
| 475 | - mi->sample_count = 0; |
| 476 | - |
| 477 | if (mi->supported[MINSTREL_CCK_GROUP]) |
| 478 | group = MINSTREL_CCK_GROUP; |
| 479 | else if (mi->supported[MINSTREL_OFDM_GROUP]) |
| 480 | @@ -884,8 +1080,6 @@ minstrel_ht_update_stats(struct minstrel |
| 481 | if (!mi->supported[group]) |
| 482 | continue; |
| 483 | |
| 484 | - mi->sample_count++; |
| 485 | - |
| 486 | /* (re)Initialize group rate indexes */ |
| 487 | for(j = 0; j < MAX_THR_RATES; j++) |
| 488 | tmp_group_tp_rate[j] = MI_RATE(group, 0); |
| 489 | @@ -952,9 +1146,7 @@ minstrel_ht_update_stats(struct minstrel |
| 490 | |
| 491 | /* Try to increase robustness of max_prob_rate*/ |
| 492 | minstrel_ht_prob_rate_reduce_streams(mi); |
| 493 | - |
| 494 | - /* try to sample half of all available rates during each interval */ |
| 495 | - mi->sample_count *= 4; |
| 496 | + minstrel_ht_refill_sample_rates(mi); |
| 497 | |
| 498 | if (sample) |
| 499 | minstrel_ht_rate_sample_switch(mp, mi); |
| 500 | @@ -971,6 +1163,7 @@ minstrel_ht_update_stats(struct minstrel |
| 501 | |
| 502 | /* Reset update timer */ |
| 503 | mi->last_stats_update = jiffies; |
| 504 | + mi->sample_time = jiffies; |
| 505 | } |
| 506 | |
| 507 | static bool |
| 508 | @@ -1001,28 +1194,6 @@ minstrel_ht_txstat_valid(struct minstrel |
| 509 | } |
| 510 | |
| 511 | static void |
| 512 | -minstrel_set_next_sample_idx(struct minstrel_ht_sta *mi) |
| 513 | -{ |
| 514 | - struct minstrel_mcs_group_data *mg; |
| 515 | - |
| 516 | - for (;;) { |
| 517 | - mi->sample_group++; |
| 518 | - mi->sample_group %= ARRAY_SIZE(minstrel_mcs_groups); |
| 519 | - mg = &mi->groups[mi->sample_group]; |
| 520 | - |
| 521 | - if (!mi->supported[mi->sample_group]) |
| 522 | - continue; |
| 523 | - |
| 524 | - if (++mg->index >= MCS_GROUP_RATES) { |
| 525 | - mg->index = 0; |
| 526 | - if (++mg->column >= ARRAY_SIZE(sample_table)) |
| 527 | - mg->column = 0; |
| 528 | - } |
| 529 | - break; |
| 530 | - } |
| 531 | -} |
| 532 | - |
| 533 | -static void |
| 534 | minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary) |
| 535 | { |
| 536 | int group, orig_group; |
| 537 | @@ -1107,14 +1278,6 @@ minstrel_ht_tx_status(void *priv, struct |
| 538 | mi->ampdu_packets++; |
| 539 | mi->ampdu_len += info->status.ampdu_len; |
| 540 | |
| 541 | - if (!mi->sample_wait && !mi->sample_tries && mi->sample_count > 0) { |
| 542 | - int avg_ampdu_len = minstrel_ht_avg_ampdu_len(mi); |
| 543 | - |
| 544 | - mi->sample_wait = 16 + 2 * avg_ampdu_len; |
| 545 | - mi->sample_tries = 1; |
| 546 | - mi->sample_count--; |
| 547 | - } |
| 548 | - |
| 549 | if (mi->sample_mode != MINSTREL_SAMPLE_IDLE) |
| 550 | rate_sample = minstrel_get_ratestats(mi, mi->sample_rate); |
| 551 | |
| 552 | @@ -1386,97 +1549,20 @@ minstrel_ht_update_rates(struct minstrel |
| 553 | rate_control_set_rates(mp->hw, mi->sta, rates); |
| 554 | } |
| 555 | |
| 556 | -static int |
| 557 | -minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) |
| 558 | +static u16 |
| 559 | +minstrel_ht_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) |
| 560 | { |
| 561 | - struct minstrel_rate_stats *mrs; |
| 562 | - struct minstrel_mcs_group_data *mg; |
| 563 | - unsigned int sample_dur, sample_group, cur_max_tp_streams; |
| 564 | - int tp_rate1, tp_rate2; |
| 565 | - int sample_idx = 0; |
| 566 | - |
| 567 | - if (mp->hw->max_rates == 1 && mp->sample_switch && |
| 568 | - (mi->total_packets_cur >= SAMPLE_SWITCH_THR || |
| 569 | - mp->sample_switch == 1)) |
| 570 | - return -1; |
| 571 | - |
| 572 | - if (mi->sample_wait > 0) { |
| 573 | - mi->sample_wait--; |
| 574 | - return -1; |
| 575 | - } |
| 576 | - |
| 577 | - if (!mi->sample_tries) |
| 578 | - return -1; |
| 579 | - |
| 580 | - sample_group = mi->sample_group; |
| 581 | - mg = &mi->groups[sample_group]; |
| 582 | - sample_idx = sample_table[mg->column][mg->index]; |
| 583 | - minstrel_set_next_sample_idx(mi); |
| 584 | - |
| 585 | - if (!(mi->supported[sample_group] & BIT(sample_idx))) |
| 586 | - return -1; |
| 587 | + u8 seq; |
| 588 | |
| 589 | - mrs = &mg->rates[sample_idx]; |
| 590 | - sample_idx += MI_RATE(sample_group, 0); |
| 591 | - |
| 592 | - tp_rate1 = mi->max_tp_rate[0]; |
| 593 | - |
| 594 | - /* Set tp_rate2 to the second highest max_tp_rate */ |
| 595 | - if (minstrel_get_duration(mi->max_tp_rate[0]) > |
| 596 | - minstrel_get_duration(mi->max_tp_rate[1])) { |
| 597 | - tp_rate2 = mi->max_tp_rate[0]; |
| 598 | + if (mp->hw->max_rates > 1) { |
| 599 | + seq = mi->sample_seq; |
| 600 | + mi->sample_seq = (seq + 1) % ARRAY_SIZE(minstrel_sample_seq); |
| 601 | + seq = minstrel_sample_seq[seq]; |
| 602 | } else { |
| 603 | - tp_rate2 = mi->max_tp_rate[1]; |
| 604 | + seq = MINSTREL_SAMPLE_TYPE_INC; |
| 605 | } |
| 606 | |
| 607 | - /* |
| 608 | - * Sampling might add some overhead (RTS, no aggregation) |
| 609 | - * to the frame. Hence, don't use sampling for the highest currently |
| 610 | - * used highest throughput or probability rate. |
| 611 | - */ |
| 612 | - if (sample_idx == mi->max_tp_rate[0] || sample_idx == mi->max_prob_rate) |
| 613 | - return -1; |
| 614 | - |
| 615 | - /* |
| 616 | - * Do not sample if the probability is already higher than 95%, |
| 617 | - * or if the rate is 3 times slower than the current max probability |
| 618 | - * rate, to avoid wasting airtime. |
| 619 | - */ |
| 620 | - sample_dur = minstrel_get_duration(sample_idx); |
| 621 | - if (mrs->prob_avg > MINSTREL_FRAC(95, 100) || |
| 622 | - minstrel_get_duration(mi->max_prob_rate) * 3 < sample_dur) |
| 623 | - return -1; |
| 624 | - |
| 625 | - |
| 626 | - /* |
| 627 | - * For devices with no configurable multi-rate retry, skip sampling |
| 628 | - * below the per-group max throughput rate, and only use one sampling |
| 629 | - * attempt per rate |
| 630 | - */ |
| 631 | - if (mp->hw->max_rates == 1 && |
| 632 | - (minstrel_get_duration(mg->max_group_tp_rate[0]) < sample_dur || |
| 633 | - mrs->attempts)) |
| 634 | - return -1; |
| 635 | - |
| 636 | - /* Skip already sampled slow rates */ |
| 637 | - if (sample_dur >= minstrel_get_duration(tp_rate1) && mrs->attempts) |
| 638 | - return -1; |
| 639 | - |
| 640 | - /* |
| 641 | - * Make sure that lower rates get sampled only occasionally, |
| 642 | - * if the link is working perfectly. |
| 643 | - */ |
| 644 | - |
| 645 | - cur_max_tp_streams = minstrel_mcs_groups[MI_RATE_GROUP(tp_rate1)].streams; |
| 646 | - if (sample_dur >= minstrel_get_duration(tp_rate2) && |
| 647 | - (cur_max_tp_streams - 1 < |
| 648 | - minstrel_mcs_groups[sample_group].streams || |
| 649 | - sample_dur >= minstrel_get_duration(mi->max_prob_rate))) |
| 650 | - return -1; |
| 651 | - |
| 652 | - mi->sample_tries--; |
| 653 | - |
| 654 | - return sample_idx; |
| 655 | + return __minstrel_ht_get_sample_rate(mi, seq); |
| 656 | } |
| 657 | |
| 658 | static void |
| 659 | @@ -1488,7 +1574,7 @@ minstrel_ht_get_rate(void *priv, struct |
| 660 | struct ieee80211_tx_rate *rate = &info->status.rates[0]; |
| 661 | struct minstrel_ht_sta *mi = priv_sta; |
| 662 | struct minstrel_priv *mp = priv; |
| 663 | - int sample_idx; |
| 664 | + u16 sample_idx; |
| 665 | |
| 666 | if (!(info->flags & IEEE80211_TX_CTL_AMPDU) && |
| 667 | !minstrel_ht_is_legacy_group(MI_RATE_GROUP(mi->max_prob_rate))) |
| 668 | @@ -1504,11 +1590,19 @@ minstrel_ht_get_rate(void *priv, struct |
| 669 | /* Don't use EAPOL frames for sampling on non-mrr hw */ |
| 670 | if (mp->hw->max_rates == 1 && |
| 671 | (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO)) |
| 672 | - sample_idx = -1; |
| 673 | - else |
| 674 | - sample_idx = minstrel_get_sample_rate(mp, mi); |
| 675 | + return; |
| 676 | + |
| 677 | + if (mp->hw->max_rates == 1 && mp->sample_switch && |
| 678 | + (mi->total_packets_cur >= SAMPLE_SWITCH_THR || |
| 679 | + mp->sample_switch == 1)) |
| 680 | + return; |
| 681 | + |
| 682 | + if (time_is_before_jiffies(mi->sample_time)) |
| 683 | + return; |
| 684 | |
| 685 | - if (sample_idx < 0) |
| 686 | + mi->sample_time = jiffies + MINSTREL_SAMPLE_INTERVAL; |
| 687 | + sample_idx = minstrel_ht_get_sample_rate(mp, mi); |
| 688 | + if (!sample_idx) |
| 689 | return; |
| 690 | |
| 691 | sample_group = &minstrel_mcs_groups[MI_RATE_GROUP(sample_idx)]; |
| 692 | @@ -1629,16 +1723,6 @@ minstrel_ht_update_caps(void *priv, stru |
| 693 | |
| 694 | mi->avg_ampdu_len = MINSTREL_FRAC(1, 1); |
| 695 | |
| 696 | - /* When using MRR, sample more on the first attempt, without delay */ |
| 697 | - if (mp->has_mrr) { |
| 698 | - mi->sample_count = 16; |
| 699 | - mi->sample_wait = 0; |
| 700 | - } else { |
| 701 | - mi->sample_count = 8; |
| 702 | - mi->sample_wait = 8; |
| 703 | - } |
| 704 | - mi->sample_tries = 4; |
| 705 | - |
| 706 | if (!use_vht) { |
| 707 | stbc = (ht_cap & IEEE80211_HT_CAP_RX_STBC) >> |
| 708 | IEEE80211_HT_CAP_RX_STBC_SHIFT; |
| 709 | --- a/net/mac80211/rc80211_minstrel_ht.h |
| 710 | +++ b/net/mac80211/rc80211_minstrel_ht.h |
| 711 | @@ -69,6 +69,8 @@ |
| 712 | #define MI_RATE_IDX(_rate) FIELD_GET(MI_RATE_IDX_MASK, _rate) |
| 713 | #define MI_RATE_GROUP(_rate) FIELD_GET(MI_RATE_GROUP_MASK, _rate) |
| 714 | |
| 715 | +#define MINSTREL_SAMPLE_RATES 5 /* rates per sample type */ |
| 716 | +#define MINSTREL_SAMPLE_INTERVAL (HZ / 50) |
| 717 | |
| 718 | struct minstrel_priv { |
| 719 | struct ieee80211_hw *hw; |
| 720 | @@ -126,6 +128,13 @@ struct minstrel_rate_stats { |
| 721 | bool retry_updated; |
| 722 | }; |
| 723 | |
| 724 | +enum minstrel_sample_type { |
| 725 | + MINSTREL_SAMPLE_TYPE_INC, |
| 726 | + MINSTREL_SAMPLE_TYPE_JUMP, |
| 727 | + MINSTREL_SAMPLE_TYPE_SLOW, |
| 728 | + __MINSTREL_SAMPLE_TYPE_MAX |
| 729 | +}; |
| 730 | + |
| 731 | struct minstrel_mcs_group_data { |
| 732 | u8 index; |
| 733 | u8 column; |
| 734 | @@ -144,6 +153,12 @@ enum minstrel_sample_mode { |
| 735 | MINSTREL_SAMPLE_PENDING, |
| 736 | }; |
| 737 | |
| 738 | +struct minstrel_sample_category { |
| 739 | + u8 sample_group; |
| 740 | + u16 sample_rates[MINSTREL_SAMPLE_RATES]; |
| 741 | + u16 cur_sample_rates[MINSTREL_SAMPLE_RATES]; |
| 742 | +}; |
| 743 | + |
| 744 | struct minstrel_ht_sta { |
| 745 | struct ieee80211_sta *sta; |
| 746 | |
| 747 | @@ -175,16 +190,14 @@ struct minstrel_ht_sta { |
| 748 | /* tx flags to add for frames for this sta */ |
| 749 | u32 tx_flags; |
| 750 | |
| 751 | - u8 sample_wait; |
| 752 | - u8 sample_tries; |
| 753 | - u8 sample_count; |
| 754 | + unsigned long sample_time; |
| 755 | + struct minstrel_sample_category sample[__MINSTREL_SAMPLE_TYPE_MAX]; |
| 756 | + |
| 757 | + u8 sample_seq; |
| 758 | |
| 759 | enum minstrel_sample_mode sample_mode; |
| 760 | u16 sample_rate; |
| 761 | |
| 762 | - /* current MCS group to be sampled */ |
| 763 | - u8 sample_group; |
| 764 | - |
| 765 | u8 band; |
| 766 | |
| 767 | /* Bitfield of supported MCS rates of all groups */ |