aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLennert Buytenhek <buytenh@wantstofly.org>2009-11-30 12:13:04 -0500
committerJohn W. Linville <linville@tuxdriver.com>2009-12-07 16:51:21 -0500
commit7e1112d34aea10fdd689422e6bdc918309043bf3 (patch)
tree40b4445de8a802f603142822863d065c89d70b1b /drivers
parent0c9cc640225f4bd7c9aad87b1431bd8d9a29b338 (diff)
mwl8k: allow more time for transmit rings to drain
Before issuing any firmware commands, we wait for the transmit rings to drain, to prevent control versus data path synchronization issues. In some cases, this can end up taking longer than the current hardcoded limit of 5 seconds, for example if the transmit rings are filled with packets for a host that has dropped off the air and we end up retransmitting every pending packet at the lowest rate a couple of times. This patch changes mwl8k_tx_wait_empty() to only bail out on timeout expiry if there was no change in the number of packets pending in the transmit rings during the waiting period. If at least one transmit ring entry was reclaimed while we were waiting, we are apparently still making progress, and we'll allow waiting for another timeout period. Signed-off-by: Lennert Buytenhek <buytenh@marvell.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/mwl8k.c127
1 files changed, 67 insertions, 60 deletions
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c
index 793a83e10e16..a16255810d15 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -1237,99 +1237,106 @@ static inline void mwl8k_tx_start(struct mwl8k_priv *priv)
1237 ioread32(priv->regs + MWL8K_HIU_INT_CODE); 1237 ioread32(priv->regs + MWL8K_HIU_INT_CODE);
1238} 1238}
1239 1239
1240struct mwl8k_txq_info { 1240static void mwl8k_dump_tx_rings(struct ieee80211_hw *hw)
1241 u32 fw_owned;
1242 u32 drv_owned;
1243 u32 unused;
1244 u32 len;
1245 u32 head;
1246 u32 tail;
1247};
1248
1249static int mwl8k_scan_tx_ring(struct mwl8k_priv *priv,
1250 struct mwl8k_txq_info *txinfo)
1251{ 1241{
1252 int count, desc, status; 1242 struct mwl8k_priv *priv = hw->priv;
1253 struct mwl8k_tx_queue *txq; 1243 int i;
1254 struct mwl8k_tx_desc *tx_desc;
1255 int ndescs = 0;
1256 1244
1257 memset(txinfo, 0, MWL8K_TX_QUEUES * sizeof(struct mwl8k_txq_info)); 1245 for (i = 0; i < MWL8K_TX_QUEUES; i++) {
1246 struct mwl8k_tx_queue *txq = priv->txq + i;
1247 int fw_owned = 0;
1248 int drv_owned = 0;
1249 int unused = 0;
1250 int desc;
1258 1251
1259 for (count = 0; count < MWL8K_TX_QUEUES; count++) {
1260 txq = priv->txq + count;
1261 txinfo[count].len = txq->stats.len;
1262 txinfo[count].head = txq->head;
1263 txinfo[count].tail = txq->tail;
1264 for (desc = 0; desc < MWL8K_TX_DESCS; desc++) { 1252 for (desc = 0; desc < MWL8K_TX_DESCS; desc++) {
1265 tx_desc = txq->txd + desc; 1253 struct mwl8k_tx_desc *tx_desc = txq->txd + desc;
1266 status = le32_to_cpu(tx_desc->status); 1254 u32 status;
1267 1255
1256 status = le32_to_cpu(tx_desc->status);
1268 if (status & MWL8K_TXD_STATUS_FW_OWNED) 1257 if (status & MWL8K_TXD_STATUS_FW_OWNED)
1269 txinfo[count].fw_owned++; 1258 fw_owned++;
1270 else 1259 else
1271 txinfo[count].drv_owned++; 1260 drv_owned++;
1272 1261
1273 if (tx_desc->pkt_len == 0) 1262 if (tx_desc->pkt_len == 0)
1274 txinfo[count].unused++; 1263 unused++;
1275 } 1264 }
1276 }
1277 1265
1278 return ndescs; 1266 printk(KERN_ERR "%s: txq[%d] len=%d head=%d tail=%d "
1267 "fw_owned=%d drv_owned=%d unused=%d\n",
1268 wiphy_name(hw->wiphy), i,
1269 txq->stats.len, txq->head, txq->tail,
1270 fw_owned, drv_owned, unused);
1271 }
1279} 1272}
1280 1273
1281/* 1274/*
1282 * Must be called with priv->fw_mutex held and tx queues stopped. 1275 * Must be called with priv->fw_mutex held and tx queues stopped.
1283 */ 1276 */
1277#define MWL8K_TX_WAIT_TIMEOUT_MS 1000
1278
1284static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) 1279static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)
1285{ 1280{
1286 struct mwl8k_priv *priv = hw->priv; 1281 struct mwl8k_priv *priv = hw->priv;
1287 DECLARE_COMPLETION_ONSTACK(tx_wait); 1282 DECLARE_COMPLETION_ONSTACK(tx_wait);
1288 u32 count; 1283 int retry;
1289 unsigned long timeout; 1284 int rc;
1290 1285
1291 might_sleep(); 1286 might_sleep();
1292 1287
1288 /*
1289 * The TX queues are stopped at this point, so this test
1290 * doesn't need to take ->tx_lock.
1291 */
1292 if (!priv->pending_tx_pkts)
1293 return 0;
1294
1295 retry = 0;
1296 rc = 0;
1297
1293 spin_lock_bh(&priv->tx_lock); 1298 spin_lock_bh(&priv->tx_lock);
1294 count = priv->pending_tx_pkts; 1299 priv->tx_wait = &tx_wait;
1295 if (count) 1300 while (!rc) {
1296 priv->tx_wait = &tx_wait; 1301 int oldcount;
1297 spin_unlock_bh(&priv->tx_lock); 1302 unsigned long timeout;
1298 1303
1299 if (count) { 1304 oldcount = priv->pending_tx_pkts;
1300 struct mwl8k_txq_info txinfo[MWL8K_TX_QUEUES];
1301 int index;
1302 int newcount;
1303 1305
1306 spin_unlock_bh(&priv->tx_lock);
1304 timeout = wait_for_completion_timeout(&tx_wait, 1307 timeout = wait_for_completion_timeout(&tx_wait,
1305 msecs_to_jiffies(5000)); 1308 msecs_to_jiffies(MWL8K_TX_WAIT_TIMEOUT_MS));
1306 if (timeout)
1307 return 0;
1308
1309 spin_lock_bh(&priv->tx_lock); 1309 spin_lock_bh(&priv->tx_lock);
1310 priv->tx_wait = NULL;
1311 newcount = priv->pending_tx_pkts;
1312 mwl8k_scan_tx_ring(priv, txinfo);
1313 spin_unlock_bh(&priv->tx_lock);
1314 1310
1315 printk(KERN_ERR "%s(%u) TIMEDOUT:5000ms Pend:%u-->%u\n", 1311 if (timeout) {
1316 __func__, __LINE__, count, newcount); 1312 WARN_ON(priv->pending_tx_pkts);
1313 if (retry) {
1314 printk(KERN_NOTICE "%s: tx rings drained\n",
1315 wiphy_name(hw->wiphy));
1316 }
1317 break;
1318 }
1317 1319
1318 for (index = 0; index < MWL8K_TX_QUEUES; index++) 1320 if (priv->pending_tx_pkts < oldcount) {
1319 printk(KERN_ERR "TXQ:%u L:%u H:%u T:%u FW:%u " 1321 printk(KERN_NOTICE "%s: timeout waiting for tx "
1320 "DRV:%u U:%u\n", 1322 "rings to drain (%d -> %d pkts), retrying\n",
1321 index, 1323 wiphy_name(hw->wiphy), oldcount,
1322 txinfo[index].len, 1324 priv->pending_tx_pkts);
1323 txinfo[index].head, 1325 retry = 1;
1324 txinfo[index].tail, 1326 continue;
1325 txinfo[index].fw_owned, 1327 }
1326 txinfo[index].drv_owned, 1328
1327 txinfo[index].unused); 1329 priv->tx_wait = NULL;
1330
1331 printk(KERN_ERR "%s: tx rings stuck for %d ms\n",
1332 wiphy_name(hw->wiphy), MWL8K_TX_WAIT_TIMEOUT_MS);
1333 mwl8k_dump_tx_rings(hw);
1328 1334
1329 return -ETIMEDOUT; 1335 rc = -ETIMEDOUT;
1330 } 1336 }
1337 spin_unlock_bh(&priv->tx_lock);
1331 1338
1332 return 0; 1339 return rc;
1333} 1340}
1334 1341
1335#define MWL8K_TXD_SUCCESS(status) \ 1342#define MWL8K_TXD_SUCCESS(status) \