summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2012-04-09 17:46:54 -0700
committerJohn W. Linville <linville@tuxdriver.com>2012-04-12 15:06:08 -0400
commit7c5ba4a830cbb730770129b0004e2a06e47dbac5 (patch)
tree3c86c616dbe30b31cd07d9d8720092932f84d05b /drivers/net/wireless/iwlwifi/iwl-trans-pcie.c
parent52bcbff762a4cfedf97c46a58ec9890464212420 (diff)
downloadop-kernel-dev-7c5ba4a830cbb730770129b0004e2a06e47dbac5.zip
op-kernel-dev-7c5ba4a830cbb730770129b0004e2a06e47dbac5.tar.gz
iwlwifi: move queue watchdog into transport
This removes one of the two sources of device restarts in the upper layer -- those are a bit inconvenient because normal restarts originate in the transport. By moving the watchdog down it can be treated the same. Also rewrite the watchdog logic. Timers are much more efficient when they never fire, so instead firing a timer every 500ms set up a timer for each TX queue and fire it only when the queue is really stuck. This avoids the CPU waking up when everything is working well. While at it, remove the wd_disable config item and replace it by simply setting wd_timeout to IWL_WATCHHDOG_DISABLED (0). Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-trans-pcie.c')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-pcie.c77
1 files changed, 40 insertions, 37 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c
index 1d10049..f3695fe 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c
@@ -299,6 +299,33 @@ static inline void iwlagn_free_dma_ptr(struct iwl_trans *trans,
memset(ptr, 0, sizeof(*ptr));
}
+static void iwl_trans_pcie_queue_stuck_timer(unsigned long data)
+{
+ struct iwl_tx_queue *txq = (void *)data;
+ struct iwl_trans_pcie *trans_pcie = txq->trans_pcie;
+ struct iwl_trans *trans = iwl_trans_pcie_get_trans(trans_pcie);
+
+ spin_lock(&txq->lock);
+ /* check if triggered erroneously */
+ if (txq->q.read_ptr == txq->q.write_ptr) {
+ spin_unlock(&txq->lock);
+ return;
+ }
+ spin_unlock(&txq->lock);
+
+
+ IWL_ERR(trans, "Queue %d stuck for %u ms.\n", txq->q.id,
+ jiffies_to_msecs(trans_pcie->wd_timeout));
+ IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n",
+ txq->q.read_ptr, txq->q.write_ptr);
+ IWL_ERR(trans, "Current HW read_ptr %d write_ptr %d\n",
+ iwl_read_prph(trans, SCD_QUEUE_RDPTR(txq->q.id))
+ & (TFD_QUEUE_SIZE_MAX - 1),
+ iwl_read_prph(trans, SCD_QUEUE_WRPTR(txq->q.id)));
+
+ iwl_op_mode_nic_error(trans->op_mode);
+}
+
static int iwl_trans_txq_alloc(struct iwl_trans *trans,
struct iwl_tx_queue *txq, int slots_num,
u32 txq_id)
@@ -310,6 +337,10 @@ static int iwl_trans_txq_alloc(struct iwl_trans *trans,
if (WARN_ON(txq->meta || txq->cmd || txq->skbs || txq->tfds))
return -EINVAL;
+ setup_timer(&txq->stuck_timer, iwl_trans_pcie_queue_stuck_timer,
+ (unsigned long)txq);
+ txq->trans_pcie = trans_pcie;
+
txq->q.n_window = slots_num;
txq->meta = kcalloc(slots_num, sizeof(txq->meta[0]), GFP_KERNEL);
@@ -472,6 +503,8 @@ static void iwl_tx_queue_free(struct iwl_trans *trans, int txq_id)
txq->cmd = NULL;
txq->meta = NULL;
+ del_timer_sync(&txq->stuck_timer);
+
/* 0-fill queue descriptor structure */
memset(txq, 0, sizeof(*txq));
}
@@ -1347,6 +1380,10 @@ static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
&dev_cmd->hdr, firstlen,
skb->data + hdr_len, secondlen);
+ /* start timer if queue currently empty */
+ if (q->read_ptr == q->write_ptr && trans_pcie->wd_timeout)
+ mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout);
+
/* Tell device the write index *just past* this latest filled TFD */
q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
iwl_txq_update_write_ptr(trans, txq);
@@ -1442,8 +1479,6 @@ static void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn,
spin_lock(&txq->lock);
- txq->time_stamp = jiffies;
-
if (txq->q.read_ptr != tfd_num) {
IWL_DEBUG_TX_REPLY(trans, "[Q %d] %d -> %d (%d)\n",
txq_id, txq->q.read_ptr, tfd_num, ssn);
@@ -1500,6 +1535,9 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans,
trans_pcie->rx_page_order = get_order(8 * 1024);
else
trans_pcie->rx_page_order = get_order(4 * 1024);
+
+ trans_pcie->wd_timeout =
+ msecs_to_jiffies(trans_cfg->queue_watchdog_timeout);
}
static void iwl_trans_pcie_free(struct iwl_trans *trans)
@@ -1589,40 +1627,6 @@ static int iwl_trans_pcie_wait_tx_queue_empty(struct iwl_trans *trans)
return ret;
}
-/*
- * On every watchdog tick we check (latest) time stamp. If it does not
- * change during timeout period and queue is not empty we reset firmware.
- */
-static int iwl_trans_pcie_check_stuck_queue(struct iwl_trans *trans, int cnt)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- struct iwl_tx_queue *txq = &trans_pcie->txq[cnt];
- struct iwl_queue *q = &txq->q;
- unsigned long timeout;
-
- if (q->read_ptr == q->write_ptr) {
- txq->time_stamp = jiffies;
- return 0;
- }
-
- timeout = txq->time_stamp +
- msecs_to_jiffies(hw_params(trans).wd_timeout);
-
- if (time_after(jiffies, timeout)) {
- IWL_ERR(trans, "Queue %d stuck for %u ms.\n", q->id,
- hw_params(trans).wd_timeout);
- IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n",
- q->read_ptr, q->write_ptr);
- IWL_ERR(trans, "Current HW read_ptr %d write_ptr %d\n",
- iwl_read_prph(trans, SCD_QUEUE_RDPTR(cnt))
- & (TFD_QUEUE_SIZE_MAX - 1),
- iwl_read_prph(trans, SCD_QUEUE_WRPTR(cnt)));
- return 1;
- }
-
- return 0;
-}
-
static const char *get_fh_string(int cmd)
{
switch (cmd) {
@@ -2039,7 +2043,6 @@ const struct iwl_trans_ops trans_ops_pcie = {
.dbgfs_register = iwl_trans_pcie_dbgfs_register,
.wait_tx_queue_empty = iwl_trans_pcie_wait_tx_queue_empty,
- .check_stuck_queue = iwl_trans_pcie_check_stuck_queue,
#ifdef CONFIG_PM_SLEEP
.suspend = iwl_trans_pcie_suspend,
OpenPOWER on IntegriCloud