From: Arend van Spriel <aspriel@xxxxxxxxx> With the addition of TXQ stats in the per-tid statistics the struct station_info grew significantly. This resulted in stack limit warnings like below: CC [M] drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.o drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c: In function ‘brcmf_notify_connect_status_ap’: drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c:5530:1: error: the frame size of 1592 bytes is larger than 1024 bytes This patch adds an allocation function that those who want to provide per-tid stats should use to allocate the tid array, ie. struct station_info::pertid. Cc: Toke Høiland-Jørgensen <toke@xxxxxxx> Fixes: 52539ca89f36 ("cfg80211: Expose TXQ stats and parameters to userspace") Signed-off-by: Arend van Spriel <aspriel@xxxxxxxxx> --- + linux-wireless list Johannes, Toke, Here an alternative approach. Currently the only cfg80211-based driver providing per-tid stats is mac80211. This patch only changes mac80211 and the other driver can keep using stack allocation. Even mac80211 could if wanted, but I left that part as is. I considered making the new helper more generic and allocate 'pertid' based on filled flags, ie.: int cfg80211_sinfo_prepare(struct station_info *sinfo, gfp_t gfp) { : if (sinfo->filled & BIT(NL80211_STA_INFO_TID_STATS)) { sinfo->pertid = kzalloc(..., gfp); if (!pertid) return -ENOMEM; } : return 0; } but decided to stick close to the issue. Regards, Arend --- include/net/cfg80211.h | 9 ++++++++- net/mac80211/sta_info.c | 11 ++++++----- net/wireless/nl80211.c | 4 +++- net/wireless/util.c | 12 ++++++++++++ 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 8db6071..784377a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1230,7 +1230,7 @@ struct station_info { u64 rx_beacon; u64 rx_duration; u8 rx_beacon_signal_avg; - struct cfg80211_tid_stats pertid[IEEE80211_NUM_TIDS + 1]; + struct cfg80211_tid_stats *pertid; s8 ack_signal; s8 avg_ack_signal; }; @@ -5701,6 +5701,13 @@ void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, gfp_t gfp); +/** + * cfg80211_sinfo_alloc_tid_stats - allocate per-tid statistics. + * + * @sinfo: the station information + * @gfp: allocation flags + */ +int cfg80211_sinfo_alloc_tid_stats(struct station_info *sinfo, gfp_t gfp); /** * cfg80211_new_sta - notify userspace about station diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 43f34aa..25e8b15 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -2233,11 +2233,12 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE); } - sinfo->filled |= BIT(NL80211_STA_INFO_TID_STATS); - for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) { - struct cfg80211_tid_stats *tidstats = &sinfo->pertid[i]; - - sta_set_tidstats(sta, tidstats, i); + if (!cfg80211_sinfo_alloc_tid_stats(sinfo, GFP_KERNEL)) { + for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) { + struct cfg80211_tid_stats *tidstats = &sinfo->pertid[i]; + + sta_set_tidstats(sta, tidstats, i); + } } if (ieee80211_vif_is_mesh(&sdata->vif)) { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index f7715b8..e51cae7 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -4663,7 +4663,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, int tid; tidsattr = nla_nest_start(msg, NL80211_STA_INFO_TID_STATS); - if (!tidsattr) + if (!tidsattr || !sinfo->pertid) goto nla_put_failure; for (tid = 0; tid < IEEE80211_NUM_TIDS + 1; tid++) { @@ -4702,6 +4702,8 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, } nla_nest_end(msg, tidsattr); + + kfree(sinfo->pertid); } nla_nest_end(msg, sinfoattr); diff --git a/net/wireless/util.c b/net/wireless/util.c index d112e9a..b956c23fe 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1750,6 +1750,18 @@ int cfg80211_get_station(struct net_device *dev, const u8 *mac_addr, } EXPORT_SYMBOL(cfg80211_get_station); +int cfg80211_sinfo_alloc_tid_stats(struct station_info *sinfo, gfp_t gfp) +{ + sinfo->pertid = kcalloc(sizeof(*(sinfo->pertid)), + IEEE80211_NUM_TIDS + 1, gfp); + if (!sinfo->pertid) + return -ENOMEM; + + sinfo->filled |= NL80211_STA_INFO_TID_STATS; + return 0; +} +EXPORT_SYMBOL(cfg80211_sinfo_alloc_tid_stats); + void cfg80211_free_nan_func(struct cfg80211_nan_func *f) { int i; -- 2.7.4