Report the time since last beacon was received for each beaconing peer to userspace. In MLO, this information is reported per link and additionally, it is reported at the MLD level, the timestamp of the most recently received beacon across all affiliated links to give a unified view of beacon reception status. This allows applications to detect potential beacon misses and make informed decisions. Signed-off-by: Maharaja Kennadyrajan --- include/net/cfg80211.h | 9 +++++++++ include/uapi/linux/nl80211.h | 2 ++ net/wireless/nl80211.c | 3 +++ 3 files changed, 14 insertions(+) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 406626ff6cc8..58e0c07b0e31 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2094,6 +2094,9 @@ struct cfg80211_tid_stats { * an FCS error. This counter should be incremented only when TA of the * received packet with an FCS error matches the peer MAC address. * @addr: For MLO STA connection, filled with address of the link of station. + * @last_beacon_seen_msec_ago: Time (in milliseconds) since the last beacon + * was received from this link of station. This is useful for + * monitoring beacon reception status per link in MLO scenario. */ struct link_station_info { u64 filled; @@ -2137,6 +2140,8 @@ struct link_station_info { u32 fcs_err_count; u8 addr[ETH_ALEN] __aligned(2); + + u32 last_beacon_seen_msec_ago; }; /** @@ -2228,6 +2233,8 @@ struct link_station_info { * get_station() and dump_station() callbacks. * @links: reference to Link sta entries for MLO STA, all link specific * information is accessed through links[link_id]. + * @last_beacon_seen_msec_ago: Time since last beacon is received in + * milliseconds. */ struct station_info { u64 filled; @@ -2295,6 +2302,8 @@ struct station_info { u16 valid_links; struct link_station_info *links[IEEE80211_MLD_MAX_NUM_LINKS]; + + u32 last_beacon_seen_msec_ago; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index d1a14f2892d9..5ad31418b4a3 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3975,6 +3975,7 @@ enum nl80211_sta_bss_param { * of STA's association * @NL80211_STA_INFO_CONNECTED_TO_AS: set to true if STA has a path to a * authentication server (u8, 0 or 1) + * @NL80211_STA_INFO_BEACON_SEEN_MSEC_AGO: time since last beacon is received (u32, msecs) * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -4023,6 +4024,7 @@ enum nl80211_sta_info { NL80211_STA_INFO_AIRTIME_LINK_METRIC, NL80211_STA_INFO_ASSOC_AT_BOOTTIME, NL80211_STA_INFO_CONNECTED_TO_AS, + NL80211_STA_INFO_BEACON_SEEN_MSEC_AGO, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 89519aa52893..08102cf347c9 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6963,6 +6963,8 @@ static int nl80211_fill_link_station(struct sk_buff *msg, PUT_LINK_SINFO(TX_FAILED, tx_failed, u32); PUT_LINK_SINFO(EXPECTED_THROUGHPUT, expected_throughput, u32); PUT_LINK_SINFO(BEACON_LOSS, beacon_loss_count, u32); + PUT_LINK_SINFO(BEACON_SEEN_MSEC_AGO, last_beacon_seen_msec_ago, + u32); if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_BSS_PARAM)) { bss_param = nla_nest_start_noflag(msg, @@ -7161,6 +7163,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, PUT_SINFO(TX_FAILED, tx_failed, u32); PUT_SINFO(EXPECTED_THROUGHPUT, expected_throughput, u32); PUT_SINFO(BEACON_LOSS, beacon_loss_count, u32); + PUT_SINFO(BEACON_SEEN_MSEC_AGO, last_beacon_seen_msec_ago, u32); PUT_SINFO(LLID, llid, u16); PUT_SINFO(PLID, plid, u16); -- 2.17.1 Report the time since the last beacon was received from a peer station in station info using the NL attribute NL80211_STA_INFO_BEACON_SEEN_MSEC_AGO. This applies to non-AP STA, mesh and adhoc modes. In non-AP MLD STA mode, this attribute is reported per link and additionally, it is reported at the MLD level, the timestamp of the most recently received beacon across all affiliated AP STA links to give a unified view of beacon reception status. Signed-off-by: Maharaja Kennadyrajan --- net/mac80211/mesh.c | 4 ++++ net/mac80211/mesh.h | 4 ++++ net/mac80211/mesh_plink.c | 18 ++++++++++++++++++ net/mac80211/mlme.c | 2 ++ net/mac80211/sta_info.c | 31 ++++++++++++++++++++++++++++++- net/mac80211/sta_info.h | 3 +++ 6 files changed, 61 insertions(+), 1 deletion(-) diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index a4a715f6f1c3..a6b83633ec33 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -1508,6 +1508,10 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, if (ifmsh->csa_role != IEEE80211_MESH_CSA_ROLE_INIT && !sdata->vif.bss_conf.csa_active) ieee80211_mesh_process_chnswitch(sdata, elems, true); + + if (stype == IEEE80211_STYPE_BEACON) + mesh_last_beacon_seen_msec_ago(sdata, mgmt, elems, + rx_status); } if (ifmsh->sync_ops) diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 3f9664e4e00c..3e13834a5804 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -327,6 +327,10 @@ int mesh_path_send_to_gates(struct mesh_path *mpath); int mesh_gate_num(struct ieee80211_sub_if_data *sdata); u32 airtime_link_metric_get(struct ieee80211_local *local, struct sta_info *sta); +void mesh_last_beacon_seen_msec_ago(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, + struct ieee802_11_elems *ie, + struct ieee80211_rx_status *rx_status); /* Mesh plinks */ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index cb45a5d2009d..d84a49372d20 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -613,6 +613,24 @@ mesh_sta_info_get(struct ieee80211_sub_if_data *sdata, return sta; } +void mesh_last_beacon_seen_msec_ago(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, + struct ieee802_11_elems *elems, + struct ieee80211_rx_status *rx_status) +{ + struct sta_info *sta; + + /* mesh_sta_info_get api returns with rcu_read_lock */ + sta = mesh_sta_info_get(sdata, mgmt->sa, elems, rx_status); + if (!sta) + goto unlock_rcu; + + sta->deflink.last_beacon_seen_msec_ago = jiffies; + +unlock_rcu: + rcu_read_unlock(); +} + /* * mesh_neighbour_update - update or initialize new mesh neighbor. * diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 1008eb8e9b13..5194c0f4f887 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -7567,6 +7567,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, goto free; } + link_sta->last_beacon_seen_msec_ago = jiffies; + if (WARN_ON(!bss_conf->chanreq.oper.chan)) goto free; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 8c550aab9bdc..aa3501ec2096 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -2954,6 +2954,15 @@ static void sta_set_link_sinfo(struct sta_info *sta, link_sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL_AVG); } + + if (!(link_sinfo->filled & + BIT_ULL(NL80211_STA_INFO_BEACON_SEEN_MSEC_AGO))) { + link_sinfo->last_beacon_seen_msec_ago = + jiffies_to_msecs(jiffies - + link_sta_info->last_beacon_seen_msec_ago); + link_sinfo->filled |= + BIT_ULL(NL80211_STA_INFO_BEACON_SEEN_MSEC_AGO); + } } void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, @@ -2961,7 +2970,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, { struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; - u32 thr = 0; + u32 thr = 0, last_beacon_seen_msec_ago = 0; int i, ac, cpu, link_id; struct ieee80211_sta_rx_stats *last_rxstats; @@ -3204,6 +3213,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, if (sta->sta.valid_links) { struct ieee80211_link_data *link; struct link_sta_info *link_sta; + bool init = false; ether_addr_copy(sinfo->mld_addr, sta->addr); for_each_valid_link(sinfo, link_id) { @@ -3218,7 +3228,26 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo, sinfo->valid_links = sta->sta.valid_links; sta_set_link_sinfo(sta, sinfo->links[link_id], link, tidstats); + if (!init || + link_sta->last_beacon_seen_msec_ago > + last_beacon_seen_msec_ago) + last_beacon_seen_msec_ago = + link_sta->last_beacon_seen_msec_ago; + init = true; } + } else { + last_beacon_seen_msec_ago = + sta->deflink.last_beacon_seen_msec_ago; + } + + if (!(sinfo->filled & + BIT_ULL(NL80211_STA_INFO_BEACON_SEEN_MSEC_AGO)) && + last_beacon_seen_msec_ago) { + sinfo->last_beacon_seen_msec_ago = + jiffies_to_msecs(jiffies - + last_beacon_seen_msec_ago); + sinfo->filled |= + BIT_ULL(NL80211_STA_INFO_BEACON_SEEN_MSEC_AGO); } } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 5288d5286651..a4f976d58a93 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -510,6 +510,7 @@ struct ieee80211_fragment_cache { * during finalize * @debugfs_dir: debug filesystem directory dentry * @pub: public (driver visible) link STA data + * @last_beacon_seen_msec_ago: timestamp of last received beacon in jiffies * TODO Move other link params from sta_info as required for MLD operation */ struct link_sta_info { @@ -565,6 +566,8 @@ struct link_sta_info { struct dentry *debugfs_dir; #endif + unsigned long last_beacon_seen_msec_ago; + struct ieee80211_link_sta *pub; }; -- 2.17.1