提交 c965c74b 编写于 作者: I Ivo van Doorn 提交者: John W. Linville

rt2x00: Implement watchdog monitoring

Implement watchdog monitoring for USB devices (PCI support can
be added later). This will determine if URBs being uploaded to
the hardware are actually returning. Both rt2500usb and rt2800usb
have shown that URBs being uploaded can remain hanging without
being released by the hardware.
By using this watchdog, a queue can be reset when this occurs.
For rt2800usb it has been tested that the connection is preserved
even though this interruption.
Signed-off-by: NIvo van Doorn <IvDoorn@gmail.com>
Signed-off-by: NJohn W. Linville <linville@tuxdriver.com>
上级 223dcc26
...@@ -1736,6 +1736,7 @@ static int rt2500usb_probe_hw(struct rt2x00_dev *rt2x00dev) ...@@ -1736,6 +1736,7 @@ static int rt2500usb_probe_hw(struct rt2x00_dev *rt2x00dev)
__set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags); __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags);
__set_bit(DRIVER_REQUIRE_COPY_IV, &rt2x00dev->flags); __set_bit(DRIVER_REQUIRE_COPY_IV, &rt2x00dev->flags);
} }
__set_bit(DRIVER_SUPPORT_WATCHDOG, &rt2x00dev->flags);
/* /*
* Set the rssi offset. * Set the rssi offset.
...@@ -1772,6 +1773,7 @@ static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = { ...@@ -1772,6 +1773,7 @@ static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = {
.rfkill_poll = rt2500usb_rfkill_poll, .rfkill_poll = rt2500usb_rfkill_poll,
.link_stats = rt2500usb_link_stats, .link_stats = rt2500usb_link_stats,
.reset_tuner = rt2500usb_reset_tuner, .reset_tuner = rt2500usb_reset_tuner,
.watchdog = rt2x00usb_watchdog,
.write_tx_desc = rt2500usb_write_tx_desc, .write_tx_desc = rt2500usb_write_tx_desc,
.write_beacon = rt2500usb_write_beacon, .write_beacon = rt2500usb_write_beacon,
.get_tx_data_len = rt2500usb_get_tx_data_len, .get_tx_data_len = rt2500usb_get_tx_data_len,
......
...@@ -633,6 +633,7 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev) ...@@ -633,6 +633,7 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
if (!modparam_nohwcrypt) if (!modparam_nohwcrypt)
__set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags); __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags);
__set_bit(DRIVER_SUPPORT_LINK_TUNING, &rt2x00dev->flags); __set_bit(DRIVER_SUPPORT_LINK_TUNING, &rt2x00dev->flags);
__set_bit(DRIVER_SUPPORT_WATCHDOG, &rt2x00dev->flags);
/* /*
* Set the rssi offset. * Set the rssi offset.
...@@ -655,6 +656,7 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = { ...@@ -655,6 +656,7 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
.link_stats = rt2800_link_stats, .link_stats = rt2800_link_stats,
.reset_tuner = rt2800_reset_tuner, .reset_tuner = rt2800_reset_tuner,
.link_tuner = rt2800_link_tuner, .link_tuner = rt2800_link_tuner,
.watchdog = rt2x00usb_watchdog,
.write_tx_desc = rt2800usb_write_tx_desc, .write_tx_desc = rt2800usb_write_tx_desc,
.write_tx_data = rt2800usb_write_tx_data, .write_tx_data = rt2800usb_write_tx_data,
.write_beacon = rt2800_write_beacon, .write_beacon = rt2800_write_beacon,
......
...@@ -332,6 +332,11 @@ struct link { ...@@ -332,6 +332,11 @@ struct link {
* Work structure for scheduling periodic link tuning. * Work structure for scheduling periodic link tuning.
*/ */
struct delayed_work work; struct delayed_work work;
/*
* Work structure for scheduling periodic watchdog monitoring.
*/
struct delayed_work watchdog_work;
}; };
/* /*
...@@ -543,6 +548,7 @@ struct rt2x00lib_ops { ...@@ -543,6 +548,7 @@ struct rt2x00lib_ops {
struct link_qual *qual); struct link_qual *qual);
void (*link_tuner) (struct rt2x00_dev *rt2x00dev, void (*link_tuner) (struct rt2x00_dev *rt2x00dev,
struct link_qual *qual, const u32 count); struct link_qual *qual, const u32 count);
void (*watchdog) (struct rt2x00_dev *rt2x00dev);
/* /*
* TX control handlers * TX control handlers
...@@ -648,6 +654,7 @@ enum rt2x00_flags { ...@@ -648,6 +654,7 @@ enum rt2x00_flags {
DRIVER_SUPPORT_CONTROL_FILTERS, DRIVER_SUPPORT_CONTROL_FILTERS,
DRIVER_SUPPORT_CONTROL_FILTER_PSPOLL, DRIVER_SUPPORT_CONTROL_FILTER_PSPOLL,
DRIVER_SUPPORT_LINK_TUNING, DRIVER_SUPPORT_LINK_TUNING,
DRIVER_SUPPORT_WATCHDOG,
/* /*
* Driver configuration * Driver configuration
......
...@@ -69,6 +69,11 @@ int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev) ...@@ -69,6 +69,11 @@ int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev)
*/ */
rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON); rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON);
/*
* Start watchdog monitoring.
*/
rt2x00link_start_watchdog(rt2x00dev);
/* /*
* Start the TX queues. * Start the TX queues.
*/ */
...@@ -88,6 +93,11 @@ void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev) ...@@ -88,6 +93,11 @@ void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev)
ieee80211_stop_queues(rt2x00dev->hw); ieee80211_stop_queues(rt2x00dev->hw);
rt2x00queue_stop_queues(rt2x00dev); rt2x00queue_stop_queues(rt2x00dev);
/*
* Stop watchdog monitoring.
*/
rt2x00link_stop_watchdog(rt2x00dev);
/* /*
* Disable RX. * Disable RX.
*/ */
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
/* /*
* Interval defines * Interval defines
*/ */
#define WATCHDOG_INTERVAL round_jiffies_relative(HZ)
#define LINK_TUNE_INTERVAL round_jiffies_relative(HZ) #define LINK_TUNE_INTERVAL round_jiffies_relative(HZ)
/* /*
...@@ -257,11 +258,30 @@ void rt2x00link_stop_tuner(struct rt2x00_dev *rt2x00dev); ...@@ -257,11 +258,30 @@ void rt2x00link_stop_tuner(struct rt2x00_dev *rt2x00dev);
void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna); void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna);
/** /**
* rt2x00link_register - Initialize link tuning functionality * rt2x00link_start_watchdog - Start periodic watchdog monitoring
* @rt2x00dev: Pointer to &struct rt2x00_dev. * @rt2x00dev: Pointer to &struct rt2x00_dev.
* *
* Initialize work structure and all link tuning related * This start the watchdog periodic work, this work will
* parameters. This will not start the link tuning process itself. *be executed periodically until &rt2x00link_stop_watchdog has
* been called.
*/
void rt2x00link_start_watchdog(struct rt2x00_dev *rt2x00dev);
/**
* rt2x00link_stop_watchdog - Stop periodic watchdog monitoring
* @rt2x00dev: Pointer to &struct rt2x00_dev.
*
* After this function completed the watchdog monitoring will not
* be running until &rt2x00link_start_watchdog is called.
*/
void rt2x00link_stop_watchdog(struct rt2x00_dev *rt2x00dev);
/**
* rt2x00link_register - Initialize link tuning & watchdog functionality
* @rt2x00dev: Pointer to &struct rt2x00_dev.
*
* Initialize work structure and all link tuning and watchdog related
* parameters. This will not start the periodic work itself.
*/ */
void rt2x00link_register(struct rt2x00_dev *rt2x00dev); void rt2x00link_register(struct rt2x00_dev *rt2x00dev);
......
...@@ -407,7 +407,45 @@ static void rt2x00link_tuner(struct work_struct *work) ...@@ -407,7 +407,45 @@ static void rt2x00link_tuner(struct work_struct *work)
&link->work, LINK_TUNE_INTERVAL); &link->work, LINK_TUNE_INTERVAL);
} }
void rt2x00link_start_watchdog(struct rt2x00_dev *rt2x00dev)
{
struct link *link = &rt2x00dev->link;
if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) ||
!test_bit(DRIVER_SUPPORT_WATCHDOG, &rt2x00dev->flags))
return;
ieee80211_queue_delayed_work(rt2x00dev->hw,
&link->watchdog_work, WATCHDOG_INTERVAL);
}
void rt2x00link_stop_watchdog(struct rt2x00_dev *rt2x00dev)
{
cancel_delayed_work_sync(&rt2x00dev->link.watchdog_work);
}
static void rt2x00link_watchdog(struct work_struct *work)
{
struct rt2x00_dev *rt2x00dev =
container_of(work, struct rt2x00_dev, link.watchdog_work.work);
struct link *link = &rt2x00dev->link;
/*
* When the radio is shutting down we should
* immediately cease the watchdog monitoring.
*/
if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
return;
rt2x00dev->ops->lib->watchdog(rt2x00dev);
if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
ieee80211_queue_delayed_work(rt2x00dev->hw,
&link->watchdog_work, WATCHDOG_INTERVAL);
}
void rt2x00link_register(struct rt2x00_dev *rt2x00dev) void rt2x00link_register(struct rt2x00_dev *rt2x00dev)
{ {
INIT_DELAYED_WORK(&rt2x00dev->link.watchdog_work, rt2x00link_watchdog);
INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00link_tuner); INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00link_tuner);
} }
...@@ -688,9 +688,11 @@ void rt2x00queue_index_inc(struct data_queue *queue, enum queue_index index) ...@@ -688,9 +688,11 @@ void rt2x00queue_index_inc(struct data_queue *queue, enum queue_index index)
if (index == Q_INDEX) { if (index == Q_INDEX) {
queue->length++; queue->length++;
queue->last_index = jiffies;
} else if (index == Q_INDEX_DONE) { } else if (index == Q_INDEX_DONE) {
queue->length--; queue->length--;
queue->count++; queue->count++;
queue->last_index_done = jiffies;
} }
spin_unlock_irqrestore(&queue->lock, irqflags); spin_unlock_irqrestore(&queue->lock, irqflags);
...@@ -704,6 +706,8 @@ static void rt2x00queue_reset(struct data_queue *queue) ...@@ -704,6 +706,8 @@ static void rt2x00queue_reset(struct data_queue *queue)
queue->count = 0; queue->count = 0;
queue->length = 0; queue->length = 0;
queue->last_index = jiffies;
queue->last_index_done = jiffies;
memset(queue->index, 0, sizeof(queue->index)); memset(queue->index, 0, sizeof(queue->index));
spin_unlock_irqrestore(&queue->lock, irqflags); spin_unlock_irqrestore(&queue->lock, irqflags);
......
...@@ -446,6 +446,8 @@ struct data_queue { ...@@ -446,6 +446,8 @@ struct data_queue {
enum data_queue_qid qid; enum data_queue_qid qid;
spinlock_t lock; spinlock_t lock;
unsigned long last_index;
unsigned long last_index_done;
unsigned int count; unsigned int count;
unsigned short limit; unsigned short limit;
unsigned short threshold; unsigned short threshold;
...@@ -598,6 +600,15 @@ static inline int rt2x00queue_threshold(struct data_queue *queue) ...@@ -598,6 +600,15 @@ static inline int rt2x00queue_threshold(struct data_queue *queue)
return rt2x00queue_available(queue) < queue->threshold; return rt2x00queue_available(queue) < queue->threshold;
} }
/**
* rt2x00queue_timeout - Check if a timeout occured for this queue
* @queue: Queue to check.
*/
static inline int rt2x00queue_timeout(struct data_queue *queue)
{
return time_after(queue->last_index, queue->last_index_done + (HZ / 10));
}
/** /**
* _rt2x00_desc_read - Read a word from the hardware descriptor. * _rt2x00_desc_read - Read a word from the hardware descriptor.
* @desc: Base descriptor address * @desc: Base descriptor address
......
...@@ -292,6 +292,56 @@ void rt2x00usb_kill_tx_queue(struct rt2x00_dev *rt2x00dev, ...@@ -292,6 +292,56 @@ void rt2x00usb_kill_tx_queue(struct rt2x00_dev *rt2x00dev,
} }
EXPORT_SYMBOL_GPL(rt2x00usb_kill_tx_queue); EXPORT_SYMBOL_GPL(rt2x00usb_kill_tx_queue);
static void rt2x00usb_watchdog_reset_tx(struct data_queue *queue)
{
struct queue_entry_priv_usb *entry_priv;
unsigned short threshold = queue->threshold;
WARNING(queue->rt2x00dev, "TX queue %d timed out, invoke reset", queue->qid);
/*
* Temporarily disable the TX queue, this will force mac80211
* to use the other queues until this queue has been restored.
*
* Set the queue threshold to the queue limit. This prevents the
* queue from being enabled during the txdone handler.
*/
queue->threshold = queue->limit;
ieee80211_stop_queue(queue->rt2x00dev->hw, queue->qid);
/*
* Reset all currently uploaded TX frames.
*/
while (!rt2x00queue_empty(queue)) {
entry_priv = rt2x00queue_get_entry(queue, Q_INDEX_DONE)->priv_data;
usb_kill_urb(entry_priv->urb);
/*
* We need a short delay here to wait for
* the URB to be canceled and invoked the tx_done handler.
*/
udelay(200);
}
/*
* The queue has been reset, and mac80211 is allowed to use the
* queue again.
*/
queue->threshold = threshold;
ieee80211_wake_queue(queue->rt2x00dev->hw, queue->qid);
}
void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev)
{
struct data_queue *queue;
tx_queue_for_each(rt2x00dev, queue) {
if (rt2x00queue_timeout(queue))
rt2x00usb_watchdog_reset_tx(queue);
}
}
EXPORT_SYMBOL_GPL(rt2x00usb_watchdog);
/* /*
* RX data handlers. * RX data handlers.
*/ */
......
...@@ -399,6 +399,16 @@ void rt2x00usb_kick_tx_queue(struct rt2x00_dev *rt2x00dev, ...@@ -399,6 +399,16 @@ void rt2x00usb_kick_tx_queue(struct rt2x00_dev *rt2x00dev,
void rt2x00usb_kill_tx_queue(struct rt2x00_dev *rt2x00dev, void rt2x00usb_kill_tx_queue(struct rt2x00_dev *rt2x00dev,
const enum data_queue_qid qid); const enum data_queue_qid qid);
/**
* rt2x00usb_watchdog - Watchdog for USB communication
* @rt2x00dev: Pointer to &struct rt2x00_dev
*
* Check the health of the USB communication and determine
* if timeouts have occured. If this is the case, this function
* will reset all communication to restore functionality again.
*/
void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev);
/* /*
* Device initialization handlers. * Device initialization handlers.
*/ */
......
...@@ -2136,6 +2136,7 @@ static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev) ...@@ -2136,6 +2136,7 @@ static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev)
if (!modparam_nohwcrypt) if (!modparam_nohwcrypt)
__set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags); __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags);
__set_bit(DRIVER_SUPPORT_LINK_TUNING, &rt2x00dev->flags); __set_bit(DRIVER_SUPPORT_LINK_TUNING, &rt2x00dev->flags);
__set_bit(DRIVER_SUPPORT_WATCHDOG, &rt2x00dev->flags);
/* /*
* Set the rssi offset. * Set the rssi offset.
...@@ -2251,6 +2252,7 @@ static const struct rt2x00lib_ops rt73usb_rt2x00_ops = { ...@@ -2251,6 +2252,7 @@ static const struct rt2x00lib_ops rt73usb_rt2x00_ops = {
.link_stats = rt73usb_link_stats, .link_stats = rt73usb_link_stats,
.reset_tuner = rt73usb_reset_tuner, .reset_tuner = rt73usb_reset_tuner,
.link_tuner = rt73usb_link_tuner, .link_tuner = rt73usb_link_tuner,
.watchdog = rt2x00usb_watchdog,
.write_tx_desc = rt73usb_write_tx_desc, .write_tx_desc = rt73usb_write_tx_desc,
.write_beacon = rt73usb_write_beacon, .write_beacon = rt73usb_write_beacon,
.get_tx_data_len = rt73usb_get_tx_data_len, .get_tx_data_len = rt73usb_get_tx_data_len,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册