linux/drivers/net/wireless/mediatek/mt76/sdio.c
Felix Fietkau e1378e5228 mt76: rely on AQL for burst size limits on tx queueing
Now that AQL works well on all mt76 drivers, completely replace the arbitrary
burst sizing and number of bursts logic for tx scheduling.
For the short period of time in which AQL does not work well yet, limit each
stations to 16 in-flight packets that have no estimated tx time.
This should avoid filling the queue if a station connects and queues up a
large number of packets before rate control information is available, especially
with hardware rate control

Signed-off-by: Felix Fietkau <nbd@nbd.name>
2020-09-24 18:10:17 +02:00

360 lines
7.4 KiB
C

// SPDX-License-Identifier: ISC
/* Copyright (C) 2020 MediaTek Inc.
*
* This file is written based on mt76/usb.c.
*
* Author: Felix Fietkau <nbd@nbd.name>
* Lorenzo Bianconi <lorenzo@kernel.org>
* Sean Wang <sean.wang@mediatek.com>
*/
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mmc/sdio_func.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include "mt76.h"
static int
mt76s_alloc_rx_queue(struct mt76_dev *dev, enum mt76_rxq_id qid)
{
struct mt76_queue *q = &dev->q_rx[qid];
spin_lock_init(&q->lock);
q->entry = devm_kcalloc(dev->dev,
MT_NUM_RX_ENTRIES, sizeof(*q->entry),
GFP_KERNEL);
if (!q->entry)
return -ENOMEM;
q->ndesc = MT_NUM_RX_ENTRIES;
q->head = q->tail = 0;
q->queued = 0;
return 0;
}
static int mt76s_alloc_tx(struct mt76_dev *dev)
{
struct mt76_queue *q;
int i;
for (i = 0; i < MT_TXQ_MCU_WA; i++) {
q = devm_kzalloc(dev->dev, sizeof(*q), GFP_KERNEL);
if (!q)
return -ENOMEM;
spin_lock_init(&q->lock);
q->hw_idx = i;
dev->q_tx[i].q = q;
q->entry = devm_kcalloc(dev->dev,
MT_NUM_TX_ENTRIES, sizeof(*q->entry),
GFP_KERNEL);
if (!q->entry)
return -ENOMEM;
q->ndesc = MT_NUM_TX_ENTRIES;
}
return 0;
}
void mt76s_stop_txrx(struct mt76_dev *dev)
{
struct mt76_sdio *sdio = &dev->sdio;
cancel_work_sync(&sdio->tx.xmit_work);
cancel_work_sync(&sdio->tx.status_work);
cancel_work_sync(&sdio->rx.recv_work);
cancel_work_sync(&sdio->rx.net_work);
cancel_work_sync(&sdio->stat_work);
clear_bit(MT76_READING_STATS, &dev->phy.state);
mt76_tx_status_check(dev, NULL, true);
}
EXPORT_SYMBOL_GPL(mt76s_stop_txrx);
int mt76s_alloc_queues(struct mt76_dev *dev)
{
int err;
err = mt76s_alloc_rx_queue(dev, MT_RXQ_MAIN);
if (err < 0)
return err;
return mt76s_alloc_tx(dev);
}
EXPORT_SYMBOL_GPL(mt76s_alloc_queues);
static struct mt76_queue_entry *
mt76s_get_next_rx_entry(struct mt76_queue *q)
{
struct mt76_queue_entry *e = NULL;
spin_lock_bh(&q->lock);
if (q->queued > 0) {
e = &q->entry[q->tail];
q->tail = (q->tail + 1) % q->ndesc;
q->queued--;
}
spin_unlock_bh(&q->lock);
return e;
}
static int
mt76s_process_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
{
int qid = q - &dev->q_rx[MT_RXQ_MAIN];
int nframes = 0;
while (true) {
struct mt76_queue_entry *e;
if (!test_bit(MT76_STATE_INITIALIZED, &dev->phy.state))
break;
e = mt76s_get_next_rx_entry(q);
if (!e || !e->skb)
break;
dev->drv->rx_skb(dev, MT_RXQ_MAIN, e->skb);
e->skb = NULL;
nframes++;
}
if (qid == MT_RXQ_MAIN)
mt76_rx_poll_complete(dev, MT_RXQ_MAIN, NULL);
return nframes;
}
static void mt76s_process_tx_queue(struct mt76_dev *dev, enum mt76_txq_id qid)
{
struct mt76_sw_queue *sq = &dev->q_tx[qid];
struct mt76_queue_entry entry;
struct mt76_queue *q = sq->q;
bool wake;
while (q->queued > 0) {
if (!q->entry[q->tail].done)
break;
entry = q->entry[q->tail];
q->entry[q->tail].done = false;
if (qid == MT_TXQ_MCU) {
dev_kfree_skb(entry.skb);
entry.skb = NULL;
}
mt76_queue_tx_complete(dev, q, &entry);
}
wake = q->stopped && q->queued < q->ndesc - 8;
if (wake)
q->stopped = false;
if (!q->queued)
wake_up(&dev->tx_wait);
if (qid == MT_TXQ_MCU)
return;
mt76_txq_schedule(&dev->phy, qid);
if (wake)
ieee80211_wake_queue(dev->hw, qid);
}
static void mt76s_tx_status_data(struct work_struct *work)
{
struct mt76_sdio *sdio;
struct mt76_dev *dev;
u8 update = 1;
u16 count = 0;
sdio = container_of(work, struct mt76_sdio, stat_work);
dev = container_of(sdio, struct mt76_dev, sdio);
while (true) {
if (test_bit(MT76_REMOVED, &dev->phy.state))
break;
if (!dev->drv->tx_status_data(dev, &update))
break;
count++;
}
if (count && test_bit(MT76_STATE_RUNNING, &dev->phy.state))
queue_work(dev->wq, &sdio->stat_work);
else
clear_bit(MT76_READING_STATS, &dev->phy.state);
}
static int
mt76s_tx_queue_skb(struct mt76_dev *dev, enum mt76_txq_id qid,
struct sk_buff *skb, struct mt76_wcid *wcid,
struct ieee80211_sta *sta)
{
struct mt76_queue *q = dev->q_tx[qid].q;
struct mt76_tx_info tx_info = {
.skb = skb,
};
int err, len = skb->len;
u16 idx = q->head;
if (q->queued == q->ndesc)
return -ENOSPC;
skb->prev = skb->next = NULL;
err = dev->drv->tx_prepare_skb(dev, NULL, qid, wcid, sta, &tx_info);
if (err < 0)
return err;
q->entry[q->head].skb = tx_info.skb;
q->entry[q->head].buf_sz = len;
q->head = (q->head + 1) % q->ndesc;
q->queued++;
return idx;
}
static int
mt76s_tx_queue_skb_raw(struct mt76_dev *dev, enum mt76_txq_id qid,
struct sk_buff *skb, u32 tx_info)
{
struct mt76_queue *q = dev->q_tx[qid].q;
int ret = -ENOSPC, len = skb->len;
if (q->queued == q->ndesc)
goto error;
ret = mt76_skb_adjust_pad(skb);
if (ret)
goto error;
spin_lock_bh(&q->lock);
q->entry[q->head].buf_sz = len;
q->entry[q->head].skb = skb;
q->head = (q->head + 1) % q->ndesc;
q->queued++;
spin_unlock_bh(&q->lock);
return 0;
error:
dev_kfree_skb(skb);
return ret;
}
static void mt76s_tx_kick(struct mt76_dev *dev, struct mt76_queue *q)
{
struct mt76_sdio *sdio = &dev->sdio;
queue_work(sdio->txrx_wq, &sdio->tx.xmit_work);
}
static const struct mt76_queue_ops sdio_queue_ops = {
.tx_queue_skb = mt76s_tx_queue_skb,
.kick = mt76s_tx_kick,
.tx_queue_skb_raw = mt76s_tx_queue_skb_raw,
};
static void mt76s_tx_work(struct work_struct *work)
{
struct mt76_sdio *sdio = container_of(work, struct mt76_sdio,
tx.status_work);
struct mt76_dev *dev = container_of(sdio, struct mt76_dev, sdio);
int i;
for (i = 0; i < MT_TXQ_MCU_WA; i++)
mt76s_process_tx_queue(dev, i);
if (dev->drv->tx_status_data &&
!test_and_set_bit(MT76_READING_STATS, &dev->phy.state))
queue_work(dev->wq, &dev->sdio.stat_work);
}
static void mt76s_rx_work(struct work_struct *work)
{
struct mt76_sdio *sdio = container_of(work, struct mt76_sdio,
rx.net_work);
struct mt76_dev *dev = container_of(sdio, struct mt76_dev, sdio);
int i;
/* rx processing */
local_bh_disable();
rcu_read_lock();
mt76_for_each_q_rx(dev, i)
mt76s_process_rx_queue(dev, &dev->q_rx[i]);
rcu_read_unlock();
local_bh_enable();
}
void mt76s_deinit(struct mt76_dev *dev)
{
struct mt76_sdio *sdio = &dev->sdio;
int i;
mt76s_stop_txrx(dev);
if (sdio->txrx_wq) {
destroy_workqueue(sdio->txrx_wq);
sdio->txrx_wq = NULL;
}
sdio_claim_host(sdio->func);
sdio_release_irq(sdio->func);
sdio_release_host(sdio->func);
mt76_for_each_q_rx(dev, i) {
struct mt76_queue *q = &dev->q_rx[i];
int j;
for (j = 0; j < q->ndesc; j++) {
struct mt76_queue_entry *e = &q->entry[j];
if (!e->skb)
continue;
dev_kfree_skb(e->skb);
e->skb = NULL;
}
}
}
EXPORT_SYMBOL_GPL(mt76s_deinit);
int mt76s_init(struct mt76_dev *dev, struct sdio_func *func,
const struct mt76_bus_ops *bus_ops)
{
struct mt76_sdio *sdio = &dev->sdio;
sdio->txrx_wq = alloc_workqueue("mt76s_txrx_wq",
WQ_UNBOUND | WQ_HIGHPRI,
WQ_UNBOUND_MAX_ACTIVE);
if (!sdio->txrx_wq)
return -ENOMEM;
INIT_WORK(&sdio->stat_work, mt76s_tx_status_data);
INIT_WORK(&sdio->tx.status_work, mt76s_tx_work);
INIT_WORK(&sdio->rx.net_work, mt76s_rx_work);
mutex_init(&sdio->sched.lock);
dev->queue_ops = &sdio_queue_ops;
dev->bus = bus_ops;
dev->sdio.func = func;
return 0;
}
EXPORT_SYMBOL_GPL(mt76s_init);
MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>");
MODULE_LICENSE("Dual BSD/GPL");