Commit 7b2385b9 authored by Arend van Spriel's avatar Arend van Spriel Committed by John W. Linville

brcmsmac: rework of mac80211 .flush() callback operation

This patch addresses a long standing issue of the driver with the
mac80211 .flush() callback. Since implementing the .flush() callback
a number of issues have been fixed, but a WARN_ON_ONCE() was still
triggered because the timeout on the flush could still occur.

This patch changes the awkward design using msleep() into one using
a waitqueue. The waiting flush() context will kick the transmit dma
when it is idle and the timeout used waiting for the event is set
to 500 ms. Worst case there can be 64 frames outstanding for transmit
in the driver. At a rate of 1Mbps that would take 1.5 seconds assuming
MTU is 1500 bytes and ignoring retries. The WARN_ON_ONCE() is also
removed as this was put in to indicate the flush timeout as a reason
for the driver to stall. That was not happening since fixing endless
AMPDU retries with following upstream commit:

commit 85091fc0
Author: Arend van Spriel <arend@broadcom.com>
Date:   Thu Feb 23 18:38:22 2012 +0100

    brcm80211: smac: fix endless retry of A-MPDU transmissions

bugzilla: 42840 <https://bugzilla.kernel.org/show_bug.cgi?id=42840>
bugzilla@redhat: <https://bugzilla.redhat.com/show_bug.cgi?id=799168>
bugzilla@redhat: <https://bugzilla.redhat.com/show_bug.cgi?id=787649>

Cc: Jonathan Nieder <jrnieder@gmail.com>
Cc: Stanislaw Gruszka <sgruszka@redhat.com>
Cc: Camaleón <noelamac@gmail.com>
Cc: Milan Bouchet-Valat <nalimilan@club-internet.fr>
Cc: Seth Forshee <seth.forshee@canonical.com>
Reviewed-by: default avatarPieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: default avatarHante Meuleman <meuleman@broadcom.com>
Reviewed-by: default avatarPiotr Haber <phaber@broadcom.com>
Signed-off-by: default avatarArend van Spriel <arend@broadcom.com>
Acked-by: default avatarSeth Forshee <seth.forshee@canonical.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 600485ed
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include "debug.h" #include "debug.h"
#define N_TX_QUEUES 4 /* #tx queues on mac80211<->driver interface */ #define N_TX_QUEUES 4 /* #tx queues on mac80211<->driver interface */
#define BRCMS_FLUSH_TIMEOUT 500 /* msec */
/* Flags we support */ /* Flags we support */
#define MAC_FILTERS (FIF_PROMISC_IN_BSS | \ #define MAC_FILTERS (FIF_PROMISC_IN_BSS | \
...@@ -708,16 +709,29 @@ static void brcms_ops_rfkill_poll(struct ieee80211_hw *hw) ...@@ -708,16 +709,29 @@ static void brcms_ops_rfkill_poll(struct ieee80211_hw *hw)
wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, blocked); wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, blocked);
} }
static bool brcms_tx_flush_completed(struct brcms_info *wl)
{
bool result;
spin_lock_bh(&wl->lock);
result = brcms_c_tx_flush_completed(wl->wlc);
spin_unlock_bh(&wl->lock);
return result;
}
static void brcms_ops_flush(struct ieee80211_hw *hw, bool drop) static void brcms_ops_flush(struct ieee80211_hw *hw, bool drop)
{ {
struct brcms_info *wl = hw->priv; struct brcms_info *wl = hw->priv;
int ret;
no_printk("%s: drop = %s\n", __func__, drop ? "true" : "false"); no_printk("%s: drop = %s\n", __func__, drop ? "true" : "false");
/* wait for packet queue and dma fifos to run empty */ ret = wait_event_timeout(wl->tx_flush_wq,
spin_lock_bh(&wl->lock); brcms_tx_flush_completed(wl),
brcms_c_wait_for_tx_completion(wl->wlc, drop); msecs_to_jiffies(BRCMS_FLUSH_TIMEOUT));
spin_unlock_bh(&wl->lock);
brcms_dbg_mac80211(wl->wlc->hw->d11core,
"ret=%d\n", jiffies_to_msecs(ret));
} }
static const struct ieee80211_ops brcms_ops = { static const struct ieee80211_ops brcms_ops = {
...@@ -772,6 +786,7 @@ void brcms_dpc(unsigned long data) ...@@ -772,6 +786,7 @@ void brcms_dpc(unsigned long data)
done: done:
spin_unlock_bh(&wl->lock); spin_unlock_bh(&wl->lock);
wake_up(&wl->tx_flush_wq);
} }
/* /*
...@@ -1020,6 +1035,8 @@ static struct brcms_info *brcms_attach(struct bcma_device *pdev) ...@@ -1020,6 +1035,8 @@ static struct brcms_info *brcms_attach(struct bcma_device *pdev)
atomic_set(&wl->callbacks, 0); atomic_set(&wl->callbacks, 0);
init_waitqueue_head(&wl->tx_flush_wq);
/* setup the bottom half handler */ /* setup the bottom half handler */
tasklet_init(&wl->tasklet, brcms_dpc, (unsigned long) wl); tasklet_init(&wl->tasklet, brcms_dpc, (unsigned long) wl);
...@@ -1609,13 +1626,3 @@ bool brcms_rfkill_set_hw_state(struct brcms_info *wl) ...@@ -1609,13 +1626,3 @@ bool brcms_rfkill_set_hw_state(struct brcms_info *wl)
spin_lock_bh(&wl->lock); spin_lock_bh(&wl->lock);
return blocked; return blocked;
} }
/*
* precondition: perimeter lock has been acquired
*/
void brcms_msleep(struct brcms_info *wl, uint ms)
{
spin_unlock_bh(&wl->lock);
msleep(ms);
spin_lock_bh(&wl->lock);
}
...@@ -68,6 +68,8 @@ struct brcms_info { ...@@ -68,6 +68,8 @@ struct brcms_info {
spinlock_t lock; /* per-device perimeter lock */ spinlock_t lock; /* per-device perimeter lock */
spinlock_t isr_lock; /* per-device ISR synchronization lock */ spinlock_t isr_lock; /* per-device ISR synchronization lock */
/* tx flush */
wait_queue_head_t tx_flush_wq;
/* timer related fields */ /* timer related fields */
atomic_t callbacks; /* # outstanding callback functions */ atomic_t callbacks; /* # outstanding callback functions */
...@@ -100,7 +102,6 @@ extern struct brcms_timer *brcms_init_timer(struct brcms_info *wl, ...@@ -100,7 +102,6 @@ extern struct brcms_timer *brcms_init_timer(struct brcms_info *wl,
extern void brcms_free_timer(struct brcms_timer *timer); extern void brcms_free_timer(struct brcms_timer *timer);
extern void brcms_add_timer(struct brcms_timer *timer, uint ms, int periodic); extern void brcms_add_timer(struct brcms_timer *timer, uint ms, int periodic);
extern bool brcms_del_timer(struct brcms_timer *timer); extern bool brcms_del_timer(struct brcms_timer *timer);
extern void brcms_msleep(struct brcms_info *wl, uint ms);
extern void brcms_dpc(unsigned long data); extern void brcms_dpc(unsigned long data);
extern void brcms_timer(struct brcms_timer *t); extern void brcms_timer(struct brcms_timer *t);
extern void brcms_fatal_error(struct brcms_info *wl); extern void brcms_fatal_error(struct brcms_info *wl);
......
...@@ -7511,25 +7511,16 @@ int brcms_c_get_curband(struct brcms_c_info *wlc) ...@@ -7511,25 +7511,16 @@ int brcms_c_get_curband(struct brcms_c_info *wlc)
return wlc->band->bandunit; return wlc->band->bandunit;
} }
void brcms_c_wait_for_tx_completion(struct brcms_c_info *wlc, bool drop) bool brcms_c_tx_flush_completed(struct brcms_c_info *wlc)
{ {
int timeout = 20;
int i; int i;
/* Kick DMA to send any pending AMPDU */ /* Kick DMA to send any pending AMPDU */
for (i = 0; i < ARRAY_SIZE(wlc->hw->di); i++) for (i = 0; i < ARRAY_SIZE(wlc->hw->di); i++)
if (wlc->hw->di[i]) if (wlc->hw->di[i])
dma_txflush(wlc->hw->di[i]); dma_kick_tx(wlc->hw->di[i]);
/* wait for queue and DMA fifos to run dry */ return !brcms_txpktpendtot(wlc);
while (brcms_txpktpendtot(wlc) > 0) {
brcms_msleep(wlc->wl, 1);
if (--timeout == 0)
break;
}
WARN_ON_ONCE(timeout == 0);
} }
void brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc, u8 interval) void brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc, u8 interval)
......
...@@ -314,8 +314,6 @@ extern void brcms_c_associate_upd(struct brcms_c_info *wlc, bool state); ...@@ -314,8 +314,6 @@ extern void brcms_c_associate_upd(struct brcms_c_info *wlc, bool state);
extern void brcms_c_scan_start(struct brcms_c_info *wlc); extern void brcms_c_scan_start(struct brcms_c_info *wlc);
extern void brcms_c_scan_stop(struct brcms_c_info *wlc); extern void brcms_c_scan_stop(struct brcms_c_info *wlc);
extern int brcms_c_get_curband(struct brcms_c_info *wlc); extern int brcms_c_get_curband(struct brcms_c_info *wlc);
extern void brcms_c_wait_for_tx_completion(struct brcms_c_info *wlc,
bool drop);
extern int brcms_c_set_channel(struct brcms_c_info *wlc, u16 channel); extern int brcms_c_set_channel(struct brcms_c_info *wlc, u16 channel);
extern int brcms_c_set_rate_limit(struct brcms_c_info *wlc, u16 srl, u16 lrl); extern int brcms_c_set_rate_limit(struct brcms_c_info *wlc, u16 srl, u16 lrl);
extern void brcms_c_get_current_rateset(struct brcms_c_info *wlc, extern void brcms_c_get_current_rateset(struct brcms_c_info *wlc,
...@@ -332,5 +330,6 @@ extern int brcms_c_set_tx_power(struct brcms_c_info *wlc, int txpwr); ...@@ -332,5 +330,6 @@ extern int brcms_c_set_tx_power(struct brcms_c_info *wlc, int txpwr);
extern int brcms_c_get_tx_power(struct brcms_c_info *wlc); extern int brcms_c_get_tx_power(struct brcms_c_info *wlc);
extern bool brcms_c_check_radio_disabled(struct brcms_c_info *wlc); extern bool brcms_c_check_radio_disabled(struct brcms_c_info *wlc);
extern void brcms_c_mute(struct brcms_c_info *wlc, bool on); extern void brcms_c_mute(struct brcms_c_info *wlc, bool on);
extern bool brcms_c_tx_flush_completed(struct brcms_c_info *wlc);
#endif /* _BRCM_PUB_H_ */ #endif /* _BRCM_PUB_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment