Skip to content

Commit

Permalink
libertas: detect TX lockups and reset hardware
Browse files Browse the repository at this point in the history
Recent patches added support for resetting the SD8686 hardware when
commands time out, which seems to happen quite frequently soon after
resuming the system from a Wake-on-WLAN-triggered resume.

At http://dev.laptop.org/ticket/10969 we see the same thing happen
with transmits. In this case, the hardware will fail to respond to
a frame passed for transmission, and libertas (correctly) will block
all further commands and transmissions as the hardware can only
deal with one thing at a time. This results in a lockup while the
system waits indefinitely for the dead card to respond.

Hook up a TX lockup timer to detect this and reset the hardware.

Signed-off-by: Daniel Drake <dsd@laptop.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Daniel Drake authored and John W. Linville committed Oct 3, 2011
1 parent 8a3a3c8 commit 8f641d9
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/net/wireless/libertas/dev.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ struct lbs_private {
/* protected by hard_start_xmit serialization */
u8 txretrycount;
struct sk_buff *currenttxskb;
struct timer_list tx_lockup_timer;

/* Locks */
struct mutex lock;
Expand Down
35 changes: 35 additions & 0 deletions drivers/net/wireless/libertas/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ int lbs_stop_iface(struct lbs_private *priv)
spin_unlock_irqrestore(&priv->driver_lock, flags);

cancel_work_sync(&priv->mcast_work);
del_timer_sync(&priv->tx_lockup_timer);

/* Disable command processing, and wait for all commands to complete */
lbs_deb_main("waiting for commands to complete\n");
Expand Down Expand Up @@ -243,6 +244,7 @@ void lbs_host_to_card_done(struct lbs_private *priv)
lbs_deb_enter(LBS_DEB_THREAD);

spin_lock_irqsave(&priv->driver_lock, flags);
del_timer(&priv->tx_lockup_timer);

priv->dnld_sent = DNLD_RES_RECEIVED;

Expand Down Expand Up @@ -585,6 +587,9 @@ static int lbs_thread(void *data)
if (ret) {
lbs_deb_tx("host_to_card failed %d\n", ret);
priv->dnld_sent = DNLD_RES_RECEIVED;
} else {
mod_timer(&priv->tx_lockup_timer,
jiffies + (HZ * 5));
}
priv->tx_pending_len = 0;
if (!priv->currenttxskb) {
Expand All @@ -601,6 +606,7 @@ static int lbs_thread(void *data)
}

del_timer(&priv->command_timer);
del_timer(&priv->tx_lockup_timer);
del_timer(&priv->auto_deepsleep_timer);

lbs_deb_leave(LBS_DEB_THREAD);
Expand Down Expand Up @@ -734,6 +740,32 @@ static void lbs_cmd_timeout_handler(unsigned long data)
lbs_deb_leave(LBS_DEB_CMD);
}

/**
* lbs_tx_lockup_handler - handles the timeout of the passing of TX frames
* to the hardware. This is known to frequently happen with SD8686 when
* waking up after a Wake-on-WLAN-triggered resume.
*
* @data: &struct lbs_private pointer
*/
static void lbs_tx_lockup_handler(unsigned long data)
{
struct lbs_private *priv = (struct lbs_private *)data;
unsigned long flags;

lbs_deb_enter(LBS_DEB_TX);
spin_lock_irqsave(&priv->driver_lock, flags);

netdev_info(priv->dev, "TX lockup detected\n");
if (priv->reset_card)
priv->reset_card(priv);

priv->dnld_sent = DNLD_RES_RECEIVED;
wake_up_interruptible(&priv->waitq);

spin_unlock_irqrestore(&priv->driver_lock, flags);
lbs_deb_leave(LBS_DEB_TX);
}

/**
* auto_deepsleep_timer_fn - put the device back to deep sleep mode when
* timer expires and no activity (command, event, data etc.) is detected.
Expand Down Expand Up @@ -820,6 +852,8 @@ static int lbs_init_adapter(struct lbs_private *priv)

setup_timer(&priv->command_timer, lbs_cmd_timeout_handler,
(unsigned long)priv);
setup_timer(&priv->tx_lockup_timer, lbs_tx_lockup_handler,
(unsigned long)priv);
setup_timer(&priv->auto_deepsleep_timer, auto_deepsleep_timer_fn,
(unsigned long)priv);

Expand Down Expand Up @@ -857,6 +891,7 @@ static void lbs_free_adapter(struct lbs_private *priv)
lbs_free_cmd_buffer(priv);
kfifo_free(&priv->event_fifo);
del_timer(&priv->command_timer);
del_timer(&priv->tx_lockup_timer);
del_timer(&priv->auto_deepsleep_timer);

lbs_deb_leave(LBS_DEB_MAIN);
Expand Down

0 comments on commit 8f641d9

Please sign in to comment.