Skip to content

Commit 3ace10f

Browse files
kan-yanjmberg-intel
authored andcommitted
mac80211: Implement Airtime-based Queue Limit (AQL)
In order for the Fq_CoDel algorithm integrated in mac80211 layer to operate effectively to control excessive queueing latency, the CoDel algorithm requires an accurate measure of how long packets stays in the queue, AKA sojourn time. The sojourn time measured at the mac80211 layer doesn't include queueing latency in the lower layer (firmware/hardware) and CoDel expects lower layer to have a short queue. However, most 802.11ac chipsets offload tasks such TX aggregation to firmware or hardware, thus have a deep lower layer queue. Without a mechanism to control the lower layer queue size, packets only stay in mac80211 layer transiently before being sent to firmware queue. As a result, the sojourn time measured by CoDel in the mac80211 layer is almost always lower than the CoDel latency target, hence CoDel does little to control the latency, even when the lower layer queue causes excessive latency. The Byte Queue Limits (BQL) mechanism is commonly used to address the similar issue with wired network interface. However, this method cannot be applied directly to the wireless network interface. "Bytes" is not a suitable measure of queue depth in the wireless network, as the data rate can vary dramatically from station to station in the same network, from a few Mbps to over Gbps. This patch implements an Airtime-based Queue Limit (AQL) to make CoDel work effectively with wireless drivers that utilized firmware/hardware offloading. AQL allows each txq to release just enough packets to the lower layer to form 1-2 large aggregations to keep hardware fully utilized and retains the rest of the frames in mac80211 layer to be controlled by the CoDel algorithm. Signed-off-by: Kan Yan <[email protected]> [ Toke: Keep API to set pending airtime internal, fix nits in commit msg ] Signed-off-by: Toke Høiland-Jørgensen <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Johannes Berg <[email protected]>
1 parent db3e1c4 commit 3ace10f

File tree

9 files changed

+244
-14
lines changed

9 files changed

+244
-14
lines changed

include/net/cfg80211.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2606,6 +2606,13 @@ enum wiphy_params_flags {
26062606

26072607
#define IEEE80211_DEFAULT_AIRTIME_WEIGHT 256
26082608

2609+
/* The per TXQ device queue limit in airtime */
2610+
#define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L 5000
2611+
#define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H 12000
2612+
2613+
/* The per interface airtime threshold to switch to lower queue limit */
2614+
#define IEEE80211_AQL_THRESHOLD 24000
2615+
26092616
/**
26102617
* struct cfg80211_pmksa - PMK Security Association
26112618
*

include/net/mac80211.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5565,6 +5565,18 @@ void ieee80211_send_eosp_nullfunc(struct ieee80211_sta *pubsta, int tid);
55655565
void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid,
55665566
u32 tx_airtime, u32 rx_airtime);
55675567

5568+
/**
5569+
* ieee80211_txq_airtime_check - check if a txq can send frame to device
5570+
*
5571+
* @hw: pointer obtained from ieee80211_alloc_hw()
5572+
* @txq: pointer obtained from station or virtual interface
5573+
*
5574+
* Return true if the AQL's airtime limit has not been reached and the txq can
5575+
* continue to send more packets to the device. Otherwise return false.
5576+
*/
5577+
bool
5578+
ieee80211_txq_airtime_check(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
5579+
55685580
/**
55695581
* ieee80211_iter_keys - iterate keys programmed into the device
55705582
* @hw: pointer obtained from ieee80211_alloc_hw()

net/mac80211/debugfs.c

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,87 @@ static const struct file_operations aqm_ops = {
150150
.llseek = default_llseek,
151151
};
152152

153+
static ssize_t aql_txq_limit_read(struct file *file,
154+
char __user *user_buf,
155+
size_t count,
156+
loff_t *ppos)
157+
{
158+
struct ieee80211_local *local = file->private_data;
159+
char buf[400];
160+
int len = 0;
161+
162+
len = scnprintf(buf, sizeof(buf),
163+
"AC AQL limit low AQL limit high\n"
164+
"VO %u %u\n"
165+
"VI %u %u\n"
166+
"BE %u %u\n"
167+
"BK %u %u\n",
168+
local->aql_txq_limit_low[IEEE80211_AC_VO],
169+
local->aql_txq_limit_high[IEEE80211_AC_VO],
170+
local->aql_txq_limit_low[IEEE80211_AC_VI],
171+
local->aql_txq_limit_high[IEEE80211_AC_VI],
172+
local->aql_txq_limit_low[IEEE80211_AC_BE],
173+
local->aql_txq_limit_high[IEEE80211_AC_BE],
174+
local->aql_txq_limit_low[IEEE80211_AC_BK],
175+
local->aql_txq_limit_high[IEEE80211_AC_BK]);
176+
return simple_read_from_buffer(user_buf, count, ppos,
177+
buf, len);
178+
}
179+
180+
static ssize_t aql_txq_limit_write(struct file *file,
181+
const char __user *user_buf,
182+
size_t count,
183+
loff_t *ppos)
184+
{
185+
struct ieee80211_local *local = file->private_data;
186+
char buf[100];
187+
size_t len;
188+
u32 ac, q_limit_low, q_limit_high, q_limit_low_old, q_limit_high_old;
189+
struct sta_info *sta;
190+
191+
if (count > sizeof(buf))
192+
return -EINVAL;
193+
194+
if (copy_from_user(buf, user_buf, count))
195+
return -EFAULT;
196+
197+
buf[sizeof(buf) - 1] = 0;
198+
len = strlen(buf);
199+
if (len > 0 && buf[len - 1] == '\n')
200+
buf[len - 1] = 0;
201+
202+
if (sscanf(buf, "%u %u %u", &ac, &q_limit_low, &q_limit_high) != 3)
203+
return -EINVAL;
204+
205+
if (ac >= IEEE80211_NUM_ACS)
206+
return -EINVAL;
207+
208+
q_limit_low_old = local->aql_txq_limit_low[ac];
209+
q_limit_high_old = local->aql_txq_limit_high[ac];
210+
211+
local->aql_txq_limit_low[ac] = q_limit_low;
212+
local->aql_txq_limit_high[ac] = q_limit_high;
213+
214+
mutex_lock(&local->sta_mtx);
215+
list_for_each_entry(sta, &local->sta_list, list) {
216+
/* If a sta has customized queue limits, keep it */
217+
if (sta->airtime[ac].aql_limit_low == q_limit_low_old &&
218+
sta->airtime[ac].aql_limit_high == q_limit_high_old) {
219+
sta->airtime[ac].aql_limit_low = q_limit_low;
220+
sta->airtime[ac].aql_limit_high = q_limit_high;
221+
}
222+
}
223+
mutex_unlock(&local->sta_mtx);
224+
return count;
225+
}
226+
227+
static const struct file_operations aql_txq_limit_ops = {
228+
.write = aql_txq_limit_write,
229+
.read = aql_txq_limit_read,
230+
.open = simple_open,
231+
.llseek = default_llseek,
232+
};
233+
153234
static ssize_t force_tx_status_read(struct file *file,
154235
char __user *user_buf,
155236
size_t count,
@@ -444,6 +525,10 @@ void debugfs_hw_add(struct ieee80211_local *local)
444525
debugfs_create_u16("airtime_flags", 0600,
445526
phyd, &local->airtime_flags);
446527

528+
DEBUGFS_ADD(aql_txq_limit);
529+
debugfs_create_u32("aql_threshold", 0600,
530+
phyd, &local->aql_threshold);
531+
447532
statsd = debugfs_create_dir("statistics", phyd);
448533

449534
/* if the dir failed, don't put all the other things into the root! */

net/mac80211/debugfs_sta.c

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -197,10 +197,12 @@ static ssize_t sta_airtime_read(struct file *file, char __user *userbuf,
197197
{
198198
struct sta_info *sta = file->private_data;
199199
struct ieee80211_local *local = sta->sdata->local;
200-
size_t bufsz = 200;
200+
size_t bufsz = 400;
201201
char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf;
202202
u64 rx_airtime = 0, tx_airtime = 0;
203203
s64 deficit[IEEE80211_NUM_ACS];
204+
u32 q_depth[IEEE80211_NUM_ACS];
205+
u32 q_limit_l[IEEE80211_NUM_ACS], q_limit_h[IEEE80211_NUM_ACS];
204206
ssize_t rv;
205207
int ac;
206208

@@ -212,19 +214,22 @@ static ssize_t sta_airtime_read(struct file *file, char __user *userbuf,
212214
rx_airtime += sta->airtime[ac].rx_airtime;
213215
tx_airtime += sta->airtime[ac].tx_airtime;
214216
deficit[ac] = sta->airtime[ac].deficit;
217+
q_limit_l[ac] = sta->airtime[ac].aql_limit_low;
218+
q_limit_h[ac] = sta->airtime[ac].aql_limit_high;
215219
spin_unlock_bh(&local->active_txq_lock[ac]);
220+
q_depth[ac] = atomic_read(&sta->airtime[ac].aql_tx_pending);
216221
}
217222

218223
p += scnprintf(p, bufsz + buf - p,
219224
"RX: %llu us\nTX: %llu us\nWeight: %u\n"
220-
"Deficit: VO: %lld us VI: %lld us BE: %lld us BK: %lld us\n",
221-
rx_airtime,
222-
tx_airtime,
223-
sta->airtime_weight,
224-
deficit[0],
225-
deficit[1],
226-
deficit[2],
227-
deficit[3]);
225+
"Deficit: VO: %lld us VI: %lld us BE: %lld us BK: %lld us\n"
226+
"Q depth: VO: %u us VI: %u us BE: %u us BK: %u us\n"
227+
"Q limit[low/high]: VO: %u/%u VI: %u/%u BE: %u/%u BK: %u/%u\n",
228+
rx_airtime, tx_airtime, sta->airtime_weight,
229+
deficit[0], deficit[1], deficit[2], deficit[3],
230+
q_depth[0], q_depth[1], q_depth[2], q_depth[3],
231+
q_limit_l[0], q_limit_h[0], q_limit_l[1], q_limit_h[1],
232+
q_limit_l[2], q_limit_h[2], q_limit_l[3], q_limit_h[3]),
228233

229234
rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
230235
kfree(buf);
@@ -236,7 +241,25 @@ static ssize_t sta_airtime_write(struct file *file, const char __user *userbuf,
236241
{
237242
struct sta_info *sta = file->private_data;
238243
struct ieee80211_local *local = sta->sdata->local;
239-
int ac;
244+
u32 ac, q_limit_l, q_limit_h;
245+
char _buf[100] = {}, *buf = _buf;
246+
247+
if (count > sizeof(_buf))
248+
return -EINVAL;
249+
250+
if (copy_from_user(buf, userbuf, count))
251+
return -EFAULT;
252+
253+
buf[sizeof(_buf) - 1] = '\0';
254+
if (sscanf(buf, "queue limit %u %u %u", &ac, &q_limit_l, &q_limit_h)
255+
!= 3)
256+
return -EINVAL;
257+
258+
if (ac >= IEEE80211_NUM_ACS)
259+
return -EINVAL;
260+
261+
sta->airtime[ac].aql_limit_low = q_limit_l;
262+
sta->airtime[ac].aql_limit_high = q_limit_h;
240263

241264
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
242265
spin_lock_bh(&local->active_txq_lock[ac]);

net/mac80211/ieee80211_i.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,6 +1142,10 @@ struct ieee80211_local {
11421142
u16 schedule_round[IEEE80211_NUM_ACS];
11431143

11441144
u16 airtime_flags;
1145+
u32 aql_txq_limit_low[IEEE80211_NUM_ACS];
1146+
u32 aql_txq_limit_high[IEEE80211_NUM_ACS];
1147+
u32 aql_threshold;
1148+
atomic_t aql_total_pending_airtime;
11451149

11461150
const struct ieee80211_ops *ops;
11471151

net/mac80211/main.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -667,8 +667,16 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
667667
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
668668
INIT_LIST_HEAD(&local->active_txqs[i]);
669669
spin_lock_init(&local->active_txq_lock[i]);
670+
local->aql_txq_limit_low[i] = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L;
671+
local->aql_txq_limit_high[i] =
672+
IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H;
670673
}
671-
local->airtime_flags = AIRTIME_USE_TX | AIRTIME_USE_RX;
674+
675+
local->airtime_flags = AIRTIME_USE_TX |
676+
AIRTIME_USE_RX |
677+
AIRTIME_USE_AQL;
678+
local->aql_threshold = IEEE80211_AQL_THRESHOLD;
679+
atomic_set(&local->aql_total_pending_airtime, 0);
672680

673681
INIT_LIST_HEAD(&local->chanctx_list);
674682
mutex_init(&local->chanctx_mtx);

net/mac80211/sta_info.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,9 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
410410
skb_queue_head_init(&sta->ps_tx_buf[i]);
411411
skb_queue_head_init(&sta->tx_filtered[i]);
412412
sta->airtime[i].deficit = sta->airtime_weight;
413+
atomic_set(&sta->airtime[i].aql_tx_pending, 0);
414+
sta->airtime[i].aql_limit_low = local->aql_txq_limit_low[i];
415+
sta->airtime[i].aql_limit_high = local->aql_txq_limit_high[i];
413416
}
414417

415418
for (i = 0; i < IEEE80211_NUM_TIDS; i++)
@@ -1907,6 +1910,41 @@ void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid,
19071910
}
19081911
EXPORT_SYMBOL(ieee80211_sta_register_airtime);
19091912

1913+
void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
1914+
struct sta_info *sta, u8 ac,
1915+
u16 tx_airtime, bool tx_completed)
1916+
{
1917+
int tx_pending;
1918+
1919+
if (!tx_completed) {
1920+
if (sta)
1921+
atomic_add(tx_airtime,
1922+
&sta->airtime[ac].aql_tx_pending);
1923+
1924+
atomic_add(tx_airtime, &local->aql_total_pending_airtime);
1925+
return;
1926+
}
1927+
1928+
if (sta) {
1929+
tx_pending = atomic_sub_return(tx_airtime,
1930+
&sta->airtime[ac].aql_tx_pending);
1931+
if (WARN_ONCE(tx_pending < 0,
1932+
"STA %pM AC %d txq pending airtime underflow: %u, %u",
1933+
sta->addr, ac, tx_pending, tx_airtime))
1934+
atomic_cmpxchg(&sta->airtime[ac].aql_tx_pending,
1935+
tx_pending, 0);
1936+
}
1937+
1938+
tx_pending = atomic_sub_return(tx_airtime,
1939+
&local->aql_total_pending_airtime);
1940+
if (WARN_ONCE(tx_pending < 0,
1941+
"Device %s AC %d pending airtime underflow: %u, %u",
1942+
wiphy_name(local->hw.wiphy), ac, tx_pending,
1943+
tx_airtime))
1944+
atomic_cmpxchg(&local->aql_total_pending_airtime,
1945+
tx_pending, 0);
1946+
}
1947+
19101948
int sta_info_move_state(struct sta_info *sta,
19111949
enum ieee80211_sta_state new_state)
19121950
{

net/mac80211/sta_info.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,13 +127,21 @@ enum ieee80211_agg_stop_reason {
127127
/* Debugfs flags to enable/disable use of RX/TX airtime in scheduler */
128128
#define AIRTIME_USE_TX BIT(0)
129129
#define AIRTIME_USE_RX BIT(1)
130+
#define AIRTIME_USE_AQL BIT(2)
130131

131132
struct airtime_info {
132133
u64 rx_airtime;
133134
u64 tx_airtime;
134135
s64 deficit;
136+
atomic_t aql_tx_pending; /* Estimated airtime for frames pending */
137+
u32 aql_limit_low;
138+
u32 aql_limit_high;
135139
};
136140

141+
void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
142+
struct sta_info *sta, u8 ac,
143+
u16 tx_airtime, bool tx_completed);
144+
137145
struct sta_info;
138146

139147
/**

net/mac80211/tx.c

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3677,7 +3677,8 @@ struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac)
36773677
{
36783678
struct ieee80211_local *local = hw_to_local(hw);
36793679
struct ieee80211_txq *ret = NULL;
3680-
struct txq_info *txqi = NULL;
3680+
struct txq_info *txqi = NULL, *head = NULL;
3681+
bool found_eligible_txq = false;
36813682

36823683
spin_lock_bh(&local->active_txq_lock[ac]);
36833684

@@ -3688,13 +3689,30 @@ struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac)
36883689
if (!txqi)
36893690
goto out;
36903691

3692+
if (txqi == head) {
3693+
if (!found_eligible_txq)
3694+
goto out;
3695+
else
3696+
found_eligible_txq = false;
3697+
}
3698+
3699+
if (!head)
3700+
head = txqi;
3701+
36913702
if (txqi->txq.sta) {
36923703
struct sta_info *sta = container_of(txqi->txq.sta,
3693-
struct sta_info, sta);
3704+
struct sta_info, sta);
3705+
bool aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq);
3706+
s64 deficit = sta->airtime[txqi->txq.ac].deficit;
36943707

3695-
if (sta->airtime[txqi->txq.ac].deficit < 0) {
3708+
if (aql_check)
3709+
found_eligible_txq = true;
3710+
3711+
if (deficit < 0)
36963712
sta->airtime[txqi->txq.ac].deficit +=
36973713
sta->airtime_weight;
3714+
3715+
if (deficit < 0 || !aql_check) {
36983716
list_move_tail(&txqi->schedule_order,
36993717
&local->active_txqs[txqi->txq.ac]);
37003718
goto begin;
@@ -3748,6 +3766,33 @@ void __ieee80211_schedule_txq(struct ieee80211_hw *hw,
37483766
}
37493767
EXPORT_SYMBOL(__ieee80211_schedule_txq);
37503768

3769+
bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw,
3770+
struct ieee80211_txq *txq)
3771+
{
3772+
struct sta_info *sta;
3773+
struct ieee80211_local *local = hw_to_local(hw);
3774+
3775+
if (!(local->airtime_flags & AIRTIME_USE_AQL))
3776+
return true;
3777+
3778+
if (!txq->sta)
3779+
return true;
3780+
3781+
sta = container_of(txq->sta, struct sta_info, sta);
3782+
if (atomic_read(&sta->airtime[txq->ac].aql_tx_pending) <
3783+
sta->airtime[txq->ac].aql_limit_low)
3784+
return true;
3785+
3786+
if (atomic_read(&local->aql_total_pending_airtime) <
3787+
local->aql_threshold &&
3788+
atomic_read(&sta->airtime[txq->ac].aql_tx_pending) <
3789+
sta->airtime[txq->ac].aql_limit_high)
3790+
return true;
3791+
3792+
return false;
3793+
}
3794+
EXPORT_SYMBOL(ieee80211_txq_airtime_check);
3795+
37513796
bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
37523797
struct ieee80211_txq *txq)
37533798
{

0 commit comments

Comments
 (0)