Skip to content

Commit

Permalink
ath9k: rework tx queue selection and fix queue stopping/waking
Browse files Browse the repository at this point in the history
The current ath9k tx queue handling code showed a few issues that could
lead to locking issues, tx stalls due to stopped queues, and maybe even
DMA issues.

The main source of these issues is that in some places the queue is
selected via skb queue mapping in places where this mapping may no
longer be valid. One such place is when data frames are transmitted via
the CAB queue (for powersave buffered frames). This is made even worse
by a lookup WMM AC values from the assigned tx queue (which is
undefined for the CAB queue).

This messed up the pending frame counting, which in turn caused issues
with queues getting stopped, but not woken again.

To fix these issues, this patch removes an unnecessary abstraction
separating a driver internal queue number from the skb queue number
(not to be confused with the hardware queue number).

It seems that this abstraction may have been necessary because of tx
queue preinitialization from the initvals. This patch avoids breakage
here by pushing the software <-> hardware queue mapping to the function
that assigns the tx queues and redefining the WMM AC definitions to
match the numbers used by mac80211 (also affects ath9k_htc).

To ensure consistency wrt. pending frame count tracking, these counters
are moved to the ath_txq struct, updated with the txq lock held, but
only where the tx queue selected by the skb queue map actually matches
the tx queue used by the driver for the frame.

Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Reported-by: Björn Smedman <bjorn.smedman@venatech.se>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Felix Fietkau authored and John W. Linville committed Nov 15, 2010
1 parent 21e731a commit 066dae9
Show file tree
Hide file tree
Showing 11 changed files with 109 additions and 197 deletions.
9 changes: 3 additions & 6 deletions drivers/net/wireless/ath/ath9k/ath9k.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,6 @@ enum ATH_AGGR_STATUS {

#define ATH_TXFIFO_DEPTH 8
struct ath_txq {
int axq_class;
u32 axq_qnum;
u32 *axq_link;
struct list_head axq_q;
Expand All @@ -208,11 +207,12 @@ struct ath_txq {
struct list_head txq_fifo_pending;
u8 txq_headidx;
u8 txq_tailidx;
int pending_frames;
};

struct ath_atx_ac {
struct ath_txq *txq;
int sched;
int qnum;
struct list_head list;
struct list_head tid_q;
};
Expand Down Expand Up @@ -290,12 +290,11 @@ struct ath_tx_control {
struct ath_tx {
u16 seq_no;
u32 txqsetup;
int hwq_map[WME_NUM_AC];
spinlock_t txbuflock;
struct list_head txbuf;
struct ath_txq txq[ATH9K_NUM_TX_QUEUES];
struct ath_descdma txdma;
int pending_frames[WME_NUM_AC];
struct ath_txq *txq_map[WME_NUM_AC];
};

struct ath_rx_edma {
Expand Down Expand Up @@ -325,7 +324,6 @@ void ath_rx_cleanup(struct ath_softc *sc);
int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp);
struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype);
void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq);
int ath_tx_setup(struct ath_softc *sc, int haltype);
void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx);
void ath_draintxq(struct ath_softc *sc,
struct ath_txq *txq, bool retry_tx);
Expand Down Expand Up @@ -665,7 +663,6 @@ struct ath_wiphy {

void ath9k_tasklet(unsigned long data);
int ath_reset(struct ath_softc *sc, bool retry_tx);
int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc);
int ath_cabq_update(struct ath_softc *);

static inline void ath_read_cachesize(struct ath_common *common, int *csz)
Expand Down
6 changes: 3 additions & 3 deletions drivers/net/wireless/ath/ath9k/beacon.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ int ath_beaconq_config(struct ath_softc *sc)
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_tx_queue_info qi, qi_be;
int qnum;
struct ath_txq *txq;

ath9k_hw_get_txq_props(ah, sc->beacon.beaconq, &qi);
if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
Expand All @@ -38,8 +38,8 @@ int ath_beaconq_config(struct ath_softc *sc)
qi.tqi_cwmax = 0;
} else {
/* Adhoc mode; important thing is to use 2x cwmin. */
qnum = sc->tx.hwq_map[WME_AC_BE];
ath9k_hw_get_txq_props(ah, qnum, &qi_be);
txq = sc->tx.txq_map[WME_AC_BE];
ath9k_hw_get_txq_props(ah, txq->axq_qnum, &qi_be);
qi.tqi_aifs = qi_be.tqi_aifs;
qi.tqi_cwmin = 4*qi_be.tqi_cwmin;
qi.tqi_cwmax = qi_be.tqi_cwmax;
Expand Down
9 changes: 5 additions & 4 deletions drivers/net/wireless/ath/ath9k/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@
#define WME_MAX_BA WME_BA_BMP_SIZE
#define ATH_TID_MAX_BUFS (2 * WME_MAX_BA)

#define WME_AC_BE 0
#define WME_AC_BK 1
#define WME_AC_VI 2
#define WME_AC_VO 3
/* These must match mac80211 skb queue mapping numbers */
#define WME_AC_VO 0
#define WME_AC_VI 1
#define WME_AC_BE 2
#define WME_AC_BK 3
#define WME_NUM_AC 4

#define ATH_RSSI_DUMMY_MARKER 0x127
Expand Down
36 changes: 19 additions & 17 deletions drivers/net/wireless/ath/ath9k/debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -585,10 +585,10 @@ static const struct file_operations fops_wiphy = {
do { \
len += snprintf(buf + len, size - len, \
"%s%13u%11u%10u%10u\n", str, \
sc->debug.stats.txstats[sc->tx.hwq_map[WME_AC_BE]].elem, \
sc->debug.stats.txstats[sc->tx.hwq_map[WME_AC_BK]].elem, \
sc->debug.stats.txstats[sc->tx.hwq_map[WME_AC_VI]].elem, \
sc->debug.stats.txstats[sc->tx.hwq_map[WME_AC_VO]].elem); \
sc->debug.stats.txstats[WME_AC_BE].elem, \
sc->debug.stats.txstats[WME_AC_BK].elem, \
sc->debug.stats.txstats[WME_AC_VI].elem, \
sc->debug.stats.txstats[WME_AC_VO].elem); \
} while(0)

static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
Expand Down Expand Up @@ -630,33 +630,35 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
return retval;
}

void ath_debug_stat_tx(struct ath_softc *sc, struct ath_txq *txq,
struct ath_buf *bf, struct ath_tx_status *ts)
void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf,
struct ath_tx_status *ts)
{
TX_STAT_INC(txq->axq_qnum, tx_pkts_all);
sc->debug.stats.txstats[txq->axq_qnum].tx_bytes_all += bf->bf_mpdu->len;
int qnum = skb_get_queue_mapping(bf->bf_mpdu);

TX_STAT_INC(qnum, tx_pkts_all);
sc->debug.stats.txstats[qnum].tx_bytes_all += bf->bf_mpdu->len;

if (bf_isampdu(bf)) {
if (bf_isxretried(bf))
TX_STAT_INC(txq->axq_qnum, a_xretries);
TX_STAT_INC(qnum, a_xretries);
else
TX_STAT_INC(txq->axq_qnum, a_completed);
TX_STAT_INC(qnum, a_completed);
} else {
TX_STAT_INC(txq->axq_qnum, completed);
TX_STAT_INC(qnum, completed);
}

if (ts->ts_status & ATH9K_TXERR_FIFO)
TX_STAT_INC(txq->axq_qnum, fifo_underrun);
TX_STAT_INC(qnum, fifo_underrun);
if (ts->ts_status & ATH9K_TXERR_XTXOP)
TX_STAT_INC(txq->axq_qnum, xtxop);
TX_STAT_INC(qnum, xtxop);
if (ts->ts_status & ATH9K_TXERR_TIMER_EXPIRED)
TX_STAT_INC(txq->axq_qnum, timer_exp);
TX_STAT_INC(qnum, timer_exp);
if (ts->ts_flags & ATH9K_TX_DESC_CFG_ERR)
TX_STAT_INC(txq->axq_qnum, desc_cfg_err);
TX_STAT_INC(qnum, desc_cfg_err);
if (ts->ts_flags & ATH9K_TX_DATA_UNDERRUN)
TX_STAT_INC(txq->axq_qnum, data_underrun);
TX_STAT_INC(qnum, data_underrun);
if (ts->ts_flags & ATH9K_TX_DELIM_UNDERRUN)
TX_STAT_INC(txq->axq_qnum, delim_underrun);
TX_STAT_INC(qnum, delim_underrun);
}

static const struct file_operations fops_xmit = {
Expand Down
5 changes: 2 additions & 3 deletions drivers/net/wireless/ath/ath9k/debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,8 @@ void ath9k_exit_debug(struct ath_hw *ah);
int ath9k_debug_create_root(void);
void ath9k_debug_remove_root(void);
void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status);
void ath_debug_stat_tx(struct ath_softc *sc, struct ath_txq *txq,
struct ath_buf *bf, struct ath_tx_status *ts);
void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf,
struct ath_tx_status *ts);
void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs);

#else
Expand Down Expand Up @@ -199,7 +199,6 @@ static inline void ath_debug_stat_interrupt(struct ath_softc *sc,
}

static inline void ath_debug_stat_tx(struct ath_softc *sc,
struct ath_txq *txq,
struct ath_buf *bf,
struct ath_tx_status *ts)
{
Expand Down
9 changes: 8 additions & 1 deletion drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,15 @@
/* TX */
/******/

static const int subtype_txq_to_hwq[] = {
[WME_AC_BE] = ATH_TXQ_AC_BE,
[WME_AC_BK] = ATH_TXQ_AC_BK,
[WME_AC_VI] = ATH_TXQ_AC_VI,
[WME_AC_VO] = ATH_TXQ_AC_VO,
};

#define ATH9K_HTC_INIT_TXQ(subtype) do { \
qi.tqi_subtype = subtype; \
qi.tqi_subtype = subtype_txq_to_hwq[subtype]; \
qi.tqi_aifs = ATH9K_TXQ_USEDEFAULT; \
qi.tqi_cwmin = ATH9K_TXQ_USEDEFAULT; \
qi.tqi_cwmax = ATH9K_TXQ_USEDEFAULT; \
Expand Down
7 changes: 7 additions & 0 deletions drivers/net/wireless/ath/ath9k/hw.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,13 @@
#define PAPRD_GAIN_TABLE_ENTRIES 32
#define PAPRD_TABLE_SZ 24

enum ath_hw_txq_subtype {
ATH_TXQ_AC_BE = 0,
ATH_TXQ_AC_BK = 1,
ATH_TXQ_AC_VI = 2,
ATH_TXQ_AC_VO = 3,
};

enum ath_ini_subsys {
ATH_INI_PRE = 0,
ATH_INI_CORE,
Expand Down
52 changes: 6 additions & 46 deletions drivers/net/wireless/ath/ath9k/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,8 @@ static void ath9k_init_crypto(struct ath_softc *sc)

static int ath9k_init_btcoex(struct ath_softc *sc)
{
int r, qnum;
struct ath_txq *txq;
int r;

switch (sc->sc_ah->btcoex_hw.scheme) {
case ATH_BTCOEX_CFG_NONE:
Expand All @@ -408,8 +409,8 @@ static int ath9k_init_btcoex(struct ath_softc *sc)
r = ath_init_btcoex_timer(sc);
if (r)
return -1;
qnum = sc->tx.hwq_map[WME_AC_BE];
ath9k_hw_init_btcoex_hw(sc->sc_ah, qnum);
txq = sc->tx.txq_map[WME_AC_BE];
ath9k_hw_init_btcoex_hw(sc->sc_ah, txq->axq_qnum);
sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW;
break;
default:
Expand All @@ -422,59 +423,18 @@ static int ath9k_init_btcoex(struct ath_softc *sc)

static int ath9k_init_queues(struct ath_softc *sc)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
int i = 0;

for (i = 0; i < ARRAY_SIZE(sc->tx.hwq_map); i++)
sc->tx.hwq_map[i] = -1;

sc->beacon.beaconq = ath9k_hw_beaconq_setup(sc->sc_ah);
if (sc->beacon.beaconq == -1) {
ath_print(common, ATH_DBG_FATAL,
"Unable to setup a beacon xmit queue\n");
goto err;
}

sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0);
if (sc->beacon.cabq == NULL) {
ath_print(common, ATH_DBG_FATAL,
"Unable to setup CAB xmit queue\n");
goto err;
}

sc->config.cabqReadytime = ATH_CABQ_READY_TIME;
ath_cabq_update(sc);

if (!ath_tx_setup(sc, WME_AC_BK)) {
ath_print(common, ATH_DBG_FATAL,
"Unable to setup xmit queue for BK traffic\n");
goto err;
}

if (!ath_tx_setup(sc, WME_AC_BE)) {
ath_print(common, ATH_DBG_FATAL,
"Unable to setup xmit queue for BE traffic\n");
goto err;
}
if (!ath_tx_setup(sc, WME_AC_VI)) {
ath_print(common, ATH_DBG_FATAL,
"Unable to setup xmit queue for VI traffic\n");
goto err;
}
if (!ath_tx_setup(sc, WME_AC_VO)) {
ath_print(common, ATH_DBG_FATAL,
"Unable to setup xmit queue for VO traffic\n");
goto err;
}
for (i = 0; i < WME_NUM_AC; i++)
sc->tx.txq_map[i] = ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, i);

return 0;

err:
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
if (ATH_TXQ_SETUP(sc, i))
ath_tx_cleanupq(sc, &sc->tx.txq[i]);

return -EIO;
}

static int ath9k_init_channels_rates(struct ath_softc *sc)
Expand Down
Loading

0 comments on commit 066dae9

Please sign in to comment.