netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] ath9k_htc: add adaptive usb receive flow control to repair soft lockup with monitor mode
@ 2015-02-09 18:31 Yuwei Zheng
  2015-02-11  9:20 ` Kalle Valo
  0 siblings, 1 reply; 9+ messages in thread
From: Yuwei Zheng @ 2015-02-09 18:31 UTC (permalink / raw)
  To: linux-kernel, ath9k-devel, linux-wireless, kvalo, ath9k-devel
  Cc: netdev, zhengyuwei, Yuwei Zheng

The ath9k_hif_usb_rx_cb function excute on  the interrupt context, and ath9k_rx_tasklet excute
on the soft irq context. In other words, the ath9k_hif_usb_rx_cb have more chance to excute than
ath9k_rx_tasklet.  So in the worst condition,  the rx.rxbuf receive list is always full,
and the do {}while(true) loop will not be break. The kernel get a soft lockup panic. 
 
[59011.007210] BUG: soft lockup - CPU#0 stuck for 23s!
[kworker/0:0:30609]
[59011.030560] BUG: scheduling while atomic: kworker/0:0/30609/0x40010100
[59013.804486] BUG: scheduling while atomic: kworker/0:0/30609/0x40010100
[59013.858522] Kernel panic - not syncing: softlockup: hung tasks
 
[59014.038891] Exception stack(0xdf4bbc38 to 0xdf4bbc80)
[59014.046834] bc20:                                                       de57b950 60000113
[59014.059579] bc40: 00000000 bb32bb32 60000113 de57b948 de57b500 dc7bb440 df4bbcd0 00000000
[59014.072337] bc60: de57b950 60000113 df4bbcd0 df4bbc80 c04c259d c04c25a0 60000133 ffffffff
[59014.085233] [<c04c28db>] (__irq_svc+0x3b/0x5c) from [<c04c25a0>] (_raw_spin_unlock_irqrestore+0xc/0x10)
[59014.100437] [<c04c25a0>] (_raw_spin_unlock_irqrestore+0xc/0x10) from [<bf9c2089>] (ath9k_rx_tasklet+0x290/0x490 [ath9k_htc])
[59014.118267] [<bf9c2089>] (ath9k_rx_tasklet+0x290/0x490 [ath9k_htc]) from [<c0036d23>] (tasklet_action+0x3b/0x98)
[59014.134132] [<c0036d23>] (tasklet_action+0x3b/0x98) from [<c0036709>] (__do_softirq+0x99/0x16c)
[59014.147784] [<c0036709>] (__do_softirq+0x99/0x16c) from [<c00369f7>] (irq_exit+0x5b/0x5c)
[59014.160653] [<c00369f7>] (irq_exit+0x5b/0x5c) from [<c000cfc3>] (handle_IRQ+0x37/0x78)
[59014.173124] [<c000cfc3>] (handle_IRQ+0x37/0x78) from [<c00085df>] (omap3_intc_handle_irq+0x5f/0x68)
[59014.187225] [<c00085df>] (omap3_intc_handle_irq+0x5f/0x68) from [<c04c28db>](__irq_svc+0x3b/0x5c)
 
This bug can be see with low performance board, such as uniprocessor beagle bone board. Add some debug message in the ath9k_hif_usb_rx_cb
function may trigger this bug quickly.
 
Signed-off-by: Yuwei Zheng <yuweizheng@139.com>
---
 drivers/net/wireless/ath/ath9k/hif_usb.c       | 78 +++++++++++++++++++++++---
 drivers/net/wireless/ath/ath9k/hif_usb.h       | 13 +++++
 drivers/net/wireless/ath/ath9k/htc.h           | 19 +++++++
 drivers/net/wireless/ath/ath9k/htc_drv_debug.c | 53 +++++++++++++++++
 drivers/net/wireless/ath/ath9k/htc_drv_txrx.c  | 58 +++++++++++++++++++
 5 files changed, 214 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c
index 8e7153b..2e73e19 100644
--- a/drivers/net/wireless/ath/ath9k/hif_usb.c
+++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
@@ -640,6 +640,7 @@ static void ath9k_hif_usb_rx_cb(struct urb *urb)
 	struct hif_device_usb *hif_dev =
 		usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
 	int ret;
+	int delay;
 
 	if (!skb)
 		return;
@@ -658,7 +659,6 @@ static void ath9k_hif_usb_rx_cb(struct urb *urb)
 	default:
 		goto resubmit;
 	}
-
 	if (likely(urb->actual_length != 0)) {
 		skb_put(skb, urb->actual_length);
 		ath9k_hif_usb_rx_stream(hif_dev, skb);
@@ -667,12 +667,23 @@ static void ath9k_hif_usb_rx_cb(struct urb *urb)
 resubmit:
 	skb_reset_tail_pointer(skb);
 	skb_trim(skb, 0);
-
-	usb_anchor_urb(urb, &hif_dev->rx_submitted);
-	ret = usb_submit_urb(urb, GFP_ATOMIC);
-	if (ret) {
-		usb_unanchor_urb(urb);
-		goto free;
+	spin_lock(&hif_dev->aurfc_lock);
+	/* submit the urb more slowly for flow control */
+	if (atomic_read(&hif_dev->aurfc_submit_delay) > 0 &&
+	    hif_dev->aurfc_active == 1) {
+		usb_anchor_urb(urb, &hif_dev->rx_delayed_submitted);
+		delay = atomic_read(&hif_dev->aurfc_submit_delay);
+		schedule_delayed_work(&hif_dev->aurfc_delayed_work,
+				      msecs_to_jiffies(delay));
+		spin_unlock(&hif_dev->aurfc_lock);
+	} else {
+		spin_unlock(&hif_dev->aurfc_lock);
+		usb_anchor_urb(urb, &hif_dev->rx_submitted);
+		ret = usb_submit_urb(urb, GFP_ATOMIC);
+		if (ret) {
+			usb_unanchor_urb(urb);
+			goto free;
+		}
 	}
 
 	return;
@@ -818,9 +829,53 @@ err:
 	return -ENOMEM;
 }
 
+static void aurfc_submit_handler(struct work_struct *work)
+{
+	struct hif_device_usb *hif_dev =
+		container_of(work,
+			     struct hif_device_usb,
+			     aurfc_delayed_work.work);
+	struct urb *urb = NULL;
+	struct sk_buff *skb = NULL;
+	int ret;
+	int loop_times = 0;
+
+	AURFC_STAT_INC(aurfc_called);
+	while (true) {
+		loop_times++;
+		if (loop_times > MAX_RX_URB_NUM)
+			atomic_add(AURFC_STEP,
+				   &hif_dev->aurfc_submit_delay);
+
+		urb = usb_get_from_anchor(
+				&hif_dev->rx_delayed_submitted);
+		if (urb) {
+			skb = (struct sk_buff *)urb->context;
+			ret = usb_submit_urb(urb, GFP_KERNEL);
+			if (ret != 0) {
+				usb_unanchor_urb(urb);
+				dev_kfree_skb_any(skb);
+				urb->context = NULL;
+			}
+		} else {
+			break;
+		}
+	}
+}
+
 static void ath9k_hif_usb_dealloc_rx_urbs(struct hif_device_usb *hif_dev)
 {
+	unsigned long flags;
+
+	spin_lock_irqsave(&hif_dev->aurfc_lock, flags);
+	hif_dev->aurfc_active = 0;
+	/* excute the last queued work immediately */
+	mod_delayed_work(system_wq, &hif_dev->aurfc_delayed_work, 0);
+	spin_unlock_irqrestore(&hif_dev->aurfc_lock, flags);
+	/* wait the last work finish, otherwise kill urbs may deadlock */
+	flush_delayed_work(&hif_dev->aurfc_delayed_work);
 	usb_kill_anchored_urbs(&hif_dev->rx_submitted);
+	usb_kill_anchored_urbs(&hif_dev->rx_delayed_submitted);
 }
 
 static int ath9k_hif_usb_alloc_rx_urbs(struct hif_device_usb *hif_dev)
@@ -830,8 +885,17 @@ static int ath9k_hif_usb_alloc_rx_urbs(struct hif_device_usb *hif_dev)
 	int i, ret;
 
 	init_usb_anchor(&hif_dev->rx_submitted);
+	init_usb_anchor(&hif_dev->rx_delayed_submitted);
+
 	spin_lock_init(&hif_dev->rx_lock);
 
+	/* add for adaptive usb receive flow control*/
+	atomic_set(&hif_dev->aurfc_submit_delay, 0);
+	INIT_DELAYED_WORK(&hif_dev->aurfc_delayed_work,
+			  aurfc_submit_handler);
+	spin_lock_init(&hif_dev->aurfc_lock);
+	hif_dev->aurfc_active = 1;
+
 	for (i = 0; i < MAX_RX_URB_NUM; i++) {
 
 		/* Allocate URB */
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.h b/drivers/net/wireless/ath/ath9k/hif_usb.h
index 51496e7..2ff59be 100644
--- a/drivers/net/wireless/ath/ath9k/hif_usb.h
+++ b/drivers/net/wireless/ath/ath9k/hif_usb.h
@@ -41,6 +41,7 @@
 #define MAX_RX_URB_NUM  8
 #define MAX_RX_BUF_SIZE 16384
 #define MAX_PKT_NUM_IN_TRANSFER 10
+#define AURFC_STEP 70  /* millisecond */
 
 #define MAX_REG_OUT_URB_NUM  1
 #define MAX_REG_IN_URB_NUM   64
@@ -98,9 +99,21 @@ struct hif_device_usb {
 	struct hif_usb_tx tx;
 	struct usb_anchor regout_submitted;
 	struct usb_anchor rx_submitted;
+	/* anchor delayed urb */
+	struct usb_anchor rx_delayed_submitted;
 	struct usb_anchor reg_in_submitted;
 	struct usb_anchor mgmt_submitted;
 	struct sk_buff *remain_skb;
+
+	/* adaptive usb receive flow control */
+	struct delayed_work aurfc_delayed_work;
+	/* to protect the delayed work */
+	spinlock_t aurfc_lock;
+	/* urb submit delay, in millisecond */
+	atomic_t  aurfc_submit_delay;
+	/* set to 1, if the urb can be delayed submit */
+	int aurfc_active;
+
 	const char *fw_name;
 	int rx_remain_len;
 	int rx_pkt_len;
diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
index 9dde265..1586bd2 100644
--- a/drivers/net/wireless/ath/ath9k/htc.h
+++ b/drivers/net/wireless/ath/ath9k/htc.h
@@ -331,6 +331,13 @@ static inline struct ath9k_htc_tx_ctl *HTC_SKB_CB(struct sk_buff *skb)
 
 #define TX_QSTAT_INC(q) (priv->debug.tx_stats.queue_stats[q]++)
 
+#define AURFC_STAT_INC(c) \
+	(hif_dev->htc_handle->drv_priv->debug.aurfc_stats.c++)
+#define AURFC_STAT_ADD(c, a) \
+	(hif_dev->htc_handle->drv_priv->debug.aurfc_stats.c += a)
+#define AURFC_STAT_SET(c, a) \
+	(hif_dev->htc_handle->drv_priv->debug.aurfc_stats.c = a)
+
 void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv,
 			   struct ath_rx_status *rs);
 
@@ -352,11 +359,20 @@ struct ath_skbrx_stats {
 	u32 skb_dropped;
 };
 
+struct ath_aurfc_stats {
+	u32 aurfc_highwater;
+	u32 aurfc_lowwater;
+	u32 aurfc_wm_triggered;
+	u32 aurfc_submit_delay;
+	u32 aurfc_called;
+};
+
 struct ath9k_debug {
 	struct dentry *debugfs_phy;
 	struct ath_tx_stats tx_stats;
 	struct ath_rx_stats rx_stats;
 	struct ath_skbrx_stats skbrx_stats;
+	struct ath_aurfc_stats aurfc_stats;
 };
 
 void ath9k_htc_get_et_strings(struct ieee80211_hw *hw,
@@ -377,6 +393,9 @@ void ath9k_htc_get_et_stats(struct ieee80211_hw *hw,
 
 #define TX_QSTAT_INC(c) do { } while (0)
 
+#define AURFC_STAT_INC(c) do {} while (0)
+#define AURFC_STAT_ADD(c, a) do {} while (0)
+#define AURFC_STAT_SET(c, a) do {} while (0)
 static inline void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv,
 					 struct ath_rx_status *rs)
 {
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
index 8cef1ed..a6be9be 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
@@ -286,6 +286,54 @@ static const struct file_operations fops_skb_rx = {
 	.llseek = default_llseek,
 };
 
+static ssize_t read_file_aurfc(struct file *file,
+			       char __user *user_buf,
+			       size_t count, loff_t *ppos)
+{
+	struct ath9k_htc_priv *priv = file->private_data;
+	char *buf;
+	unsigned int len = 0, size = 1500;
+	ssize_t retval = 0;
+
+	buf = kzalloc(size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	len += scnprintf(buf + len, size - len,
+			"%20s : %10u\n", "High watermark",
+			priv->debug.aurfc_stats.aurfc_highwater);
+	len += scnprintf(buf + len, size - len,
+			"%20s : %10u\n", "Low watermark",
+			priv->debug.aurfc_stats.aurfc_lowwater);
+
+	len += scnprintf(buf + len, size - len,
+			"%20s : %10u\n", "WM triggered",
+			priv->debug.aurfc_stats.aurfc_wm_triggered);
+
+	len += scnprintf(buf + len, size - len,
+			"%20s : %10u\n", "Handler called",
+			priv->debug.aurfc_stats.aurfc_called);
+
+	len += scnprintf(buf + len, size - len,
+			"%20s : %10u\n", "Submit delay",
+			priv->debug.aurfc_stats.aurfc_submit_delay);
+	if (len > size)
+		len = size;
+
+	retval = simple_read_from_buffer(user_buf, count,
+					 ppos, buf, len);
+	kfree(buf);
+
+	return retval;
+}
+
+static const struct file_operations fops_aurfc = {
+	.read = read_file_aurfc,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
 static ssize_t read_file_slot(struct file *file, char __user *user_buf,
 			      size_t count, loff_t *ppos)
 {
@@ -518,7 +566,12 @@ int ath9k_htc_init_debug(struct ath_hw *ah)
 	debugfs_create_file("skb_rx", S_IRUSR, priv->debug.debugfs_phy,
 			    priv, &fops_skb_rx);
 
+	debugfs_create_file("aurfc_stats", S_IRUSR,
+			    priv->debug.debugfs_phy,
+			    priv, &fops_aurfc);
+
 	ath9k_cmn_debug_recv(priv->debug.debugfs_phy, &priv->debug.rx_stats);
+
 	ath9k_cmn_debug_phy_err(priv->debug.debugfs_phy, &priv->debug.rx_stats);
 
 	debugfs_create_file("slot", S_IRUSR, priv->debug.debugfs_phy,
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
index a0f58e2..1c8ebc5 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
@@ -1061,7 +1061,31 @@ void ath9k_rx_tasklet(unsigned long data)
 	unsigned long flags;
 	struct ieee80211_hdr *hdr;
 
+	/* add for adaptive usb receive flow control*/
+	int looptimes = 0;
+	int highwatermark = ATH9K_HTC_RXBUF*3/4;
+	int lowwatermark = ATH9K_HTC_RXBUF/32;
+	unsigned int delay = 0;
+
+	struct htc_target *htc = priv->htc;
+	struct hif_device_usb *hif_dev = htc->hif_dev;
+
+	AURFC_STAT_SET(aurfc_highwater, highwatermark);
+	AURFC_STAT_SET(aurfc_lowwater, lowwatermark);
+
 	do {
+		looptimes++;
+		/* when trigger high wartermark, tell the
+		 * urb callback to submit more slowlly.
+		 */
+		if (looptimes > highwatermark) {
+			delay = looptimes*AURFC_STEP;
+			atomic_set(&hif_dev->aurfc_submit_delay,
+				   delay);
+			AURFC_STAT_INC(aurfc_wm_triggered);
+			AURFC_STAT_SET(aurfc_submit_delay, delay);
+		}
+
 		spin_lock_irqsave(&priv->rx.rxbuflock, flags);
 		list_for_each_entry(tmp_buf, &priv->rx.rxbuf, list) {
 			if (tmp_buf->in_process) {
@@ -1072,6 +1096,29 @@ void ath9k_rx_tasklet(unsigned long data)
 
 		if (rxbuf == NULL) {
 			spin_unlock_irqrestore(&priv->rx.rxbuflock, flags);
+			spin_lock_irqsave(&hif_dev->aurfc_lock,
+					  flags);
+			/* the rxbuf list is empty now, the
+			 * queued work could be scheduled
+			 * to submit urbs immediately.
+			 */
+			if (atomic_read(
+				&hif_dev->aurfc_submit_delay) > 0 &&
+			    hif_dev->aurfc_active > 0)
+				mod_delayed_work(system_wq,
+					&hif_dev->aurfc_delayed_work,
+						 0);
+			spin_unlock_irqrestore(&hif_dev->aurfc_lock,
+					       flags);
+			/* reset submit delay to guaranteed
+			 * usb receive performance.
+			 */
+			if (looptimes < lowwatermark) {
+				atomic_set(&hif_dev->aurfc_submit_delay
+					   , 0);
+				AURFC_STAT_SET(aurfc_submit_delay,
+					       0);
+			}
 			break;
 		}
 
@@ -1114,6 +1161,10 @@ void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb,
 	struct ath_common *common = ath9k_hw_common(ah);
 	struct ath9k_htc_rxbuf *rxbuf = NULL, *tmp_buf = NULL;
 
+	struct htc_target *htc = priv->htc;
+	struct hif_device_usb *hif_dev = htc->hif_dev;
+	int delay = ATH9K_HTC_RXBUF * AURFC_STEP;
+
 	spin_lock(&priv->rx.rxbuflock);
 	list_for_each_entry(tmp_buf, &priv->rx.rxbuf, list) {
 		if (!tmp_buf->in_process) {
@@ -1124,6 +1175,13 @@ void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb,
 	spin_unlock(&priv->rx.rxbuflock);
 
 	if (rxbuf == NULL) {
+		/* The rxbuf list is full now, tell the urb callback
+		 * to submit more slowly. Otherwise, the soft lockup
+		 * may be triggerd immediately.
+		 */
+		atomic_set(&hif_dev->aurfc_submit_delay, delay);
+		AURFC_STAT_INC(aurfc_wm_triggered);
+		AURFC_STAT_SET(aurfc_submit_delay, delay);
 		ath_dbg(common, ANY, "No free RX buffer\n");
 		goto err;
 	}
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH] ath9k_htc: add adaptive usb receive flow control to repair soft lockup with monitor mode
  2015-02-11  9:20 ` Kalle Valo
@ 2015-02-10  0:36   ` Yuwei Zheng
  0 siblings, 0 replies; 9+ messages in thread
From: Yuwei Zheng @ 2015-02-10  0:36 UTC (permalink / raw)
  To: Kalle Valo
  Cc: linux-kernel, ath9k-devel, linux-wireless, ath9k-devel, netdev,
	zhengyuwei

On 三, 2015-02-11 at 11:20 +0200, Kalle Valo wrote:
> Yuwei Zheng <yuweizheng@139.com> writes:
> 
> > The ath9k_hif_usb_rx_cb function excute on  the interrupt context, and ath9k_rx_tasklet excute
> > on the soft irq context. In other words, the ath9k_hif_usb_rx_cb have more chance to excute than
> > ath9k_rx_tasklet.  So in the worst condition,  the rx.rxbuf receive list is always full,
> > and the do {}while(true) loop will not be break. The kernel get a soft lockup panic. 
> >  
> > [59011.007210] BUG: soft lockup - CPU#0 stuck for 23s!
> > [kworker/0:0:30609]
> > [59011.030560] BUG: scheduling while atomic: kworker/0:0/30609/0x40010100
> > [59013.804486] BUG: scheduling while atomic: kworker/0:0/30609/0x40010100
> > [59013.858522] Kernel panic - not syncing: softlockup: hung tasks
> >  
> > [59014.038891] Exception stack(0xdf4bbc38 to 0xdf4bbc80)
> > [59014.046834] bc20:                                                       de57b950 60000113
> > [59014.059579] bc40: 00000000 bb32bb32 60000113 de57b948 de57b500 dc7bb440 df4bbcd0 00000000
> > [59014.072337] bc60: de57b950 60000113 df4bbcd0 df4bbc80 c04c259d c04c25a0 60000133 ffffffff
> > [59014.085233] [<c04c28db>] (__irq_svc+0x3b/0x5c) from [<c04c25a0>] (_raw_spin_unlock_irqrestore+0xc/0x10)
> > [59014.100437] [<c04c25a0>] (_raw_spin_unlock_irqrestore+0xc/0x10) from [<bf9c2089>] (ath9k_rx_tasklet+0x290/0x490 [ath9k_htc])
> > [59014.118267] [<bf9c2089>] (ath9k_rx_tasklet+0x290/0x490 [ath9k_htc]) from [<c0036d23>] (tasklet_action+0x3b/0x98)
> > [59014.134132] [<c0036d23>] (tasklet_action+0x3b/0x98) from [<c0036709>] (__do_softirq+0x99/0x16c)
> > [59014.147784] [<c0036709>] (__do_softirq+0x99/0x16c) from [<c00369f7>] (irq_exit+0x5b/0x5c)
> > [59014.160653] [<c00369f7>] (irq_exit+0x5b/0x5c) from [<c000cfc3>] (handle_IRQ+0x37/0x78)
> > [59014.173124] [<c000cfc3>] (handle_IRQ+0x37/0x78) from [<c00085df>] (omap3_intc_handle_irq+0x5f/0x68)
> > [59014.187225] [<c00085df>] (omap3_intc_handle_irq+0x5f/0x68) from [<c04c28db>](__irq_svc+0x3b/0x5c)
> >  
> > This bug can be see with low performance board, such as uniprocessor beagle bone board. Add some debug message in the ath9k_hif_usb_rx_cb
> > function may trigger this bug quickly.
> >  
> > Signed-off-by: Yuwei Zheng <yuweizheng@139.com>
> 
> The word wrapping is still wrong, please limit the line length to 72
> chars or so. This is the second time I'm mentioning that. Also add v2,
> v3 and so on when you send new versions of the patch, otherwise I will
> not know what version I should use. And even better if you add a
> changelog after "---" line.
> 
> Documentation/SubmittingPatches should tell you all this.
> 

I have modified and resubmited as you suggest.
Thanks.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] ath9k_htc: add adaptive usb receive flow control to repair soft lockup with monitor mode
  2015-02-09 18:31 [PATCH] ath9k_htc: add adaptive usb receive flow control to repair soft lockup with monitor mode Yuwei Zheng
@ 2015-02-11  9:20 ` Kalle Valo
  2015-02-10  0:36   ` Yuwei Zheng
  0 siblings, 1 reply; 9+ messages in thread
From: Kalle Valo @ 2015-02-11  9:20 UTC (permalink / raw)
  To: Yuwei Zheng
  Cc: linux-kernel, ath9k-devel, linux-wireless, ath9k-devel, netdev,
	zhengyuwei

Yuwei Zheng <yuweizheng@139.com> writes:

> The ath9k_hif_usb_rx_cb function excute on  the interrupt context, and ath9k_rx_tasklet excute
> on the soft irq context. In other words, the ath9k_hif_usb_rx_cb have more chance to excute than
> ath9k_rx_tasklet.  So in the worst condition,  the rx.rxbuf receive list is always full,
> and the do {}while(true) loop will not be break. The kernel get a soft lockup panic. 
>  
> [59011.007210] BUG: soft lockup - CPU#0 stuck for 23s!
> [kworker/0:0:30609]
> [59011.030560] BUG: scheduling while atomic: kworker/0:0/30609/0x40010100
> [59013.804486] BUG: scheduling while atomic: kworker/0:0/30609/0x40010100
> [59013.858522] Kernel panic - not syncing: softlockup: hung tasks
>  
> [59014.038891] Exception stack(0xdf4bbc38 to 0xdf4bbc80)
> [59014.046834] bc20:                                                       de57b950 60000113
> [59014.059579] bc40: 00000000 bb32bb32 60000113 de57b948 de57b500 dc7bb440 df4bbcd0 00000000
> [59014.072337] bc60: de57b950 60000113 df4bbcd0 df4bbc80 c04c259d c04c25a0 60000133 ffffffff
> [59014.085233] [<c04c28db>] (__irq_svc+0x3b/0x5c) from [<c04c25a0>] (_raw_spin_unlock_irqrestore+0xc/0x10)
> [59014.100437] [<c04c25a0>] (_raw_spin_unlock_irqrestore+0xc/0x10) from [<bf9c2089>] (ath9k_rx_tasklet+0x290/0x490 [ath9k_htc])
> [59014.118267] [<bf9c2089>] (ath9k_rx_tasklet+0x290/0x490 [ath9k_htc]) from [<c0036d23>] (tasklet_action+0x3b/0x98)
> [59014.134132] [<c0036d23>] (tasklet_action+0x3b/0x98) from [<c0036709>] (__do_softirq+0x99/0x16c)
> [59014.147784] [<c0036709>] (__do_softirq+0x99/0x16c) from [<c00369f7>] (irq_exit+0x5b/0x5c)
> [59014.160653] [<c00369f7>] (irq_exit+0x5b/0x5c) from [<c000cfc3>] (handle_IRQ+0x37/0x78)
> [59014.173124] [<c000cfc3>] (handle_IRQ+0x37/0x78) from [<c00085df>] (omap3_intc_handle_irq+0x5f/0x68)
> [59014.187225] [<c00085df>] (omap3_intc_handle_irq+0x5f/0x68) from [<c04c28db>](__irq_svc+0x3b/0x5c)
>  
> This bug can be see with low performance board, such as uniprocessor beagle bone board. Add some debug message in the ath9k_hif_usb_rx_cb
> function may trigger this bug quickly.
>  
> Signed-off-by: Yuwei Zheng <yuweizheng@139.com>

The word wrapping is still wrong, please limit the line length to 72
chars or so. This is the second time I'm mentioning that. Also add v2,
v3 and so on when you send new versions of the patch, otherwise I will
not know what version I should use. And even better if you add a
changelog after "---" line.

Documentation/SubmittingPatches should tell you all this.

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] ath9k_htc: add adaptive usb receive flow control to repair soft lockup with monitor mode
  2015-02-09  7:13 yuweizheng
@ 2015-02-09 10:06 ` Oleksij Rempel
  0 siblings, 0 replies; 9+ messages in thread
From: Oleksij Rempel @ 2015-02-09 10:06 UTC (permalink / raw)
  To: yuweizheng, linux-kernel, ath9k-devel, linux-wireless, kvalo,
	ath9k-devel
  Cc: netdev

[-- Attachment #1: Type: text/plain, Size: 15333 bytes --]

Hi Yuwei,

here is some review according lates changes.

Please add some comments to the code. Probably it is just me, but it is
hard to get idea of the solution. It can happen that by the next
workorgy i or someone else can skrew it :D

Am 09.02.2015 um 08:13 schrieb yuweizheng@139.com:
> From: Yuwei Zheng <yuweizheng@139.com>
> 
> In the environment with heavy wifi traffic, set the ar9271 into monitor mode, will
> trigger a deadloop panic.
>  
> The ath9k_hif_usb_rx_cb function excute on  the interrupt context, and ath9k_rx_tasklet excute
> on the soft irq context. In other words, the ath9k_hif_usb_rx_cb have more chance to excute than
> ath9k_rx_tasklet.  So in the worst condition,  the rx.rxbuf receive list is always full,
> and the do {}while(true) loop will not be break. The kernel get a soft lockup panic. 
>  
> [59011.007210] BUG: soft lockup - CPU#0 stuck for 23s!
> [kworker/0:0:30609]
> [59011.030560] BUG: scheduling while atomic: kworker/0:0/30609/0x40010100
> [59013.804486] BUG: scheduling while atomic: kworker/0:0/30609/0x40010100
> [59013.858522] Kernel panic - not syncing: softlockup: hung tasks
>  
> [59014.038891] Exception stack(0xdf4bbc38 to 0xdf4bbc80)
> [59014.046834] bc20:                                                       de57b950 60000113
> [59014.059579] bc40: 00000000 bb32bb32 60000113 de57b948 de57b500 dc7bb440 df4bbcd0 00000000
> [59014.072337] bc60: de57b950 60000113 df4bbcd0 df4bbc80 c04c259d c04c25a0 60000133 ffffffff
> [59014.085233] [<c04c28db>] (__irq_svc+0x3b/0x5c) from [<c04c25a0>] (_raw_spin_unlock_irqrestore+0xc/0x10)
> [59014.100437] [<c04c25a0>] (_raw_spin_unlock_irqrestore+0xc/0x10) from [<bf9c2089>] (ath9k_rx_tasklet+0x290/0x490 [ath9k_htc])
> [59014.118267] [<bf9c2089>] (ath9k_rx_tasklet+0x290/0x490 [ath9k_htc]) from [<c0036d23>] (tasklet_action+0x3b/0x98)
> [59014.134132] [<c0036d23>] (tasklet_action+0x3b/0x98) from [<c0036709>] (__do_softirq+0x99/0x16c)
> [59014.147784] [<c0036709>] (__do_softirq+0x99/0x16c) from [<c00369f7>] (irq_exit+0x5b/0x5c)
> [59014.160653] [<c00369f7>] (irq_exit+0x5b/0x5c) from [<c000cfc3>] (handle_IRQ+0x37/0x78)
> [59014.173124] [<c000cfc3>] (handle_IRQ+0x37/0x78) from [<c00085df>] (omap3_intc_handle_irq+0x5f/0x68)
> [59014.187225] [<c00085df>] (omap3_intc_handle_irq+0x5f/0x68) from [<c04c28db>](__irq_svc+0x3b/0x5c)
>  
> This bug can be see with low performance board, such as uniprocessor beagle bone board. 
>  
> Signed-off-by: Yuwei Zheng <yuweizheng@139.com>
> 
> ---
>  drivers/net/wireless/ath/ath9k/hif_usb.c       | 75 +++++++++++++++++++++++---
>  drivers/net/wireless/ath/ath9k/hif_usb.h       |  9 ++++
>  drivers/net/wireless/ath/ath9k/htc.h           | 19 +++++++
>  drivers/net/wireless/ath/ath9k/htc_drv_debug.c | 53 ++++++++++++++++++
>  drivers/net/wireless/ath/ath9k/htc_drv_txrx.c  | 44 +++++++++++++++
>  5 files changed, 193 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c
> index 8e7153b..d05cc0b 100644
> --- a/drivers/net/wireless/ath/ath9k/hif_usb.c
> +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
> @@ -640,6 +640,7 @@ static void ath9k_hif_usb_rx_cb(struct urb *urb)
>  	struct hif_device_usb *hif_dev =
>  		usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
>  	int ret;
> +	int delay;
>  
>  	if (!skb)
>  		return;
> @@ -658,7 +659,6 @@ static void ath9k_hif_usb_rx_cb(struct urb *urb)
>  	default:
>  		goto resubmit;
>  	}
> -

please avoid this small changes

>  	if (likely(urb->actual_length != 0)) {
>  		skb_put(skb, urb->actual_length);
>  		ath9k_hif_usb_rx_stream(hif_dev, skb);
> @@ -667,12 +667,22 @@ static void ath9k_hif_usb_rx_cb(struct urb *urb)
>  resubmit:
>  	skb_reset_tail_pointer(skb);
>  	skb_trim(skb, 0);
> -
> -	usb_anchor_urb(urb, &hif_dev->rx_submitted);
> -	ret = usb_submit_urb(urb, GFP_ATOMIC);
> -	if (ret) {
> -		usb_unanchor_urb(urb);
> -		goto free;
> +	spin_lock(&hif_dev->aurfc_lock);
> +	if (atomic_read(&hif_dev->aurfc_submit_delay) > 0 &&
> +	    hif_dev->aurfc_active == 1) {

why do we need hif_dev->aurfc_active? Is it possible situation where
aurfc_submit_delay) > 0 and hif_dev->aurfc_active == 0?

> +		usb_anchor_urb(urb, &hif_dev->rx_delayed_submitted);
> +		delay = atomic_read(&hif_dev->aurfc_submit_delay);
> +		schedule_delayed_work(&hif_dev->aurfc_delayed_work,
> +				      msecs_to_jiffies(delay));

schedule_delayed_work has internal locks. Do we really need extra locks
here?

> +		spin_unlock(&hif_dev->aurfc_lock);
> +	} else {
> +		spin_unlock(&hif_dev->aurfc_lock);
> +		usb_anchor_urb(urb, &hif_dev->rx_submitted);
> +		ret = usb_submit_urb(urb, GFP_ATOMIC);
> +		if (ret) {
> +			usb_unanchor_urb(urb);
> +			goto free;
> +		}
>  	}
>  
>  	return;
> @@ -818,9 +828,51 @@ err:
>  	return -ENOMEM;
>  }
>  
> +static void aurfc_submit_handler(struct work_struct *work)
> +{
> +	struct hif_device_usb *hif_dev =
> +		container_of(work,
> +			     struct hif_device_usb,
> +			     aurfc_delayed_work.work);
> +	struct urb *urb = NULL;
> +	struct sk_buff *skb = NULL;
> +	int ret;
> +	int loop_times = 0;
> +
> +	AURFC_STAT_INC(aurfc_called);
> +	while (true) {
> +		loop_times++;
> +		if (loop_times > MAX_RX_URB_NUM)
> +			atomic_add(AURFC_STEP,
> +				   &hif_dev->aurfc_submit_delay);
> +
> +		urb = usb_get_from_anchor(
> +				&hif_dev->rx_delayed_submitted);
> +		if (urb) {
> +			skb = (struct sk_buff *)urb->context;
> +			ret = usb_submit_urb(urb, GFP_KERNEL);
> +			if (ret != 0) {
> +				usb_unanchor_urb(urb);
> +				dev_kfree_skb_any(skb);
> +				urb->context = NULL;
> +			}
> +		} else {
> +			break;
> +		}
> +	}
> +}
> +
>  static void ath9k_hif_usb_dealloc_rx_urbs(struct hif_device_usb *hif_dev)
>  {
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&hif_dev->aurfc_lock, flags);

really needed?

> +	hif_dev->aurfc_active = 0;

same here?

> +	mod_delayed_work(system_wq, &hif_dev->aurfc_delayed_work, 0);

flush_delayed_work - "Delayed timer is cancelled and the pending work is
queued for immediate execution". No need to modify timer here.

> +	spin_unlock_irqrestore(&hif_dev->aurfc_lock, flags);
> +	flush_delayed_work(&hif_dev->aurfc_delayed_work);
>  	usb_kill_anchored_urbs(&hif_dev->rx_submitted);
> +	usb_kill_anchored_urbs(&hif_dev->rx_delayed_submitted);
>  }
>  
>  static int ath9k_hif_usb_alloc_rx_urbs(struct hif_device_usb *hif_dev)
> @@ -830,8 +882,17 @@ static int ath9k_hif_usb_alloc_rx_urbs(struct hif_device_usb *hif_dev)
>  	int i, ret;
>  
>  	init_usb_anchor(&hif_dev->rx_submitted);
> +	init_usb_anchor(&hif_dev->rx_delayed_submitted);
> +
>  	spin_lock_init(&hif_dev->rx_lock);
>  
> +	/* add for adaptive usb receive control*/
> +	atomic_set(&hif_dev->aurfc_submit_delay, 0);
> +	INIT_DELAYED_WORK(&hif_dev->aurfc_delayed_work,
> +			  aurfc_submit_handler);
> +	spin_lock_init(&hif_dev->aurfc_lock);
> +	hif_dev->aurfc_active = 1;

aurfc_active and aurfc_lock - do we really need it?

>  	for (i = 0; i < MAX_RX_URB_NUM; i++) {
>  
>  		/* Allocate URB */
> diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.h b/drivers/net/wireless/ath/ath9k/hif_usb.h
> index 51496e7..8e7b67e 100644
> --- a/drivers/net/wireless/ath/ath9k/hif_usb.h
> +++ b/drivers/net/wireless/ath/ath9k/hif_usb.h
> @@ -41,6 +41,7 @@
>  #define MAX_RX_URB_NUM  8
>  #define MAX_RX_BUF_SIZE 16384
>  #define MAX_PKT_NUM_IN_TRANSFER 10
> +#define AURFC_STEP 70  /* ms */
>  
>  #define MAX_REG_OUT_URB_NUM  1
>  #define MAX_REG_IN_URB_NUM   64
> @@ -98,9 +99,17 @@ struct hif_device_usb {
>  	struct hif_usb_tx tx;
>  	struct usb_anchor regout_submitted;
>  	struct usb_anchor rx_submitted;
> +	struct usb_anchor rx_delayed_submitted;
>  	struct usb_anchor reg_in_submitted;
>  	struct usb_anchor mgmt_submitted;
>  	struct sk_buff *remain_skb;
> +
> +	/* adaptive usb receive flow control */
> +	struct delayed_work aurfc_delayed_work;
> +	spinlock_t aurfc_lock; /* to protect work */
> +	atomic_t  aurfc_submit_delay; /* ms */
> +	int aurfc_active;
> +
>  	const char *fw_name;
>  	int rx_remain_len;
>  	int rx_pkt_len;
> diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
> index 9dde265..1586bd2 100644
> --- a/drivers/net/wireless/ath/ath9k/htc.h
> +++ b/drivers/net/wireless/ath/ath9k/htc.h
> @@ -331,6 +331,13 @@ static inline struct ath9k_htc_tx_ctl *HTC_SKB_CB(struct sk_buff *skb)
>  
>  #define TX_QSTAT_INC(q) (priv->debug.tx_stats.queue_stats[q]++)
>  
> +#define AURFC_STAT_INC(c) \
> +	(hif_dev->htc_handle->drv_priv->debug.aurfc_stats.c++)
> +#define AURFC_STAT_ADD(c, a) \
> +	(hif_dev->htc_handle->drv_priv->debug.aurfc_stats.c += a)
> +#define AURFC_STAT_SET(c, a) \
> +	(hif_dev->htc_handle->drv_priv->debug.aurfc_stats.c = a)
> +
>  void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv,
>  			   struct ath_rx_status *rs);
>  
> @@ -352,11 +359,20 @@ struct ath_skbrx_stats {
>  	u32 skb_dropped;
>  };
>  
> +struct ath_aurfc_stats {
> +	u32 aurfc_highwater;
> +	u32 aurfc_lowwater;
> +	u32 aurfc_wm_triggered;
> +	u32 aurfc_submit_delay;
> +	u32 aurfc_called;
> +};
> +
>  struct ath9k_debug {
>  	struct dentry *debugfs_phy;
>  	struct ath_tx_stats tx_stats;
>  	struct ath_rx_stats rx_stats;
>  	struct ath_skbrx_stats skbrx_stats;
> +	struct ath_aurfc_stats aurfc_stats;
>  };
>  
>  void ath9k_htc_get_et_strings(struct ieee80211_hw *hw,
> @@ -377,6 +393,9 @@ void ath9k_htc_get_et_stats(struct ieee80211_hw *hw,
>  
>  #define TX_QSTAT_INC(c) do { } while (0)
>  
> +#define AURFC_STAT_INC(c) do {} while (0)
> +#define AURFC_STAT_ADD(c, a) do {} while (0)
> +#define AURFC_STAT_SET(c, a) do {} while (0)
>  static inline void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv,
>  					 struct ath_rx_status *rs)
>  {
> diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
> index 8cef1ed..a6be9be 100644
> --- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
> +++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
> @@ -286,6 +286,54 @@ static const struct file_operations fops_skb_rx = {
>  	.llseek = default_llseek,
>  };
>  
> +static ssize_t read_file_aurfc(struct file *file,
> +			       char __user *user_buf,
> +			       size_t count, loff_t *ppos)
> +{
> +	struct ath9k_htc_priv *priv = file->private_data;
> +	char *buf;
> +	unsigned int len = 0, size = 1500;
> +	ssize_t retval = 0;
> +
> +	buf = kzalloc(size, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	len += scnprintf(buf + len, size - len,
> +			"%20s : %10u\n", "High watermark",
> +			priv->debug.aurfc_stats.aurfc_highwater);
> +	len += scnprintf(buf + len, size - len,
> +			"%20s : %10u\n", "Low watermark",
> +			priv->debug.aurfc_stats.aurfc_lowwater);
> +
> +	len += scnprintf(buf + len, size - len,
> +			"%20s : %10u\n", "WM triggered",
> +			priv->debug.aurfc_stats.aurfc_wm_triggered);
> +
> +	len += scnprintf(buf + len, size - len,
> +			"%20s : %10u\n", "Handler called",
> +			priv->debug.aurfc_stats.aurfc_called);
> +
> +	len += scnprintf(buf + len, size - len,
> +			"%20s : %10u\n", "Submit delay",
> +			priv->debug.aurfc_stats.aurfc_submit_delay);
> +	if (len > size)
> +		len = size;
> +
> +	retval = simple_read_from_buffer(user_buf, count,
> +					 ppos, buf, len);
> +	kfree(buf);
> +
> +	return retval;
> +}
> +
> +static const struct file_operations fops_aurfc = {
> +	.read = read_file_aurfc,
> +	.open = simple_open,
> +	.owner = THIS_MODULE,
> +	.llseek = default_llseek,
> +};
> +
>  static ssize_t read_file_slot(struct file *file, char __user *user_buf,
>  			      size_t count, loff_t *ppos)
>  {
> @@ -518,7 +566,12 @@ int ath9k_htc_init_debug(struct ath_hw *ah)
>  	debugfs_create_file("skb_rx", S_IRUSR, priv->debug.debugfs_phy,
>  			    priv, &fops_skb_rx);
>  
> +	debugfs_create_file("aurfc_stats", S_IRUSR,
> +			    priv->debug.debugfs_phy,
> +			    priv, &fops_aurfc);
> +
>  	ath9k_cmn_debug_recv(priv->debug.debugfs_phy, &priv->debug.rx_stats);
> +
>  	ath9k_cmn_debug_phy_err(priv->debug.debugfs_phy, &priv->debug.rx_stats);
>  
>  	debugfs_create_file("slot", S_IRUSR, priv->debug.debugfs_phy,
> diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
> index a0f58e2..939d008 100644
> --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
> +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
> @@ -1061,7 +1061,28 @@ void ath9k_rx_tasklet(unsigned long data)
>  	unsigned long flags;
>  	struct ieee80211_hdr *hdr;
>  
> +	/* add for adaptive usb receive flow control*/
> +	int looptimes = 0;
> +	int highwatermark = ATH9K_HTC_RXBUF*3/4;
> +	int lowwatermark = ATH9K_HTC_RXBUF/32;
> +	unsigned int delay = 0;
> +
> +	struct htc_target *htc = priv->htc;
> +	struct hif_device_usb *hif_dev = htc->hif_dev;
> +
> +	AURFC_STAT_SET(aurfc_highwater, highwatermark);
> +	AURFC_STAT_SET(aurfc_lowwater, lowwatermark);
> +
>  	do {
> +		looptimes++;
> +		if (looptimes > highwatermark) {
> +			delay = looptimes*AURFC_STEP;
> +			atomic_set(&hif_dev->aurfc_submit_delay,
> +				   delay);
> +			AURFC_STAT_INC(aurfc_wm_triggered);
> +			AURFC_STAT_SET(aurfc_submit_delay, delay);
> +		}
> +
>  		spin_lock_irqsave(&priv->rx.rxbuflock, flags);
>  		list_for_each_entry(tmp_buf, &priv->rx.rxbuf, list) {
>  			if (tmp_buf->in_process) {
> @@ -1072,6 +1093,22 @@ void ath9k_rx_tasklet(unsigned long data)
>  
>  		if (rxbuf == NULL) {
>  			spin_unlock_irqrestore(&priv->rx.rxbuflock, flags);
> +			spin_lock_irqsave(&hif_dev->aurfc_lock,
> +					  flags);
> +			if (atomic_read(
> +				&hif_dev->aurfc_submit_delay) > 0 &&
> +			    hif_dev->aurfc_active > 0)
> +				mod_delayed_work(system_wq,
> +					&hif_dev->aurfc_delayed_work,
> +						 0);

mod_delayed_work or may be you mean flush_delayed_work? In both cases,
local irqs are disabled.

> +			spin_unlock_irqrestore(&hif_dev->aurfc_lock,
> +					       flags);
> +			if (looptimes < lowwatermark) {
> +				atomic_set(&hif_dev->aurfc_submit_delay
> +					   , 0);
> +				AURFC_STAT_SET(aurfc_submit_delay,
> +					       0);
> +			}
>  			break;
>  		}
>  
> @@ -1114,6 +1151,10 @@ void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb,
>  	struct ath_common *common = ath9k_hw_common(ah);
>  	struct ath9k_htc_rxbuf *rxbuf = NULL, *tmp_buf = NULL;
>  

please, no space here.

> +	struct htc_target *htc = priv->htc;
> +	struct hif_device_usb *hif_dev = htc->hif_dev;
> +	int delay = ATH9K_HTC_RXBUF * AURFC_STEP;
> +
>  	spin_lock(&priv->rx.rxbuflock);
>  	list_for_each_entry(tmp_buf, &priv->rx.rxbuf, list) {
>  		if (!tmp_buf->in_process) {
> @@ -1124,6 +1165,9 @@ void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb,
>  	spin_unlock(&priv->rx.rxbuflock);
>  
>  	if (rxbuf == NULL) {
> +		atomic_set(&hif_dev->aurfc_submit_delay, delay);
> +		AURFC_STAT_INC(aurfc_wm_triggered);
> +		AURFC_STAT_SET(aurfc_submit_delay, delay);
>  		ath_dbg(common, ANY, "No free RX buffer\n");
>  		goto err;
>  	}
> 


-- 
Regards,
Oleksij


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 213 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] ath9k_htc: add adaptive usb receive flow control to repair soft lockup with monitor mode
  2015-02-09  7:20   ` Yuwei Zheng
@ 2015-02-09  9:16     ` Oleksij Rempel
  0 siblings, 0 replies; 9+ messages in thread
From: Oleksij Rempel @ 2015-02-09  9:16 UTC (permalink / raw)
  To: Yuwei Zheng
  Cc: linux-kernel, ath9k-devel, linux-wireless, kvalo, ath9k-devel,
	netdev, zhengyuwei

[-- Attachment #1: Type: text/plain, Size: 15983 bytes --]

Am 09.02.2015 um 08:20 schrieb Yuwei Zheng:
> 
> On 五, 2015-02-06 at 23:24 +0100, Oleksij Rempel wrote:
>> formatting of this patch is still badly broken.
>> ./scripts/checkpatch.pl ~/Downloads/patch.eml
>> ....
>> ERROR: DOS line endings
>> #442: FILE: drivers/net/wireless/ath/ath9k/htc_drv_txrx.c:1170:
>> +^I^IAURFC_STAT_SET(aurfc_submit_delay, delay);^M$
>>
>> total: 195 errors, 6 warnings, 1 checks, 310 lines checked
> 
> 
> Don't save the email as microsoft eml format.  The mail tool may 
> translate it to DOS line endings if you save it as eml format. 
> 
> I have tested the patch and save it with Evolution as mobox format. 
> The check result is 
> 
> total: 0 errors, 0 warnings, 1 checks, 310 lines checked

You right!

thank you for the tip.


>>
>> Am 06.02.2015 um 11:46 schrieb yuweizheng@139.com:
>>> From: Yuwei Zheng <yuweizheng@139.com>
>>>
>>> In the environment with heavy wifi traffic, set the ar9271 into monitor mode, will
>>> trigger a deadloop panic.
>>>  
>>> The ath9k_hif_usb_rx_cb function excute on  the interrupt context, and ath9k_rx_tasklet excute
>>> on the soft irq context. In other words, the ath9k_hif_usb_rx_cb have more chance to excute than
>>> ath9k_rx_tasklet.  So in the worst condition,  the rx.rxbuf receive list is always full,
>>> and the do {}while(true) loop will not be break. The kernel get a soft lockup panic. 
>>>  
>>> [59011.007210] BUG: soft lockup - CPU#0 stuck for 23s!
>>> [kworker/0:0:30609]
>>> [59011.030560] BUG: scheduling while atomic: kworker/0:0/30609/0x40010100
>>> [59013.804486] BUG: scheduling while atomic: kworker/0:0/30609/0x40010100
>>> [59013.858522] Kernel panic - not syncing: softlockup: hung tasks
>>>  
>>> [59014.038891] Exception stack(0xdf4bbc38 to 0xdf4bbc80)
>>> [59014.046834] bc20:                                                       de57b950 60000113
>>> [59014.059579] bc40: 00000000 bb32bb32 60000113 de57b948 de57b500 dc7bb440 df4bbcd0 00000000
>>> [59014.072337] bc60: de57b950 60000113 df4bbcd0 df4bbc80 c04c259d c04c25a0 60000133 ffffffff
>>> [59014.085233] [<c04c28db>] (__irq_svc+0x3b/0x5c) from [<c04c25a0>] (_raw_spin_unlock_irqrestore+0xc/0x10)
>>> [59014.100437] [<c04c25a0>] (_raw_spin_unlock_irqrestore+0xc/0x10) from [<bf9c2089>] (ath9k_rx_tasklet+0x290/0x490 [ath9k_htc])
>>> [59014.118267] [<bf9c2089>] (ath9k_rx_tasklet+0x290/0x490 [ath9k_htc]) from [<c0036d23>] (tasklet_action+0x3b/0x98)
>>> [59014.134132] [<c0036d23>] (tasklet_action+0x3b/0x98) from [<c0036709>] (__do_softirq+0x99/0x16c)
>>> [59014.147784] [<c0036709>] (__do_softirq+0x99/0x16c) from [<c00369f7>] (irq_exit+0x5b/0x5c)
>>> [59014.160653] [<c00369f7>] (irq_exit+0x5b/0x5c) from [<c000cfc3>] (handle_IRQ+0x37/0x78)
>>> [59014.173124] [<c000cfc3>] (handle_IRQ+0x37/0x78) from [<c00085df>] (omap3_intc_handle_irq+0x5f/0x68)
>>> [59014.187225] [<c00085df>] (omap3_intc_handle_irq+0x5f/0x68) from [<c04c28db>](__irq_svc+0x3b/0x5c)
>>>  
>>> This bug can be see with low performance board, such as uniprocessor beagle bone board. 
>>>  
>>> Signed-off-by: Yuwei Zheng <yuweizheng@139.com>
>>>
>>> ---
>>>  drivers/net/wireless/ath/ath9k/hif_usb.c       | 74 +++++++++++++++++++++++---
>>>  drivers/net/wireless/ath/ath9k/hif_usb.h       |  9 ++++
>>>  drivers/net/wireless/ath/ath9k/htc.h           | 19 +++++++
>>>  drivers/net/wireless/ath/ath9k/htc_drv_debug.c | 53 ++++++++++++++++++
>>>  drivers/net/wireless/ath/ath9k/htc_drv_txrx.c  | 44 +++++++++++++++
>>>  5 files changed, 192 insertions(+), 7 deletions(-)
>>>
>>> diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c
>>> index 8e7153b..90ee568 100644
>>> --- a/drivers/net/wireless/ath/ath9k/hif_usb.c
>>> +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
>>> @@ -640,6 +640,7 @@ static void ath9k_hif_usb_rx_cb(struct urb *urb)
>>>  	struct hif_device_usb *hif_dev =
>>>  		usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
>>>  	int ret;
>>> +	int delay;
>>>  
>>>  	if (!skb)
>>>  		return;
>>> @@ -658,7 +659,6 @@ static void ath9k_hif_usb_rx_cb(struct urb *urb)
>>>  	default:
>>>  		goto resubmit;
>>>  	}
>>> -
>>>  	if (likely(urb->actual_length != 0)) {
>>>  		skb_put(skb, urb->actual_length);
>>>  		ath9k_hif_usb_rx_stream(hif_dev, skb);
>>> @@ -667,12 +667,22 @@ static void ath9k_hif_usb_rx_cb(struct urb *urb)
>>>  resubmit:
>>>  	skb_reset_tail_pointer(skb);
>>>  	skb_trim(skb, 0);
>>> -
>>> -	usb_anchor_urb(urb, &hif_dev->rx_submitted);
>>> -	ret = usb_submit_urb(urb, GFP_ATOMIC);
>>> -	if (ret) {
>>> -		usb_unanchor_urb(urb);
>>> -		goto free;
>>> +	spin_lock(&hif_dev->aurfc_lock);
>>> +	if (atomic_read(&hif_dev->aurfc_submit_delay) > 0 &&
>>> +	    hif_dev->aurfc_active == 1) {
>>> +		usb_anchor_urb(urb, &hif_dev->rx_delayed_submitted);
>>> +		delay = atomic_read(&hif_dev->aurfc_submit_delay);
>>> +		schedule_delayed_work(&hif_dev->aurfc_delayed_work,
>>> +				      msecs_to_jiffies(delay));
>>> +		spin_unlock(&hif_dev->aurfc_lock);
>>> +	} else {
>>> +		spin_unlock(&hif_dev->aurfc_lock);
>>> +		usb_anchor_urb(urb, &hif_dev->rx_submitted);
>>> +		ret = usb_submit_urb(urb, GFP_ATOMIC);
>>> +		if (ret) {
>>> +			usb_unanchor_urb(urb);
>>> +			goto free;
>>> +		}
>>>  	}
>>>  
>>>  	return;
>>> @@ -818,9 +828,50 @@ err:
>>>  	return -ENOMEM;
>>>  }
>>>  
>>> +static void aurfc_submit_handler(struct work_struct *work)
>>> +{
>>> +	struct hif_device_usb *hif_dev =
>>> +		container_of(work,
>>> +			     struct hif_device_usb,
>>> +			     aurfc_delayed_work.work);
>>> +
>>> +	struct urb *urb = NULL;
>>> +	struct sk_buff *skb = NULL;
>>> +	int ret;
>>> +	int loop_times = 0;
>>> +
>>> +	while (true) {
>>> +		loop_times++;
>>> +		if (loop_times > MAX_RX_URB_NUM)
>>> +			atomic_add(AURFC_STEP,
>>> +				   &hif_dev->aurfc_submit_delay);
>>> +
>>> +		urb = usb_get_from_anchor(
>>> +				&hif_dev->rx_delayed_submitted);
>>> +		if (urb) {
>>> +			skb = (struct sk_buff *)urb->context;
>>> +			ret = usb_submit_urb(urb, GFP_KERNEL);
>>> +			if (ret != 0) {
>>> +				usb_unanchor_urb(urb);
>>> +				dev_kfree_skb_any(skb);
>>> +				urb->context = NULL;
>>> +			}
>>> +		} else {
>>> +			break;
>>> +		}
>>> +	}
>>> +}
>>> +
>>>  static void ath9k_hif_usb_dealloc_rx_urbs(struct hif_device_usb *hif_dev)
>>>  {
>>> +	unsigned long flags;
>>> +
>>> +	spin_lock_irqsave(&hif_dev->aurfc_lock, flags);
>>> +	hif_dev->aurfc_active = 0;
>>> +	mod_delayed_work(system_wq, &hif_dev->aurfc_delayed_work, 0);
>>> +	spin_unlock_irqrestore(&hif_dev->aurfc_lock, flags);
>>>  	usb_kill_anchored_urbs(&hif_dev->rx_submitted);
>>> +	usb_kill_anchored_urbs(&hif_dev->rx_delayed_submitted);
>>>  }
>>>  
>>>  static int ath9k_hif_usb_alloc_rx_urbs(struct hif_device_usb *hif_dev)
>>> @@ -830,8 +881,17 @@ static int ath9k_hif_usb_alloc_rx_urbs(struct hif_device_usb *hif_dev)
>>>  	int i, ret;
>>>  
>>>  	init_usb_anchor(&hif_dev->rx_submitted);
>>> +	init_usb_anchor(&hif_dev->rx_delayed_submitted);
>>> +
>>>  	spin_lock_init(&hif_dev->rx_lock);
>>>  
>>> +	/* add for adaptive usb receive control*/
>>> +	atomic_set(&hif_dev->aurfc_submit_delay, 0);
>>> +	INIT_DELAYED_WORK(&hif_dev->aurfc_delayed_work,
>>> +			  aurfc_submit_handler);
>>> +	spin_lock_init(&hif_dev->aurfc_lock);
>>> +	hif_dev->aurfc_active = 1;
>>> +
>>>  	for (i = 0; i < MAX_RX_URB_NUM; i++) {
>>>  
>>>  		/* Allocate URB */
>>> diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.h b/drivers/net/wireless/ath/ath9k/hif_usb.h
>>> index 51496e7..2050a74 100644
>>> --- a/drivers/net/wireless/ath/ath9k/hif_usb.h
>>> +++ b/drivers/net/wireless/ath/ath9k/hif_usb.h
>>> @@ -41,6 +41,7 @@
>>>  #define MAX_RX_URB_NUM  8
>>>  #define MAX_RX_BUF_SIZE 16384
>>>  #define MAX_PKT_NUM_IN_TRANSFER 10
>>> +#define AURFC_STEP 10  /* ms */
>>>  
>>>  #define MAX_REG_OUT_URB_NUM  1
>>>  #define MAX_REG_IN_URB_NUM   64
>>> @@ -98,9 +99,17 @@ struct hif_device_usb {
>>>  	struct hif_usb_tx tx;
>>>  	struct usb_anchor regout_submitted;
>>>  	struct usb_anchor rx_submitted;
>>> +	struct usb_anchor rx_delayed_submitted;
>>>  	struct usb_anchor reg_in_submitted;
>>>  	struct usb_anchor mgmt_submitted;
>>>  	struct sk_buff *remain_skb;
>>> +
>>> +	/* adaptive usb receive flow control */
>>> +	struct delayed_work aurfc_delayed_work;
>>> +	spinlock_t aurfc_lock; /* to protect work */
>>> +	atomic_t  aurfc_submit_delay; /* ms */
>>> +	int aurfc_active;
>>> +
>>>  	const char *fw_name;
>>>  	int rx_remain_len;
>>>  	int rx_pkt_len;
>>> diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
>>> index 9dde265..1586bd2 100644
>>> --- a/drivers/net/wireless/ath/ath9k/htc.h
>>> +++ b/drivers/net/wireless/ath/ath9k/htc.h
>>> @@ -331,6 +331,13 @@ static inline struct ath9k_htc_tx_ctl *HTC_SKB_CB(struct sk_buff *skb)
>>>  
>>>  #define TX_QSTAT_INC(q) (priv->debug.tx_stats.queue_stats[q]++)
>>>  
>>> +#define AURFC_STAT_INC(c) \
>>> +	(hif_dev->htc_handle->drv_priv->debug.aurfc_stats.c++)
>>> +#define AURFC_STAT_ADD(c, a) \
>>> +	(hif_dev->htc_handle->drv_priv->debug.aurfc_stats.c += a)
>>> +#define AURFC_STAT_SET(c, a) \
>>> +	(hif_dev->htc_handle->drv_priv->debug.aurfc_stats.c = a)
>>> +
>>>  void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv,
>>>  			   struct ath_rx_status *rs);
>>>  
>>> @@ -352,11 +359,20 @@ struct ath_skbrx_stats {
>>>  	u32 skb_dropped;
>>>  };
>>>  
>>> +struct ath_aurfc_stats {
>>> +	u32 aurfc_highwater;
>>> +	u32 aurfc_lowwater;
>>> +	u32 aurfc_wm_triggered;
>>> +	u32 aurfc_submit_delay;
>>> +	u32 aurfc_called;
>>> +};
>>> +
>>>  struct ath9k_debug {
>>>  	struct dentry *debugfs_phy;
>>>  	struct ath_tx_stats tx_stats;
>>>  	struct ath_rx_stats rx_stats;
>>>  	struct ath_skbrx_stats skbrx_stats;
>>> +	struct ath_aurfc_stats aurfc_stats;
>>>  };
>>>  
>>>  void ath9k_htc_get_et_strings(struct ieee80211_hw *hw,
>>> @@ -377,6 +393,9 @@ void ath9k_htc_get_et_stats(struct ieee80211_hw *hw,
>>>  
>>>  #define TX_QSTAT_INC(c) do { } while (0)
>>>  
>>> +#define AURFC_STAT_INC(c) do {} while (0)
>>> +#define AURFC_STAT_ADD(c, a) do {} while (0)
>>> +#define AURFC_STAT_SET(c, a) do {} while (0)
>>>  static inline void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv,
>>>  					 struct ath_rx_status *rs)
>>>  {
>>> diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
>>> index 8cef1ed..a6be9be 100644
>>> --- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
>>> +++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
>>> @@ -286,6 +286,54 @@ static const struct file_operations fops_skb_rx = {
>>>  	.llseek = default_llseek,
>>>  };
>>>  
>>> +static ssize_t read_file_aurfc(struct file *file,
>>> +			       char __user *user_buf,
>>> +			       size_t count, loff_t *ppos)
>>> +{
>>> +	struct ath9k_htc_priv *priv = file->private_data;
>>> +	char *buf;
>>> +	unsigned int len = 0, size = 1500;
>>> +	ssize_t retval = 0;
>>> +
>>> +	buf = kzalloc(size, GFP_KERNEL);
>>> +	if (!buf)
>>> +		return -ENOMEM;
>>> +
>>> +	len += scnprintf(buf + len, size - len,
>>> +			"%20s : %10u\n", "High watermark",
>>> +			priv->debug.aurfc_stats.aurfc_highwater);
>>> +	len += scnprintf(buf + len, size - len,
>>> +			"%20s : %10u\n", "Low watermark",
>>> +			priv->debug.aurfc_stats.aurfc_lowwater);
>>> +
>>> +	len += scnprintf(buf + len, size - len,
>>> +			"%20s : %10u\n", "WM triggered",
>>> +			priv->debug.aurfc_stats.aurfc_wm_triggered);
>>> +
>>> +	len += scnprintf(buf + len, size - len,
>>> +			"%20s : %10u\n", "Handler called",
>>> +			priv->debug.aurfc_stats.aurfc_called);
>>> +
>>> +	len += scnprintf(buf + len, size - len,
>>> +			"%20s : %10u\n", "Submit delay",
>>> +			priv->debug.aurfc_stats.aurfc_submit_delay);
>>> +	if (len > size)
>>> +		len = size;
>>> +
>>> +	retval = simple_read_from_buffer(user_buf, count,
>>> +					 ppos, buf, len);
>>> +	kfree(buf);
>>> +
>>> +	return retval;
>>> +}
>>> +
>>> +static const struct file_operations fops_aurfc = {
>>> +	.read = read_file_aurfc,
>>> +	.open = simple_open,
>>> +	.owner = THIS_MODULE,
>>> +	.llseek = default_llseek,
>>> +};
>>> +
>>>  static ssize_t read_file_slot(struct file *file, char __user *user_buf,
>>>  			      size_t count, loff_t *ppos)
>>>  {
>>> @@ -518,7 +566,12 @@ int ath9k_htc_init_debug(struct ath_hw *ah)
>>>  	debugfs_create_file("skb_rx", S_IRUSR, priv->debug.debugfs_phy,
>>>  			    priv, &fops_skb_rx);
>>>  
>>> +	debugfs_create_file("aurfc_stats", S_IRUSR,
>>> +			    priv->debug.debugfs_phy,
>>> +			    priv, &fops_aurfc);
>>> +
>>>  	ath9k_cmn_debug_recv(priv->debug.debugfs_phy, &priv->debug.rx_stats);
>>> +
>>>  	ath9k_cmn_debug_phy_err(priv->debug.debugfs_phy, &priv->debug.rx_stats);
>>>  
>>>  	debugfs_create_file("slot", S_IRUSR, priv->debug.debugfs_phy,
>>> diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
>>> index a0f58e2..939d008 100644
>>> --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
>>> +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
>>> @@ -1061,7 +1061,28 @@ void ath9k_rx_tasklet(unsigned long data)
>>>  	unsigned long flags;
>>>  	struct ieee80211_hdr *hdr;
>>>  
>>> +	/* add for adaptive usb receive flow control*/
>>> +	int looptimes = 0;
>>> +	int highwatermark = ATH9K_HTC_RXBUF*3/4;
>>> +	int lowwatermark = ATH9K_HTC_RXBUF/32;
>>> +	unsigned int delay = 0;
>>> +
>>> +	struct htc_target *htc = priv->htc;
>>> +	struct hif_device_usb *hif_dev = htc->hif_dev;
>>> +
>>> +	AURFC_STAT_SET(aurfc_highwater, highwatermark);
>>> +	AURFC_STAT_SET(aurfc_lowwater, lowwatermark);
>>> +
>>>  	do {
>>> +		looptimes++;
>>> +		if (looptimes > highwatermark) {
>>> +			delay = looptimes*AURFC_STEP;
>>> +			atomic_set(&hif_dev->aurfc_submit_delay,
>>> +				   delay);
>>> +			AURFC_STAT_INC(aurfc_wm_triggered);
>>> +			AURFC_STAT_SET(aurfc_submit_delay, delay);
>>> +		}
>>> +
>>>  		spin_lock_irqsave(&priv->rx.rxbuflock, flags);
>>>  		list_for_each_entry(tmp_buf, &priv->rx.rxbuf, list) {
>>>  			if (tmp_buf->in_process) {
>>> @@ -1072,6 +1093,22 @@ void ath9k_rx_tasklet(unsigned long data)
>>>  
>>>  		if (rxbuf == NULL) {
>>>  			spin_unlock_irqrestore(&priv->rx.rxbuflock, flags);
>>> +			spin_lock_irqsave(&hif_dev->aurfc_lock,
>>> +					  flags);
>>> +			if (atomic_read(
>>> +				&hif_dev->aurfc_submit_delay) > 0 &&
>>> +			    hif_dev->aurfc_active > 0)
>>> +				mod_delayed_work(system_wq,
>>> +					&hif_dev->aurfc_delayed_work,
>>> +						 0);
>>> +			spin_unlock_irqrestore(&hif_dev->aurfc_lock,
>>> +					       flags);
>>> +			if (looptimes < lowwatermark) {
>>> +				atomic_set(&hif_dev->aurfc_submit_delay
>>> +					   , 0);
>>> +				AURFC_STAT_SET(aurfc_submit_delay,
>>> +					       0);
>>> +			}
>>>  			break;
>>>  		}
>>>  
>>> @@ -1114,6 +1151,10 @@ void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb,
>>>  	struct ath_common *common = ath9k_hw_common(ah);
>>>  	struct ath9k_htc_rxbuf *rxbuf = NULL, *tmp_buf = NULL;
>>>  
>>> +	struct htc_target *htc = priv->htc;
>>> +	struct hif_device_usb *hif_dev = htc->hif_dev;
>>> +	int delay = ATH9K_HTC_RXBUF * AURFC_STEP;
>>> +
>>>  	spin_lock(&priv->rx.rxbuflock);
>>>  	list_for_each_entry(tmp_buf, &priv->rx.rxbuf, list) {
>>>  		if (!tmp_buf->in_process) {
>>> @@ -1124,6 +1165,9 @@ void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb,
>>>  	spin_unlock(&priv->rx.rxbuflock);
>>>  
>>>  	if (rxbuf == NULL) {
>>> +		atomic_set(&hif_dev->aurfc_submit_delay, delay);
>>> +		AURFC_STAT_INC(aurfc_wm_triggered);
>>> +		AURFC_STAT_SET(aurfc_submit_delay, delay);
>>>  		ath_dbg(common, ANY, "No free RX buffer\n");
>>>  		goto err;
>>>  	}
>>>
>>
>>
> 
> 
> 
> 


-- 
Regards,
Oleksij


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 213 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] ath9k_htc: add adaptive usb receive flow control to repair soft lockup with monitor mode
  2015-02-06 22:24 ` Oleksij Rempel
@ 2015-02-09  7:20   ` Yuwei Zheng
  2015-02-09  9:16     ` Oleksij Rempel
  0 siblings, 1 reply; 9+ messages in thread
From: Yuwei Zheng @ 2015-02-09  7:20 UTC (permalink / raw)
  To: Oleksij Rempel
  Cc: linux-kernel, ath9k-devel, linux-wireless, kvalo, ath9k-devel,
	netdev, zhengyuwei


On 五, 2015-02-06 at 23:24 +0100, Oleksij Rempel wrote:
> formatting of this patch is still badly broken.
> ./scripts/checkpatch.pl ~/Downloads/patch.eml
> ....
> ERROR: DOS line endings
> #442: FILE: drivers/net/wireless/ath/ath9k/htc_drv_txrx.c:1170:
> +^I^IAURFC_STAT_SET(aurfc_submit_delay, delay);^M$
> 
> total: 195 errors, 6 warnings, 1 checks, 310 lines checked


Don't save the email as microsoft eml format.  The mail tool may 
translate it to DOS line endings if you save it as eml format. 

I have tested the patch and save it with Evolution as mobox format. 
The check result is 

total: 0 errors, 0 warnings, 1 checks, 310 lines checked





> 
> 
> Am 06.02.2015 um 11:46 schrieb yuweizheng@139.com:
> > From: Yuwei Zheng <yuweizheng@139.com>
> > 
> > In the environment with heavy wifi traffic, set the ar9271 into monitor mode, will
> > trigger a deadloop panic.
> >  
> > The ath9k_hif_usb_rx_cb function excute on  the interrupt context, and ath9k_rx_tasklet excute
> > on the soft irq context. In other words, the ath9k_hif_usb_rx_cb have more chance to excute than
> > ath9k_rx_tasklet.  So in the worst condition,  the rx.rxbuf receive list is always full,
> > and the do {}while(true) loop will not be break. The kernel get a soft lockup panic. 
> >  
> > [59011.007210] BUG: soft lockup - CPU#0 stuck for 23s!
> > [kworker/0:0:30609]
> > [59011.030560] BUG: scheduling while atomic: kworker/0:0/30609/0x40010100
> > [59013.804486] BUG: scheduling while atomic: kworker/0:0/30609/0x40010100
> > [59013.858522] Kernel panic - not syncing: softlockup: hung tasks
> >  
> > [59014.038891] Exception stack(0xdf4bbc38 to 0xdf4bbc80)
> > [59014.046834] bc20:                                                       de57b950 60000113
> > [59014.059579] bc40: 00000000 bb32bb32 60000113 de57b948 de57b500 dc7bb440 df4bbcd0 00000000
> > [59014.072337] bc60: de57b950 60000113 df4bbcd0 df4bbc80 c04c259d c04c25a0 60000133 ffffffff
> > [59014.085233] [<c04c28db>] (__irq_svc+0x3b/0x5c) from [<c04c25a0>] (_raw_spin_unlock_irqrestore+0xc/0x10)
> > [59014.100437] [<c04c25a0>] (_raw_spin_unlock_irqrestore+0xc/0x10) from [<bf9c2089>] (ath9k_rx_tasklet+0x290/0x490 [ath9k_htc])
> > [59014.118267] [<bf9c2089>] (ath9k_rx_tasklet+0x290/0x490 [ath9k_htc]) from [<c0036d23>] (tasklet_action+0x3b/0x98)
> > [59014.134132] [<c0036d23>] (tasklet_action+0x3b/0x98) from [<c0036709>] (__do_softirq+0x99/0x16c)
> > [59014.147784] [<c0036709>] (__do_softirq+0x99/0x16c) from [<c00369f7>] (irq_exit+0x5b/0x5c)
> > [59014.160653] [<c00369f7>] (irq_exit+0x5b/0x5c) from [<c000cfc3>] (handle_IRQ+0x37/0x78)
> > [59014.173124] [<c000cfc3>] (handle_IRQ+0x37/0x78) from [<c00085df>] (omap3_intc_handle_irq+0x5f/0x68)
> > [59014.187225] [<c00085df>] (omap3_intc_handle_irq+0x5f/0x68) from [<c04c28db>](__irq_svc+0x3b/0x5c)
> >  
> > This bug can be see with low performance board, such as uniprocessor beagle bone board. 
> >  
> > Signed-off-by: Yuwei Zheng <yuweizheng@139.com>
> > 
> > ---
> >  drivers/net/wireless/ath/ath9k/hif_usb.c       | 74 +++++++++++++++++++++++---
> >  drivers/net/wireless/ath/ath9k/hif_usb.h       |  9 ++++
> >  drivers/net/wireless/ath/ath9k/htc.h           | 19 +++++++
> >  drivers/net/wireless/ath/ath9k/htc_drv_debug.c | 53 ++++++++++++++++++
> >  drivers/net/wireless/ath/ath9k/htc_drv_txrx.c  | 44 +++++++++++++++
> >  5 files changed, 192 insertions(+), 7 deletions(-)
> > 
> > diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c
> > index 8e7153b..90ee568 100644
> > --- a/drivers/net/wireless/ath/ath9k/hif_usb.c
> > +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
> > @@ -640,6 +640,7 @@ static void ath9k_hif_usb_rx_cb(struct urb *urb)
> >  	struct hif_device_usb *hif_dev =
> >  		usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
> >  	int ret;
> > +	int delay;
> >  
> >  	if (!skb)
> >  		return;
> > @@ -658,7 +659,6 @@ static void ath9k_hif_usb_rx_cb(struct urb *urb)
> >  	default:
> >  		goto resubmit;
> >  	}
> > -
> >  	if (likely(urb->actual_length != 0)) {
> >  		skb_put(skb, urb->actual_length);
> >  		ath9k_hif_usb_rx_stream(hif_dev, skb);
> > @@ -667,12 +667,22 @@ static void ath9k_hif_usb_rx_cb(struct urb *urb)
> >  resubmit:
> >  	skb_reset_tail_pointer(skb);
> >  	skb_trim(skb, 0);
> > -
> > -	usb_anchor_urb(urb, &hif_dev->rx_submitted);
> > -	ret = usb_submit_urb(urb, GFP_ATOMIC);
> > -	if (ret) {
> > -		usb_unanchor_urb(urb);
> > -		goto free;
> > +	spin_lock(&hif_dev->aurfc_lock);
> > +	if (atomic_read(&hif_dev->aurfc_submit_delay) > 0 &&
> > +	    hif_dev->aurfc_active == 1) {
> > +		usb_anchor_urb(urb, &hif_dev->rx_delayed_submitted);
> > +		delay = atomic_read(&hif_dev->aurfc_submit_delay);
> > +		schedule_delayed_work(&hif_dev->aurfc_delayed_work,
> > +				      msecs_to_jiffies(delay));
> > +		spin_unlock(&hif_dev->aurfc_lock);
> > +	} else {
> > +		spin_unlock(&hif_dev->aurfc_lock);
> > +		usb_anchor_urb(urb, &hif_dev->rx_submitted);
> > +		ret = usb_submit_urb(urb, GFP_ATOMIC);
> > +		if (ret) {
> > +			usb_unanchor_urb(urb);
> > +			goto free;
> > +		}
> >  	}
> >  
> >  	return;
> > @@ -818,9 +828,50 @@ err:
> >  	return -ENOMEM;
> >  }
> >  
> > +static void aurfc_submit_handler(struct work_struct *work)
> > +{
> > +	struct hif_device_usb *hif_dev =
> > +		container_of(work,
> > +			     struct hif_device_usb,
> > +			     aurfc_delayed_work.work);
> > +
> > +	struct urb *urb = NULL;
> > +	struct sk_buff *skb = NULL;
> > +	int ret;
> > +	int loop_times = 0;
> > +
> > +	while (true) {
> > +		loop_times++;
> > +		if (loop_times > MAX_RX_URB_NUM)
> > +			atomic_add(AURFC_STEP,
> > +				   &hif_dev->aurfc_submit_delay);
> > +
> > +		urb = usb_get_from_anchor(
> > +				&hif_dev->rx_delayed_submitted);
> > +		if (urb) {
> > +			skb = (struct sk_buff *)urb->context;
> > +			ret = usb_submit_urb(urb, GFP_KERNEL);
> > +			if (ret != 0) {
> > +				usb_unanchor_urb(urb);
> > +				dev_kfree_skb_any(skb);
> > +				urb->context = NULL;
> > +			}
> > +		} else {
> > +			break;
> > +		}
> > +	}
> > +}
> > +
> >  static void ath9k_hif_usb_dealloc_rx_urbs(struct hif_device_usb *hif_dev)
> >  {
> > +	unsigned long flags;
> > +
> > +	spin_lock_irqsave(&hif_dev->aurfc_lock, flags);
> > +	hif_dev->aurfc_active = 0;
> > +	mod_delayed_work(system_wq, &hif_dev->aurfc_delayed_work, 0);
> > +	spin_unlock_irqrestore(&hif_dev->aurfc_lock, flags);
> >  	usb_kill_anchored_urbs(&hif_dev->rx_submitted);
> > +	usb_kill_anchored_urbs(&hif_dev->rx_delayed_submitted);
> >  }
> >  
> >  static int ath9k_hif_usb_alloc_rx_urbs(struct hif_device_usb *hif_dev)
> > @@ -830,8 +881,17 @@ static int ath9k_hif_usb_alloc_rx_urbs(struct hif_device_usb *hif_dev)
> >  	int i, ret;
> >  
> >  	init_usb_anchor(&hif_dev->rx_submitted);
> > +	init_usb_anchor(&hif_dev->rx_delayed_submitted);
> > +
> >  	spin_lock_init(&hif_dev->rx_lock);
> >  
> > +	/* add for adaptive usb receive control*/
> > +	atomic_set(&hif_dev->aurfc_submit_delay, 0);
> > +	INIT_DELAYED_WORK(&hif_dev->aurfc_delayed_work,
> > +			  aurfc_submit_handler);
> > +	spin_lock_init(&hif_dev->aurfc_lock);
> > +	hif_dev->aurfc_active = 1;
> > +
> >  	for (i = 0; i < MAX_RX_URB_NUM; i++) {
> >  
> >  		/* Allocate URB */
> > diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.h b/drivers/net/wireless/ath/ath9k/hif_usb.h
> > index 51496e7..2050a74 100644
> > --- a/drivers/net/wireless/ath/ath9k/hif_usb.h
> > +++ b/drivers/net/wireless/ath/ath9k/hif_usb.h
> > @@ -41,6 +41,7 @@
> >  #define MAX_RX_URB_NUM  8
> >  #define MAX_RX_BUF_SIZE 16384
> >  #define MAX_PKT_NUM_IN_TRANSFER 10
> > +#define AURFC_STEP 10  /* ms */
> >  
> >  #define MAX_REG_OUT_URB_NUM  1
> >  #define MAX_REG_IN_URB_NUM   64
> > @@ -98,9 +99,17 @@ struct hif_device_usb {
> >  	struct hif_usb_tx tx;
> >  	struct usb_anchor regout_submitted;
> >  	struct usb_anchor rx_submitted;
> > +	struct usb_anchor rx_delayed_submitted;
> >  	struct usb_anchor reg_in_submitted;
> >  	struct usb_anchor mgmt_submitted;
> >  	struct sk_buff *remain_skb;
> > +
> > +	/* adaptive usb receive flow control */
> > +	struct delayed_work aurfc_delayed_work;
> > +	spinlock_t aurfc_lock; /* to protect work */
> > +	atomic_t  aurfc_submit_delay; /* ms */
> > +	int aurfc_active;
> > +
> >  	const char *fw_name;
> >  	int rx_remain_len;
> >  	int rx_pkt_len;
> > diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
> > index 9dde265..1586bd2 100644
> > --- a/drivers/net/wireless/ath/ath9k/htc.h
> > +++ b/drivers/net/wireless/ath/ath9k/htc.h
> > @@ -331,6 +331,13 @@ static inline struct ath9k_htc_tx_ctl *HTC_SKB_CB(struct sk_buff *skb)
> >  
> >  #define TX_QSTAT_INC(q) (priv->debug.tx_stats.queue_stats[q]++)
> >  
> > +#define AURFC_STAT_INC(c) \
> > +	(hif_dev->htc_handle->drv_priv->debug.aurfc_stats.c++)
> > +#define AURFC_STAT_ADD(c, a) \
> > +	(hif_dev->htc_handle->drv_priv->debug.aurfc_stats.c += a)
> > +#define AURFC_STAT_SET(c, a) \
> > +	(hif_dev->htc_handle->drv_priv->debug.aurfc_stats.c = a)
> > +
> >  void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv,
> >  			   struct ath_rx_status *rs);
> >  
> > @@ -352,11 +359,20 @@ struct ath_skbrx_stats {
> >  	u32 skb_dropped;
> >  };
> >  
> > +struct ath_aurfc_stats {
> > +	u32 aurfc_highwater;
> > +	u32 aurfc_lowwater;
> > +	u32 aurfc_wm_triggered;
> > +	u32 aurfc_submit_delay;
> > +	u32 aurfc_called;
> > +};
> > +
> >  struct ath9k_debug {
> >  	struct dentry *debugfs_phy;
> >  	struct ath_tx_stats tx_stats;
> >  	struct ath_rx_stats rx_stats;
> >  	struct ath_skbrx_stats skbrx_stats;
> > +	struct ath_aurfc_stats aurfc_stats;
> >  };
> >  
> >  void ath9k_htc_get_et_strings(struct ieee80211_hw *hw,
> > @@ -377,6 +393,9 @@ void ath9k_htc_get_et_stats(struct ieee80211_hw *hw,
> >  
> >  #define TX_QSTAT_INC(c) do { } while (0)
> >  
> > +#define AURFC_STAT_INC(c) do {} while (0)
> > +#define AURFC_STAT_ADD(c, a) do {} while (0)
> > +#define AURFC_STAT_SET(c, a) do {} while (0)
> >  static inline void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv,
> >  					 struct ath_rx_status *rs)
> >  {
> > diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
> > index 8cef1ed..a6be9be 100644
> > --- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
> > +++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
> > @@ -286,6 +286,54 @@ static const struct file_operations fops_skb_rx = {
> >  	.llseek = default_llseek,
> >  };
> >  
> > +static ssize_t read_file_aurfc(struct file *file,
> > +			       char __user *user_buf,
> > +			       size_t count, loff_t *ppos)
> > +{
> > +	struct ath9k_htc_priv *priv = file->private_data;
> > +	char *buf;
> > +	unsigned int len = 0, size = 1500;
> > +	ssize_t retval = 0;
> > +
> > +	buf = kzalloc(size, GFP_KERNEL);
> > +	if (!buf)
> > +		return -ENOMEM;
> > +
> > +	len += scnprintf(buf + len, size - len,
> > +			"%20s : %10u\n", "High watermark",
> > +			priv->debug.aurfc_stats.aurfc_highwater);
> > +	len += scnprintf(buf + len, size - len,
> > +			"%20s : %10u\n", "Low watermark",
> > +			priv->debug.aurfc_stats.aurfc_lowwater);
> > +
> > +	len += scnprintf(buf + len, size - len,
> > +			"%20s : %10u\n", "WM triggered",
> > +			priv->debug.aurfc_stats.aurfc_wm_triggered);
> > +
> > +	len += scnprintf(buf + len, size - len,
> > +			"%20s : %10u\n", "Handler called",
> > +			priv->debug.aurfc_stats.aurfc_called);
> > +
> > +	len += scnprintf(buf + len, size - len,
> > +			"%20s : %10u\n", "Submit delay",
> > +			priv->debug.aurfc_stats.aurfc_submit_delay);
> > +	if (len > size)
> > +		len = size;
> > +
> > +	retval = simple_read_from_buffer(user_buf, count,
> > +					 ppos, buf, len);
> > +	kfree(buf);
> > +
> > +	return retval;
> > +}
> > +
> > +static const struct file_operations fops_aurfc = {
> > +	.read = read_file_aurfc,
> > +	.open = simple_open,
> > +	.owner = THIS_MODULE,
> > +	.llseek = default_llseek,
> > +};
> > +
> >  static ssize_t read_file_slot(struct file *file, char __user *user_buf,
> >  			      size_t count, loff_t *ppos)
> >  {
> > @@ -518,7 +566,12 @@ int ath9k_htc_init_debug(struct ath_hw *ah)
> >  	debugfs_create_file("skb_rx", S_IRUSR, priv->debug.debugfs_phy,
> >  			    priv, &fops_skb_rx);
> >  
> > +	debugfs_create_file("aurfc_stats", S_IRUSR,
> > +			    priv->debug.debugfs_phy,
> > +			    priv, &fops_aurfc);
> > +
> >  	ath9k_cmn_debug_recv(priv->debug.debugfs_phy, &priv->debug.rx_stats);
> > +
> >  	ath9k_cmn_debug_phy_err(priv->debug.debugfs_phy, &priv->debug.rx_stats);
> >  
> >  	debugfs_create_file("slot", S_IRUSR, priv->debug.debugfs_phy,
> > diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
> > index a0f58e2..939d008 100644
> > --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
> > +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
> > @@ -1061,7 +1061,28 @@ void ath9k_rx_tasklet(unsigned long data)
> >  	unsigned long flags;
> >  	struct ieee80211_hdr *hdr;
> >  
> > +	/* add for adaptive usb receive flow control*/
> > +	int looptimes = 0;
> > +	int highwatermark = ATH9K_HTC_RXBUF*3/4;
> > +	int lowwatermark = ATH9K_HTC_RXBUF/32;
> > +	unsigned int delay = 0;
> > +
> > +	struct htc_target *htc = priv->htc;
> > +	struct hif_device_usb *hif_dev = htc->hif_dev;
> > +
> > +	AURFC_STAT_SET(aurfc_highwater, highwatermark);
> > +	AURFC_STAT_SET(aurfc_lowwater, lowwatermark);
> > +
> >  	do {
> > +		looptimes++;
> > +		if (looptimes > highwatermark) {
> > +			delay = looptimes*AURFC_STEP;
> > +			atomic_set(&hif_dev->aurfc_submit_delay,
> > +				   delay);
> > +			AURFC_STAT_INC(aurfc_wm_triggered);
> > +			AURFC_STAT_SET(aurfc_submit_delay, delay);
> > +		}
> > +
> >  		spin_lock_irqsave(&priv->rx.rxbuflock, flags);
> >  		list_for_each_entry(tmp_buf, &priv->rx.rxbuf, list) {
> >  			if (tmp_buf->in_process) {
> > @@ -1072,6 +1093,22 @@ void ath9k_rx_tasklet(unsigned long data)
> >  
> >  		if (rxbuf == NULL) {
> >  			spin_unlock_irqrestore(&priv->rx.rxbuflock, flags);
> > +			spin_lock_irqsave(&hif_dev->aurfc_lock,
> > +					  flags);
> > +			if (atomic_read(
> > +				&hif_dev->aurfc_submit_delay) > 0 &&
> > +			    hif_dev->aurfc_active > 0)
> > +				mod_delayed_work(system_wq,
> > +					&hif_dev->aurfc_delayed_work,
> > +						 0);
> > +			spin_unlock_irqrestore(&hif_dev->aurfc_lock,
> > +					       flags);
> > +			if (looptimes < lowwatermark) {
> > +				atomic_set(&hif_dev->aurfc_submit_delay
> > +					   , 0);
> > +				AURFC_STAT_SET(aurfc_submit_delay,
> > +					       0);
> > +			}
> >  			break;
> >  		}
> >  
> > @@ -1114,6 +1151,10 @@ void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb,
> >  	struct ath_common *common = ath9k_hw_common(ah);
> >  	struct ath9k_htc_rxbuf *rxbuf = NULL, *tmp_buf = NULL;
> >  
> > +	struct htc_target *htc = priv->htc;
> > +	struct hif_device_usb *hif_dev = htc->hif_dev;
> > +	int delay = ATH9K_HTC_RXBUF * AURFC_STEP;
> > +
> >  	spin_lock(&priv->rx.rxbuflock);
> >  	list_for_each_entry(tmp_buf, &priv->rx.rxbuf, list) {
> >  		if (!tmp_buf->in_process) {
> > @@ -1124,6 +1165,9 @@ void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb,
> >  	spin_unlock(&priv->rx.rxbuflock);
> >  
> >  	if (rxbuf == NULL) {
> > +		atomic_set(&hif_dev->aurfc_submit_delay, delay);
> > +		AURFC_STAT_INC(aurfc_wm_triggered);
> > +		AURFC_STAT_SET(aurfc_submit_delay, delay);
> >  		ath_dbg(common, ANY, "No free RX buffer\n");
> >  		goto err;
> >  	}
> > 
> 
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH] ath9k_htc: add adaptive usb receive flow control to repair soft lockup with monitor mode
@ 2015-02-09  7:13 yuweizheng
  2015-02-09 10:06 ` Oleksij Rempel
  0 siblings, 1 reply; 9+ messages in thread
From: yuweizheng @ 2015-02-09  7:13 UTC (permalink / raw)
  To: linux-kernel, ath9k-devel, linux-wireless, kvalo, ath9k-devel
  Cc: netdev, Yuwei Zheng

From: Yuwei Zheng <yuweizheng@139.com>

In the environment with heavy wifi traffic, set the ar9271 into monitor mode, will
trigger a deadloop panic.
 
The ath9k_hif_usb_rx_cb function excute on  the interrupt context, and ath9k_rx_tasklet excute
on the soft irq context. In other words, the ath9k_hif_usb_rx_cb have more chance to excute than
ath9k_rx_tasklet.  So in the worst condition,  the rx.rxbuf receive list is always full,
and the do {}while(true) loop will not be break. The kernel get a soft lockup panic. 
 
[59011.007210] BUG: soft lockup - CPU#0 stuck for 23s!
[kworker/0:0:30609]
[59011.030560] BUG: scheduling while atomic: kworker/0:0/30609/0x40010100
[59013.804486] BUG: scheduling while atomic: kworker/0:0/30609/0x40010100
[59013.858522] Kernel panic - not syncing: softlockup: hung tasks
 
[59014.038891] Exception stack(0xdf4bbc38 to 0xdf4bbc80)
[59014.046834] bc20:                                                       de57b950 60000113
[59014.059579] bc40: 00000000 bb32bb32 60000113 de57b948 de57b500 dc7bb440 df4bbcd0 00000000
[59014.072337] bc60: de57b950 60000113 df4bbcd0 df4bbc80 c04c259d c04c25a0 60000133 ffffffff
[59014.085233] [<c04c28db>] (__irq_svc+0x3b/0x5c) from [<c04c25a0>] (_raw_spin_unlock_irqrestore+0xc/0x10)
[59014.100437] [<c04c25a0>] (_raw_spin_unlock_irqrestore+0xc/0x10) from [<bf9c2089>] (ath9k_rx_tasklet+0x290/0x490 [ath9k_htc])
[59014.118267] [<bf9c2089>] (ath9k_rx_tasklet+0x290/0x490 [ath9k_htc]) from [<c0036d23>] (tasklet_action+0x3b/0x98)
[59014.134132] [<c0036d23>] (tasklet_action+0x3b/0x98) from [<c0036709>] (__do_softirq+0x99/0x16c)
[59014.147784] [<c0036709>] (__do_softirq+0x99/0x16c) from [<c00369f7>] (irq_exit+0x5b/0x5c)
[59014.160653] [<c00369f7>] (irq_exit+0x5b/0x5c) from [<c000cfc3>] (handle_IRQ+0x37/0x78)
[59014.173124] [<c000cfc3>] (handle_IRQ+0x37/0x78) from [<c00085df>] (omap3_intc_handle_irq+0x5f/0x68)
[59014.187225] [<c00085df>] (omap3_intc_handle_irq+0x5f/0x68) from [<c04c28db>](__irq_svc+0x3b/0x5c)
 
This bug can be see with low performance board, such as uniprocessor beagle bone board. 
 
Signed-off-by: Yuwei Zheng <yuweizheng@139.com>

---
 drivers/net/wireless/ath/ath9k/hif_usb.c       | 75 +++++++++++++++++++++++---
 drivers/net/wireless/ath/ath9k/hif_usb.h       |  9 ++++
 drivers/net/wireless/ath/ath9k/htc.h           | 19 +++++++
 drivers/net/wireless/ath/ath9k/htc_drv_debug.c | 53 ++++++++++++++++++
 drivers/net/wireless/ath/ath9k/htc_drv_txrx.c  | 44 +++++++++++++++
 5 files changed, 193 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c
index 8e7153b..d05cc0b 100644
--- a/drivers/net/wireless/ath/ath9k/hif_usb.c
+++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
@@ -640,6 +640,7 @@ static void ath9k_hif_usb_rx_cb(struct urb *urb)
 	struct hif_device_usb *hif_dev =
 		usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
 	int ret;
+	int delay;
 
 	if (!skb)
 		return;
@@ -658,7 +659,6 @@ static void ath9k_hif_usb_rx_cb(struct urb *urb)
 	default:
 		goto resubmit;
 	}
-
 	if (likely(urb->actual_length != 0)) {
 		skb_put(skb, urb->actual_length);
 		ath9k_hif_usb_rx_stream(hif_dev, skb);
@@ -667,12 +667,22 @@ static void ath9k_hif_usb_rx_cb(struct urb *urb)
 resubmit:
 	skb_reset_tail_pointer(skb);
 	skb_trim(skb, 0);
-
-	usb_anchor_urb(urb, &hif_dev->rx_submitted);
-	ret = usb_submit_urb(urb, GFP_ATOMIC);
-	if (ret) {
-		usb_unanchor_urb(urb);
-		goto free;
+	spin_lock(&hif_dev->aurfc_lock);
+	if (atomic_read(&hif_dev->aurfc_submit_delay) > 0 &&
+	    hif_dev->aurfc_active == 1) {
+		usb_anchor_urb(urb, &hif_dev->rx_delayed_submitted);
+		delay = atomic_read(&hif_dev->aurfc_submit_delay);
+		schedule_delayed_work(&hif_dev->aurfc_delayed_work,
+				      msecs_to_jiffies(delay));
+		spin_unlock(&hif_dev->aurfc_lock);
+	} else {
+		spin_unlock(&hif_dev->aurfc_lock);
+		usb_anchor_urb(urb, &hif_dev->rx_submitted);
+		ret = usb_submit_urb(urb, GFP_ATOMIC);
+		if (ret) {
+			usb_unanchor_urb(urb);
+			goto free;
+		}
 	}
 
 	return;
@@ -818,9 +828,51 @@ err:
 	return -ENOMEM;
 }
 
+static void aurfc_submit_handler(struct work_struct *work)
+{
+	struct hif_device_usb *hif_dev =
+		container_of(work,
+			     struct hif_device_usb,
+			     aurfc_delayed_work.work);
+	struct urb *urb = NULL;
+	struct sk_buff *skb = NULL;
+	int ret;
+	int loop_times = 0;
+
+	AURFC_STAT_INC(aurfc_called);
+	while (true) {
+		loop_times++;
+		if (loop_times > MAX_RX_URB_NUM)
+			atomic_add(AURFC_STEP,
+				   &hif_dev->aurfc_submit_delay);
+
+		urb = usb_get_from_anchor(
+				&hif_dev->rx_delayed_submitted);
+		if (urb) {
+			skb = (struct sk_buff *)urb->context;
+			ret = usb_submit_urb(urb, GFP_KERNEL);
+			if (ret != 0) {
+				usb_unanchor_urb(urb);
+				dev_kfree_skb_any(skb);
+				urb->context = NULL;
+			}
+		} else {
+			break;
+		}
+	}
+}
+
 static void ath9k_hif_usb_dealloc_rx_urbs(struct hif_device_usb *hif_dev)
 {
+	unsigned long flags;
+
+	spin_lock_irqsave(&hif_dev->aurfc_lock, flags);
+	hif_dev->aurfc_active = 0;
+	mod_delayed_work(system_wq, &hif_dev->aurfc_delayed_work, 0);
+	spin_unlock_irqrestore(&hif_dev->aurfc_lock, flags);
+	flush_delayed_work(&hif_dev->aurfc_delayed_work);
 	usb_kill_anchored_urbs(&hif_dev->rx_submitted);
+	usb_kill_anchored_urbs(&hif_dev->rx_delayed_submitted);
 }
 
 static int ath9k_hif_usb_alloc_rx_urbs(struct hif_device_usb *hif_dev)
@@ -830,8 +882,17 @@ static int ath9k_hif_usb_alloc_rx_urbs(struct hif_device_usb *hif_dev)
 	int i, ret;
 
 	init_usb_anchor(&hif_dev->rx_submitted);
+	init_usb_anchor(&hif_dev->rx_delayed_submitted);
+
 	spin_lock_init(&hif_dev->rx_lock);
 
+	/* add for adaptive usb receive control*/
+	atomic_set(&hif_dev->aurfc_submit_delay, 0);
+	INIT_DELAYED_WORK(&hif_dev->aurfc_delayed_work,
+			  aurfc_submit_handler);
+	spin_lock_init(&hif_dev->aurfc_lock);
+	hif_dev->aurfc_active = 1;
+
 	for (i = 0; i < MAX_RX_URB_NUM; i++) {
 
 		/* Allocate URB */
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.h b/drivers/net/wireless/ath/ath9k/hif_usb.h
index 51496e7..8e7b67e 100644
--- a/drivers/net/wireless/ath/ath9k/hif_usb.h
+++ b/drivers/net/wireless/ath/ath9k/hif_usb.h
@@ -41,6 +41,7 @@
 #define MAX_RX_URB_NUM  8
 #define MAX_RX_BUF_SIZE 16384
 #define MAX_PKT_NUM_IN_TRANSFER 10
+#define AURFC_STEP 70  /* ms */
 
 #define MAX_REG_OUT_URB_NUM  1
 #define MAX_REG_IN_URB_NUM   64
@@ -98,9 +99,17 @@ struct hif_device_usb {
 	struct hif_usb_tx tx;
 	struct usb_anchor regout_submitted;
 	struct usb_anchor rx_submitted;
+	struct usb_anchor rx_delayed_submitted;
 	struct usb_anchor reg_in_submitted;
 	struct usb_anchor mgmt_submitted;
 	struct sk_buff *remain_skb;
+
+	/* adaptive usb receive flow control */
+	struct delayed_work aurfc_delayed_work;
+	spinlock_t aurfc_lock; /* to protect work */
+	atomic_t  aurfc_submit_delay; /* ms */
+	int aurfc_active;
+
 	const char *fw_name;
 	int rx_remain_len;
 	int rx_pkt_len;
diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
index 9dde265..1586bd2 100644
--- a/drivers/net/wireless/ath/ath9k/htc.h
+++ b/drivers/net/wireless/ath/ath9k/htc.h
@@ -331,6 +331,13 @@ static inline struct ath9k_htc_tx_ctl *HTC_SKB_CB(struct sk_buff *skb)
 
 #define TX_QSTAT_INC(q) (priv->debug.tx_stats.queue_stats[q]++)
 
+#define AURFC_STAT_INC(c) \
+	(hif_dev->htc_handle->drv_priv->debug.aurfc_stats.c++)
+#define AURFC_STAT_ADD(c, a) \
+	(hif_dev->htc_handle->drv_priv->debug.aurfc_stats.c += a)
+#define AURFC_STAT_SET(c, a) \
+	(hif_dev->htc_handle->drv_priv->debug.aurfc_stats.c = a)
+
 void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv,
 			   struct ath_rx_status *rs);
 
@@ -352,11 +359,20 @@ struct ath_skbrx_stats {
 	u32 skb_dropped;
 };
 
+struct ath_aurfc_stats {
+	u32 aurfc_highwater;
+	u32 aurfc_lowwater;
+	u32 aurfc_wm_triggered;
+	u32 aurfc_submit_delay;
+	u32 aurfc_called;
+};
+
 struct ath9k_debug {
 	struct dentry *debugfs_phy;
 	struct ath_tx_stats tx_stats;
 	struct ath_rx_stats rx_stats;
 	struct ath_skbrx_stats skbrx_stats;
+	struct ath_aurfc_stats aurfc_stats;
 };
 
 void ath9k_htc_get_et_strings(struct ieee80211_hw *hw,
@@ -377,6 +393,9 @@ void ath9k_htc_get_et_stats(struct ieee80211_hw *hw,
 
 #define TX_QSTAT_INC(c) do { } while (0)
 
+#define AURFC_STAT_INC(c) do {} while (0)
+#define AURFC_STAT_ADD(c, a) do {} while (0)
+#define AURFC_STAT_SET(c, a) do {} while (0)
 static inline void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv,
 					 struct ath_rx_status *rs)
 {
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
index 8cef1ed..a6be9be 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
@@ -286,6 +286,54 @@ static const struct file_operations fops_skb_rx = {
 	.llseek = default_llseek,
 };
 
+static ssize_t read_file_aurfc(struct file *file,
+			       char __user *user_buf,
+			       size_t count, loff_t *ppos)
+{
+	struct ath9k_htc_priv *priv = file->private_data;
+	char *buf;
+	unsigned int len = 0, size = 1500;
+	ssize_t retval = 0;
+
+	buf = kzalloc(size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	len += scnprintf(buf + len, size - len,
+			"%20s : %10u\n", "High watermark",
+			priv->debug.aurfc_stats.aurfc_highwater);
+	len += scnprintf(buf + len, size - len,
+			"%20s : %10u\n", "Low watermark",
+			priv->debug.aurfc_stats.aurfc_lowwater);
+
+	len += scnprintf(buf + len, size - len,
+			"%20s : %10u\n", "WM triggered",
+			priv->debug.aurfc_stats.aurfc_wm_triggered);
+
+	len += scnprintf(buf + len, size - len,
+			"%20s : %10u\n", "Handler called",
+			priv->debug.aurfc_stats.aurfc_called);
+
+	len += scnprintf(buf + len, size - len,
+			"%20s : %10u\n", "Submit delay",
+			priv->debug.aurfc_stats.aurfc_submit_delay);
+	if (len > size)
+		len = size;
+
+	retval = simple_read_from_buffer(user_buf, count,
+					 ppos, buf, len);
+	kfree(buf);
+
+	return retval;
+}
+
+static const struct file_operations fops_aurfc = {
+	.read = read_file_aurfc,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
 static ssize_t read_file_slot(struct file *file, char __user *user_buf,
 			      size_t count, loff_t *ppos)
 {
@@ -518,7 +566,12 @@ int ath9k_htc_init_debug(struct ath_hw *ah)
 	debugfs_create_file("skb_rx", S_IRUSR, priv->debug.debugfs_phy,
 			    priv, &fops_skb_rx);
 
+	debugfs_create_file("aurfc_stats", S_IRUSR,
+			    priv->debug.debugfs_phy,
+			    priv, &fops_aurfc);
+
 	ath9k_cmn_debug_recv(priv->debug.debugfs_phy, &priv->debug.rx_stats);
+
 	ath9k_cmn_debug_phy_err(priv->debug.debugfs_phy, &priv->debug.rx_stats);
 
 	debugfs_create_file("slot", S_IRUSR, priv->debug.debugfs_phy,
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
index a0f58e2..939d008 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
@@ -1061,7 +1061,28 @@ void ath9k_rx_tasklet(unsigned long data)
 	unsigned long flags;
 	struct ieee80211_hdr *hdr;
 
+	/* add for adaptive usb receive flow control*/
+	int looptimes = 0;
+	int highwatermark = ATH9K_HTC_RXBUF*3/4;
+	int lowwatermark = ATH9K_HTC_RXBUF/32;
+	unsigned int delay = 0;
+
+	struct htc_target *htc = priv->htc;
+	struct hif_device_usb *hif_dev = htc->hif_dev;
+
+	AURFC_STAT_SET(aurfc_highwater, highwatermark);
+	AURFC_STAT_SET(aurfc_lowwater, lowwatermark);
+
 	do {
+		looptimes++;
+		if (looptimes > highwatermark) {
+			delay = looptimes*AURFC_STEP;
+			atomic_set(&hif_dev->aurfc_submit_delay,
+				   delay);
+			AURFC_STAT_INC(aurfc_wm_triggered);
+			AURFC_STAT_SET(aurfc_submit_delay, delay);
+		}
+
 		spin_lock_irqsave(&priv->rx.rxbuflock, flags);
 		list_for_each_entry(tmp_buf, &priv->rx.rxbuf, list) {
 			if (tmp_buf->in_process) {
@@ -1072,6 +1093,22 @@ void ath9k_rx_tasklet(unsigned long data)
 
 		if (rxbuf == NULL) {
 			spin_unlock_irqrestore(&priv->rx.rxbuflock, flags);
+			spin_lock_irqsave(&hif_dev->aurfc_lock,
+					  flags);
+			if (atomic_read(
+				&hif_dev->aurfc_submit_delay) > 0 &&
+			    hif_dev->aurfc_active > 0)
+				mod_delayed_work(system_wq,
+					&hif_dev->aurfc_delayed_work,
+						 0);
+			spin_unlock_irqrestore(&hif_dev->aurfc_lock,
+					       flags);
+			if (looptimes < lowwatermark) {
+				atomic_set(&hif_dev->aurfc_submit_delay
+					   , 0);
+				AURFC_STAT_SET(aurfc_submit_delay,
+					       0);
+			}
 			break;
 		}
 
@@ -1114,6 +1151,10 @@ void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb,
 	struct ath_common *common = ath9k_hw_common(ah);
 	struct ath9k_htc_rxbuf *rxbuf = NULL, *tmp_buf = NULL;
 
+	struct htc_target *htc = priv->htc;
+	struct hif_device_usb *hif_dev = htc->hif_dev;
+	int delay = ATH9K_HTC_RXBUF * AURFC_STEP;
+
 	spin_lock(&priv->rx.rxbuflock);
 	list_for_each_entry(tmp_buf, &priv->rx.rxbuf, list) {
 		if (!tmp_buf->in_process) {
@@ -1124,6 +1165,9 @@ void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb,
 	spin_unlock(&priv->rx.rxbuflock);
 
 	if (rxbuf == NULL) {
+		atomic_set(&hif_dev->aurfc_submit_delay, delay);
+		AURFC_STAT_INC(aurfc_wm_triggered);
+		AURFC_STAT_SET(aurfc_submit_delay, delay);
 		ath_dbg(common, ANY, "No free RX buffer\n");
 		goto err;
 	}
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH] ath9k_htc: add adaptive usb receive flow control to repair soft lockup with monitor mode
  2015-02-06 10:46 yuweizheng
@ 2015-02-06 22:24 ` Oleksij Rempel
  2015-02-09  7:20   ` Yuwei Zheng
  0 siblings, 1 reply; 9+ messages in thread
From: Oleksij Rempel @ 2015-02-06 22:24 UTC (permalink / raw)
  To: yuweizheng, linux-kernel, ath9k-devel, linux-wireless, kvalo,
	ath9k-devel
  Cc: netdev, zhengyuwei

[-- Attachment #1: Type: text/plain, Size: 14700 bytes --]

formatting of this patch is still badly broken.
./scripts/checkpatch.pl ~/Downloads/patch.eml
....
ERROR: DOS line endings
#442: FILE: drivers/net/wireless/ath/ath9k/htc_drv_txrx.c:1170:
+^I^IAURFC_STAT_SET(aurfc_submit_delay, delay);^M$

total: 195 errors, 6 warnings, 1 checks, 310 lines checked


Am 06.02.2015 um 11:46 schrieb yuweizheng@139.com:
> From: Yuwei Zheng <yuweizheng@139.com>
> 
> In the environment with heavy wifi traffic, set the ar9271 into monitor mode, will
> trigger a deadloop panic.
>  
> The ath9k_hif_usb_rx_cb function excute on  the interrupt context, and ath9k_rx_tasklet excute
> on the soft irq context. In other words, the ath9k_hif_usb_rx_cb have more chance to excute than
> ath9k_rx_tasklet.  So in the worst condition,  the rx.rxbuf receive list is always full,
> and the do {}while(true) loop will not be break. The kernel get a soft lockup panic. 
>  
> [59011.007210] BUG: soft lockup - CPU#0 stuck for 23s!
> [kworker/0:0:30609]
> [59011.030560] BUG: scheduling while atomic: kworker/0:0/30609/0x40010100
> [59013.804486] BUG: scheduling while atomic: kworker/0:0/30609/0x40010100
> [59013.858522] Kernel panic - not syncing: softlockup: hung tasks
>  
> [59014.038891] Exception stack(0xdf4bbc38 to 0xdf4bbc80)
> [59014.046834] bc20:                                                       de57b950 60000113
> [59014.059579] bc40: 00000000 bb32bb32 60000113 de57b948 de57b500 dc7bb440 df4bbcd0 00000000
> [59014.072337] bc60: de57b950 60000113 df4bbcd0 df4bbc80 c04c259d c04c25a0 60000133 ffffffff
> [59014.085233] [<c04c28db>] (__irq_svc+0x3b/0x5c) from [<c04c25a0>] (_raw_spin_unlock_irqrestore+0xc/0x10)
> [59014.100437] [<c04c25a0>] (_raw_spin_unlock_irqrestore+0xc/0x10) from [<bf9c2089>] (ath9k_rx_tasklet+0x290/0x490 [ath9k_htc])
> [59014.118267] [<bf9c2089>] (ath9k_rx_tasklet+0x290/0x490 [ath9k_htc]) from [<c0036d23>] (tasklet_action+0x3b/0x98)
> [59014.134132] [<c0036d23>] (tasklet_action+0x3b/0x98) from [<c0036709>] (__do_softirq+0x99/0x16c)
> [59014.147784] [<c0036709>] (__do_softirq+0x99/0x16c) from [<c00369f7>] (irq_exit+0x5b/0x5c)
> [59014.160653] [<c00369f7>] (irq_exit+0x5b/0x5c) from [<c000cfc3>] (handle_IRQ+0x37/0x78)
> [59014.173124] [<c000cfc3>] (handle_IRQ+0x37/0x78) from [<c00085df>] (omap3_intc_handle_irq+0x5f/0x68)
> [59014.187225] [<c00085df>] (omap3_intc_handle_irq+0x5f/0x68) from [<c04c28db>](__irq_svc+0x3b/0x5c)
>  
> This bug can be see with low performance board, such as uniprocessor beagle bone board. 
>  
> Signed-off-by: Yuwei Zheng <yuweizheng@139.com>
> 
> ---
>  drivers/net/wireless/ath/ath9k/hif_usb.c       | 74 +++++++++++++++++++++++---
>  drivers/net/wireless/ath/ath9k/hif_usb.h       |  9 ++++
>  drivers/net/wireless/ath/ath9k/htc.h           | 19 +++++++
>  drivers/net/wireless/ath/ath9k/htc_drv_debug.c | 53 ++++++++++++++++++
>  drivers/net/wireless/ath/ath9k/htc_drv_txrx.c  | 44 +++++++++++++++
>  5 files changed, 192 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c
> index 8e7153b..90ee568 100644
> --- a/drivers/net/wireless/ath/ath9k/hif_usb.c
> +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
> @@ -640,6 +640,7 @@ static void ath9k_hif_usb_rx_cb(struct urb *urb)
>  	struct hif_device_usb *hif_dev =
>  		usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
>  	int ret;
> +	int delay;
>  
>  	if (!skb)
>  		return;
> @@ -658,7 +659,6 @@ static void ath9k_hif_usb_rx_cb(struct urb *urb)
>  	default:
>  		goto resubmit;
>  	}
> -
>  	if (likely(urb->actual_length != 0)) {
>  		skb_put(skb, urb->actual_length);
>  		ath9k_hif_usb_rx_stream(hif_dev, skb);
> @@ -667,12 +667,22 @@ static void ath9k_hif_usb_rx_cb(struct urb *urb)
>  resubmit:
>  	skb_reset_tail_pointer(skb);
>  	skb_trim(skb, 0);
> -
> -	usb_anchor_urb(urb, &hif_dev->rx_submitted);
> -	ret = usb_submit_urb(urb, GFP_ATOMIC);
> -	if (ret) {
> -		usb_unanchor_urb(urb);
> -		goto free;
> +	spin_lock(&hif_dev->aurfc_lock);
> +	if (atomic_read(&hif_dev->aurfc_submit_delay) > 0 &&
> +	    hif_dev->aurfc_active == 1) {
> +		usb_anchor_urb(urb, &hif_dev->rx_delayed_submitted);
> +		delay = atomic_read(&hif_dev->aurfc_submit_delay);
> +		schedule_delayed_work(&hif_dev->aurfc_delayed_work,
> +				      msecs_to_jiffies(delay));
> +		spin_unlock(&hif_dev->aurfc_lock);
> +	} else {
> +		spin_unlock(&hif_dev->aurfc_lock);
> +		usb_anchor_urb(urb, &hif_dev->rx_submitted);
> +		ret = usb_submit_urb(urb, GFP_ATOMIC);
> +		if (ret) {
> +			usb_unanchor_urb(urb);
> +			goto free;
> +		}
>  	}
>  
>  	return;
> @@ -818,9 +828,50 @@ err:
>  	return -ENOMEM;
>  }
>  
> +static void aurfc_submit_handler(struct work_struct *work)
> +{
> +	struct hif_device_usb *hif_dev =
> +		container_of(work,
> +			     struct hif_device_usb,
> +			     aurfc_delayed_work.work);
> +
> +	struct urb *urb = NULL;
> +	struct sk_buff *skb = NULL;
> +	int ret;
> +	int loop_times = 0;
> +
> +	while (true) {
> +		loop_times++;
> +		if (loop_times > MAX_RX_URB_NUM)
> +			atomic_add(AURFC_STEP,
> +				   &hif_dev->aurfc_submit_delay);
> +
> +		urb = usb_get_from_anchor(
> +				&hif_dev->rx_delayed_submitted);
> +		if (urb) {
> +			skb = (struct sk_buff *)urb->context;
> +			ret = usb_submit_urb(urb, GFP_KERNEL);
> +			if (ret != 0) {
> +				usb_unanchor_urb(urb);
> +				dev_kfree_skb_any(skb);
> +				urb->context = NULL;
> +			}
> +		} else {
> +			break;
> +		}
> +	}
> +}
> +
>  static void ath9k_hif_usb_dealloc_rx_urbs(struct hif_device_usb *hif_dev)
>  {
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&hif_dev->aurfc_lock, flags);
> +	hif_dev->aurfc_active = 0;
> +	mod_delayed_work(system_wq, &hif_dev->aurfc_delayed_work, 0);
> +	spin_unlock_irqrestore(&hif_dev->aurfc_lock, flags);
>  	usb_kill_anchored_urbs(&hif_dev->rx_submitted);
> +	usb_kill_anchored_urbs(&hif_dev->rx_delayed_submitted);
>  }
>  
>  static int ath9k_hif_usb_alloc_rx_urbs(struct hif_device_usb *hif_dev)
> @@ -830,8 +881,17 @@ static int ath9k_hif_usb_alloc_rx_urbs(struct hif_device_usb *hif_dev)
>  	int i, ret;
>  
>  	init_usb_anchor(&hif_dev->rx_submitted);
> +	init_usb_anchor(&hif_dev->rx_delayed_submitted);
> +
>  	spin_lock_init(&hif_dev->rx_lock);
>  
> +	/* add for adaptive usb receive control*/
> +	atomic_set(&hif_dev->aurfc_submit_delay, 0);
> +	INIT_DELAYED_WORK(&hif_dev->aurfc_delayed_work,
> +			  aurfc_submit_handler);
> +	spin_lock_init(&hif_dev->aurfc_lock);
> +	hif_dev->aurfc_active = 1;
> +
>  	for (i = 0; i < MAX_RX_URB_NUM; i++) {
>  
>  		/* Allocate URB */
> diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.h b/drivers/net/wireless/ath/ath9k/hif_usb.h
> index 51496e7..2050a74 100644
> --- a/drivers/net/wireless/ath/ath9k/hif_usb.h
> +++ b/drivers/net/wireless/ath/ath9k/hif_usb.h
> @@ -41,6 +41,7 @@
>  #define MAX_RX_URB_NUM  8
>  #define MAX_RX_BUF_SIZE 16384
>  #define MAX_PKT_NUM_IN_TRANSFER 10
> +#define AURFC_STEP 10  /* ms */
>  
>  #define MAX_REG_OUT_URB_NUM  1
>  #define MAX_REG_IN_URB_NUM   64
> @@ -98,9 +99,17 @@ struct hif_device_usb {
>  	struct hif_usb_tx tx;
>  	struct usb_anchor regout_submitted;
>  	struct usb_anchor rx_submitted;
> +	struct usb_anchor rx_delayed_submitted;
>  	struct usb_anchor reg_in_submitted;
>  	struct usb_anchor mgmt_submitted;
>  	struct sk_buff *remain_skb;
> +
> +	/* adaptive usb receive flow control */
> +	struct delayed_work aurfc_delayed_work;
> +	spinlock_t aurfc_lock; /* to protect work */
> +	atomic_t  aurfc_submit_delay; /* ms */
> +	int aurfc_active;
> +
>  	const char *fw_name;
>  	int rx_remain_len;
>  	int rx_pkt_len;
> diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
> index 9dde265..1586bd2 100644
> --- a/drivers/net/wireless/ath/ath9k/htc.h
> +++ b/drivers/net/wireless/ath/ath9k/htc.h
> @@ -331,6 +331,13 @@ static inline struct ath9k_htc_tx_ctl *HTC_SKB_CB(struct sk_buff *skb)
>  
>  #define TX_QSTAT_INC(q) (priv->debug.tx_stats.queue_stats[q]++)
>  
> +#define AURFC_STAT_INC(c) \
> +	(hif_dev->htc_handle->drv_priv->debug.aurfc_stats.c++)
> +#define AURFC_STAT_ADD(c, a) \
> +	(hif_dev->htc_handle->drv_priv->debug.aurfc_stats.c += a)
> +#define AURFC_STAT_SET(c, a) \
> +	(hif_dev->htc_handle->drv_priv->debug.aurfc_stats.c = a)
> +
>  void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv,
>  			   struct ath_rx_status *rs);
>  
> @@ -352,11 +359,20 @@ struct ath_skbrx_stats {
>  	u32 skb_dropped;
>  };
>  
> +struct ath_aurfc_stats {
> +	u32 aurfc_highwater;
> +	u32 aurfc_lowwater;
> +	u32 aurfc_wm_triggered;
> +	u32 aurfc_submit_delay;
> +	u32 aurfc_called;
> +};
> +
>  struct ath9k_debug {
>  	struct dentry *debugfs_phy;
>  	struct ath_tx_stats tx_stats;
>  	struct ath_rx_stats rx_stats;
>  	struct ath_skbrx_stats skbrx_stats;
> +	struct ath_aurfc_stats aurfc_stats;
>  };
>  
>  void ath9k_htc_get_et_strings(struct ieee80211_hw *hw,
> @@ -377,6 +393,9 @@ void ath9k_htc_get_et_stats(struct ieee80211_hw *hw,
>  
>  #define TX_QSTAT_INC(c) do { } while (0)
>  
> +#define AURFC_STAT_INC(c) do {} while (0)
> +#define AURFC_STAT_ADD(c, a) do {} while (0)
> +#define AURFC_STAT_SET(c, a) do {} while (0)
>  static inline void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv,
>  					 struct ath_rx_status *rs)
>  {
> diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
> index 8cef1ed..a6be9be 100644
> --- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
> +++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
> @@ -286,6 +286,54 @@ static const struct file_operations fops_skb_rx = {
>  	.llseek = default_llseek,
>  };
>  
> +static ssize_t read_file_aurfc(struct file *file,
> +			       char __user *user_buf,
> +			       size_t count, loff_t *ppos)
> +{
> +	struct ath9k_htc_priv *priv = file->private_data;
> +	char *buf;
> +	unsigned int len = 0, size = 1500;
> +	ssize_t retval = 0;
> +
> +	buf = kzalloc(size, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	len += scnprintf(buf + len, size - len,
> +			"%20s : %10u\n", "High watermark",
> +			priv->debug.aurfc_stats.aurfc_highwater);
> +	len += scnprintf(buf + len, size - len,
> +			"%20s : %10u\n", "Low watermark",
> +			priv->debug.aurfc_stats.aurfc_lowwater);
> +
> +	len += scnprintf(buf + len, size - len,
> +			"%20s : %10u\n", "WM triggered",
> +			priv->debug.aurfc_stats.aurfc_wm_triggered);
> +
> +	len += scnprintf(buf + len, size - len,
> +			"%20s : %10u\n", "Handler called",
> +			priv->debug.aurfc_stats.aurfc_called);
> +
> +	len += scnprintf(buf + len, size - len,
> +			"%20s : %10u\n", "Submit delay",
> +			priv->debug.aurfc_stats.aurfc_submit_delay);
> +	if (len > size)
> +		len = size;
> +
> +	retval = simple_read_from_buffer(user_buf, count,
> +					 ppos, buf, len);
> +	kfree(buf);
> +
> +	return retval;
> +}
> +
> +static const struct file_operations fops_aurfc = {
> +	.read = read_file_aurfc,
> +	.open = simple_open,
> +	.owner = THIS_MODULE,
> +	.llseek = default_llseek,
> +};
> +
>  static ssize_t read_file_slot(struct file *file, char __user *user_buf,
>  			      size_t count, loff_t *ppos)
>  {
> @@ -518,7 +566,12 @@ int ath9k_htc_init_debug(struct ath_hw *ah)
>  	debugfs_create_file("skb_rx", S_IRUSR, priv->debug.debugfs_phy,
>  			    priv, &fops_skb_rx);
>  
> +	debugfs_create_file("aurfc_stats", S_IRUSR,
> +			    priv->debug.debugfs_phy,
> +			    priv, &fops_aurfc);
> +
>  	ath9k_cmn_debug_recv(priv->debug.debugfs_phy, &priv->debug.rx_stats);
> +
>  	ath9k_cmn_debug_phy_err(priv->debug.debugfs_phy, &priv->debug.rx_stats);
>  
>  	debugfs_create_file("slot", S_IRUSR, priv->debug.debugfs_phy,
> diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
> index a0f58e2..939d008 100644
> --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
> +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
> @@ -1061,7 +1061,28 @@ void ath9k_rx_tasklet(unsigned long data)
>  	unsigned long flags;
>  	struct ieee80211_hdr *hdr;
>  
> +	/* add for adaptive usb receive flow control*/
> +	int looptimes = 0;
> +	int highwatermark = ATH9K_HTC_RXBUF*3/4;
> +	int lowwatermark = ATH9K_HTC_RXBUF/32;
> +	unsigned int delay = 0;
> +
> +	struct htc_target *htc = priv->htc;
> +	struct hif_device_usb *hif_dev = htc->hif_dev;
> +
> +	AURFC_STAT_SET(aurfc_highwater, highwatermark);
> +	AURFC_STAT_SET(aurfc_lowwater, lowwatermark);
> +
>  	do {
> +		looptimes++;
> +		if (looptimes > highwatermark) {
> +			delay = looptimes*AURFC_STEP;
> +			atomic_set(&hif_dev->aurfc_submit_delay,
> +				   delay);
> +			AURFC_STAT_INC(aurfc_wm_triggered);
> +			AURFC_STAT_SET(aurfc_submit_delay, delay);
> +		}
> +
>  		spin_lock_irqsave(&priv->rx.rxbuflock, flags);
>  		list_for_each_entry(tmp_buf, &priv->rx.rxbuf, list) {
>  			if (tmp_buf->in_process) {
> @@ -1072,6 +1093,22 @@ void ath9k_rx_tasklet(unsigned long data)
>  
>  		if (rxbuf == NULL) {
>  			spin_unlock_irqrestore(&priv->rx.rxbuflock, flags);
> +			spin_lock_irqsave(&hif_dev->aurfc_lock,
> +					  flags);
> +			if (atomic_read(
> +				&hif_dev->aurfc_submit_delay) > 0 &&
> +			    hif_dev->aurfc_active > 0)
> +				mod_delayed_work(system_wq,
> +					&hif_dev->aurfc_delayed_work,
> +						 0);
> +			spin_unlock_irqrestore(&hif_dev->aurfc_lock,
> +					       flags);
> +			if (looptimes < lowwatermark) {
> +				atomic_set(&hif_dev->aurfc_submit_delay
> +					   , 0);
> +				AURFC_STAT_SET(aurfc_submit_delay,
> +					       0);
> +			}
>  			break;
>  		}
>  
> @@ -1114,6 +1151,10 @@ void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb,
>  	struct ath_common *common = ath9k_hw_common(ah);
>  	struct ath9k_htc_rxbuf *rxbuf = NULL, *tmp_buf = NULL;
>  
> +	struct htc_target *htc = priv->htc;
> +	struct hif_device_usb *hif_dev = htc->hif_dev;
> +	int delay = ATH9K_HTC_RXBUF * AURFC_STEP;
> +
>  	spin_lock(&priv->rx.rxbuflock);
>  	list_for_each_entry(tmp_buf, &priv->rx.rxbuf, list) {
>  		if (!tmp_buf->in_process) {
> @@ -1124,6 +1165,9 @@ void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb,
>  	spin_unlock(&priv->rx.rxbuflock);
>  
>  	if (rxbuf == NULL) {
> +		atomic_set(&hif_dev->aurfc_submit_delay, delay);
> +		AURFC_STAT_INC(aurfc_wm_triggered);
> +		AURFC_STAT_SET(aurfc_submit_delay, delay);
>  		ath_dbg(common, ANY, "No free RX buffer\n");
>  		goto err;
>  	}
> 


-- 
Regards,
Oleksij


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 213 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH] ath9k_htc: add adaptive usb receive flow control to repair soft lockup with monitor mode
@ 2015-02-06 10:46 yuweizheng
  2015-02-06 22:24 ` Oleksij Rempel
  0 siblings, 1 reply; 9+ messages in thread
From: yuweizheng @ 2015-02-06 10:46 UTC (permalink / raw)
  To: linux-kernel, ath9k-devel, linux-wireless, kvalo, ath9k-devel
  Cc: netdev, zhengyuwei, Yuwei Zheng

From: Yuwei Zheng <yuweizheng@139.com>

In the environment with heavy wifi traffic, set the ar9271 into monitor mode, will
trigger a deadloop panic.
 
The ath9k_hif_usb_rx_cb function excute on  the interrupt context, and ath9k_rx_tasklet excute
on the soft irq context. In other words, the ath9k_hif_usb_rx_cb have more chance to excute than
ath9k_rx_tasklet.  So in the worst condition,  the rx.rxbuf receive list is always full,
and the do {}while(true) loop will not be break. The kernel get a soft lockup panic. 
 
[59011.007210] BUG: soft lockup - CPU#0 stuck for 23s!
[kworker/0:0:30609]
[59011.030560] BUG: scheduling while atomic: kworker/0:0/30609/0x40010100
[59013.804486] BUG: scheduling while atomic: kworker/0:0/30609/0x40010100
[59013.858522] Kernel panic - not syncing: softlockup: hung tasks
 
[59014.038891] Exception stack(0xdf4bbc38 to 0xdf4bbc80)
[59014.046834] bc20:                                                       de57b950 60000113
[59014.059579] bc40: 00000000 bb32bb32 60000113 de57b948 de57b500 dc7bb440 df4bbcd0 00000000
[59014.072337] bc60: de57b950 60000113 df4bbcd0 df4bbc80 c04c259d c04c25a0 60000133 ffffffff
[59014.085233] [<c04c28db>] (__irq_svc+0x3b/0x5c) from [<c04c25a0>] (_raw_spin_unlock_irqrestore+0xc/0x10)
[59014.100437] [<c04c25a0>] (_raw_spin_unlock_irqrestore+0xc/0x10) from [<bf9c2089>] (ath9k_rx_tasklet+0x290/0x490 [ath9k_htc])
[59014.118267] [<bf9c2089>] (ath9k_rx_tasklet+0x290/0x490 [ath9k_htc]) from [<c0036d23>] (tasklet_action+0x3b/0x98)
[59014.134132] [<c0036d23>] (tasklet_action+0x3b/0x98) from [<c0036709>] (__do_softirq+0x99/0x16c)
[59014.147784] [<c0036709>] (__do_softirq+0x99/0x16c) from [<c00369f7>] (irq_exit+0x5b/0x5c)
[59014.160653] [<c00369f7>] (irq_exit+0x5b/0x5c) from [<c000cfc3>] (handle_IRQ+0x37/0x78)
[59014.173124] [<c000cfc3>] (handle_IRQ+0x37/0x78) from [<c00085df>] (omap3_intc_handle_irq+0x5f/0x68)
[59014.187225] [<c00085df>] (omap3_intc_handle_irq+0x5f/0x68) from [<c04c28db>](__irq_svc+0x3b/0x5c)
 
This bug can be see with low performance board, such as uniprocessor beagle bone board. 
 
Signed-off-by: Yuwei Zheng <yuweizheng@139.com>

---
 drivers/net/wireless/ath/ath9k/hif_usb.c       | 74 +++++++++++++++++++++++---
 drivers/net/wireless/ath/ath9k/hif_usb.h       |  9 ++++
 drivers/net/wireless/ath/ath9k/htc.h           | 19 +++++++
 drivers/net/wireless/ath/ath9k/htc_drv_debug.c | 53 ++++++++++++++++++
 drivers/net/wireless/ath/ath9k/htc_drv_txrx.c  | 44 +++++++++++++++
 5 files changed, 192 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c
index 8e7153b..90ee568 100644
--- a/drivers/net/wireless/ath/ath9k/hif_usb.c
+++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
@@ -640,6 +640,7 @@ static void ath9k_hif_usb_rx_cb(struct urb *urb)
 	struct hif_device_usb *hif_dev =
 		usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0));
 	int ret;
+	int delay;
 
 	if (!skb)
 		return;
@@ -658,7 +659,6 @@ static void ath9k_hif_usb_rx_cb(struct urb *urb)
 	default:
 		goto resubmit;
 	}
-
 	if (likely(urb->actual_length != 0)) {
 		skb_put(skb, urb->actual_length);
 		ath9k_hif_usb_rx_stream(hif_dev, skb);
@@ -667,12 +667,22 @@ static void ath9k_hif_usb_rx_cb(struct urb *urb)
 resubmit:
 	skb_reset_tail_pointer(skb);
 	skb_trim(skb, 0);
-
-	usb_anchor_urb(urb, &hif_dev->rx_submitted);
-	ret = usb_submit_urb(urb, GFP_ATOMIC);
-	if (ret) {
-		usb_unanchor_urb(urb);
-		goto free;
+	spin_lock(&hif_dev->aurfc_lock);
+	if (atomic_read(&hif_dev->aurfc_submit_delay) > 0 &&
+	    hif_dev->aurfc_active == 1) {
+		usb_anchor_urb(urb, &hif_dev->rx_delayed_submitted);
+		delay = atomic_read(&hif_dev->aurfc_submit_delay);
+		schedule_delayed_work(&hif_dev->aurfc_delayed_work,
+				      msecs_to_jiffies(delay));
+		spin_unlock(&hif_dev->aurfc_lock);
+	} else {
+		spin_unlock(&hif_dev->aurfc_lock);
+		usb_anchor_urb(urb, &hif_dev->rx_submitted);
+		ret = usb_submit_urb(urb, GFP_ATOMIC);
+		if (ret) {
+			usb_unanchor_urb(urb);
+			goto free;
+		}
 	}
 
 	return;
@@ -818,9 +828,50 @@ err:
 	return -ENOMEM;
 }
 
+static void aurfc_submit_handler(struct work_struct *work)
+{
+	struct hif_device_usb *hif_dev =
+		container_of(work,
+			     struct hif_device_usb,
+			     aurfc_delayed_work.work);
+
+	struct urb *urb = NULL;
+	struct sk_buff *skb = NULL;
+	int ret;
+	int loop_times = 0;
+
+	while (true) {
+		loop_times++;
+		if (loop_times > MAX_RX_URB_NUM)
+			atomic_add(AURFC_STEP,
+				   &hif_dev->aurfc_submit_delay);
+
+		urb = usb_get_from_anchor(
+				&hif_dev->rx_delayed_submitted);
+		if (urb) {
+			skb = (struct sk_buff *)urb->context;
+			ret = usb_submit_urb(urb, GFP_KERNEL);
+			if (ret != 0) {
+				usb_unanchor_urb(urb);
+				dev_kfree_skb_any(skb);
+				urb->context = NULL;
+			}
+		} else {
+			break;
+		}
+	}
+}
+
 static void ath9k_hif_usb_dealloc_rx_urbs(struct hif_device_usb *hif_dev)
 {
+	unsigned long flags;
+
+	spin_lock_irqsave(&hif_dev->aurfc_lock, flags);
+	hif_dev->aurfc_active = 0;
+	mod_delayed_work(system_wq, &hif_dev->aurfc_delayed_work, 0);
+	spin_unlock_irqrestore(&hif_dev->aurfc_lock, flags);
 	usb_kill_anchored_urbs(&hif_dev->rx_submitted);
+	usb_kill_anchored_urbs(&hif_dev->rx_delayed_submitted);
 }
 
 static int ath9k_hif_usb_alloc_rx_urbs(struct hif_device_usb *hif_dev)
@@ -830,8 +881,17 @@ static int ath9k_hif_usb_alloc_rx_urbs(struct hif_device_usb *hif_dev)
 	int i, ret;
 
 	init_usb_anchor(&hif_dev->rx_submitted);
+	init_usb_anchor(&hif_dev->rx_delayed_submitted);
+
 	spin_lock_init(&hif_dev->rx_lock);
 
+	/* add for adaptive usb receive control*/
+	atomic_set(&hif_dev->aurfc_submit_delay, 0);
+	INIT_DELAYED_WORK(&hif_dev->aurfc_delayed_work,
+			  aurfc_submit_handler);
+	spin_lock_init(&hif_dev->aurfc_lock);
+	hif_dev->aurfc_active = 1;
+
 	for (i = 0; i < MAX_RX_URB_NUM; i++) {
 
 		/* Allocate URB */
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.h b/drivers/net/wireless/ath/ath9k/hif_usb.h
index 51496e7..2050a74 100644
--- a/drivers/net/wireless/ath/ath9k/hif_usb.h
+++ b/drivers/net/wireless/ath/ath9k/hif_usb.h
@@ -41,6 +41,7 @@
 #define MAX_RX_URB_NUM  8
 #define MAX_RX_BUF_SIZE 16384
 #define MAX_PKT_NUM_IN_TRANSFER 10
+#define AURFC_STEP 10  /* ms */
 
 #define MAX_REG_OUT_URB_NUM  1
 #define MAX_REG_IN_URB_NUM   64
@@ -98,9 +99,17 @@ struct hif_device_usb {
 	struct hif_usb_tx tx;
 	struct usb_anchor regout_submitted;
 	struct usb_anchor rx_submitted;
+	struct usb_anchor rx_delayed_submitted;
 	struct usb_anchor reg_in_submitted;
 	struct usb_anchor mgmt_submitted;
 	struct sk_buff *remain_skb;
+
+	/* adaptive usb receive flow control */
+	struct delayed_work aurfc_delayed_work;
+	spinlock_t aurfc_lock; /* to protect work */
+	atomic_t  aurfc_submit_delay; /* ms */
+	int aurfc_active;
+
 	const char *fw_name;
 	int rx_remain_len;
 	int rx_pkt_len;
diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
index 9dde265..1586bd2 100644
--- a/drivers/net/wireless/ath/ath9k/htc.h
+++ b/drivers/net/wireless/ath/ath9k/htc.h
@@ -331,6 +331,13 @@ static inline struct ath9k_htc_tx_ctl *HTC_SKB_CB(struct sk_buff *skb)
 
 #define TX_QSTAT_INC(q) (priv->debug.tx_stats.queue_stats[q]++)
 
+#define AURFC_STAT_INC(c) \
+	(hif_dev->htc_handle->drv_priv->debug.aurfc_stats.c++)
+#define AURFC_STAT_ADD(c, a) \
+	(hif_dev->htc_handle->drv_priv->debug.aurfc_stats.c += a)
+#define AURFC_STAT_SET(c, a) \
+	(hif_dev->htc_handle->drv_priv->debug.aurfc_stats.c = a)
+
 void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv,
 			   struct ath_rx_status *rs);
 
@@ -352,11 +359,20 @@ struct ath_skbrx_stats {
 	u32 skb_dropped;
 };
 
+struct ath_aurfc_stats {
+	u32 aurfc_highwater;
+	u32 aurfc_lowwater;
+	u32 aurfc_wm_triggered;
+	u32 aurfc_submit_delay;
+	u32 aurfc_called;
+};
+
 struct ath9k_debug {
 	struct dentry *debugfs_phy;
 	struct ath_tx_stats tx_stats;
 	struct ath_rx_stats rx_stats;
 	struct ath_skbrx_stats skbrx_stats;
+	struct ath_aurfc_stats aurfc_stats;
 };
 
 void ath9k_htc_get_et_strings(struct ieee80211_hw *hw,
@@ -377,6 +393,9 @@ void ath9k_htc_get_et_stats(struct ieee80211_hw *hw,
 
 #define TX_QSTAT_INC(c) do { } while (0)
 
+#define AURFC_STAT_INC(c) do {} while (0)
+#define AURFC_STAT_ADD(c, a) do {} while (0)
+#define AURFC_STAT_SET(c, a) do {} while (0)
 static inline void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv,
 					 struct ath_rx_status *rs)
 {
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
index 8cef1ed..a6be9be 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
@@ -286,6 +286,54 @@ static const struct file_operations fops_skb_rx = {
 	.llseek = default_llseek,
 };
 
+static ssize_t read_file_aurfc(struct file *file,
+			       char __user *user_buf,
+			       size_t count, loff_t *ppos)
+{
+	struct ath9k_htc_priv *priv = file->private_data;
+	char *buf;
+	unsigned int len = 0, size = 1500;
+	ssize_t retval = 0;
+
+	buf = kzalloc(size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	len += scnprintf(buf + len, size - len,
+			"%20s : %10u\n", "High watermark",
+			priv->debug.aurfc_stats.aurfc_highwater);
+	len += scnprintf(buf + len, size - len,
+			"%20s : %10u\n", "Low watermark",
+			priv->debug.aurfc_stats.aurfc_lowwater);
+
+	len += scnprintf(buf + len, size - len,
+			"%20s : %10u\n", "WM triggered",
+			priv->debug.aurfc_stats.aurfc_wm_triggered);
+
+	len += scnprintf(buf + len, size - len,
+			"%20s : %10u\n", "Handler called",
+			priv->debug.aurfc_stats.aurfc_called);
+
+	len += scnprintf(buf + len, size - len,
+			"%20s : %10u\n", "Submit delay",
+			priv->debug.aurfc_stats.aurfc_submit_delay);
+	if (len > size)
+		len = size;
+
+	retval = simple_read_from_buffer(user_buf, count,
+					 ppos, buf, len);
+	kfree(buf);
+
+	return retval;
+}
+
+static const struct file_operations fops_aurfc = {
+	.read = read_file_aurfc,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
 static ssize_t read_file_slot(struct file *file, char __user *user_buf,
 			      size_t count, loff_t *ppos)
 {
@@ -518,7 +566,12 @@ int ath9k_htc_init_debug(struct ath_hw *ah)
 	debugfs_create_file("skb_rx", S_IRUSR, priv->debug.debugfs_phy,
 			    priv, &fops_skb_rx);
 
+	debugfs_create_file("aurfc_stats", S_IRUSR,
+			    priv->debug.debugfs_phy,
+			    priv, &fops_aurfc);
+
 	ath9k_cmn_debug_recv(priv->debug.debugfs_phy, &priv->debug.rx_stats);
+
 	ath9k_cmn_debug_phy_err(priv->debug.debugfs_phy, &priv->debug.rx_stats);
 
 	debugfs_create_file("slot", S_IRUSR, priv->debug.debugfs_phy,
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
index a0f58e2..939d008 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
@@ -1061,7 +1061,28 @@ void ath9k_rx_tasklet(unsigned long data)
 	unsigned long flags;
 	struct ieee80211_hdr *hdr;
 
+	/* add for adaptive usb receive flow control*/
+	int looptimes = 0;
+	int highwatermark = ATH9K_HTC_RXBUF*3/4;
+	int lowwatermark = ATH9K_HTC_RXBUF/32;
+	unsigned int delay = 0;
+
+	struct htc_target *htc = priv->htc;
+	struct hif_device_usb *hif_dev = htc->hif_dev;
+
+	AURFC_STAT_SET(aurfc_highwater, highwatermark);
+	AURFC_STAT_SET(aurfc_lowwater, lowwatermark);
+
 	do {
+		looptimes++;
+		if (looptimes > highwatermark) {
+			delay = looptimes*AURFC_STEP;
+			atomic_set(&hif_dev->aurfc_submit_delay,
+				   delay);
+			AURFC_STAT_INC(aurfc_wm_triggered);
+			AURFC_STAT_SET(aurfc_submit_delay, delay);
+		}
+
 		spin_lock_irqsave(&priv->rx.rxbuflock, flags);
 		list_for_each_entry(tmp_buf, &priv->rx.rxbuf, list) {
 			if (tmp_buf->in_process) {
@@ -1072,6 +1093,22 @@ void ath9k_rx_tasklet(unsigned long data)
 
 		if (rxbuf == NULL) {
 			spin_unlock_irqrestore(&priv->rx.rxbuflock, flags);
+			spin_lock_irqsave(&hif_dev->aurfc_lock,
+					  flags);
+			if (atomic_read(
+				&hif_dev->aurfc_submit_delay) > 0 &&
+			    hif_dev->aurfc_active > 0)
+				mod_delayed_work(system_wq,
+					&hif_dev->aurfc_delayed_work,
+						 0);
+			spin_unlock_irqrestore(&hif_dev->aurfc_lock,
+					       flags);
+			if (looptimes < lowwatermark) {
+				atomic_set(&hif_dev->aurfc_submit_delay
+					   , 0);
+				AURFC_STAT_SET(aurfc_submit_delay,
+					       0);
+			}
 			break;
 		}
 
@@ -1114,6 +1151,10 @@ void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb,
 	struct ath_common *common = ath9k_hw_common(ah);
 	struct ath9k_htc_rxbuf *rxbuf = NULL, *tmp_buf = NULL;
 
+	struct htc_target *htc = priv->htc;
+	struct hif_device_usb *hif_dev = htc->hif_dev;
+	int delay = ATH9K_HTC_RXBUF * AURFC_STEP;
+
 	spin_lock(&priv->rx.rxbuflock);
 	list_for_each_entry(tmp_buf, &priv->rx.rxbuf, list) {
 		if (!tmp_buf->in_process) {
@@ -1124,6 +1165,9 @@ void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb,
 	spin_unlock(&priv->rx.rxbuflock);
 
 	if (rxbuf == NULL) {
+		atomic_set(&hif_dev->aurfc_submit_delay, delay);
+		AURFC_STAT_INC(aurfc_wm_triggered);
+		AURFC_STAT_SET(aurfc_submit_delay, delay);
 		ath_dbg(common, ANY, "No free RX buffer\n");
 		goto err;
 	}
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2015-02-11 10:08 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-02-09 18:31 [PATCH] ath9k_htc: add adaptive usb receive flow control to repair soft lockup with monitor mode Yuwei Zheng
2015-02-11  9:20 ` Kalle Valo
2015-02-10  0:36   ` Yuwei Zheng
  -- strict thread matches above, loose matches on Subject: below --
2015-02-09  7:13 yuweizheng
2015-02-09 10:06 ` Oleksij Rempel
2015-02-06 10:46 yuweizheng
2015-02-06 22:24 ` Oleksij Rempel
2015-02-09  7:20   ` Yuwei Zheng
2015-02-09  9:16     ` Oleksij Rempel

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).