Skip to content

Commit

Permalink
iwlwifi: move queue watchdog into transport
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Johannes Berg authored and John W. Linville committed Apr 12, 2012
1 parent 52bcbff commit 7c5ba4a
Show file tree
Hide file tree
Showing 11 changed files with 91 additions and 141 deletions.
3 changes: 1 addition & 2 deletions drivers/net/wireless/iwlwifi/iwl-1000.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,8 @@ static const struct iwl_base_params iwl1000_base_params = {
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000,
.wd_timeout = IWL_DEF_WD_TIMEOUT,
.wd_timeout = IWL_WATCHHDOG_DISABLED,
.max_event_log_size = 128,
.wd_disable = true,
};

static const struct iwl_ht_params iwl1000_ht_params = {
Expand Down
3 changes: 1 addition & 2 deletions drivers/net/wireless/iwlwifi/iwl-5000.c
Original file line number Diff line number Diff line change
Expand Up @@ -312,10 +312,9 @@ static const struct iwl_base_params iwl5000_base_params = {
.led_compensation = 51,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000,
.wd_timeout = IWL_LONG_WD_TIMEOUT,
.wd_timeout = IWL_WATCHHDOG_DISABLED,
.max_event_log_size = 512,
.no_idle_support = true,
.wd_disable = true,
};

static const struct iwl_ht_params iwl5000_ht_params = {
Expand Down
18 changes: 5 additions & 13 deletions drivers/net/wireless/iwlwifi/iwl-agn.c
Original file line number Diff line number Diff line change
Expand Up @@ -741,9 +741,6 @@ int iwl_alive_start(struct iwl_priv *priv)
/* After the ALIVE response, we can send host commands to the uCode */
set_bit(STATUS_ALIVE, &priv->status);

/* Enable watchdog to monitor the driver tx queues */
iwl_setup_watchdog(priv);

if (iwl_is_rfkill(priv))
return -ERFKILL;

Expand Down Expand Up @@ -887,10 +884,6 @@ void iwl_down(struct iwl_priv *priv)
exit_pending =
test_and_set_bit(STATUS_EXIT_PENDING, &priv->status);

/* Stop TX queues watchdog. We need to have STATUS_EXIT_PENDING bit set
* to prevent rearm timer */
del_timer_sync(&priv->watchdog);

iwl_clear_ucode_stations(priv, NULL);
iwl_dealloc_bcast_stations(priv);
iwl_clear_driver_stations(priv);
Expand Down Expand Up @@ -1092,10 +1085,6 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
init_timer(&priv->ucode_trace);
priv->ucode_trace.data = (unsigned long)priv;
priv->ucode_trace.function = iwl_bg_ucode_trace;

init_timer(&priv->watchdog);
priv->watchdog.data = (unsigned long)priv;
priv->watchdog.function = iwl_bg_watchdog;
}

void iwl_cancel_deferred_work(struct iwl_priv *priv)
Expand Down Expand Up @@ -1410,8 +1399,6 @@ static void iwl_set_hw_params(struct iwl_priv *priv)
if (iwlagn_mod_params.disable_11n & IWL_DISABLE_HT_ALL)
hw_params(priv).sku &= ~EEPROM_SKU_CAP_11N_ENABLE;

hw_params(priv).wd_timeout = cfg(priv)->base_params->wd_timeout;

/* Device-specific setup */
cfg(priv)->lib->set_hw_params(priv);
}
Expand Down Expand Up @@ -1498,6 +1485,11 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
trans_cfg.no_reclaim_cmds = no_reclaim_cmds;
trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds);
trans_cfg.rx_buf_size_8k = iwlagn_mod_params.amsdu_size_8K;
if (!iwlagn_mod_params.wd_disable)
trans_cfg.queue_watchdog_timeout =
cfg(priv)->base_params->wd_timeout;
else
trans_cfg.queue_watchdog_timeout = IWL_WATCHHDOG_DISABLED;

ucode_flags = fw->ucode_capa.flags;

Expand Down
68 changes: 0 additions & 68 deletions drivers/net/wireless/iwlwifi/iwl-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -837,74 +837,6 @@ int iwl_cmd_echo_test(struct iwl_priv *priv)
return ret;
}

static inline int iwl_check_stuck_queue(struct iwl_priv *priv, int txq)
{
if (iwl_trans_check_stuck_queue(trans(priv), txq)) {
int ret;
ret = iwl_force_reset(priv, IWL_FW_RESET, false);
return (ret == -EAGAIN) ? 0 : 1;
}
return 0;
}

/*
* Making watchdog tick be a quarter of timeout assure we will
* discover the queue hung between timeout and 1.25*timeout
*/
#define IWL_WD_TICK(timeout) ((timeout) / 4)

/*
* Watchdog timer callback, we check each tx queue for stuck, if if hung
* we reset the firmware. If everything is fine just rearm the timer.
*/
void iwl_bg_watchdog(unsigned long data)
{
struct iwl_priv *priv = (struct iwl_priv *)data;
int cnt;
unsigned long timeout;

if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return;

if (iwl_is_rfkill(priv))
return;

timeout = hw_params(priv).wd_timeout;
if (timeout == 0)
return;

/* monitor and check for stuck queues */
for (cnt = 0; cnt < cfg(priv)->base_params->num_of_queues; cnt++)
if (iwl_check_stuck_queue(priv, cnt))
return;

mod_timer(&priv->watchdog, jiffies +
msecs_to_jiffies(IWL_WD_TICK(timeout)));
}

void iwl_setup_watchdog(struct iwl_priv *priv)
{
unsigned int timeout = hw_params(priv).wd_timeout;

if (!iwlagn_mod_params.wd_disable) {
/* use system default */
if (timeout && !cfg(priv)->base_params->wd_disable)
mod_timer(&priv->watchdog,
jiffies +
msecs_to_jiffies(IWL_WD_TICK(timeout)));
else
del_timer(&priv->watchdog);
} else {
/* module parameter overwrite default configuration */
if (timeout && iwlagn_mod_params.wd_disable == 2)
mod_timer(&priv->watchdog,
jiffies +
msecs_to_jiffies(IWL_WD_TICK(timeout)));
else
del_timer(&priv->watchdog);
}
}

/**
* iwl_beacon_time_mask_low - mask of lower 32 bit of beacon time
* @priv -- pointer to iwl_priv data structure
Expand Down
2 changes: 0 additions & 2 deletions drivers/net/wireless/iwlwifi/iwl-core.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ static inline void iwl_update_stats(struct iwl_priv *priv, bool is_tx,
******************************************************/
void iwl_chswitch_done(struct iwl_priv *priv, bool is_success);

void iwl_setup_watchdog(struct iwl_priv *priv);
/*****************************************************
* TX power
****************************************************/
Expand Down Expand Up @@ -193,7 +192,6 @@ int __must_check iwl_scan_initiate(struct iwl_priv *priv,
* S e n d i n g H o s t C o m m a n d s *
*****************************************************/

void iwl_bg_watchdog(unsigned long data);
u32 iwl_usecs_to_beacons(struct iwl_priv *priv, u32 usec, u32 beacon_interval);
__le32 iwl_add_beacon_time(struct iwl_priv *priv, u32 base,
u32 addon, u32 beacon_interval);
Expand Down
2 changes: 1 addition & 1 deletion drivers/net/wireless/iwlwifi/iwl-dev.h
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,7 @@ struct iwl_event_log {
#define IWL_DELAY_NEXT_FORCE_FW_RELOAD (HZ*5)

/* TX queue watchdog timeouts in mSecs */
#define IWL_WATCHHDOG_DISABLED (0)
#define IWL_DEF_WD_TIMEOUT (2000)
#define IWL_LONG_WD_TIMEOUT (10000)
#define IWL_MAX_WD_TIMEOUT (120000)
Expand Down Expand Up @@ -973,7 +974,6 @@ struct iwl_priv {
struct work_struct run_time_calib_work;
struct timer_list statistics_periodic;
struct timer_list ucode_trace;
struct timer_list watchdog;

struct iwl_event_log event_log;

Expand Down
4 changes: 0 additions & 4 deletions drivers/net/wireless/iwlwifi/iwl-shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,6 @@ struct iwl_mod_params {
* @ct_kill_threshold: temperature threshold - in hw dependent unit
* @ct_kill_exit_threshold: when to reeable the device - in hw dependent unit
* relevant for 1000, 6000 and up
* @wd_timeout: TX queues watchdog timeout
* @struct iwl_sensitivity_ranges: range of sensitivity values
* @use_rts_for_aggregation: use rts/cts protection for HT traffic
*/
Expand All @@ -183,7 +182,6 @@ struct iwl_hw_params {
u16 sku;
u32 ct_kill_threshold;
u32 ct_kill_exit_threshold;
unsigned int wd_timeout;

const struct iwl_sensitivity_ranges *sens;
};
Expand Down Expand Up @@ -221,7 +219,6 @@ enum iwl_led_mode {
* @shadow_reg_enable: HW shadhow register bit
* @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up
* @no_idle_support: do not support idle mode
* wd_disable: disable watchdog timer
*/
struct iwl_base_params {
int eeprom_size;
Expand All @@ -241,7 +238,6 @@ struct iwl_base_params {
const bool shadow_reg_enable;
const bool hd_v2;
const bool no_idle_support;
const bool wd_disable;
};

/*
Expand Down
16 changes: 15 additions & 1 deletion drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <linux/skbuff.h>
#include <linux/wait.h>
#include <linux/pci.h>
#include <linux/timer.h>

#include "iwl-fh.h"
#include "iwl-csr.h"
Expand Down Expand Up @@ -204,7 +205,8 @@ struct iwl_tx_queue {
struct iwl_cmd_meta *meta;
struct sk_buff **skbs;
spinlock_t lock;
unsigned long time_stamp;
struct timer_list stuck_timer;
struct iwl_trans_pcie *trans_pcie;
u8 need_update;
u8 active;
};
Expand All @@ -227,6 +229,7 @@ struct iwl_tx_queue {
* @cmd_queue - command queue number
* @rx_buf_size_8k: 8 kB RX buffer size
* @rx_page_order: page order for receive buffer size
* @wd_timeout: queue watchdog timeout (jiffies)
*/
struct iwl_trans_pcie {
struct iwl_rx_queue rxq;
Expand Down Expand Up @@ -269,11 +272,22 @@ struct iwl_trans_pcie {

bool rx_buf_size_8k;
u32 rx_page_order;


/* queue watchdog */
unsigned long wd_timeout;
};

#define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \
((struct iwl_trans_pcie *) ((_iwl_trans)->trans_specific))

static inline struct iwl_trans *
iwl_trans_pcie_get_trans(struct iwl_trans_pcie *trans_pcie)
{
return container_of((void *)trans_pcie, struct iwl_trans,
trans_specific);
}

/*****************************************************
* RX
******************************************************/
Expand Down
27 changes: 25 additions & 2 deletions drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,10 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
trace_bufs[2], trace_lens[2]);
#endif

/* 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);

/* Increment and update queue's write index */
q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
iwl_txq_update_write_ptr(trans, txq);
Expand All @@ -677,6 +681,22 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
return idx;
}

static inline void iwl_queue_progress(struct iwl_trans_pcie *trans_pcie,
struct iwl_tx_queue *txq)
{
if (!trans_pcie->wd_timeout)
return;

/*
* if empty delete timer, otherwise move timer forward
* since we're making progress on this queue
*/
if (txq->q.read_ptr == txq->q.write_ptr)
del_timer(&txq->stuck_timer);
else
mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout);
}

/**
* iwl_hcmd_queue_reclaim - Reclaim TX command queue entries already Tx'd
*
Expand Down Expand Up @@ -711,6 +731,8 @@ static void iwl_hcmd_queue_reclaim(struct iwl_trans *trans, int txq_id,
}

}

iwl_queue_progress(trans_pcie, txq);
}

/**
Expand Down Expand Up @@ -754,8 +776,6 @@ void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_cmd_buffer *rxb,
cmd = txq->cmd[cmd_index];
meta = &txq->meta[cmd_index];

txq->time_stamp = jiffies;

iwlagn_unmap_tfd(trans, meta, &txq->tfds[index],
DMA_BIDIRECTIONAL);

Expand Down Expand Up @@ -949,5 +969,8 @@ int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index,
iwlagn_txq_free_tfd(trans, txq, txq->q.read_ptr, DMA_TO_DEVICE);
freed++;
}

iwl_queue_progress(trans_pcie, txq);

return freed;
}
Loading

0 comments on commit 7c5ba4a

Please sign in to comment.