Message ID | 1423219598-17982-1-git-send-email-yuweizheng@139.com (mailing list archive) |
---|---|
State | Superseded |
Delegated to: | Kalle Valo |
Headers | show |
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; > } >
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; > > } > > > > -- To unsubscribe from this list: send the line "unsubscribe linux-wireless" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
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; >>> } >>> >> >> > > > >
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; }