Skip to content

Commit b1275cd

Browse files
DragonBluepKalle Valo
authored andcommitted
wifi: rt2x00: introduce DMA busy check watchdog for rt2800
When I tried to fix the watchdog of rt2800, I found that sometimes the watchdog can not reset the hung device. This is because the queue is not completely stuck, it just becomes very slow. The MTK vendor driver for the new chip MT7603/MT7612 has a DMA busy watchdog to detect device hangs by checking DMA busy status. This watchdog implementation is something similar to it. To reduce unnecessary reset, we can check the INT_SOURCE_CSR register together as I found that when the radio hung, the RX/TX coherent interrupt will always stuck at triggered state. The 'watchdog' module parameter has been extended to control all watchdogs(0=disabled, 1=hang watchdog, 2=DMA watchdog, 3=both). This new watchdog function is a slight schedule and it won't affect the transmission speed. So we can turn on it by default. Due to the INT_SOURCE_CSR register is invalid on rt2800 USB NICs, the DMA busy watchdog will be automatically disabled for them. Tested on MT7620 and RT5350. Signed-off-by: Shiji Yang <[email protected]> Acked-by: Stanislaw Gruszka <[email protected]> Signed-off-by: Kalle Valo <[email protected]> Link: https://lore.kernel.org/r/TYAP286MB0315D7462CE08A119A99DE34BCA4A@TYAP286MB0315.JPNP286.PROD.OUTLOOK.COM
1 parent 53ee0b3 commit b1275cd

File tree

3 files changed

+73
-11
lines changed

3 files changed

+73
-11
lines changed

drivers/net/wireless/ralink/rt2x00/rt2800.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3194,4 +3194,8 @@ enum rt2800_eeprom_word {
31943194
*/
31953195
#define BCN_TBTT_OFFSET 64
31963196

3197+
/* Watchdog type mask */
3198+
#define RT2800_WATCHDOG_HANG BIT(0)
3199+
#define RT2800_WATCHDOG_DMA_BUSY BIT(1)
3200+
31973201
#endif /* RT2800_H */

drivers/net/wireless/ralink/rt2x00/rt2800lib.c

Lines changed: 66 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@
3030
#include "rt2800lib.h"
3131
#include "rt2800.h"
3232

33-
static bool modparam_watchdog;
34-
module_param_named(watchdog, modparam_watchdog, bool, S_IRUGO);
35-
MODULE_PARM_DESC(watchdog, "Enable watchdog to detect tx/rx hangs and reset hardware if detected");
33+
static unsigned int modparam_watchdog = RT2800_WATCHDOG_DMA_BUSY;
34+
module_param_named(watchdog, modparam_watchdog, uint, 0444);
35+
MODULE_PARM_DESC(watchdog, "Enable watchdog to recover tx/rx hangs.\n"
36+
"\t\t(0=disabled, 1=hang watchdog, 2=DMA watchdog(default), 3=both)");
3637

3738
/*
3839
* Register access.
@@ -1261,15 +1262,12 @@ static void rt2800_update_survey(struct rt2x00_dev *rt2x00dev)
12611262
chan_survey->time_ext_busy += rt2800_register_read(rt2x00dev, CH_BUSY_STA_SEC);
12621263
}
12631264

1264-
void rt2800_watchdog(struct rt2x00_dev *rt2x00dev)
1265+
static bool rt2800_watchdog_hung(struct rt2x00_dev *rt2x00dev)
12651266
{
12661267
struct data_queue *queue;
12671268
bool hung_tx = false;
12681269
bool hung_rx = false;
12691270

1270-
if (test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags))
1271-
return;
1272-
12731271
rt2800_update_survey(rt2x00dev);
12741272

12751273
queue_for_each(rt2x00dev, queue) {
@@ -1297,18 +1295,72 @@ void rt2800_watchdog(struct rt2x00_dev *rt2x00dev)
12971295
}
12981296
}
12991297

1298+
if (!hung_tx && !hung_rx)
1299+
return false;
1300+
13001301
if (hung_tx)
13011302
rt2x00_warn(rt2x00dev, "Watchdog TX hung detected\n");
13021303

13031304
if (hung_rx)
13041305
rt2x00_warn(rt2x00dev, "Watchdog RX hung detected\n");
13051306

1306-
if (hung_tx || hung_rx) {
1307-
queue_for_each(rt2x00dev, queue)
1308-
queue->wd_count = 0;
1307+
queue_for_each(rt2x00dev, queue)
1308+
queue->wd_count = 0;
1309+
1310+
return true;
1311+
}
1312+
1313+
static bool rt2800_watchdog_dma_busy(struct rt2x00_dev *rt2x00dev)
1314+
{
1315+
bool busy_rx, busy_tx;
1316+
u32 reg_cfg = rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG);
1317+
u32 reg_int = rt2800_register_read(rt2x00dev, INT_SOURCE_CSR);
1318+
1319+
if (rt2x00_get_field32(reg_cfg, WPDMA_GLO_CFG_RX_DMA_BUSY) &&
1320+
rt2x00_get_field32(reg_int, INT_SOURCE_CSR_RX_COHERENT))
1321+
rt2x00dev->rxdma_busy++;
1322+
else
1323+
rt2x00dev->rxdma_busy = 0;
13091324

1325+
if (rt2x00_get_field32(reg_cfg, WPDMA_GLO_CFG_TX_DMA_BUSY) &&
1326+
rt2x00_get_field32(reg_int, INT_SOURCE_CSR_TX_COHERENT))
1327+
rt2x00dev->txdma_busy++;
1328+
else
1329+
rt2x00dev->txdma_busy = 0;
1330+
1331+
busy_rx = rt2x00dev->rxdma_busy > 30 ? true : false;
1332+
busy_tx = rt2x00dev->txdma_busy > 30 ? true : false;
1333+
1334+
if (!busy_rx && !busy_tx)
1335+
return false;
1336+
1337+
if (busy_rx)
1338+
rt2x00_warn(rt2x00dev, "Watchdog RX DMA busy detected\n");
1339+
1340+
if (busy_tx)
1341+
rt2x00_warn(rt2x00dev, "Watchdog TX DMA busy detected\n");
1342+
1343+
rt2x00dev->rxdma_busy = 0;
1344+
rt2x00dev->txdma_busy = 0;
1345+
1346+
return true;
1347+
}
1348+
1349+
void rt2800_watchdog(struct rt2x00_dev *rt2x00dev)
1350+
{
1351+
bool reset = false;
1352+
1353+
if (test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags))
1354+
return;
1355+
1356+
if (modparam_watchdog & RT2800_WATCHDOG_DMA_BUSY)
1357+
reset = rt2800_watchdog_dma_busy(rt2x00dev);
1358+
1359+
if (modparam_watchdog & RT2800_WATCHDOG_HANG)
1360+
reset = rt2800_watchdog_hung(rt2x00dev) || reset;
1361+
1362+
if (reset)
13101363
ieee80211_restart_hw(rt2x00dev->hw);
1311-
}
13121364
}
13131365
EXPORT_SYMBOL_GPL(rt2800_watchdog);
13141366

@@ -12013,6 +12065,9 @@ int rt2800_probe_hw(struct rt2x00_dev *rt2x00dev)
1201312065
__set_bit(REQUIRE_TASKLET_CONTEXT, &rt2x00dev->cap_flags);
1201412066
}
1201512067

12068+
/* USB NICs don't support DMA watchdog as INT_SOURCE_CSR is invalid */
12069+
if (rt2x00_is_usb(rt2x00dev))
12070+
modparam_watchdog &= ~RT2800_WATCHDOG_DMA_BUSY;
1201612071
if (modparam_watchdog) {
1201712072
__set_bit(CAPABILITY_RESTART_HW, &rt2x00dev->cap_flags);
1201812073
rt2x00dev->link.watchdog_interval = msecs_to_jiffies(100);

drivers/net/wireless/ralink/rt2x00/rt2x00.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,9 @@ struct rt2x00_dev {
926926
*/
927927
u16 beacon_int;
928928

929+
/* Rx/Tx DMA busy watchdog counter */
930+
u16 rxdma_busy, txdma_busy;
931+
929932
/**
930933
* Timestamp of last received beacon
931934
*/

0 commit comments

Comments
 (0)