2011-03-28 20:57:19

by Luis R. Rodriguez

[permalink] [raw]
Subject: [PATCH 0/9] ath9k/ath9k_htc: pending stable patches for 2.6.37.y

Greg,

here are a few stable patches were required backport and also
a few which are marked as stable but which have not yet been
propagated to the older stable trees. I've been carrying these
on the compat-wireless-2.6.37 release for a while now so they
have gotten some consumption and only now just got a chance to
send them over to you.

Felix Fietkau (2):
ath9k_hw: fix potential spurious tx error bit interpretation
ath9k: add missing ps wakeup/restore calls

Luis R. Rodriguez (5):
ath9k: simplify hw reset locking
ath9k: move the PCU lock to the sc structure
ath9k: content DMA start / stop through the PCU lock
ath9k: Fix power save count imbalance on ath_radio_enable()
ath9k_hw: ASPM interoperability fix for AR9380/AR9382

Mohammed Shafi Shajakhan (1):
ath9k: Fix memory leak due to failed PAPRD frames

Stanislaw Gruszka (1):
ath9k_htc: fix race conditions when stop device

drivers/net/wireless/ath/ath9k/ar9002_mac.c | 21 +++--
.../net/wireless/ath/ath9k/ar9003_2p2_initvals.h | 2 +-
drivers/net/wireless/ath/ath9k/ar9003_hw.c | 4 +-
drivers/net/wireless/ath/ath9k/ath9k.h | 3 +-
drivers/net/wireless/ath/ath9k/htc_drv_init.c | 3 -
drivers/net/wireless/ath/ath9k/htc_drv_main.c | 19 +++-
drivers/net/wireless/ath/ath9k/init.c | 1 -
drivers/net/wireless/ath/ath9k/main.c | 85 +++++++++----------
drivers/net/wireless/ath/ath9k/recv.c | 2 +-
drivers/net/wireless/ath/ath9k/xmit.c | 2 -
10 files changed, 71 insertions(+), 71 deletions(-)

--
1.7.4.15.g7811d



2011-03-28 20:57:48

by Luis R. Rodriguez

[permalink] [raw]
Subject: [PATCH 9/9] ath9k: Fix memory leak due to failed PAPRD frames

From: Mohammed Shafi Shajakhan <[email protected]>

commit 479600777bb588724d044815415f7d708d06644b upstream

free the skb's when the Tx of PAPRD frames fails and also add a debug
message indicating that.

Signed-off-by: Mohammed Shafi Shajakhan <[email protected]>
Cc: [email protected]
Signed-off-by: John W. Linville <[email protected]>
---
drivers/net/wireless/ath/ath9k/main.c | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 1049313..16b8e36 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -384,8 +384,11 @@ void ath_paprd_calibrate(struct work_struct *work)
init_completion(&sc->paprd_complete);
ar9003_paprd_setup_gain_table(ah, chain);
txctl.paprd = BIT(chain);
- if (ath_tx_start(hw, skb, &txctl) != 0)
+ if (ath_tx_start(hw, skb, &txctl) != 0) {
+ ath_print(common, ATH_DBG_XMIT, "PAPRD TX failed\n");
+ dev_kfree_skb_any(skb);
break;
+ }

time_left = wait_for_completion_timeout(&sc->paprd_complete,
msecs_to_jiffies(ATH_PAPRD_TIMEOUT));
--
1.7.4.15.g7811d


2011-03-28 20:57:39

by Luis R. Rodriguez

[permalink] [raw]
Subject: [PATCH 5/9] ath9k_hw: fix potential spurious tx error bit interpretation

From: Felix Fietkau <[email protected]>

commit ff32d9cd2c4107224a28f39d3c72eec66d566e09 upstream

According to documentation, AR_ExcessiveRetries, AR_Filtered and
AR_FIFOUnderrun are only valid if AR_FrmXmitOK is clear.

Not checking this might result in suboptimal FIFO settings, unnecessary
retransmissions, or other connectivity issues.

Signed-off-by: Felix Fietkau <[email protected]>
Cc: [email protected]
Signed-off-by: John W. Linville <[email protected]>
---
drivers/net/wireless/ath/ath9k/ar9002_mac.c | 21 ++++++++++++---------
1 files changed, 12 insertions(+), 9 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ar9002_mac.c b/drivers/net/wireless/ath/ath9k/ar9002_mac.c
index 50dda39..cebb97b 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c
@@ -227,18 +227,21 @@ static int ar9002_hw_proc_txdesc(struct ath_hw *ah, void *ds,
ts->ts_status = 0;
ts->ts_flags = 0;

+ if (ads->ds_txstatus9 & AR_TxOpExceeded)
+ ts->ts_status |= ATH9K_TXERR_XTXOP;
+
if (ads->ds_txstatus1 & AR_FrmXmitOK)
ts->ts_status |= ATH9K_TX_ACKED;
- if (ads->ds_txstatus1 & AR_ExcessiveRetries)
- ts->ts_status |= ATH9K_TXERR_XRETRY;
- if (ads->ds_txstatus1 & AR_Filtered)
- ts->ts_status |= ATH9K_TXERR_FILT;
- if (ads->ds_txstatus1 & AR_FIFOUnderrun) {
- ts->ts_status |= ATH9K_TXERR_FIFO;
- ath9k_hw_updatetxtriglevel(ah, true);
+ else {
+ if (ads->ds_txstatus1 & AR_ExcessiveRetries)
+ ts->ts_status |= ATH9K_TXERR_XRETRY;
+ if (ads->ds_txstatus1 & AR_Filtered)
+ ts->ts_status |= ATH9K_TXERR_FILT;
+ if (ads->ds_txstatus1 & AR_FIFOUnderrun) {
+ ts->ts_status |= ATH9K_TXERR_FIFO;
+ ath9k_hw_updatetxtriglevel(ah, true);
+ }
}
- if (ads->ds_txstatus9 & AR_TxOpExceeded)
- ts->ts_status |= ATH9K_TXERR_XTXOP;
if (ads->ds_txstatus1 & AR_TxTimerExpired)
ts->ts_status |= ATH9K_TXERR_TIMER_EXPIRED;

--
1.7.4.15.g7811d


2011-03-28 20:57:36

by Luis R. Rodriguez

[permalink] [raw]
Subject: [PATCH 4/9] ath9k: Fix power save count imbalance on ath_radio_enable()

commit c2731b814e2aaaa40072ee761b7373c052d86e37 upstream

Upon a failure we never call ath9k_ps_restore() on ath_radio_enable(),
this will throw off the sc->ps_usecount. When the sc->ps_usecount
is > 0 we never put the chip to full sleep. This drains battery,
and will also make the chip fail upon resume with:

ath: Starting driver with initial channel: 5745 MHz
ath: timeout (100000 us) on reg 0x7000: 0xdeadbeef & 0x00000003 != 0x00000000

This would make the chip useless upon resume.

I cannot prove this can happen but in theory it is so best to
avoid this race completely and not have users complain about
a broken device after resume.

Cc: [email protected]
Cc: Paul Stewart <[email protected]>
Cc: Amod Bodas <[email protected]>
Signed-off-by: Luis R. Rodriguez <[email protected]>
Signed-off-by: John W. Linville <[email protected]>
---
drivers/net/wireless/ath/ath9k/main.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 5a5f59f..20cdf3d 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -898,8 +898,7 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
if (ath_startrecv(sc) != 0) {
ath_print(common, ATH_DBG_FATAL,
"Unable to restart recv logic\n");
- spin_unlock_bh(&sc->sc_pcu_lock);
- return;
+ goto out;
}
if (sc->sc_flags & SC_OP_BEACONS)
ath_beacon_config(sc, NULL); /* restart beacons */
@@ -913,6 +912,7 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
ath9k_hw_set_gpio(ah, ah->led_pin, 0);

ieee80211_wake_queues(hw);
+out:
spin_unlock_bh(&sc->sc_pcu_lock);

ath9k_ps_restore(sc);
--
1.7.4.15.g7811d


2011-03-28 20:57:43

by Luis R. Rodriguez

[permalink] [raw]
Subject: [PATCH 7/9] ath9k: add missing ps wakeup/restore calls

From: Felix Fietkau <[email protected]>

commit 783cd01e140d9db5c2d2279a96b81e16f9d81fef upstream

There are several places where ath_reset() was called without proper
calls to ath9k_ps_wakeup/ath9k_ps_restore. To fix this, add those calls
directly to ath_reset and drop them from callers where it makes sense.

Also add them to the config callback around ath_update_txpow to fix a
crash that happens when the tx power changed before any vif is brought up.

Signed-off-by: Felix Fietkau <[email protected]>
Cc: [email protected]
Signed-off-by: John W. Linville <[email protected]>
---
drivers/net/wireless/ath/ath9k/main.c | 8 +++++---
drivers/net/wireless/ath/ath9k/xmit.c | 2 --
2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 20cdf3d..1049313 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -602,14 +602,12 @@ void ath9k_tasklet(unsigned long data)
u32 status = sc->intrstatus;
u32 rxmask;

- ath9k_ps_wakeup(sc);
-
if (status & ATH9K_INT_FATAL) {
ath_reset(sc, true);
- ath9k_ps_restore(sc);
return;
}

+ ath9k_ps_wakeup(sc);
spin_lock_bh(&sc->sc_pcu_lock);

if (!ath9k_hw_check_alive(ah))
@@ -977,6 +975,7 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
/* Stop ANI */
del_timer_sync(&common->ani.timer);

+ ath9k_ps_wakeup(sc);
spin_lock_bh(&sc->sc_pcu_lock);

ieee80211_stop_queues(hw);
@@ -1024,6 +1023,7 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)

/* Start ANI */
ath_start_ani(common);
+ ath9k_ps_restore(sc);

return r;
}
@@ -1731,7 +1731,9 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
skip_chan_change:
if (changed & IEEE80211_CONF_CHANGE_POWER) {
sc->config.txpowlimit = 2 * conf->power_level;
+ ath9k_ps_wakeup(sc);
ath_update_txpow(sc);
+ ath9k_ps_restore(sc);
}

spin_lock_bh(&sc->wiphy_lock);
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index aff0478..19ff656 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -2152,9 +2152,7 @@ static void ath_tx_complete_poll_work(struct work_struct *work)
if (needreset) {
ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_RESET,
"tx hung, resetting the chip\n");
- ath9k_ps_wakeup(sc);
ath_reset(sc, true);
- ath9k_ps_restore(sc);
}

ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work,
--
1.7.4.15.g7811d


2011-03-28 21:02:34

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH 0/9] ath9k/ath9k_htc: pending stable patches for 2.6.37.y

On Mon, Mar 28, 2011 at 01:57:06PM -0700, Luis R. Rodriguez wrote:
> Greg,
>
> here are a few stable patches were required backport and also
> a few which are marked as stable but which have not yet been
> propagated to the older stable trees. I've been carrying these
> on the compat-wireless-2.6.37 release for a while now so they
> have gotten some consumption and only now just got a chance to
> send them over to you.

I'm sorry, but I released the last 2.6.37 kernel yesterday, that tree is
now end-of-life, so there's nothing I can do with these patches :(

sorry,

greg k-h

2011-03-28 20:57:45

by Luis R. Rodriguez

[permalink] [raw]
Subject: [PATCH 8/9] ath9k_htc: fix race conditions when stop device

From: Stanislaw Gruszka <[email protected]>

commit ea888357ec005abffb95acee2e61aac68dff429c upstream

We do not kill any scheduled tasklets when stopping device, that may
cause usage of resources after free. Disable interrupts, kill tasklets
and then works in correct order.

Cc: [email protected]
Tested-by: Sujith <[email protected]>
Signed-off-by: Stanislaw Gruszka <[email protected]>
Signed-off-by: John W. Linville <[email protected]>
---
drivers/net/wireless/ath/ath9k/htc_drv_init.c | 3 ---
drivers/net/wireless/ath/ath9k/htc_drv_main.c | 19 ++++++++++++++-----
2 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
index 8776f49..51029a2 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
@@ -142,9 +142,6 @@ static void ath9k_deinit_priv(struct ath9k_htc_priv *priv)
{
ath9k_htc_exit_debug(priv->ah);
ath9k_hw_deinit(priv->ah);
- tasklet_kill(&priv->wmi_tasklet);
- tasklet_kill(&priv->rx_tasklet);
- tasklet_kill(&priv->tx_tasklet);
kfree(priv->ah);
priv->ah = NULL;
}
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index 51977ca..41dc30d 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -1228,18 +1228,27 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw)
return;
}

- /* Cancel all the running timers/work .. */
- cancel_work_sync(&priv->ps_work);
- cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
- ath9k_led_stop_brightness(priv);
-
ath9k_htc_ps_wakeup(priv);
htc_stop(priv->htc);
WMI_CMD(WMI_DISABLE_INTR_CMDID);
WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
WMI_CMD(WMI_STOP_RECV_CMDID);
+
+ tasklet_kill(&priv->wmi_tasklet);
+ tasklet_kill(&priv->rx_tasklet);
+ tasklet_kill(&priv->tx_tasklet);
+
skb_queue_purge(&priv->tx_queue);

+ mutex_unlock(&priv->mutex);
+
+ /* Cancel all the running timers/work .. */
+ cancel_work_sync(&priv->ps_work);
+ cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
+ ath9k_led_stop_brightness(priv);
+
+ mutex_lock(&priv->mutex);
+
/* Remove monitor interface here */
if (ah->opmode == NL80211_IFTYPE_MONITOR) {
if (ath9k_htc_remove_monitor_interface(priv))
--
1.7.4.15.g7811d


2011-03-28 21:14:49

by Luis R. Rodriguez

[permalink] [raw]
Subject: Re: [PATCH 0/9] ath9k/ath9k_htc: pending stable patches for 2.6.37.y

On Mon, Mar 28, 2011 at 2:02 PM, Greg KH <[email protected]> wrote:
> On Mon, Mar 28, 2011 at 01:57:06PM -0700, Luis R. Rodriguez wrote:
>> Greg,
>>
>> here are a few stable patches were required backport and also
>> a few which are marked as stable but which have not yet been
>> propagated to the older stable trees. I've been carrying these
>> on the compat-wireless-2.6.37 release for a while now so they
>> have gotten some consumption and only now just got a chance to
>> send them over to you.
>
> I'm sorry, but I released the last 2.6.37 kernel yesterday, that tree is
> now end-of-life, so there's nothing I can do with these patches :(

Ah no problem. Die 2.6.37.

Luis

2011-03-28 20:57:30

by Luis R. Rodriguez

[permalink] [raw]
Subject: [PATCH 2/9] ath9k: move the PCU lock to the sc structure

commit 4bdd1e978ede034c1211957eb17eaf50de00d234 upstream

The PCU lock should be used to contend TX DMA as well,
this will be done next.

This is part of a series of patches which fix stopping
TX DMA completley when requested on the driver.
For more details about this issue refer to this thread:

http://marc.info/?l=linux-wireless&m=128629803703756&w=2

Tested-by: Ben Greear <[email protected]>
Cc: Kyungwan Nam <[email protected]>
Cc: [email protected]
Signed-off-by: Luis R. Rodriguez <[email protected]>
Signed-off-by: John W. Linville <[email protected]>
---
drivers/net/wireless/ath/ath9k/ath9k.h | 2 +-
drivers/net/wireless/ath/ath9k/main.c | 42 ++++++++++++++++----------------
drivers/net/wireless/ath/ath9k/recv.c | 2 +-
3 files changed, 23 insertions(+), 23 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index aaaa100..02d1cf5 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -310,7 +310,6 @@ struct ath_rx {
u8 rxotherant;
u32 *rxlink;
unsigned int rxfilter;
- spinlock_t pcu_lock;
spinlock_t rxbuflock;
struct list_head rxbuf;
struct ath_descdma rxdma;
@@ -602,6 +601,7 @@ struct ath_softc {
int irq;
spinlock_t sc_serial_rw;
spinlock_t sc_pm_lock;
+ spinlock_t sc_pcu_lock;
struct mutex mutex;
struct work_struct paprd_work;
struct work_struct hw_check_work;
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index e946ac3..5e3e979 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -246,7 +246,7 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
ath9k_hw_set_interrupts(ah, 0);
stopped = ath_drain_all_txq(sc, false);

- spin_lock_bh(&sc->rx.pcu_lock);
+ spin_lock_bh(&sc->sc_pcu_lock);

if (!ath_stoprecv(sc))
stopped = false;
@@ -273,7 +273,7 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
"Unable to reset channel (%u MHz), "
"reset status %d\n",
channel->center_freq, r);
- spin_unlock_bh(&sc->rx.pcu_lock);
+ spin_unlock_bh(&sc->sc_pcu_lock);
goto ps_restore;
}

@@ -281,11 +281,11 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
ath_print(common, ATH_DBG_FATAL,
"Unable to restart recv logic\n");
r = -EIO;
- spin_unlock_bh(&sc->rx.pcu_lock);
+ spin_unlock_bh(&sc->sc_pcu_lock);
goto ps_restore;
}

- spin_unlock_bh(&sc->rx.pcu_lock);
+ spin_unlock_bh(&sc->sc_pcu_lock);

ath_update_txpow(sc);
ath9k_hw_set_interrupts(ah, ah->imask);
@@ -622,7 +622,7 @@ void ath9k_tasklet(unsigned long data)
rxmask = (ATH9K_INT_RX | ATH9K_INT_RXEOL | ATH9K_INT_RXORN);

if (status & rxmask) {
- spin_lock_bh(&sc->rx.pcu_lock);
+ spin_lock_bh(&sc->sc_pcu_lock);

/* Check for high priority Rx first */
if ((ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) &&
@@ -630,7 +630,7 @@ void ath9k_tasklet(unsigned long data)
ath_rx_tasklet(sc, 0, true);

ath_rx_tasklet(sc, 0, false);
- spin_unlock_bh(&sc->rx.pcu_lock);
+ spin_unlock_bh(&sc->sc_pcu_lock);
}

if (status & ATH9K_INT_TX) {
@@ -885,7 +885,7 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
if (!ah->curchan)
ah->curchan = ath_get_curchannel(sc, sc->hw);

- spin_lock_bh(&sc->rx.pcu_lock);
+ spin_lock_bh(&sc->sc_pcu_lock);
r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
if (r) {
ath_print(common, ATH_DBG_FATAL,
@@ -898,10 +898,10 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
if (ath_startrecv(sc) != 0) {
ath_print(common, ATH_DBG_FATAL,
"Unable to restart recv logic\n");
- spin_unlock_bh(&sc->rx.pcu_lock);
+ spin_unlock_bh(&sc->sc_pcu_lock);
return;
}
- spin_unlock_bh(&sc->rx.pcu_lock);
+ spin_unlock_bh(&sc->sc_pcu_lock);

if (sc->sc_flags & SC_OP_BEACONS)
ath_beacon_config(sc, NULL); /* restart beacons */
@@ -941,7 +941,7 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)

ath_drain_all_txq(sc, false); /* clear pending tx frames */

- spin_lock_bh(&sc->rx.pcu_lock);
+ spin_lock_bh(&sc->sc_pcu_lock);

ath_stoprecv(sc); /* turn off frame recv */
ath_flushrecv(sc); /* flush recv queue */
@@ -959,7 +959,7 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)

ath9k_hw_phy_disable(ah);

- spin_unlock_bh(&sc->rx.pcu_lock);
+ spin_unlock_bh(&sc->sc_pcu_lock);

ath9k_hw_configpcipowersave(ah, 1, 1);
ath9k_ps_restore(sc);
@@ -981,7 +981,7 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
ath9k_hw_set_interrupts(ah, 0);
ath_drain_all_txq(sc, retry_tx);

- spin_lock_bh(&sc->rx.pcu_lock);
+ spin_lock_bh(&sc->sc_pcu_lock);

ath_stoprecv(sc);
ath_flushrecv(sc);
@@ -995,7 +995,7 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
ath_print(common, ATH_DBG_FATAL,
"Unable to start recv logic\n");

- spin_unlock_bh(&sc->rx.pcu_lock);
+ spin_unlock_bh(&sc->sc_pcu_lock);

/*
* We may be doing a reset in response to a request
@@ -1159,14 +1159,14 @@ static int ath9k_start(struct ieee80211_hw *hw)
* be followed by initialization of the appropriate bits
* and then setup of the interrupt mask.
*/
- spin_lock_bh(&sc->rx.pcu_lock);
+ spin_lock_bh(&sc->sc_pcu_lock);
r = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
if (r) {
ath_print(common, ATH_DBG_FATAL,
"Unable to reset hardware; reset status %d "
"(freq %u MHz)\n", r,
curchan->center_freq);
- spin_unlock_bh(&sc->rx.pcu_lock);
+ spin_unlock_bh(&sc->sc_pcu_lock);
goto mutex_unlock;
}

@@ -1187,10 +1187,10 @@ static int ath9k_start(struct ieee80211_hw *hw)
ath_print(common, ATH_DBG_FATAL,
"Unable to start recv logic\n");
r = -EIO;
- spin_unlock_bh(&sc->rx.pcu_lock);
+ spin_unlock_bh(&sc->sc_pcu_lock);
goto mutex_unlock;
}
- spin_unlock_bh(&sc->rx.pcu_lock);
+ spin_unlock_bh(&sc->sc_pcu_lock);

/* Setup our intr mask. */
ah->imask = ATH9K_INT_TX | ATH9K_INT_RXEOL |
@@ -1392,14 +1392,14 @@ static void ath9k_stop(struct ieee80211_hw *hw)

if (!(sc->sc_flags & SC_OP_INVALID)) {
ath_drain_all_txq(sc, false);
- spin_lock_bh(&sc->rx.pcu_lock);
+ spin_lock_bh(&sc->sc_pcu_lock);
ath_stoprecv(sc);
ath9k_hw_phy_disable(ah);
- spin_unlock_bh(&sc->rx.pcu_lock);
+ spin_unlock_bh(&sc->sc_pcu_lock);
} else {
- spin_lock_bh(&sc->rx.pcu_lock);
+ spin_lock_bh(&sc->sc_pcu_lock);
sc->rx.rxlink = NULL;
- spin_unlock_bh(&sc->rx.pcu_lock);
+ spin_unlock_bh(&sc->sc_pcu_lock);
}

/* disable HAL and put h/w to sleep */
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index d6bbb57..71b4092 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -317,7 +317,7 @@ int ath_rx_init(struct ath_softc *sc, int nbufs)
struct ath_buf *bf;
int error = 0;

- spin_lock_init(&sc->rx.pcu_lock);
+ spin_lock_init(&sc->sc_pcu_lock);
sc->sc_flags &= ~SC_OP_RXFLUSH;
spin_lock_init(&sc->rx.rxbuflock);

--
1.7.4.15.g7811d


2011-03-28 20:57:25

by Luis R. Rodriguez

[permalink] [raw]
Subject: [PATCH 1/9] ath9k: simplify hw reset locking

commit 9d94674ab754be0e275120a183670ead435f9c0d upstream

The new PCU lock is better placed so we can just contend
against that when trying to reset hardware.

This is part of a series of patches which fix stopping
TX DMA completley when requested on the driver.
For more details about this issue refer to this thread:

http://marc.info/?l=linux-wireless&m=128629803703756&w=2

Tested-by: Ben Greear <[email protected]>
Cc: Kyungwan Nam <[email protected]>
Cc: [email protected]
Signed-off-by: Luis R. Rodriguez <[email protected]>
Signed-off-by: John W. Linville <[email protected]>
---
drivers/net/wireless/ath/ath9k/ath9k.h | 1 -
drivers/net/wireless/ath/ath9k/init.c | 1 -
drivers/net/wireless/ath/ath9k/main.c | 22 ++++++----------------
3 files changed, 6 insertions(+), 18 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 94bd9bc..aaaa100 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -600,7 +600,6 @@ struct ath_softc {
struct ath_hw *sc_ah;
void __iomem *mem;
int irq;
- spinlock_t sc_resetlock;
spinlock_t sc_serial_rw;
spinlock_t sc_pm_lock;
struct mutex mutex;
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 91d9b2a..79a0659 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -580,7 +580,6 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid,
spin_lock_init(&common->cc_lock);

spin_lock_init(&sc->wiphy_lock);
- spin_lock_init(&sc->sc_resetlock);
spin_lock_init(&sc->sc_serial_rw);
spin_lock_init(&sc->sc_pm_lock);
mutex_init(&sc->mutex);
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index cb0b2b9..e946ac3 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -267,19 +267,15 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
channel->center_freq, conf_is_ht40(conf),
fastcc);

- spin_lock_bh(&sc->sc_resetlock);
-
r = ath9k_hw_reset(ah, hchan, caldata, fastcc);
if (r) {
ath_print(common, ATH_DBG_FATAL,
"Unable to reset channel (%u MHz), "
"reset status %d\n",
channel->center_freq, r);
- spin_unlock_bh(&sc->sc_resetlock);
spin_unlock_bh(&sc->rx.pcu_lock);
goto ps_restore;
}
- spin_unlock_bh(&sc->sc_resetlock);

if (ath_startrecv(sc) != 0) {
ath_print(common, ATH_DBG_FATAL,
@@ -890,7 +886,6 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
ah->curchan = ath_get_curchannel(sc, sc->hw);

spin_lock_bh(&sc->rx.pcu_lock);
- spin_lock_bh(&sc->sc_resetlock);
r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
if (r) {
ath_print(common, ATH_DBG_FATAL,
@@ -898,7 +893,6 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
"reset status %d\n",
channel->center_freq, r);
}
- spin_unlock_bh(&sc->sc_resetlock);

ath_update_txpow(sc);
if (ath_startrecv(sc) != 0) {
@@ -955,7 +949,6 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
if (!ah->curchan)
ah->curchan = ath_get_curchannel(sc, hw);

- spin_lock_bh(&sc->sc_resetlock);
r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
if (r) {
ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
@@ -963,7 +956,6 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
"reset status %d\n",
channel->center_freq, r);
}
- spin_unlock_bh(&sc->sc_resetlock);

ath9k_hw_phy_disable(ah);

@@ -994,12 +986,10 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
ath_stoprecv(sc);
ath_flushrecv(sc);

- spin_lock_bh(&sc->sc_resetlock);
r = ath9k_hw_reset(ah, sc->sc_ah->curchan, ah->caldata, false);
if (r)
ath_print(common, ATH_DBG_FATAL,
"Unable to reset hardware; reset status %d\n", r);
- spin_unlock_bh(&sc->sc_resetlock);

if (ath_startrecv(sc) != 0)
ath_print(common, ATH_DBG_FATAL,
@@ -1170,18 +1160,15 @@ static int ath9k_start(struct ieee80211_hw *hw)
* and then setup of the interrupt mask.
*/
spin_lock_bh(&sc->rx.pcu_lock);
- spin_lock_bh(&sc->sc_resetlock);
r = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
if (r) {
ath_print(common, ATH_DBG_FATAL,
"Unable to reset hardware; reset status %d "
"(freq %u MHz)\n", r,
curchan->center_freq);
- spin_unlock_bh(&sc->sc_resetlock);
spin_unlock_bh(&sc->rx.pcu_lock);
goto mutex_unlock;
}
- spin_unlock_bh(&sc->sc_resetlock);

/*
* This is needed only to setup initial state
@@ -1403,14 +1390,17 @@ static void ath9k_stop(struct ieee80211_hw *hw)
* before setting the invalid flag. */
ath9k_hw_set_interrupts(ah, 0);

- spin_lock_bh(&sc->rx.pcu_lock);
if (!(sc->sc_flags & SC_OP_INVALID)) {
ath_drain_all_txq(sc, false);
+ spin_lock_bh(&sc->rx.pcu_lock);
ath_stoprecv(sc);
ath9k_hw_phy_disable(ah);
- } else
+ spin_unlock_bh(&sc->rx.pcu_lock);
+ } else {
+ spin_lock_bh(&sc->rx.pcu_lock);
sc->rx.rxlink = NULL;
- spin_unlock_bh(&sc->rx.pcu_lock);
+ spin_unlock_bh(&sc->rx.pcu_lock);
+ }

/* disable HAL and put h/w to sleep */
ath9k_hw_disable(ah);
--
1.7.4.15.g7811d


2011-03-29 07:42:16

by Mohammed Shafi Shajakhan

[permalink] [raw]
Subject: Re: [PATCH 9/9] ath9k: Fix memory leak due to failed PAPRD frames

On Tuesday 29 March 2011 02:27 AM, Luis Rodriguez wrote:
> From: Mohammed Shafi Shajakhan<[email protected]>
>
> commit 479600777bb588724d044815415f7d708d06644b upstream
>
> free the skb's when the Tx of PAPRD frames fails and also add a debug
> message indicating that.
>
> Signed-off-by: Mohammed Shafi Shajakhan<[email protected]>
> Cc: [email protected]
> Signed-off-by: John W. Linville<[email protected]>
> ---
> drivers/net/wireless/ath/ath9k/main.c | 5 ++++-
> 1 files changed, 4 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
> index 1049313..16b8e36 100644
> --- a/drivers/net/wireless/ath/ath9k/main.c
> +++ b/drivers/net/wireless/ath/ath9k/main.c
> @@ -384,8 +384,11 @@ void ath_paprd_calibrate(struct work_struct *work)
> init_completion(&sc->paprd_complete);
> ar9003_paprd_setup_gain_table(ah, chain);
> txctl.paprd = BIT(chain);
> - if (ath_tx_start(hw, skb,&txctl) != 0)
> + if (ath_tx_start(hw, skb,&txctl) != 0) {
> + ath_print(common, ATH_DBG_XMIT, "PAPRD TX failed\n");
> + dev_kfree_skb_any(skb);
> break;
> + }
>
> time_left = wait_for_completion_timeout(&sc->paprd_complete,
> msecs_to_jiffies(ATH_PAPRD_TIMEOUT));
>
Luis, thanks!

2011-03-28 20:57:42

by Luis R. Rodriguez

[permalink] [raw]
Subject: [PATCH 6/9] ath9k_hw: ASPM interoperability fix for AR9380/AR9382

commit 58c5296991d233f2e492aa7a884635bba478cf12 upstream

There is an interoperability with AR9382/AR9380 in L1 state with a
few root complexes which can cause a hang. This is fixed by
setting some work around bits on the PCIE PHY. We fix by using
a new ini array to modify these bits when the radio is idle.

Cc: [email protected]
Cc: Jack Lee <[email protected]>
Cc: Carl Huang <[email protected]>
Cc: David Quan <[email protected]>
Cc: Nael Atallah <[email protected]>
Cc: Sarvesh Shrivastava <[email protected]>
Signed-off-by: Luis R. Rodriguez <[email protected]>
Signed-off-by: John W. Linville <[email protected]>
---
.../net/wireless/ath/ath9k/ar9003_2p2_initvals.h | 2 +-
drivers/net/wireless/ath/ath9k/ar9003_hw.c | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h b/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h
index a14a5e4..c655cd2 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h
@@ -1842,7 +1842,7 @@ static const u32 ar9300_2p2_soc_preamble[][2] = {

static const u32 ar9300PciePhy_pll_on_clkreq_disable_L1_2p2[][2] = {
/* Addr allmodes */
- {0x00004040, 0x08212e5e},
+ {0x00004040, 0x0821265e},
{0x00004040, 0x0008003b},
{0x00004044, 0x00000000},
};
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c
index c2a0571..cd8e0bd 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c
@@ -95,8 +95,8 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah)
/* Sleep Setting */

INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower,
- ar9300PciePhy_clkreq_enable_L1_2p2,
- ARRAY_SIZE(ar9300PciePhy_clkreq_enable_L1_2p2),
+ ar9300PciePhy_pll_on_clkreq_disable_L1_2p2,
+ ARRAY_SIZE(ar9300PciePhy_pll_on_clkreq_disable_L1_2p2),
2);

/* Fast clock modal settings */
--
1.7.4.15.g7811d


2011-03-28 20:57:33

by Luis R. Rodriguez

[permalink] [raw]
Subject: [PATCH 3/9] ath9k: content DMA start / stop through the PCU lock

commit 6a6733f256f18cbcf4875e13f59eedb593b755a8 upstream

This helps align resets / RX enable & disable / TX stop / start.
Locking around the PCU is important to ensure the hardware doesn't
get stale data when working with DMA'able data.

This is part of a series of patches which fix stopping
TX DMA completley when requested on the driver.
For more details about this issue refer to this thread:

http://marc.info/?l=linux-wireless&m=128629803703756&w=2

Tested-by: Ben Greear <[email protected]>
Cc: Kyungwan Nam <[email protected]>
Cc: [email protected]
Signed-off-by: Luis R. Rodriguez <[email protected]>
Signed-off-by: John W. Linville <[email protected]>
---
drivers/net/wireless/ath/ath9k/main.c | 52 ++++++++++++++++----------------
1 files changed, 26 insertions(+), 26 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 5e3e979..5a5f59f 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -234,6 +234,8 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,

ath9k_ps_wakeup(sc);

+ spin_lock_bh(&sc->sc_pcu_lock);
+
/*
* This is only performed if the channel settings have
* actually changed.
@@ -246,8 +248,6 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
ath9k_hw_set_interrupts(ah, 0);
stopped = ath_drain_all_txq(sc, false);

- spin_lock_bh(&sc->sc_pcu_lock);
-
if (!ath_stoprecv(sc))
stopped = false;

@@ -273,7 +273,6 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
"Unable to reset channel (%u MHz), "
"reset status %d\n",
channel->center_freq, r);
- spin_unlock_bh(&sc->sc_pcu_lock);
goto ps_restore;
}

@@ -281,12 +280,9 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
ath_print(common, ATH_DBG_FATAL,
"Unable to restart recv logic\n");
r = -EIO;
- spin_unlock_bh(&sc->sc_pcu_lock);
goto ps_restore;
}

- spin_unlock_bh(&sc->sc_pcu_lock);
-
ath_update_txpow(sc);
ath9k_hw_set_interrupts(ah, ah->imask);

@@ -298,6 +294,8 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
}

ps_restore:
+ spin_unlock_bh(&sc->sc_pcu_lock);
+
ath9k_ps_restore(sc);
return r;
}
@@ -612,6 +610,8 @@ void ath9k_tasklet(unsigned long data)
return;
}

+ spin_lock_bh(&sc->sc_pcu_lock);
+
if (!ath9k_hw_check_alive(ah))
ieee80211_queue_work(sc->hw, &sc->hw_check_work);

@@ -622,15 +622,12 @@ void ath9k_tasklet(unsigned long data)
rxmask = (ATH9K_INT_RX | ATH9K_INT_RXEOL | ATH9K_INT_RXORN);

if (status & rxmask) {
- spin_lock_bh(&sc->sc_pcu_lock);
-
/* Check for high priority Rx first */
if ((ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) &&
(status & ATH9K_INT_RXHP))
ath_rx_tasklet(sc, 0, true);

ath_rx_tasklet(sc, 0, false);
- spin_unlock_bh(&sc->sc_pcu_lock);
}

if (status & ATH9K_INT_TX) {
@@ -656,6 +653,8 @@ void ath9k_tasklet(unsigned long data)

/* re-enable hardware interrupt */
ath9k_hw_set_interrupts(ah, ah->imask);
+
+ spin_unlock_bh(&sc->sc_pcu_lock);
ath9k_ps_restore(sc);
}

@@ -880,12 +879,13 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
int r;

ath9k_ps_wakeup(sc);
+ spin_lock_bh(&sc->sc_pcu_lock);
+
ath9k_hw_configpcipowersave(ah, 0, 0);

if (!ah->curchan)
ah->curchan = ath_get_curchannel(sc, sc->hw);

- spin_lock_bh(&sc->sc_pcu_lock);
r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
if (r) {
ath_print(common, ATH_DBG_FATAL,
@@ -901,8 +901,6 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
spin_unlock_bh(&sc->sc_pcu_lock);
return;
}
- spin_unlock_bh(&sc->sc_pcu_lock);
-
if (sc->sc_flags & SC_OP_BEACONS)
ath_beacon_config(sc, NULL); /* restart beacons */

@@ -915,6 +913,8 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
ath9k_hw_set_gpio(ah, ah->led_pin, 0);

ieee80211_wake_queues(hw);
+ spin_unlock_bh(&sc->sc_pcu_lock);
+
ath9k_ps_restore(sc);
}

@@ -925,6 +925,8 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
int r;

ath9k_ps_wakeup(sc);
+ spin_lock_bh(&sc->sc_pcu_lock);
+
ieee80211_stop_queues(hw);

/*
@@ -941,8 +943,6 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)

ath_drain_all_txq(sc, false); /* clear pending tx frames */

- spin_lock_bh(&sc->sc_pcu_lock);
-
ath_stoprecv(sc); /* turn off frame recv */
ath_flushrecv(sc); /* flush recv queue */

@@ -959,10 +959,11 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)

ath9k_hw_phy_disable(ah);

- spin_unlock_bh(&sc->sc_pcu_lock);
-
ath9k_hw_configpcipowersave(ah, 1, 1);
+
+ spin_unlock_bh(&sc->sc_pcu_lock);
ath9k_ps_restore(sc);
+
ath9k_setpower(sc, ATH9K_PM_FULL_SLEEP);
}

@@ -976,13 +977,13 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
/* Stop ANI */
del_timer_sync(&common->ani.timer);

+ spin_lock_bh(&sc->sc_pcu_lock);
+
ieee80211_stop_queues(hw);

ath9k_hw_set_interrupts(ah, 0);
ath_drain_all_txq(sc, retry_tx);

- spin_lock_bh(&sc->sc_pcu_lock);
-
ath_stoprecv(sc);
ath_flushrecv(sc);

@@ -995,8 +996,6 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
ath_print(common, ATH_DBG_FATAL,
"Unable to start recv logic\n");

- spin_unlock_bh(&sc->sc_pcu_lock);
-
/*
* We may be doing a reset in response to a request
* that changes the channel so update any state that
@@ -1021,6 +1020,7 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
}

ieee80211_wake_queues(hw);
+ spin_unlock_bh(&sc->sc_pcu_lock);

/* Start ANI */
ath_start_ani(common);
@@ -1386,25 +1386,25 @@ static void ath9k_stop(struct ieee80211_hw *hw)
ath9k_btcoex_timer_pause(sc);
}

+ spin_lock_bh(&sc->sc_pcu_lock);
+
/* make sure h/w will not generate any interrupt
* before setting the invalid flag. */
ath9k_hw_set_interrupts(ah, 0);

if (!(sc->sc_flags & SC_OP_INVALID)) {
ath_drain_all_txq(sc, false);
- spin_lock_bh(&sc->sc_pcu_lock);
ath_stoprecv(sc);
ath9k_hw_phy_disable(ah);
- spin_unlock_bh(&sc->sc_pcu_lock);
- } else {
- spin_lock_bh(&sc->sc_pcu_lock);
+ } else
sc->rx.rxlink = NULL;
- spin_unlock_bh(&sc->sc_pcu_lock);
- }

/* disable HAL and put h/w to sleep */
ath9k_hw_disable(ah);
ath9k_hw_configpcipowersave(ah, 1, 1);
+
+ spin_unlock_bh(&sc->sc_pcu_lock);
+
ath9k_ps_restore(sc);

sc->ps_idle = true;
--
1.7.4.15.g7811d