Skip to content

Commit

Permalink
ath9k_htc: Queue WMI events
Browse files Browse the repository at this point in the history
Use a queue to handle WMI events and schedule a tasklet
to process the events. This fixes the race between the
WMI event ISR and the SWBA tasklet when the arrival of
WMI events in quick succession could overwrite the SWBA
data before the tasklet from a previous iteration could
have been scheduled. Also, drain the WMI queue properly.

Signed-off-by: Sujith Manoharan <Sujith.Manoharan@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Sujith Manoharan authored and John W. Linville committed Apr 13, 2011
1 parent b0a6ba9 commit f4c8899
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 57 deletions.
3 changes: 2 additions & 1 deletion drivers/net/wireless/ath/ath9k/htc.h
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,8 @@ void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv);
void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv,
struct ieee80211_vif *vif);
void ath9k_htc_beacon_reconfig(struct ath9k_htc_priv *priv);
void ath9k_htc_swba(struct ath9k_htc_priv *priv);
void ath9k_htc_swba(struct ath9k_htc_priv *priv,
struct wmi_event_swba *swba);

void ath9k_htc_rxep(void *priv, struct sk_buff *skb,
enum htc_endpoint_id ep_id);
Expand Down
27 changes: 11 additions & 16 deletions drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
Original file line number Diff line number Diff line change
Expand Up @@ -401,21 +401,18 @@ static void ath9k_htc_send_beacon(struct ath9k_htc_priv *priv,
spin_unlock_bh(&priv->beacon_lock);
}

static int ath9k_htc_choose_bslot(struct ath9k_htc_priv *priv)
static int ath9k_htc_choose_bslot(struct ath9k_htc_priv *priv,
struct wmi_event_swba *swba)
{
struct ath_common *common = ath9k_hw_common(priv->ah);
unsigned long flags;
u64 tsf;
u32 tsftu;
u16 intval;
int slot;

intval = priv->cur_beacon_conf.beacon_interval & ATH9K_BEACON_PERIOD;

spin_lock_irqsave(&priv->wmi->wmi_lock, flags);
tsf = priv->wmi->tsf;
spin_unlock_irqrestore(&priv->wmi->wmi_lock, flags);

tsf = be64_to_cpu(swba->tsf);
tsftu = TSF_TO_TU(tsf >> 32, tsf);
slot = ((tsftu % intval) * ATH9K_HTC_MAX_BCN_VIF) / intval;
slot = ATH9K_HTC_MAX_BCN_VIF - slot - 1;
Expand All @@ -427,33 +424,31 @@ static int ath9k_htc_choose_bslot(struct ath9k_htc_priv *priv)
return slot;
}

void ath9k_htc_swba(struct ath9k_htc_priv *priv)
void ath9k_htc_swba(struct ath9k_htc_priv *priv,
struct wmi_event_swba *swba)
{
struct ath_common *common = ath9k_hw_common(priv->ah);
unsigned long flags;
int slot;

spin_lock_irqsave(&priv->wmi->wmi_lock, flags);
if (priv->wmi->beacon_pending != 0) {
spin_unlock_irqrestore(&priv->wmi->wmi_lock, flags);
if (swba->beacon_pending != 0) {
priv->cur_beacon_conf.bmiss_cnt++;
if (priv->cur_beacon_conf.bmiss_cnt > BSTUCK_THRESHOLD) {
ath_dbg(common, ATH_DBG_BEACON,
ath_dbg(common, ATH_DBG_BSTUCK,
"Beacon stuck, HW reset\n");
ath9k_htc_reset(priv);
ieee80211_queue_work(priv->hw,
&priv->fatal_work);
}
return;
}
spin_unlock_irqrestore(&priv->wmi->wmi_lock, flags);

if (priv->cur_beacon_conf.bmiss_cnt) {
ath_dbg(common, ATH_DBG_BEACON,
ath_dbg(common, ATH_DBG_BSTUCK,
"Resuming beacon xmit after %u misses\n",
priv->cur_beacon_conf.bmiss_cnt);
priv->cur_beacon_conf.bmiss_cnt = 0;
}

slot = ath9k_htc_choose_bslot(priv);
slot = ath9k_htc_choose_bslot(priv, swba);
spin_lock_bh(&priv->beacon_lock);
if (priv->cur_beacon_conf.bslot[slot] == NULL) {
spin_unlock_bh(&priv->beacon_lock);
Expand Down
3 changes: 3 additions & 0 deletions drivers/net/wireless/ath/ath9k/htc_drv_gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,9 @@ void ath9k_htc_radio_disable(struct ieee80211_hw *hw)
/* Stop RX */
WMI_CMD(WMI_STOP_RECV_CMDID);

/* Clear the WMI event queue */
ath9k_wmi_event_drain(priv);

/*
* The MIB counters have to be disabled here,
* since the target doesn't do it.
Expand Down
2 changes: 0 additions & 2 deletions drivers/net/wireless/ath/ath9k/htc_drv_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -676,8 +676,6 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv,
spin_lock_init(&priv->tx_lock);
mutex_init(&priv->mutex);
mutex_init(&priv->htc_pm_lock);
tasklet_init(&priv->swba_tasklet, ath9k_swba_tasklet,
(unsigned long)priv);
tasklet_init(&priv->rx_tasklet, ath9k_rx_tasklet,
(unsigned long)priv);
tasklet_init(&priv->tx_tasklet, ath9k_tx_tasklet,
Expand Down
7 changes: 6 additions & 1 deletion drivers/net/wireless/ath/ath9k/htc_drv_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ void ath9k_htc_reset(struct ath9k_htc_priv *priv)
WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
WMI_CMD(WMI_STOP_RECV_CMDID);

ath9k_wmi_event_drain(priv);

caldata = &priv->caldata;
ret = ath9k_hw_reset(ah, ah->curchan, caldata, false);
if (ret) {
Expand Down Expand Up @@ -255,6 +257,8 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
WMI_CMD(WMI_STOP_RECV_CMDID);

ath9k_wmi_event_drain(priv);

ath_dbg(common, ATH_DBG_CONFIG,
"(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n",
priv->ah->curchan->channel,
Expand Down Expand Up @@ -1172,12 +1176,13 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw)
WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
WMI_CMD(WMI_STOP_RECV_CMDID);

tasklet_kill(&priv->swba_tasklet);
tasklet_kill(&priv->rx_tasklet);
tasklet_kill(&priv->tx_tasklet);

skb_queue_purge(&priv->tx_queue);

ath9k_wmi_event_drain(priv);

mutex_unlock(&priv->mutex);

/* Cancel all the running timers/work .. */
Expand Down
97 changes: 63 additions & 34 deletions drivers/net/wireless/ath/ath9k/wmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,12 @@ struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv)

wmi->drv_priv = priv;
wmi->stopped = false;
skb_queue_head_init(&wmi->wmi_event_queue);
mutex_init(&wmi->op_mutex);
mutex_init(&wmi->multi_write_mutex);
init_completion(&wmi->cmd_wait);
tasklet_init(&wmi->wmi_event_tasklet, ath9k_wmi_event_tasklet,
(unsigned long)wmi);

return wmi;
}
Expand All @@ -122,11 +125,64 @@ void ath9k_deinit_wmi(struct ath9k_htc_priv *priv)
kfree(priv->wmi);
}

void ath9k_swba_tasklet(unsigned long data)
void ath9k_wmi_event_drain(struct ath9k_htc_priv *priv)
{
struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data;
unsigned long flags;

ath9k_htc_swba(priv);
tasklet_kill(&priv->wmi->wmi_event_tasklet);
spin_lock_irqsave(&priv->wmi->wmi_lock, flags);
__skb_queue_purge(&priv->wmi->wmi_event_queue);
spin_unlock_irqrestore(&priv->wmi->wmi_lock, flags);
}

void ath9k_wmi_event_tasklet(unsigned long data)
{
struct wmi *wmi = (struct wmi *)data;
struct ath9k_htc_priv *priv = wmi->drv_priv;
struct wmi_cmd_hdr *hdr;
void *wmi_event;
struct wmi_event_swba *swba;
struct sk_buff *skb = NULL;
unsigned long flags;
u16 cmd_id;
#ifdef CONFIG_ATH9K_HTC_DEBUGFS
__be32 txrate;
#endif

do {
spin_lock_irqsave(&wmi->wmi_lock, flags);
skb = __skb_dequeue(&wmi->wmi_event_queue);
if (!skb) {
spin_unlock_irqrestore(&wmi->wmi_lock, flags);
return;
}
spin_unlock_irqrestore(&wmi->wmi_lock, flags);

hdr = (struct wmi_cmd_hdr *) skb->data;
cmd_id = be16_to_cpu(hdr->command_id);
wmi_event = skb_pull(skb, sizeof(struct wmi_cmd_hdr));

switch (cmd_id) {
case WMI_SWBA_EVENTID:
swba = (struct wmi_event_swba *) wmi_event;
ath9k_htc_swba(priv, swba);
break;
case WMI_FATAL_EVENTID:
ieee80211_queue_work(wmi->drv_priv->hw,
&wmi->drv_priv->fatal_work);
break;
case WMI_TXRATE_EVENTID:
#ifdef CONFIG_ATH9K_HTC_DEBUGFS
txrate = ((struct wmi_event_txrate *)wmi_event)->txrate;
wmi->drv_priv->debug.txrate = be32_to_cpu(txrate);
#endif
break;
default:
break;
}

kfree_skb(skb);
} while (1);
}

void ath9k_fatal_work(struct work_struct *work)
Expand Down Expand Up @@ -155,11 +211,6 @@ static void ath9k_wmi_ctrl_rx(void *priv, struct sk_buff *skb,
struct wmi *wmi = (struct wmi *) priv;
struct wmi_cmd_hdr *hdr;
u16 cmd_id;
void *wmi_event;
struct wmi_event_swba *swba;
#ifdef CONFIG_ATH9K_HTC_DEBUGFS
__be32 txrate;
#endif

if (unlikely(wmi->stopped))
goto free_skb;
Expand All @@ -168,32 +219,10 @@ static void ath9k_wmi_ctrl_rx(void *priv, struct sk_buff *skb,
cmd_id = be16_to_cpu(hdr->command_id);

if (cmd_id & 0x1000) {
wmi_event = skb_pull(skb, sizeof(struct wmi_cmd_hdr));
switch (cmd_id) {
case WMI_SWBA_EVENTID:
swba = (struct wmi_event_swba *) wmi_event;

spin_lock(&wmi->wmi_lock);
wmi->tsf = be64_to_cpu(swba->tsf);
wmi->beacon_pending = swba->beacon_pending;
spin_unlock(&wmi->wmi_lock);

tasklet_schedule(&wmi->drv_priv->swba_tasklet);
break;
case WMI_FATAL_EVENTID:
ieee80211_queue_work(wmi->drv_priv->hw,
&wmi->drv_priv->fatal_work);
break;
case WMI_TXRATE_EVENTID:
#ifdef CONFIG_ATH9K_HTC_DEBUGFS
txrate = ((struct wmi_event_txrate *)wmi_event)->txrate;
wmi->drv_priv->debug.txrate = be32_to_cpu(txrate);
#endif
break;
default:
break;
}
kfree_skb(skb);
spin_lock(&wmi->wmi_lock);
__skb_queue_tail(&wmi->wmi_event_queue, skb);
spin_unlock(&wmi->wmi_lock);
tasklet_schedule(&wmi->wmi_event_tasklet);
return;
}

Expand Down
7 changes: 4 additions & 3 deletions drivers/net/wireless/ath/ath9k/wmi.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,13 @@ struct wmi {
struct mutex op_mutex;
struct completion cmd_wait;
enum wmi_cmd_id last_cmd_id;
struct sk_buff_head wmi_event_queue;
struct tasklet_struct wmi_event_tasklet;
u16 tx_seq_id;
u8 *cmd_rsp_buf;
u32 cmd_rsp_len;
bool stopped;

u64 tsf;
u8 beacon_pending;
spinlock_t wmi_lock;

atomic_t mwrite_cnt;
Expand All @@ -129,8 +129,9 @@ int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id,
u8 *cmd_buf, u32 cmd_len,
u8 *rsp_buf, u32 rsp_len,
u32 timeout);
void ath9k_swba_tasklet(unsigned long data);
void ath9k_wmi_event_tasklet(unsigned long data);
void ath9k_fatal_work(struct work_struct *work);
void ath9k_wmi_event_drain(struct ath9k_htc_priv *priv);

#define WMI_CMD(_wmi_cmd) \
do { \
Expand Down

0 comments on commit f4c8899

Please sign in to comment.