Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

wifi: mac80211: add support to accumulate removed link statistics

Currently, if a link gets removed in between for a station then
directly accumulated data will fall down to sum of other active links.
This will bring inconsistency in station dump statistics.

For instance, let's take Tx packets
- at t=0-> link-0:2 link-1:3 Tx packets => accumulated = 5
- at t=1-> link-0:4 link-1:6 Tx packets => accumulated = 10
let say at t=2, link-0 went down => link-0:0 link-1:7 => accumulated = 7
Here, suddenly accumulated Tx packets will come down to 7 from 10.
This is showing inconsistency.

Therefore, store link-0 data when it went down and add to accumulated
Tx packet = 11.

Hence, store the removed link statistics data in sta structure and
add it in accumulated statistics for consistency.

Signed-off-by: Sarika Sharma <quic_sarishar@quicinc.com>
Link: https://patch.msgid.link/20250528054420.3050133-7-quic_sarishar@quicinc.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>

authored by

Sarika Sharma and committed by
Johannes Berg
80b2fa46 49e47223

+144
+13
net/mac80211/cfg.c
··· 886 886 ret = 0; 887 887 memcpy(mac, sta->sta.addr, ETH_ALEN); 888 888 sta_set_sinfo(sta, sinfo, true); 889 + 890 + /* Add accumulated removed link data to sinfo data for 891 + * consistency for MLO 892 + */ 893 + if (sinfo->valid_links) 894 + sta_set_accumulated_removed_links_sinfo(sta, sinfo); 895 + 889 896 } 890 897 891 898 return ret; ··· 920 913 if (sta) { 921 914 ret = 0; 922 915 sta_set_sinfo(sta, sinfo, true); 916 + 917 + /* Add accumulated removed link data to sinfo data for 918 + * consistency for MLO 919 + */ 920 + if (sinfo->valid_links) 921 + sta_set_accumulated_removed_links_sinfo(sta, sinfo); 923 922 } 924 923 925 924 return ret;
+74
net/mac80211/sta_info.c
··· 355 355 free_percpu(link_sta->pcpu_rx_stats); 356 356 } 357 357 358 + static void sta_accumulate_removed_link_stats(struct sta_info *sta, int link_id) 359 + { 360 + struct link_sta_info *link_sta = wiphy_dereference(sta->local->hw.wiphy, 361 + sta->link[link_id]); 362 + struct ieee80211_link_data *link; 363 + int ac, tid; 364 + u32 thr; 365 + 366 + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { 367 + sta->rem_link_stats.tx_packets += 368 + link_sta->tx_stats.packets[ac]; 369 + sta->rem_link_stats.tx_bytes += link_sta->tx_stats.bytes[ac]; 370 + } 371 + 372 + sta->rem_link_stats.rx_packets += link_sta->rx_stats.packets; 373 + sta->rem_link_stats.rx_bytes += link_sta->rx_stats.bytes; 374 + sta->rem_link_stats.tx_retries += link_sta->status_stats.retry_count; 375 + sta->rem_link_stats.tx_failed += link_sta->status_stats.retry_failed; 376 + sta->rem_link_stats.rx_dropped_misc += link_sta->rx_stats.dropped; 377 + 378 + thr = sta_get_expected_throughput(sta); 379 + if (thr != 0) 380 + sta->rem_link_stats.expected_throughput += thr; 381 + 382 + for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) { 383 + sta->rem_link_stats.pertid_stats.rx_msdu += 384 + link_sta->rx_stats.msdu[tid]; 385 + sta->rem_link_stats.pertid_stats.tx_msdu += 386 + link_sta->tx_stats.msdu[tid]; 387 + sta->rem_link_stats.pertid_stats.tx_msdu_retries += 388 + link_sta->status_stats.msdu_retries[tid]; 389 + sta->rem_link_stats.pertid_stats.tx_msdu_failed += 390 + link_sta->status_stats.msdu_failed[tid]; 391 + } 392 + 393 + if (sta->sdata->vif.type == NL80211_IFTYPE_STATION) { 394 + link = wiphy_dereference(sta->sdata->local->hw.wiphy, 395 + sta->sdata->link[link_id]); 396 + if (link) 397 + sta->rem_link_stats.beacon_loss_count += 398 + link->u.mgd.beacon_loss_count; 399 + } 400 + } 401 + 358 402 static void sta_remove_link(struct sta_info *sta, unsigned int link_id, 359 403 bool unhash) 360 404 { ··· 421 377 alloc = container_of(link_sta, typeof(*alloc), info); 422 378 423 379 sta->sta.valid_links &= ~BIT(link_id); 380 + 381 + /* store removed link info for accumulated stats consistency */ 382 + sta_accumulate_removed_link_stats(sta, link_id); 383 + 424 384 RCU_INIT_POINTER(sta->link[link_id], NULL); 425 385 RCU_INIT_POINTER(sta->sta.link[link_id], NULL); 426 386 if (alloc) { ··· 2692 2644 sinfo->airtime_link_metric = airtime_link_metric_get(local, sta); 2693 2645 } 2694 2646 #endif 2647 + 2648 + void sta_set_accumulated_removed_links_sinfo(struct sta_info *sta, 2649 + struct station_info *sinfo) 2650 + { 2651 + /* Accumulating the removed link statistics. */ 2652 + sinfo->tx_packets = sta->rem_link_stats.tx_packets; 2653 + sinfo->rx_packets = sta->rem_link_stats.rx_packets; 2654 + sinfo->tx_bytes = sta->rem_link_stats.tx_bytes; 2655 + sinfo->rx_bytes = sta->rem_link_stats.rx_bytes; 2656 + sinfo->tx_retries = sta->rem_link_stats.tx_retries; 2657 + sinfo->tx_failed = sta->rem_link_stats.tx_failed; 2658 + sinfo->rx_dropped_misc = sta->rem_link_stats.rx_dropped_misc; 2659 + sinfo->beacon_loss_count = sta->rem_link_stats.beacon_loss_count; 2660 + sinfo->expected_throughput = sta->rem_link_stats.expected_throughput; 2661 + 2662 + if (sinfo->pertid) { 2663 + sinfo->pertid->rx_msdu = 2664 + sta->rem_link_stats.pertid_stats.rx_msdu; 2665 + sinfo->pertid->tx_msdu = 2666 + sta->rem_link_stats.pertid_stats.tx_msdu; 2667 + sinfo->pertid->tx_msdu_retries = 2668 + sta->rem_link_stats.pertid_stats.tx_msdu_retries; 2669 + sinfo->pertid->tx_msdu_failed = 2670 + sta->rem_link_stats.pertid_stats.tx_msdu_failed; 2671 + } 2672 + } 2695 2673 2696 2674 void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, 2697 2675 bool tidstats)
+57
net/mac80211/sta_info.h
··· 569 569 }; 570 570 571 571 /** 572 + * struct ieee80211_sta_removed_link_stats - Removed link sta data 573 + * 574 + * keep required accumulated removed link data for stats 575 + * 576 + * @rx_packets: accumulated packets (MSDUs & MMPDUs) received from 577 + * this station for removed links 578 + * @tx_packets: accumulated packets (MSDUs & MMPDUs) transmitted to 579 + * this station for removed links 580 + * @rx_bytes: accumulated bytes (size of MPDUs) received from this 581 + * station for removed links 582 + * @tx_bytes: accumulated bytes (size of MPDUs) transmitted to this 583 + * station for removed links 584 + * @tx_retries: cumulative retry counts (MPDUs) for removed links 585 + * @tx_failed: accumulated number of failed transmissions (MPDUs) 586 + * (retries exceeded, no ACK) for removed links 587 + * @rx_dropped_misc: accumulated dropped packets for un-specified reason 588 + * from this station for removed links 589 + * @beacon_loss_count: Number of times beacon loss event has triggered 590 + * from this station for removed links. 591 + * @expected_throughput: expected throughput in kbps (including 802.11 592 + * headers) towards this station for removed links 593 + * @pertid_stats: accumulated per-TID statistics for removed link of 594 + * station 595 + * @pertid_stats.rx_msdu : accumulated number of received MSDUs towards 596 + * this station for removed links. 597 + * @pertid_stats.tx_msdu: accumulated number of (attempted) transmitted 598 + * MSDUs towards this station for removed links 599 + * @pertid_stats.tx_msdu_retries: accumulated number of retries (not 600 + * counting the first) for transmitted MSDUs towards this station 601 + * for removed links 602 + * @pertid_stats.tx_msdu_failed: accumulated number of failed transmitted 603 + * MSDUs towards this station for removed links 604 + */ 605 + struct ieee80211_sta_removed_link_stats { 606 + u32 rx_packets; 607 + u32 tx_packets; 608 + u64 rx_bytes; 609 + u64 tx_bytes; 610 + u32 tx_retries; 611 + u32 tx_failed; 612 + u32 rx_dropped_misc; 613 + u32 beacon_loss_count; 614 + u32 expected_throughput; 615 + struct { 616 + u64 rx_msdu; 617 + u64 tx_msdu; 618 + u64 tx_msdu_retries; 619 + u64 tx_msdu_failed; 620 + } pertid_stats; 621 + }; 622 + 623 + /** 572 624 * struct sta_info - STA information 573 625 * 574 626 * This structure collects information about a station that ··· 696 644 * @deflink address and remaining would be allocated and the address 697 645 * would be assigned to link[link_id] where link_id is the id assigned 698 646 * by the AP. 647 + * @rem_link_stats: accumulated removed link stats 699 648 */ 700 649 struct sta_info { 701 650 /* General information, mostly static */ ··· 771 718 struct ieee80211_sta_aggregates cur; 772 719 struct link_sta_info deflink; 773 720 struct link_sta_info __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; 721 + struct ieee80211_sta_removed_link_stats rem_link_stats; 774 722 775 723 /* keep last! */ 776 724 struct ieee80211_sta sta; ··· 975 921 struct rate_info *rinfo); 976 922 void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, 977 923 bool tidstats); 924 + 925 + void sta_set_accumulated_removed_links_sinfo(struct sta_info *sta, 926 + struct station_info *sinfo); 978 927 979 928 u32 sta_get_expected_throughput(struct sta_info *sta); 980 929