2011-01-10 07:12:01

by Ben Greear

[permalink] [raw]
Subject: ath9k: Resend all my pending patches.

Here is my current ath9k patch queue. These have all been
posted before, but this should make it easier to apply them.

The final 'copy-break' patch still needs some work to
function properly on non PCI busses it seems, and maybe
disable copybreak on larger packets.



2011-01-25 21:45:09

by John W. Linville

[permalink] [raw]
Subject: Re: [PATCH RESEND 09/11] ath9k: Try all queues when looking for next packet to send.

On Sun, Jan 09, 2011 at 11:11:51PM -0800, [email protected] wrote:
> From: Ben Greear <[email protected]>
>
> There can be multiple struct ath_atx_ac entries for each
> txq, so if the first one doesn't have any packets to send
> now, try the rest of them. This should help keep xmit
> going when using multiple stations, especially with AMPDU
> enabled.
>
> Signed-off-by: Ben Greear <[email protected]>

This one doesn't seem to apply any more. Can I persuade you to rebase
and resubmit it?

Thanks,

John
--
John W. Linville Someday the world will need a hero, and you
[email protected] might be all we have. Be ready.

2011-01-10 07:12:21

by Ben Greear

[permalink] [raw]
Subject: [PATCH RESEND 10/11] ath9k: Restart xmit logic in xmit watchdog.

From: Ben Greear <[email protected]>

The system can get into a state where the xmit queue
is stopped, but there are no packets pending, so
the queue will not be restarted.

Add logic to the xmit watchdog to attempt to restart
the xmit logic if this situation is detected.

Example 'dmesg' output:

ath: txq: f4e723e0 axq_qnum: 2, mac80211_qnum: 2 axq_link: f4e996c8 pending frames: 1 axq_acq empty: 1 stopped: 0 axq_depth: 0 Attempting to restart tx logic.

Signed-off-by: Ben Greear <[email protected]>
---
:100644 100644 3f5c513... 3108699... M drivers/net/wireless/ath/ath9k/ath9k.h
:100644 100644 9e009cc... b0cb792... M drivers/net/wireless/ath/ath9k/debug.c
:100644 100644 59c01ca... 5279653... M drivers/net/wireless/ath/ath9k/init.c
:100644 100644 46481fb... 8631afb... M drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/ath9k/ath9k.h | 9 +++-
drivers/net/wireless/ath/ath9k/debug.c | 4 +-
drivers/net/wireless/ath/ath9k/init.c | 5 +-
drivers/net/wireless/ath/ath9k/xmit.c | 79 ++++++++++++++++++++++----------
4 files changed, 68 insertions(+), 29 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 3f5c513..3108699 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -184,7 +184,8 @@ enum ATH_AGGR_STATUS {

#define ATH_TXFIFO_DEPTH 8
struct ath_txq {
- u32 axq_qnum;
+ int mac80211_qnum; /* mac80211 queue number, -1 means not mac80211 Q */
+ u32 axq_qnum; /* ath9k hardware queue number */
u32 *axq_link;
struct list_head axq_q;
spinlock_t axq_lock;
@@ -280,6 +281,11 @@ struct ath_tx_control {
#define ATH_TX_XRETRY 0x02
#define ATH_TX_BAR 0x04

+/**
+ * @txq_map: Index is mac80211 queue number. This is
+ * not necessarily the same as the hardware queue number
+ * (axq_qnum).
+ */
struct ath_tx {
u16 seq_no;
u32 txqsetup;
@@ -629,6 +635,7 @@ struct ath_softc {
struct ath9k_debug debug;
spinlock_t nodes_lock;
struct list_head nodes; /* basically, stations */
+ unsigned int tx_complete_poll_work_seen;
#endif
struct ath_beacon_config cur_beacon_conf;
struct delayed_work tx_complete_work;
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index 9e009cc..b0cb792 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -629,9 +629,11 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
if (buf == NULL)
return -ENOMEM;

- len += sprintf(buf, "Num-Tx-Queues: %i tx-queues-setup: 0x%x\n"
+ len += sprintf(buf, "Num-Tx-Queues: %i tx-queues-setup: 0x%x"
+ " poll-work-seen: %u\n"
"%30s %10s%10s%10s\n\n",
ATH9K_NUM_TX_QUEUES, sc->tx.txqsetup,
+ sc->tx_complete_poll_work_seen,
"BE", "BK", "VI", "VO");

PR("MPDUs Queued: ", queued);
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 59c01ca..5279653 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -442,9 +442,10 @@ static int ath9k_init_queues(struct ath_softc *sc)
sc->config.cabqReadytime = ATH_CABQ_READY_TIME;
ath_cabq_update(sc);

- for (i = 0; i < WME_NUM_AC; i++)
+ for (i = 0; i < WME_NUM_AC; i++) {
sc->tx.txq_map[i] = ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, i);
-
+ sc->tx.txq_map[i]->mac80211_qnum = i;
+ }
return 0;
}

diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 46481fb..8631afb 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -953,7 +953,7 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
[WME_AC_VI] = ATH_TXQ_AC_VI,
[WME_AC_VO] = ATH_TXQ_AC_VO,
};
- int qnum, i;
+ int axq_qnum, i;

memset(&qi, 0, sizeof(qi));
qi.tqi_subtype = subtype_txq_to_hwq[subtype];
@@ -987,24 +987,25 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
qi.tqi_qflags = TXQ_FLAG_TXEOLINT_ENABLE |
TXQ_FLAG_TXDESCINT_ENABLE;
}
- qnum = ath9k_hw_setuptxqueue(ah, qtype, &qi);
- if (qnum == -1) {
+ axq_qnum = ath9k_hw_setuptxqueue(ah, qtype, &qi);
+ if (axq_qnum == -1) {
/*
* NB: don't print a message, this happens
* normally on parts with too few tx queues
*/
return NULL;
}
- if (qnum >= ARRAY_SIZE(sc->tx.txq)) {
+ if (axq_qnum >= ARRAY_SIZE(sc->tx.txq)) {
ath_err(common, "qnum %u out of range, max %zu!\n",
- qnum, ARRAY_SIZE(sc->tx.txq));
- ath9k_hw_releasetxqueue(ah, qnum);
+ axq_qnum, ARRAY_SIZE(sc->tx.txq));
+ ath9k_hw_releasetxqueue(ah, axq_qnum);
return NULL;
}
- if (!ATH_TXQ_SETUP(sc, qnum)) {
- struct ath_txq *txq = &sc->tx.txq[qnum];
+ if (!ATH_TXQ_SETUP(sc, axq_qnum)) {
+ struct ath_txq *txq = &sc->tx.txq[axq_qnum];

- txq->axq_qnum = qnum;
+ txq->axq_qnum = axq_qnum;
+ txq->mac80211_qnum = -1;
txq->axq_link = NULL;
INIT_LIST_HEAD(&txq->axq_q);
INIT_LIST_HEAD(&txq->axq_acq);
@@ -1012,14 +1013,14 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
txq->axq_depth = 0;
txq->axq_ampdu_depth = 0;
txq->axq_tx_inprogress = false;
- sc->tx.txqsetup |= 1<<qnum;
+ sc->tx.txqsetup |= 1<<axq_qnum;

txq->txq_headidx = txq->txq_tailidx = 0;
for (i = 0; i < ATH_TXFIFO_DEPTH; i++)
INIT_LIST_HEAD(&txq->txq_fifo[i]);
INIT_LIST_HEAD(&txq->txq_fifo_pending);
}
- return &sc->tx.txq[qnum];
+ return &sc->tx.txq[axq_qnum];
}

int ath_txq_update(struct ath_softc *sc, int qnum,
@@ -1988,17 +1989,16 @@ static void ath_tx_rc_status(struct ath_buf *bf, struct ath_tx_status *ts,
tx_info->status.rates[tx_rateindex].count = ts->ts_longretry + 1;
}

-static void ath_wake_mac80211_queue(struct ath_softc *sc, int qnum)
+/* Has no locking. Must hold spin_lock_bh(&txq->axq_lock)
+ * before calling this.
+ */
+static void __ath_wake_mac80211_queue(struct ath_softc *sc, struct ath_txq *txq)
{
- struct ath_txq *txq;
-
- txq = sc->tx.txq_map[qnum];
- spin_lock_bh(&txq->axq_lock);
- if (txq->stopped && txq->pending_frames < ATH_MAX_QDEPTH) {
- if (ath_mac80211_start_queue(sc, qnum))
+ if (txq->mac80211_qnum >= 0 &&
+ txq->stopped && txq->pending_frames < ATH_MAX_QDEPTH) {
+ if (ath_mac80211_start_queue(sc, txq->mac80211_qnum))
txq->stopped = 0;
}
- spin_unlock_bh(&txq->axq_lock);
}

static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
@@ -2101,10 +2101,9 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
else
ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, txok, 0);

- if (txq == sc->tx.txq_map[qnum])
- ath_wake_mac80211_queue(sc, qnum);
-
spin_lock_bh(&txq->axq_lock);
+ __ath_wake_mac80211_queue(sc, txq);
+
if (sc->sc_flags & SC_OP_TXAGGR)
ath_txq_schedule(sc, txq);
spin_unlock_bh(&txq->axq_lock);
@@ -2118,6 +2117,9 @@ static void ath_tx_complete_poll_work(struct work_struct *work)
struct ath_txq *txq;
int i;
bool needreset = false;
+#ifdef CONFIG_ATH9K_DEBUGFS
+ sc->tx_complete_poll_work_seen++;
+#endif

for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
if (ATH_TXQ_SETUP(sc, i)) {
@@ -2131,6 +2133,34 @@ static void ath_tx_complete_poll_work(struct work_struct *work)
} else {
txq->axq_tx_inprogress = true;
}
+ } else {
+ /* If the queue has pending buffers, then it
+ * should be doing tx work (and have axq_depth).
+ * Shouldn't get to this state I think..but
+ * we do.
+ */
+ if (!(sc->sc_flags & (SC_OP_OFFCHANNEL)) &&
+ (txq->pending_frames > 0 ||
+ !list_empty(&txq->axq_acq) ||
+ txq->stopped)) {
+ ath_err(ath9k_hw_common(sc->sc_ah),
+ "txq: %p axq_qnum: %u,"
+ " mac80211_qnum: %i"
+ " axq_link: %p"
+ " pending frames: %i"
+ " axq_acq empty: %i"
+ " stopped: %i"
+ " axq_depth: 0 Attempting to"
+ " restart tx logic.\n",
+ txq, txq->axq_qnum,
+ txq->mac80211_qnum,
+ txq->axq_link,
+ txq->pending_frames,
+ list_empty(&txq->axq_acq),
+ txq->stopped);
+ __ath_wake_mac80211_queue(sc, txq);
+ ath_txq_schedule(sc, txq);
+ }
}
spin_unlock_bh(&txq->axq_lock);
}
@@ -2227,10 +2257,9 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
ath_tx_complete_buf(sc, bf, txq, &bf_head,
&txs, txok, 0);

- if (txq == sc->tx.txq_map[qnum])
- ath_wake_mac80211_queue(sc, qnum);
-
spin_lock_bh(&txq->axq_lock);
+ __ath_wake_mac80211_queue(sc, txq);
+
if (!list_empty(&txq->txq_fifo_pending)) {
INIT_LIST_HEAD(&bf_head);
bf = list_first_entry(&txq->txq_fifo_pending,
--
1.7.2.3


2011-01-10 07:12:07

by Ben Greear

[permalink] [raw]
Subject: [PATCH RESEND 02/11] ath9k: Initialize ah->hw

From: Ben Greear <[email protected]>

Previous code left it NULL.

Signed-off-by: Ben Greear <[email protected]>
---
:100644 100644 767d8b8... 23b2998... M drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/init.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 767d8b8..23b2998 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -537,6 +537,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid,
if (!ah)
return -ENOMEM;

+ ah->hw = sc->hw;
ah->hw_version.devid = devid;
ah->hw_version.subsysid = subsysid;
sc->sc_ah = ah;
--
1.7.2.3


2011-01-10 07:12:30

by Ben Greear

[permalink] [raw]
Subject: [PATCH RESEND 11/11] ath9k: Implement rx copy-break.

From: Ben Greear <[email protected]>

This saves us constantly allocating large, multi-page
skbs. It should fix the order-1 allocation errors reported,
and in a 60-vif scenario, this significantly decreases CPU
utilization, and latency, and increases bandwidth.

Signed-off-by: Ben Greear <[email protected]>
---
:100644 100644 b2497b8... ea2f67c... M drivers/net/wireless/ath/ath9k/recv.c
drivers/net/wireless/ath/ath9k/recv.c | 92 ++++++++++++++++++++++-----------
1 files changed, 61 insertions(+), 31 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index b2497b8..ea2f67c 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -16,6 +16,7 @@

#include "ath9k.h"
#include "ar9003_mac.h"
+#include <linux/pci.h>

#define SKB_CB_ATHBUF(__skb) (*((struct ath_buf **)__skb->cb))

@@ -1623,7 +1624,7 @@ div_comb_done:
int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
{
struct ath_buf *bf;
- struct sk_buff *skb = NULL, *requeue_skb;
+ struct sk_buff *skb = NULL, *requeue_skb = NULL;
struct ieee80211_rx_status *rxs;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
@@ -1634,7 +1635,8 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
*/
struct ieee80211_hw *hw = NULL;
struct ieee80211_hdr *hdr;
- int retval;
+ int retval, len;
+ bool use_copybreak = true;
bool decrypt_error = false;
struct ath_rx_status rs;
enum ath9k_rx_qtype qtype;
@@ -1702,42 +1704,70 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
unlikely(tsf_lower - rs.rs_tstamp > 0x10000000))
rxs->mactime += 0x100000000ULL;

- /* Ensure we always have an skb to requeue once we are done
- * processing the current buffer's skb */
- requeue_skb = ath_rxbuf_alloc(common, common->rx_bufsize, GFP_ATOMIC);
-
- /* If there is no memory we ignore the current RX'd frame,
- * tell hardware it can give us a new frame using the old
- * skb and put it at the tail of the sc->rx.rxbuf list for
- * processing. */
- if (!requeue_skb)
- goto requeue;
-
- /* Unmap the frame */
- dma_unmap_single(sc->dev, bf->bf_buf_addr,
- common->rx_bufsize,
- dma_type);
+ len = rs.rs_datalen + ah->caps.rx_status_len;
+ if (use_copybreak) {
+ skb = netdev_alloc_skb(NULL, len);
+ if (!skb) {
+ skb = bf->bf_mpdu;
+ use_copybreak = false;
+ goto non_copybreak;
+ }
+ } else {
+non_copybreak:
+ /* Ensure we always have an skb to requeue once we are
+ * done processing the current buffer's skb */
+ requeue_skb = ath_rxbuf_alloc(common,
+ common->rx_bufsize,
+ GFP_ATOMIC);
+
+ /* If there is no memory we ignore the current RX'd
+ * frame, tell hardware it can give us a new frame
+ * using the old skb and put it at the tail of the
+ * sc->rx.rxbuf list for processing. */
+ if (!requeue_skb)
+ goto requeue;
+
+ /* Unmap the frame */
+ dma_unmap_single(sc->dev, bf->bf_buf_addr,
+ common->rx_bufsize,
+ dma_type);
+ }

- skb_put(skb, rs.rs_datalen + ah->caps.rx_status_len);
+ skb_put(skb, len);
if (ah->caps.rx_status_len)
skb_pull(skb, ah->caps.rx_status_len);

+ if (use_copybreak) {
+ struct pci_dev *pdev = to_pci_dev(sc->dev);
+ pci_dma_sync_single_for_cpu(pdev, bf->bf_buf_addr,
+ len, PCI_DMA_FROMDEVICE);
+ skb_copy_from_linear_data(bf->bf_mpdu, skb->data, len);
+ pci_dma_sync_single_for_device(pdev, bf->bf_buf_addr,
+ len, PCI_DMA_FROMDEVICE);
+ memcpy(skb->cb, bf->bf_mpdu->cb, sizeof(skb->cb));
+ rxs = IEEE80211_SKB_RXCB(skb);
+ }
+
ath9k_rx_skb_postprocess(common, skb, &rs,
rxs, decrypt_error);

- /* We will now give hardware our shiny new allocated skb */
- bf->bf_mpdu = requeue_skb;
- bf->bf_buf_addr = dma_map_single(sc->dev, requeue_skb->data,
- common->rx_bufsize,
- dma_type);
- if (unlikely(dma_mapping_error(sc->dev,
- bf->bf_buf_addr))) {
- dev_kfree_skb_any(requeue_skb);
- bf->bf_mpdu = NULL;
- bf->bf_buf_addr = 0;
- ath_err(common, "dma_mapping_error() on RX\n");
- ath_rx_send_to_mac80211(hw, sc, skb);
- break;
+ if (!use_copybreak) {
+ /* We will now give hardware our shiny new allocated
+ * skb */
+ bf->bf_mpdu = requeue_skb;
+ bf->bf_buf_addr = dma_map_single(sc->dev,
+ requeue_skb->data,
+ common->rx_bufsize,
+ dma_type);
+ if (unlikely(dma_mapping_error(sc->dev,
+ bf->bf_buf_addr))) {
+ dev_kfree_skb_any(requeue_skb);
+ bf->bf_mpdu = NULL;
+ bf->bf_buf_addr = 0;
+ ath_err(common, "dma_mapping_error() on RX\n");
+ ath_rx_send_to_mac80211(hw, sc, skb);
+ break;
+ }
}

/*
--
1.7.2.3


2011-01-25 22:01:34

by Ben Greear

[permalink] [raw]
Subject: Re: [PATCH RESEND 09/11] ath9k: Try all queues when looking for next packet to send.

On 01/25/2011 01:37 PM, John W. Linville wrote:
> On Sun, Jan 09, 2011 at 11:11:51PM -0800, [email protected] wrote:
>> From: Ben Greear<[email protected]>
>>
>> There can be multiple struct ath_atx_ac entries for each
>> txq, so if the first one doesn't have any packets to send
>> now, try the rest of them. This should help keep xmit
>> going when using multiple stations, especially with AMPDU
>> enabled.
>>
>> Signed-off-by: Ben Greear<[email protected]>
>
> This one doesn't seem to apply any more. Can I persuade you to rebase
> and resubmit it?

I think this is already taken care of, or at least the important
parts.

Felix did some initial work in this area, and then I
followed on with this one:

ath9k: Try more than one queue when scheduling new aggregate.


The only ath9k patches I have in my queue that are not upstream
are:

(From Louis Rodriguez)
ath9k: warn when we get a ATH9K_INT_TIM_TIMER and are idle

and my:

ath9k: Implement rx copy-break.

Which appears DOA as folks seem to like paged-skbs instead.

Thanks,
Ben

--
Ben Greear <[email protected]>
Candela Technologies Inc http://www.candelatech.com


2011-01-10 07:12:42

by Ben Greear

[permalink] [raw]
Subject: [PATCH RESEND 08/11] ath9k: More xmit queue debugfs information.

From: Ben Greear <[email protected]>

To try to figure out why xmit logic hangs.

Signed-off-by: Ben Greear <[email protected]>
---
:100644 100644 650f00f... 9e009cc... M drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/debug.c | 26 ++++++++++++++++++++++++++
1 files changed, 26 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index 650f00f..9e009cc 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -679,6 +679,32 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
PRQLE(tmp, txq_fifo[i]);
}

+ /* Print out more detailed queue-info */
+ for (i = 0; i <= WME_AC_BK; i++) {
+ struct ath_txq *txq = &(sc->tx.txq[i]);
+ struct ath_atx_ac *ac;
+ struct ath_atx_tid *tid;
+ if (len >= size)
+ goto done;
+ spin_lock_bh(&txq->axq_lock);
+ if (!list_empty(&txq->axq_acq)) {
+ ac = list_first_entry(&txq->axq_acq, struct ath_atx_ac,
+ list);
+ len += snprintf(buf + len, size - len,
+ "txq[%i] first-ac: %p sched: %i\n",
+ i, ac, ac->sched);
+ if (list_empty(&ac->tid_q) || (len >= size))
+ goto done_for;
+ tid = list_first_entry(&ac->tid_q, struct ath_atx_tid,
+ list);
+ len += snprintf(buf + len, size - len,
+ " first-tid: %p sched: %i paused: %i\n",
+ tid, tid->sched, tid->paused);
+ }
+ done_for:
+ spin_unlock_bh(&txq->axq_lock);
+ }
+
done:
if (len > size)
len = size;
--
1.7.2.3


2011-01-10 07:12:15

by Ben Greear

[permalink] [raw]
Subject: [PATCH RESEND 05/11] ath9k: Ensure xmit makes progress.

From: Ben Greear <[email protected]>

If the txq->axq_q is empty, the code was breaking out
of the tx_processq logic without checking to see if it should
transmit other queued AMPDU frames (txq->axq_acq).

This patches ensures ath_txq_schedule is called.

This needs review.

Signed-off-by: Ben Greear <[email protected]>
---
:100644 100644 c769037... 2248e47... M drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/ath9k/xmit.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index c769037..2248e47 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1999,6 +1999,8 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
spin_lock_bh(&txq->axq_lock);
if (list_empty(&txq->axq_q)) {
txq->axq_link = NULL;
+ if (sc->sc_flags & SC_OP_TXAGGR)
+ ath_txq_schedule(sc, txq);
spin_unlock_bh(&txq->axq_lock);
break;
}
--
1.7.2.3


2011-01-10 07:12:11

by Ben Greear

[permalink] [raw]
Subject: [PATCH RESEND 04/11] ath9k: Remove un-used member from ath_node.

From: Ben Greear <[email protected]>

Signed-off-by: Ben Greear <[email protected]>
---
:100644 100644 3681caf5.. deda815... M drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/ath9k.h | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 3681caf5..deda815 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -342,7 +342,6 @@ struct ath_vif {
__le64 tsf_adjust; /* TSF adjustment for staggered beacons */
enum nl80211_iftype av_opmode;
struct ath_buf *av_bcbuf;
- struct ath_tx_control av_btxctl;
u8 bssid[ETH_ALEN]; /* current BSSID from config_interface */
};

--
1.7.2.3


2011-01-10 07:12:03

by Ben Greear

[permalink] [raw]
Subject: [PATCH RESEND 01/11] ath9k: Show some live tx-queue values in debugfs.

From: Ben Greear <[email protected]>

I thought this might help track down stuck queues, etc.

Signed-off-by: Ben Greear <[email protected]>
---
:100644 100644 3586c43... 5075faa... M drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/debug.c | 16 ++++++++++++++++
1 files changed, 16 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index 3586c43..5075faa 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -589,6 +589,16 @@ static const struct file_operations fops_wiphy = {
sc->debug.stats.txstats[WME_AC_VO].elem); \
} while(0)

+#define PRX(str, elem) \
+do { \
+ len += snprintf(buf + len, size - len, \
+ "%s%13u%11u%10u%10u\n", str, \
+ (unsigned int)(sc->tx.txq[WME_AC_BE].elem), \
+ (unsigned int)(sc->tx.txq[WME_AC_BK].elem), \
+ (unsigned int)(sc->tx.txq[WME_AC_VI].elem), \
+ (unsigned int)(sc->tx.txq[WME_AC_VO].elem)); \
+} while(0)
+
static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
@@ -619,6 +629,12 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
PR("TX-Pkts-All: ", tx_pkts_all);
PR("TX-Bytes-All: ", tx_bytes_all);

+ PRX("axq-qnum: ", axq_qnum);
+ PRX("axq-depth: ", axq_depth);
+ PRX("axq-stopped ", stopped);
+ PRX("tx-in-progress ", axq_tx_inprogress);
+ PRX("pending-frames ", pending_frames);
+
if (len > size)
len = size;

--
1.7.2.3


2011-01-10 07:12:36

by Ben Greear

[permalink] [raw]
Subject: [PATCH RESEND 09/11] ath9k: Try all queues when looking for next packet to send.

From: Ben Greear <[email protected]>

There can be multiple struct ath_atx_ac entries for each
txq, so if the first one doesn't have any packets to send
now, try the rest of them. This should help keep xmit
going when using multiple stations, especially with AMPDU
enabled.

Signed-off-by: Ben Greear <[email protected]>
---
:100644 100644 3aae523... 46481fb... M drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/ath9k/xmit.c | 97 ++++++++++++++++++++-------------
1 files changed, 59 insertions(+), 38 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 3aae523..46481fb 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -54,8 +54,8 @@ static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq,
static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
struct ath_txq *txq, struct list_head *bf_q,
struct ath_tx_status *ts, int txok, int sendbar);
-static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
- struct list_head *head);
+static int ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
+ struct list_head *head);
static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf, int len);
static void ath_tx_rc_status(struct ath_buf *bf, struct ath_tx_status *ts,
int nframes, int nbad, int txok, bool update_rc);
@@ -789,18 +789,21 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
#undef PADBYTES
}

-static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
- struct ath_atx_tid *tid)
+/* Return number of buffers set up for transmit. */
+static int ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
+ struct ath_atx_tid *tid)
{
struct ath_buf *bf;
enum ATH_AGGR_STATUS status;
struct ath_frame_info *fi;
struct list_head bf_q;
int aggr_len;
+ int cnt = 0;
+ int rv;

do {
if (list_empty(&tid->buf_q))
- return;
+ return cnt;

INIT_LIST_HEAD(&bf_q);

@@ -811,7 +814,7 @@ static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
* block-ack window is not open.
*/
if (list_empty(&bf_q))
- break;
+ return cnt;

bf = list_first_entry(&bf_q, struct ath_buf, list);
bf->bf_lastbf = list_entry(bf_q.prev, struct ath_buf, list);
@@ -823,7 +826,11 @@ static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
bf->bf_state.bf_type &= ~BUF_AGGR;
ath9k_hw_clr11n_aggr(sc->sc_ah, bf->bf_desc);
ath_buf_set_rate(sc, bf, fi->framelen);
- ath_tx_txqaddbuf(sc, txq, &bf_q);
+ rv = ath_tx_txqaddbuf(sc, txq, &bf_q);
+ if (rv > 0) {
+ TX_STAT_INC(txq->axq_qnum, a_aggr);
+ cnt += rv;
+ }
continue;
}

@@ -835,11 +842,15 @@ static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
/* anchor last desc of aggregate */
ath9k_hw_set11n_aggr_last(sc->sc_ah, bf->bf_lastbf->bf_desc);

- ath_tx_txqaddbuf(sc, txq, &bf_q);
- TX_STAT_INC(txq->axq_qnum, a_aggr);
+ rv = ath_tx_txqaddbuf(sc, txq, &bf_q);
+ if (rv > 0) {
+ TX_STAT_INC(txq->axq_qnum, a_aggr);
+ cnt += rv;
+ }

} while (txq->axq_ampdu_depth < ATH_AGGR_MIN_QDEPTH &&
status != ATH_AGGR_BAW_CLOSED);
+ return rv;
}

int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
@@ -1218,46 +1229,54 @@ void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq)
sc->tx.txqsetup &= ~(1<<txq->axq_qnum);
}

+/** For each axq_acq entry, for each tid, if we can transmit
+ * one, do so and break out.
+ */
void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
{
- struct ath_atx_ac *ac;
+ struct ath_atx_ac *ac, *ac_tmp, *last;
struct ath_atx_tid *tid;
+ bool did_one = false;

if (list_empty(&txq->axq_acq))
return;

- ac = list_first_entry(&txq->axq_acq, struct ath_atx_ac, list);
- list_del(&ac->list);
- ac->sched = false;
+ last = list_entry(txq->axq_acq.prev, struct ath_atx_ac, list);
+ list_for_each_entry_safe(ac, ac_tmp, &txq->axq_acq, list) {
+ list_del(&ac->list);
+ ac->sched = false;

- do {
- if (list_empty(&ac->tid_q))
- return;
+ while (!list_empty(&ac->tid_q)) {

- tid = list_first_entry(&ac->tid_q, struct ath_atx_tid, list);
- list_del(&tid->list);
- tid->sched = false;
+ tid = list_first_entry(&ac->tid_q, struct ath_atx_tid, list);
+ list_del(&tid->list);
+ tid->sched = false;

- if (tid->paused)
- continue;
+ if (tid->paused)
+ continue;

- ath_tx_sched_aggr(sc, txq, tid);
+ if (ath_tx_sched_aggr(sc, txq, tid) > 0)
+ did_one = true;

- /*
- * add tid to round-robin queue if more frames
- * are pending for the tid
- */
- if (!list_empty(&tid->buf_q))
- ath_tx_queue_tid(txq, tid);
+ /*
+ * add tid to round-robin queue if more frames
+ * are pending for the tid
+ */
+ if (!list_empty(&tid->buf_q))
+ ath_tx_queue_tid(txq, tid);

- break;
- } while (!list_empty(&ac->tid_q));
+ break;
+ }

- if (!list_empty(&ac->tid_q)) {
- if (!ac->sched) {
- ac->sched = true;
- list_add_tail(&ac->list, &txq->axq_acq);
+ if (!list_empty(&ac->tid_q)) {
+ if (!ac->sched) {
+ ac->sched = true;
+ list_add_tail(&ac->list, &txq->axq_acq);
+ }
}
+
+ if (did_one || (ac == last))
+ return;
}
}

@@ -1268,9 +1287,10 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
/*
* Insert a chain of ath_buf (descriptors) on a txq and
* assume the descriptors are already chained together by caller.
+ * Return number of buffers sent to DMA.
*/
-static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
- struct list_head *head)
+static int ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
+ struct list_head *head)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
@@ -1282,7 +1302,7 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
*/

if (list_empty(head))
- return;
+ return 0;

bf = list_first_entry(head, struct ath_buf, list);

@@ -1292,7 +1312,7 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
if (txq->axq_depth >= ATH_TXFIFO_DEPTH) {
list_splice_tail_init(head, &txq->txq_fifo_pending);
- return;
+ return 0;
}
if (!list_empty(&txq->txq_fifo[txq->txq_headidx]))
ath_dbg(common, ATH_DBG_XMIT,
@@ -1326,6 +1346,7 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq,
txq->axq_depth++;
if (bf_is_ampdu_not_probing(bf))
txq->axq_ampdu_depth++;
+ return 1;
}

static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
--
1.7.2.3


2011-01-10 07:12:18

by Ben Greear

[permalink] [raw]
Subject: [PATCH RESEND 07/11] ath9k: Keep track of stations for debugfs.

From: Ben Greear <[email protected]>

The stations hold the ath_node, which holds the tid
and other xmit logic structures. In order to debug
stuck xmit logic, we need a way to print out the tid
state for the stations.

Signed-off-by: Ben Greear <[email protected]>
---
:100644 100644 deda815... 3f5c513... M drivers/net/wireless/ath/ath9k/ath9k.h
:100644 100644 faf84e4... 650f00f... M drivers/net/wireless/ath/ath9k/debug.c
:100644 100644 23b2998... 59c01ca... M drivers/net/wireless/ath/ath9k/init.c
:100644 100644 16eb891... cb9940b... M drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/ath9k.h | 7 ++-
drivers/net/wireless/ath/ath9k/debug.c | 94 +++++++++++++++++++++++++++++++-
drivers/net/wireless/ath/ath9k/init.c | 4 ++
drivers/net/wireless/ath/ath9k/main.c | 13 +++++
4 files changed, 115 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index deda815..3f5c513 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -254,7 +254,10 @@ struct ath_atx_tid {
};

struct ath_node {
- struct ath_common *common;
+#ifdef CONFIG_ATH9K_DEBUGFS
+ struct list_head list; /* for sc->nodes */
+ struct ieee80211_sta *sta; /* station struct we're part of */
+#endif
struct ath_atx_tid tid[WME_NUM_TID];
struct ath_atx_ac ac[WME_NUM_AC];
u16 maxampdu;
@@ -624,6 +627,8 @@ struct ath_softc {

#ifdef CONFIG_ATH9K_DEBUGFS
struct ath9k_debug debug;
+ spinlock_t nodes_lock;
+ struct list_head nodes; /* basically, stations */
#endif
struct ath_beacon_config cur_beacon_conf;
struct delayed_work tx_complete_work;
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index faf84e4..650f00f 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -587,6 +587,8 @@ static const struct file_operations fops_wiphy = {
sc->debug.stats.txstats[WME_AC_BK].elem, \
sc->debug.stats.txstats[WME_AC_VI].elem, \
sc->debug.stats.txstats[WME_AC_VO].elem); \
+ if (len >= size) \
+ goto done; \
} while(0)

#define PRX(str, elem) \
@@ -597,6 +599,8 @@ do { \
(unsigned int)(sc->tx.txq[WME_AC_BK].elem), \
(unsigned int)(sc->tx.txq[WME_AC_VI].elem), \
(unsigned int)(sc->tx.txq[WME_AC_VO].elem)); \
+ if (len >= size) \
+ goto done; \
} while(0)

#define PRQLE(str, elem) \
@@ -607,6 +611,8 @@ do { \
list_empty(&sc->tx.txq[WME_AC_BK].elem), \
list_empty(&sc->tx.txq[WME_AC_VI].elem), \
list_empty(&sc->tx.txq[WME_AC_VO].elem)); \
+ if (len >= size) \
+ goto done; \
} while (0)

static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
@@ -614,7 +620,7 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
{
struct ath_softc *sc = file->private_data;
char *buf;
- unsigned int len = 0, size = 4000;
+ unsigned int len = 0, size = 8000;
int i;
ssize_t retval = 0;
char tmp[32];
@@ -623,7 +629,10 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
if (buf == NULL)
return -ENOMEM;

- len += sprintf(buf, "%30s %10s%10s%10s\n\n", "BE", "BK", "VI", "VO");
+ len += sprintf(buf, "Num-Tx-Queues: %i tx-queues-setup: 0x%x\n"
+ "%30s %10s%10s%10s\n\n",
+ ATH9K_NUM_TX_QUEUES, sc->tx.txqsetup,
+ "BE", "BK", "VI", "VO");

PR("MPDUs Queued: ", queued);
PR("MPDUs Completed: ", completed);
@@ -644,6 +653,14 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
PR("hw-put-tx-buf: ", puttxbuf);
PR("hw-tx-start: ", txstart);
PR("hw-tx-proc-desc: ", txprocdesc);
+ len += snprintf(buf + len, size - len,
+ "%s%11p%11p%10p%10p\n", "txq-memory-address:",
+ &(sc->tx.txq[WME_AC_BE]),
+ &(sc->tx.txq[WME_AC_BK]),
+ &(sc->tx.txq[WME_AC_VI]),
+ &(sc->tx.txq[WME_AC_VO]));
+ if (len >= size)
+ goto done;

PRX("axq-qnum: ", axq_qnum);
PRX("axq-depth: ", axq_depth);
@@ -661,6 +678,68 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
snprintf(tmp, sizeof(tmp) - 1, "txq_fifo[%i] empty: ", i);
PRQLE(tmp, txq_fifo[i]);
}
+
+done:
+ if (len > size)
+ len = size;
+
+ retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ kfree(buf);
+
+ return retval;
+}
+
+static ssize_t read_file_stations(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ath_softc *sc = file->private_data;
+ char *buf;
+ unsigned int len = 0, size = 64000;
+ struct ath_node *an = NULL;
+ ssize_t retval = 0;
+ int q;
+
+ buf = kzalloc(size, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ len += snprintf(buf + len, size - len,
+ "Stations:\n"
+ " tid: addr sched paused buf_q-empty an ac\n"
+ " ac: addr sched tid_q-empty txq\n");
+
+ spin_lock(&sc->nodes_lock);
+ list_for_each_entry(an, &sc->nodes, list) {
+ len += snprintf(buf + len, size - len,
+ "%pM\n", an->sta->addr);
+ if (len >= size)
+ goto done;
+
+ for (q = 0; q < WME_NUM_TID; q++) {
+ struct ath_atx_tid *tid = &(an->tid[q]);
+ len += snprintf(buf + len, size - len,
+ " tid: %p %s %s %i %p %p\n",
+ tid, tid->sched ? "sched" : "idle",
+ tid->paused ? "paused" : "running",
+ list_empty(&tid->buf_q),
+ tid->an, tid->ac);
+ if (len >= size)
+ goto done;
+ }
+
+ for (q = 0; q < WME_NUM_AC; q++) {
+ struct ath_atx_ac *ac = &(an->ac[q]);
+ len += snprintf(buf + len, size - len,
+ " ac: %p %s %i %p\n",
+ ac, ac->sched ? "sched" : "idle",
+ list_empty(&ac->tid_q), ac->txq);
+ if (len >= size)
+ goto done;
+ }
+ }
+
+done:
+ spin_unlock(&sc->nodes_lock);
if (len > size)
len = size;

@@ -708,6 +787,13 @@ static const struct file_operations fops_xmit = {
.llseek = default_llseek,
};

+static const struct file_operations fops_stations = {
+ .read = read_file_stations,
+ .open = ath9k_debugfs_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
static ssize_t read_file_recv(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
@@ -945,6 +1031,10 @@ int ath9k_init_debug(struct ath_hw *ah)
sc, &fops_xmit))
goto err;

+ if (!debugfs_create_file("stations", S_IRUSR, sc->debug.debugfs_phy,
+ sc, &fops_stations))
+ goto err;
+
if (!debugfs_create_file("recv", S_IRUSR, sc->debug.debugfs_phy,
sc, &fops_recv))
goto err;
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 23b2998..59c01ca 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -559,6 +559,10 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid,
spin_lock_init(&sc->sc_serial_rw);
spin_lock_init(&sc->sc_pm_lock);
mutex_init(&sc->mutex);
+#ifdef CONFIG_ATH9K_DEBUGFS
+ spin_lock_init(&sc->nodes_lock);
+ INIT_LIST_HEAD(&sc->nodes);
+#endif
tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
tasklet_init(&sc->bcon_tasklet, ath_beacon_tasklet,
(unsigned long)sc);
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 16eb891..cb9940b 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -545,6 +545,12 @@ static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta)
struct ath_hw *ah = sc->sc_ah;
an = (struct ath_node *)sta->drv_priv;

+#ifdef CONFIG_ATH9K_DEBUGFS
+ spin_lock(&sc->nodes_lock);
+ list_add(&an->list, &sc->nodes);
+ spin_unlock(&sc->nodes_lock);
+ an->sta = sta;
+#endif
if ((ah->caps.hw_caps) & ATH9K_HW_CAP_APM)
sc->sc_flags |= SC_OP_ENABLE_APM;

@@ -560,6 +566,13 @@ static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta)
{
struct ath_node *an = (struct ath_node *)sta->drv_priv;

+#ifdef CONFIG_ATH9K_DEBUGFS
+ spin_lock(&sc->nodes_lock);
+ list_del(&an->list);
+ spin_unlock(&sc->nodes_lock);
+ an->sta = NULL;
+#endif
+
if (sc->sc_flags & SC_OP_TXAGGR)
ath_tx_node_cleanup(sc, an);
}
--
1.7.2.3


2011-01-10 07:13:44

by Ben Greear

[permalink] [raw]
Subject: [PATCH RESEND 06/11] ath9k: Add counters to distinquish AMPDU enqueues.

From: Ben Greear <[email protected]>

Show counters for pkts sent directly to hardware and
those queued in software.

Signed-off-by: Ben Greear <[email protected]>
---
:100644 100644 577bc5a... faf84e4... M drivers/net/wireless/ath/ath9k/debug.c
:100644 100644 cd2db3f... 980c9fa... M drivers/net/wireless/ath/ath9k/debug.h
:100644 100644 2248e47... 3aae523... M drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/ath9k/debug.c | 3 ++-
drivers/net/wireless/ath/ath9k/debug.h | 6 ++++--
drivers/net/wireless/ath/ath9k/xmit.c | 3 ++-
3 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index 577bc5a..faf84e4 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -628,7 +628,8 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
PR("MPDUs Queued: ", queued);
PR("MPDUs Completed: ", completed);
PR("Aggregates: ", a_aggr);
- PR("AMPDUs Queued: ", a_queued);
+ PR("AMPDUs Queued HW:", a_queued_hw);
+ PR("AMPDUs Queued SW:", a_queued_sw);
PR("AMPDUs Completed:", a_completed);
PR("AMPDUs Retried: ", a_retries);
PR("AMPDUs XRetried: ", a_xretries);
diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h
index cd2db3f..980c9fa 100644
--- a/drivers/net/wireless/ath/ath9k/debug.h
+++ b/drivers/net/wireless/ath/ath9k/debug.h
@@ -89,7 +89,8 @@ struct ath_interrupt_stats {
* @queued: Total MPDUs (non-aggr) queued
* @completed: Total MPDUs (non-aggr) completed
* @a_aggr: Total no. of aggregates queued
- * @a_queued: Total AMPDUs queued
+ * @a_queued_hw: Total AMPDUs queued to hardware
+ * @a_queued_sw: Total AMPDUs queued to software queues
* @a_completed: Total AMPDUs completed
* @a_retries: No. of AMPDUs retried (SW)
* @a_xretries: No. of AMPDUs dropped due to xretries
@@ -112,7 +113,8 @@ struct ath_tx_stats {
u32 queued;
u32 completed;
u32 a_aggr;
- u32 a_queued;
+ u32 a_queued_hw;
+ u32 a_queued_sw;
u32 a_completed;
u32 a_retries;
u32 a_xretries;
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 2248e47..3aae523 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1335,7 +1335,6 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
struct list_head bf_head;

bf->bf_state.bf_type |= BUF_AMPDU;
- TX_STAT_INC(txctl->txq->axq_qnum, a_queued);

/*
* Do not queue to h/w when any of the following conditions is true:
@@ -1351,6 +1350,7 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
* Add this frame to software queue for scheduling later
* for aggregation.
*/
+ TX_STAT_INC(txctl->txq->axq_qnum, a_queued_sw);
list_add_tail(&bf->list, &tid->buf_q);
ath_tx_queue_tid(txctl->txq, tid);
return;
@@ -1364,6 +1364,7 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid,
ath_tx_addto_baw(sc, tid, fi->seqno);

/* Queue to h/w without aggregation */
+ TX_STAT_INC(txctl->txq->axq_qnum, a_queued_hw);
bf->bf_lastbf = bf;
ath_buf_set_rate(sc, bf, fi->framelen);
ath_tx_txqaddbuf(sc, txctl->txq, &bf_head);
--
1.7.2.3


2011-01-10 07:12:33

by Ben Greear

[permalink] [raw]
Subject: [PATCH RESEND 03/11] ath9k: Add more information to debugfs xmit file.

From: Ben Greear <[email protected]>

Should help debug strange tx lockup type issues.

Signed-off-by: Ben Greear <[email protected]>
---
:100644 100644 5075faa... 577bc5a... M drivers/net/wireless/ath/ath9k/debug.c
:100644 100644 1e5078b... cd2db3f... M drivers/net/wireless/ath/ath9k/debug.h
:100644 100644 180170d... 99ce6f1... M drivers/net/wireless/ath/ath9k/mac.c
:100644 100644 332d1fe... c769037... M drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/ath9k/debug.c | 29 +++++++++++++++++++++++++++--
drivers/net/wireless/ath/ath9k/debug.h | 6 ++++++
drivers/net/wireless/ath/ath9k/mac.c | 8 ++++++++
drivers/net/wireless/ath/ath9k/xmit.c | 1 +
4 files changed, 42 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index 5075faa..577bc5a 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -599,13 +599,25 @@ do { \
(unsigned int)(sc->tx.txq[WME_AC_VO].elem)); \
} while(0)

+#define PRQLE(str, elem) \
+do { \
+ len += snprintf(buf + len, size - len, \
+ "%s%13i%11i%10i%10i\n", str, \
+ list_empty(&sc->tx.txq[WME_AC_BE].elem), \
+ list_empty(&sc->tx.txq[WME_AC_BK].elem), \
+ list_empty(&sc->tx.txq[WME_AC_VI].elem), \
+ list_empty(&sc->tx.txq[WME_AC_VO].elem)); \
+} while (0)
+
static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ath_softc *sc = file->private_data;
char *buf;
- unsigned int len = 0, size = 2048;
+ unsigned int len = 0, size = 4000;
+ int i;
ssize_t retval = 0;
+ char tmp[32];

buf = kzalloc(size, GFP_KERNEL);
if (buf == NULL)
@@ -628,13 +640,26 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
PR("DELIM Underrun: ", delim_underrun);
PR("TX-Pkts-All: ", tx_pkts_all);
PR("TX-Bytes-All: ", tx_bytes_all);
+ PR("hw-put-tx-buf: ", puttxbuf);
+ PR("hw-tx-start: ", txstart);
+ PR("hw-tx-proc-desc: ", txprocdesc);

PRX("axq-qnum: ", axq_qnum);
PRX("axq-depth: ", axq_depth);
+ PRX("axq-ampdu_depth: ", axq_ampdu_depth);
PRX("axq-stopped ", stopped);
PRX("tx-in-progress ", axq_tx_inprogress);
PRX("pending-frames ", pending_frames);
-
+ PRX("txq_headidx: ", txq_headidx);
+ PRX("txq_tailidx: ", txq_headidx);
+
+ PRQLE("axq_q empty: ", axq_q);
+ PRQLE("axq_acq empty: ", axq_acq);
+ PRQLE("txq_fifo_pending: ", txq_fifo_pending);
+ for (i = 0; i < ATH_TXFIFO_DEPTH; i++) {
+ snprintf(tmp, sizeof(tmp) - 1, "txq_fifo[%i] empty: ", i);
+ PRQLE(tmp, txq_fifo[i]);
+ }
if (len > size)
len = size;

diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h
index 1e5078b..cd2db3f 100644
--- a/drivers/net/wireless/ath/ath9k/debug.h
+++ b/drivers/net/wireless/ath/ath9k/debug.h
@@ -102,6 +102,9 @@ struct ath_interrupt_stats {
* @desc_cfg_err: Descriptor configuration errors
* @data_urn: TX data underrun errors
* @delim_urn: TX delimiter underrun errors
+ * @puttxbuf: Number of times hardware was given txbuf to write.
+ * @txstart: Number of times hardware was told to start tx.
+ * @txprocdesc: Number of times tx descriptor was processed
*/
struct ath_tx_stats {
u32 tx_pkts_all;
@@ -119,6 +122,9 @@ struct ath_tx_stats {
u32 desc_cfg_err;
u32 data_underrun;
u32 delim_underrun;
+ u32 puttxbuf;
+ u32 txstart;
+ u32 txprocdesc;
};

/**
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index 180170d..99ce6f1 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -16,6 +16,8 @@

#include "hw.h"
#include "hw-ops.h"
+#include "debug.h"
+#include "ath9k.h"

static void ath9k_hw_set_txq_interrupts(struct ath_hw *ah,
struct ath9k_tx_queue_info *qi)
@@ -50,12 +52,18 @@ EXPORT_SYMBOL(ath9k_hw_gettxbuf);

void ath9k_hw_puttxbuf(struct ath_hw *ah, u32 q, u32 txdp)
{
+ struct ath_wiphy *aphy = ah->hw->priv;
+ struct ath_softc *sc = aphy->sc;
+ TX_STAT_INC(q, puttxbuf);
REG_WRITE(ah, AR_QTXDP(q), txdp);
}
EXPORT_SYMBOL(ath9k_hw_puttxbuf);

void ath9k_hw_txstart(struct ath_hw *ah, u32 q)
{
+ struct ath_wiphy *aphy = ah->hw->priv;
+ struct ath_softc *sc = aphy->sc;
+ TX_STAT_INC(q, txstart);
ath_dbg(ath9k_hw_common(ah), ATH_DBG_QUEUE,
"Enable TXE on queue: %u\n", q);
REG_WRITE(ah, AR_Q_TXE, 1 << q);
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 332d1fe..c769037 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -2033,6 +2033,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
spin_unlock_bh(&txq->axq_lock);
break;
}
+ TX_STAT_INC(txq->axq_qnum, txprocdesc);

/*
* Remove ath_buf's of the same transmit unit from txq,
--
1.7.2.3


2011-01-10 15:19:31

by Felix Fietkau

[permalink] [raw]
Subject: Re: [PATCH RESEND 11/11] ath9k: Implement rx copy-break.

On 2011-01-10 12:11 AM, [email protected] wrote:
> From: Ben Greear<[email protected]>
>
> This saves us constantly allocating large, multi-page
> skbs. It should fix the order-1 allocation errors reported,
> and in a 60-vif scenario, this significantly decreases CPU
> utilization, and latency, and increases bandwidth.
>
> Signed-off-by: Ben Greear<[email protected]>
I think we should use Jouni's approach instead of this patch, as it also
fixes order-1 allocations, but with less data copying involved.

- Felix