forked from Minki/linux
brcmsmac: fix tx status processing
This issue was reported on the wireless list (see [1]) in which
brcmsmac ran into a fatal error:
[ 588.284074] brcmsmac bcma0:0: frameid != txh->TxFrameID
[ 588.284098] brcmsmac bcma0:0: MI_TFS: fatal
[ 588.284103] brcmsmac bcma0:0: wl0: fatal error, reinitializing
[ 588.286208] ieee80211 phy0: Hardware restart was requested
The tx status feedback is processed in a loop limiting the number of
frames processed in one run. The code terminate processing when the
limit is reached regardless the txstatus value read from the device
register. When that status is is flagged as being valid it must be
processed as the hardware will clear it after is has been read.
Bisecting was done by Seth Forshee and showed following commit as the
culprit:
commit 57fe504817
Author: Piotr Haber <phaber@broadcom.com>
Date: Wed Nov 28 21:44:07 2012 +0100
brcmsmac: fix bounds checking in tx/rx
[1] http://www.spinics.net/lists/linux-wireless/msg101293.html
Reported-by: Linus Torvalds <torvalds@linux-foundation.org>
Tested-by: Seth Forshee <seth.forshee@canonical.com>
Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
006da9ebba
commit
0e33e48ddc
@ -1027,7 +1027,6 @@ brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs)
|
|||||||
static bool
|
static bool
|
||||||
brcms_b_txstatus(struct brcms_hardware *wlc_hw, bool bound, bool *fatal)
|
brcms_b_txstatus(struct brcms_hardware *wlc_hw, bool bound, bool *fatal)
|
||||||
{
|
{
|
||||||
bool morepending = false;
|
|
||||||
struct bcma_device *core;
|
struct bcma_device *core;
|
||||||
struct tx_status txstatus, *txs;
|
struct tx_status txstatus, *txs;
|
||||||
u32 s1, s2;
|
u32 s1, s2;
|
||||||
@ -1041,23 +1040,20 @@ brcms_b_txstatus(struct brcms_hardware *wlc_hw, bool bound, bool *fatal)
|
|||||||
txs = &txstatus;
|
txs = &txstatus;
|
||||||
core = wlc_hw->d11core;
|
core = wlc_hw->d11core;
|
||||||
*fatal = false;
|
*fatal = false;
|
||||||
s1 = bcma_read32(core, D11REGOFFS(frmtxstatus));
|
|
||||||
while (!(*fatal)
|
|
||||||
&& (s1 & TXS_V)) {
|
|
||||||
/* !give others some time to run! */
|
|
||||||
if (n >= max_tx_num) {
|
|
||||||
morepending = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
while (n < max_tx_num) {
|
||||||
|
s1 = bcma_read32(core, D11REGOFFS(frmtxstatus));
|
||||||
if (s1 == 0xffffffff) {
|
if (s1 == 0xffffffff) {
|
||||||
brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit,
|
brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit,
|
||||||
__func__);
|
__func__);
|
||||||
*fatal = true;
|
*fatal = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
s2 = bcma_read32(core, D11REGOFFS(frmtxstatus2));
|
/* only process when valid */
|
||||||
|
if (!(s1 & TXS_V))
|
||||||
|
break;
|
||||||
|
|
||||||
|
s2 = bcma_read32(core, D11REGOFFS(frmtxstatus2));
|
||||||
txs->status = s1 & TXS_STATUS_MASK;
|
txs->status = s1 & TXS_STATUS_MASK;
|
||||||
txs->frameid = (s1 & TXS_FID_MASK) >> TXS_FID_SHIFT;
|
txs->frameid = (s1 & TXS_FID_MASK) >> TXS_FID_SHIFT;
|
||||||
txs->sequence = s2 & TXS_SEQ_MASK;
|
txs->sequence = s2 & TXS_SEQ_MASK;
|
||||||
@ -1065,15 +1061,12 @@ brcms_b_txstatus(struct brcms_hardware *wlc_hw, bool bound, bool *fatal)
|
|||||||
txs->lasttxtime = 0;
|
txs->lasttxtime = 0;
|
||||||
|
|
||||||
*fatal = brcms_c_dotxstatus(wlc_hw->wlc, txs);
|
*fatal = brcms_c_dotxstatus(wlc_hw->wlc, txs);
|
||||||
|
if (*fatal == true)
|
||||||
s1 = bcma_read32(core, D11REGOFFS(frmtxstatus));
|
return false;
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*fatal)
|
return n >= max_tx_num;
|
||||||
return false;
|
|
||||||
|
|
||||||
return morepending;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void brcms_c_tbtt(struct brcms_c_info *wlc)
|
static void brcms_c_tbtt(struct brcms_c_info *wlc)
|
||||||
|
Loading…
Reference in New Issue
Block a user