// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2002-2005, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2006 Jiri Benc <jbenc@suse.cz>
* Copyright 2017 Intel Deutschland GmbH
* Copyright (C) 2019, 2022-2024 Intel Corporation
*/
#include <linux/kernel.h>
#include <linux/rtnetlink.h>
#include <linux/module.h>
#include <linux/slab.h>
#include "rate.h"
#include "ieee80211_i.h"
#include "debugfs.h"
struct rate_control_alg {
struct list_head list;
const struct rate_control_ops *ops;
};
static LIST_HEAD(rate_ctrl_algs);
static DEFINE_MUTEX(rate_ctrl_mutex);
static char *ieee80211_default_rc_algo = CONFIG_MAC80211_RC_DEFAULT;
module_param(ieee80211_default_rc_algo, charp, 0644);
MODULE_PARM_DESC(ieee80211_default_rc_algo,
"Default rate control algorithm for mac80211 to use");
void rate_control_rate_init(struct link_sta_info *link_sta)
{
struct sta_info *sta = link_sta->sta;
struct ieee80211_local *local = sta->sdata->local;
struct rate_control_ref *ref = sta->rate_ctrl;
struct ieee80211_sta *ista = &sta->sta;
void *priv_sta = sta->rate_ctrl_priv;
struct ieee80211_supported_band *sband;
struct ieee80211_chanctx_conf *chanctx_conf;
ieee80211_sta_init_nss(link_sta);
if (!ref)
return;
/* SW rate control isn't supported with MLO right now */
if (WARN_ON(ieee80211_vif_is_mld(&sta->sdata->vif)))
return;
rcu_read_lock();
chanctx_conf = rcu_dereference(sta->sdata->vif.bss_conf.chanctx_conf);
if (WARN_ON(!chanctx_conf)) {
rcu_read_unlock();
return;
}
sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band];
/* TODO: check for minstrel_s1g ? */
if (sband->band == NL80211_BAND_S1GHZ) {
ieee80211_s1g_sta_rate_init(sta);
rcu_read_unlock();
return;
}
spin_lock_bh(&sta->rate_ctrl_lock);
ref->ops->rate_init(ref->priv, sband, &chanctx_conf->def, ista,
priv_sta);
spin_unlock_bh(&sta->rate_ctrl_lock);
rcu_read_unlock();
set_sta_flag(sta, WLAN_STA_RATE_CONTROL);
}
void rate_control_rate_init_all_links(struct sta_info *sta)
{
int link_id;
for (link_id = 0; link_id < ARRAY_SIZE(sta->link); link_id++) {
struct link_sta_info *link_sta;
link_sta = sdata_dereference(sta->link[link_id], sta->sdata);
if (!link_sta)
continue;
rate_control_rate_init(link_sta);
}
}
void rate_control_tx_status(struct ieee80211_local *local,
struct ieee80211_tx_status *st)
{
struct rate_control_ref *ref = local->rate_ctrl;
struct sta_info *sta = container_of(st->sta, struct sta_info, sta);
void *priv_sta = sta->rate_ctrl_priv;
struct ieee80211_supported_band *sband;
if (!ref || !test_sta_flag(sta, WLAN_STA_RATE_CONTROL))
return;
sband = local->hw.wiphy->bands[st->info->band];
spin_lock_bh(&sta->rate_ctrl_lock);
if (ref->ops->tx_status_ext)
ref->ops->tx_status_ext(ref->priv, sband, priv_sta, st);
else if (st->skb)
ref->ops->tx_status(ref->priv, sband, st->sta, priv_sta, st->skb);
else
WARN_ON_ONCE(1);
spin_unlock_bh(&sta->rate_ctrl_lock);
}
void rate_control_rate_update(struct ieee80211_local *local,
struct ieee80211_supported_band *sband,
struct link_sta_info *link_sta,
u32 changed)
{
struct rate_control_ref *ref = local->rate_ctrl;
struct sta_info *sta = link_sta->sta;
struct ieee80211_sta *ista = &sta->sta;
void *priv_sta = sta->rate_ctrl_priv;
struct ieee80211_chanctx_conf *chanctx_conf;
if (ref && ref->ops->rate_update) {
rcu_read_lock();
chanctx_conf = rcu_dereference(sta->sdata->vif.bss_conf.chanctx_conf);
if (WARN_ON(!chanctx_conf)) {
rcu_read_unlock();
return;
}
spin_lock_bh(&sta->rate_ctrl_lock);
ref->ops->rate_update(ref->priv, sband, &chanctx_conf->def,
ista, priv_sta, changed);
spin_unlock_bh(&sta->rate_ctrl_lock);
rcu_read_unlock();
}
if (sta->uploaded)
drv_link_sta_rc_update(local, sta->sdata, link_sta->pub,
changed);
}
int ieee80211_rate_control_register(const struct rate_control_ops *ops)
{
struct rate_control_alg *alg;
if (!ops->name)
return -EINVAL;
mutex_lock(&rate_ctrl_mutex);
list_for_each_entry(alg, &rate_ctrl_algs, list) {
if (!strcmp(alg->ops->name, ops->name)) {
/* don't register an algorithm twice */
WARN_ON(1);
mutex_unlock(&rate_ctrl_mutex);
return -EALREADY;
}
}
alg = kzalloc(sizeof(*alg),