2010-10-15 18:03:40

by Felix Fietkau

[permalink] [raw]
Subject: [PATCH 1/6] ath9k_hw: optimize interrupt mask changes

OProfile showed that ath9k was spending way too much time in
ath9k_hw_set_interrupts. Since most of the interrupt mask changes only
need to globally enable/disable interrupts, it makes sense to split
this part into separate functions, replacing all calls to
ath9k_hw_set_interrupts(ah, 0) with ath9k_hw_disable_interrupts(ah).

ath9k_hw_set_interrupts(ah, ah->imask) only gets changed to
ath9k_hw_enable_interrupts(ah), whenever ah->imask was not changed
since the point where interrupts were disabled.

Signed-off-by: Felix Fietkau <[email protected]>
---
drivers/net/wireless/ath/ath9k/beacon.c | 10 ++--
drivers/net/wireless/ath/ath9k/gpio.c | 4 +-
drivers/net/wireless/ath/ath9k/mac.c | 86 ++++++++++++++++++-------------
drivers/net/wireless/ath/ath9k/mac.h | 6 ++-
drivers/net/wireless/ath/ath9k/main.c | 18 +++---
5 files changed, 70 insertions(+), 54 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index 081192e..8ff342f 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -500,10 +500,10 @@ static void ath_beacon_config_ap(struct ath_softc *sc,

/* Set the computed AP beacon timers */

- ath9k_hw_set_interrupts(ah, 0);
+ ath9k_hw_disable_interrupts(ah);
ath9k_beacon_init(sc, nexttbtt, intval);
sc->beacon.bmisscnt = 0;
- ath9k_hw_set_interrupts(ah, ah->imask);
+ ath9k_hw_enable_interrupts(ah);

/* Clear the reset TSF flag, so that subsequent beacon updation
will not reset the HW TSF. */
@@ -635,7 +635,7 @@ static void ath_beacon_config_sta(struct ath_softc *sc,

/* Set the computed STA beacon timers */

- ath9k_hw_set_interrupts(ah, 0);
+ ath9k_hw_disable_interrupts(ah);
ath9k_hw_set_sta_beacon_timers(ah, &bs);
ah->imask |= ATH9K_INT_BMISS;
ath9k_hw_set_interrupts(ah, ah->imask);
@@ -683,10 +683,10 @@ static void ath_beacon_config_adhoc(struct ath_softc *sc,

/* Set the computed ADHOC beacon timers */

- ath9k_hw_set_interrupts(ah, 0);
+ ath9k_hw_disable_interrupts(ah);
ath9k_beacon_init(sc, nexttbtt, intval);
sc->beacon.bmisscnt = 0;
- ath9k_hw_set_interrupts(ah, ah->imask);
+ ath9k_hw_enable_interrupts(ah);
}

void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif)
diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c
index 4a9a68b..db9c6fe 100644
--- a/drivers/net/wireless/ath/ath9k/gpio.c
+++ b/drivers/net/wireless/ath/ath9k/gpio.c
@@ -259,7 +259,7 @@ static void ath9k_gen_timer_start(struct ath_hw *ah,
ath9k_hw_gen_timer_start(ah, timer, timer_next, timer_period);

if ((ah->imask & ATH9K_INT_GENTIMER) == 0) {
- ath9k_hw_set_interrupts(ah, 0);
+ ath9k_hw_disable_interrupts(ah);
ah->imask |= ATH9K_INT_GENTIMER;
ath9k_hw_set_interrupts(ah, ah->imask);
}
@@ -273,7 +273,7 @@ static void ath9k_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer)

/* if no timer is enabled, turn off interrupt mask */
if (timer_table->timer_mask.val == 0) {
- ath9k_hw_set_interrupts(ah, 0);
+ ath9k_hw_disable_interrupts(ah);
ah->imask &= ~ATH9K_INT_GENTIMER;
ath9k_hw_set_interrupts(ah, ah->imask);
}
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index 8c13479..65b1ee2 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -117,12 +117,11 @@ EXPORT_SYMBOL(ath9k_hw_numtxpending);
bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel)
{
u32 txcfg, curLevel, newLevel;
- enum ath9k_int omask;

if (ah->tx_trig_level >= ah->config.max_txtrig_level)
return false;

- omask = ath9k_hw_set_interrupts(ah, ah->imask & ~ATH9K_INT_GLOBAL);
+ ath9k_hw_disable_interrupts(ah);

txcfg = REG_READ(ah, AR_TXCFG);
curLevel = MS(txcfg, AR_FTRIG);
@@ -136,7 +135,7 @@ bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel)
REG_WRITE(ah, AR_TXCFG,
(txcfg & ~AR_FTRIG) | SM(newLevel, AR_FTRIG));

- ath9k_hw_set_interrupts(ah, omask);
+ ath9k_hw_enable_interrupts(ah);

ah->tx_trig_level = newLevel;

@@ -849,28 +848,59 @@ bool ath9k_hw_intrpend(struct ath_hw *ah)
}
EXPORT_SYMBOL(ath9k_hw_intrpend);

-enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah,
- enum ath9k_int ints)
+void ath9k_hw_disable_interrupts(struct ath_hw *ah)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ ath_print(common, ATH_DBG_INTERRUPT, "disable IER\n");
+ REG_WRITE(ah, AR_IER, AR_IER_DISABLE);
+ (void) REG_READ(ah, AR_IER);
+ if (!AR_SREV_9100(ah)) {
+ REG_WRITE(ah, AR_INTR_ASYNC_ENABLE, 0);
+ (void) REG_READ(ah, AR_INTR_ASYNC_ENABLE);
+
+ REG_WRITE(ah, AR_INTR_SYNC_ENABLE, 0);
+ (void) REG_READ(ah, AR_INTR_SYNC_ENABLE);
+ }
+}
+EXPORT_SYMBOL(ath9k_hw_disable_interrupts);
+
+void ath9k_hw_enable_interrupts(struct ath_hw *ah)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ if (!(ah->imask & ATH9K_INT_GLOBAL))
+ return;
+
+ ath_print(common, ATH_DBG_INTERRUPT, "enable IER\n");
+ REG_WRITE(ah, AR_IER, AR_IER_ENABLE);
+ if (!AR_SREV_9100(ah)) {
+ REG_WRITE(ah, AR_INTR_ASYNC_ENABLE,
+ AR_INTR_MAC_IRQ);
+ REG_WRITE(ah, AR_INTR_ASYNC_MASK, AR_INTR_MAC_IRQ);
+
+
+ REG_WRITE(ah, AR_INTR_SYNC_ENABLE,
+ AR_INTR_SYNC_DEFAULT);
+ REG_WRITE(ah, AR_INTR_SYNC_MASK,
+ AR_INTR_SYNC_DEFAULT);
+ }
+ ath_print(common, ATH_DBG_INTERRUPT, "AR_IMR 0x%x IER 0x%x\n",
+ REG_READ(ah, AR_IMR), REG_READ(ah, AR_IER));
+}
+EXPORT_SYMBOL(ath9k_hw_enable_interrupts);
+
+void ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints)
{
enum ath9k_int omask = ah->imask;
u32 mask, mask2;
struct ath9k_hw_capabilities *pCap = &ah->caps;
struct ath_common *common = ath9k_hw_common(ah);

- ath_print(common, ATH_DBG_INTERRUPT, "0x%x => 0x%x\n", omask, ints);
-
- if (omask & ATH9K_INT_GLOBAL) {
- ath_print(common, ATH_DBG_INTERRUPT, "disable IER\n");
- REG_WRITE(ah, AR_IER, AR_IER_DISABLE);
- (void) REG_READ(ah, AR_IER);
- if (!AR_SREV_9100(ah)) {
- REG_WRITE(ah, AR_INTR_ASYNC_ENABLE, 0);
- (void) REG_READ(ah, AR_INTR_ASYNC_ENABLE);
+ if (!(ints & ATH9K_INT_GLOBAL))
+ ath9k_hw_enable_interrupts(ah);

- REG_WRITE(ah, AR_INTR_SYNC_ENABLE, 0);
- (void) REG_READ(ah, AR_INTR_SYNC_ENABLE);
- }
- }
+ ath_print(common, ATH_DBG_INTERRUPT, "0x%x => 0x%x\n", omask, ints);

/* TODO: global int Ref count */
mask = ints & ATH9K_INT_COMMON;
@@ -946,24 +976,8 @@ enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah,
REG_CLR_BIT(ah, AR_IMR_S5, AR_IMR_S5_TIM_TIMER);
}

- if (ints & ATH9K_INT_GLOBAL) {
- ath_print(common, ATH_DBG_INTERRUPT, "enable IER\n");
- REG_WRITE(ah, AR_IER, AR_IER_ENABLE);
- if (!AR_SREV_9100(ah)) {
- REG_WRITE(ah, AR_INTR_ASYNC_ENABLE,
- AR_INTR_MAC_IRQ);
- REG_WRITE(ah, AR_INTR_ASYNC_MASK, AR_INTR_MAC_IRQ);
-
-
- REG_WRITE(ah, AR_INTR_SYNC_ENABLE,
- AR_INTR_SYNC_DEFAULT);
- REG_WRITE(ah, AR_INTR_SYNC_MASK,
- AR_INTR_SYNC_DEFAULT);
- }
- ath_print(common, ATH_DBG_INTERRUPT, "AR_IMR 0x%x IER 0x%x\n",
- REG_READ(ah, AR_IMR), REG_READ(ah, AR_IER));
- }
+ ath9k_hw_enable_interrupts(ah);

- return omask;
+ return;
}
EXPORT_SYMBOL(ath9k_hw_set_interrupts);
diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h
index 7c1a34d..538c676 100644
--- a/drivers/net/wireless/ath/ath9k/mac.h
+++ b/drivers/net/wireless/ath/ath9k/mac.h
@@ -669,6 +669,7 @@ enum ath9k_key_type {

struct ath_hw;
struct ath9k_channel;
+enum ath9k_int;

u32 ath9k_hw_gettxbuf(struct ath_hw *ah, u32 q);
void ath9k_hw_puttxbuf(struct ath_hw *ah, u32 q, u32 txdp);
@@ -700,8 +701,9 @@ int ath9k_hw_beaconq_setup(struct ath_hw *ah);

/* Interrupt Handling */
bool ath9k_hw_intrpend(struct ath_hw *ah);
-enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah,
- enum ath9k_int ints);
+void ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints);
+void ath9k_hw_enable_interrupts(struct ath_hw *ah);
+void ath9k_hw_disable_interrupts(struct ath_hw *ah);

void ar9002_hw_attach_mac_ops(struct ath_hw *ah);

diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 7bb8a0b..029c6d2 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -219,7 +219,7 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
* hardware at the new frequency, and then re-enable
* the relevant bits of the h/w.
*/
- ath9k_hw_set_interrupts(ah, 0);
+ ath9k_hw_disable_interrupts(ah);
ath_drain_all_txq(sc, false);
stopped = ath_stoprecv(sc);

@@ -626,7 +626,7 @@ void ath9k_tasklet(unsigned long data)
ath_gen_timer_isr(sc->sc_ah);

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

@@ -725,7 +725,7 @@ irqreturn_t ath_isr(int irq, void *dev)
* interrupt; otherwise it will continue to
* fire.
*/
- ath9k_hw_set_interrupts(ah, 0);
+ ath9k_hw_disable_interrupts(ah);
/*
* Let the hal handle the event. We assume
* it will clear whatever condition caused
@@ -734,7 +734,7 @@ irqreturn_t ath_isr(int irq, void *dev)
spin_lock(&common->cc_lock);
ath9k_hw_proc_mib_event(ah);
spin_unlock(&common->cc_lock);
- ath9k_hw_set_interrupts(ah, ah->imask);
+ ath9k_hw_enable_interrupts(ah);
}

if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP))
@@ -751,8 +751,8 @@ chip_reset:
ath_debug_stat_interrupt(sc, status);

if (sched) {
- /* turn off every interrupt except SWBA */
- ath9k_hw_set_interrupts(ah, (ah->imask & ATH9K_INT_SWBA));
+ /* turn off every interrupt */
+ ath9k_hw_disable_interrupts(ah);
tasklet_schedule(&sc->intr_tq);
}

@@ -907,7 +907,7 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
}

/* Disable interrupts */
- ath9k_hw_set_interrupts(ah, 0);
+ ath9k_hw_disable_interrupts(ah);

ath_drain_all_txq(sc, false); /* clear pending tx frames */
ath_stoprecv(sc); /* turn off frame recv */
@@ -944,7 +944,7 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)

ieee80211_stop_queues(hw);

- ath9k_hw_set_interrupts(ah, 0);
+ ath9k_hw_disable_interrupts(ah);
ath_drain_all_txq(sc, retry_tx);
ath_stoprecv(sc);
ath_flushrecv(sc);
@@ -1349,7 +1349,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)

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

if (!(sc->sc_flags & SC_OP_INVALID)) {
ath_drain_all_txq(sc, false);
--
1.7.2.2



2010-10-15 18:03:39

by Felix Fietkau

[permalink] [raw]
Subject: [PATCH 6/6] ath9k_hw: make ath9k_hw_gettsf32 static

It is now only used in hw.c

Signed-off-by: Felix Fietkau <[email protected]>
---
drivers/net/wireless/ath/ath9k/hw.c | 3 +--
drivers/net/wireless/ath/ath9k/hw.h | 1 -
2 files changed, 1 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index cc13ee1..d37a8ad 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -2312,11 +2312,10 @@ static u32 rightmost_index(struct ath_gen_timer_table *timer_table, u32 *mask)
return timer_table->gen_timer_index[b];
}

-u32 ath9k_hw_gettsf32(struct ath_hw *ah)
+static u32 ath9k_hw_gettsf32(struct ath_hw *ah)
{
return REG_READ(ah, AR_TSF_L32);
}
-EXPORT_SYMBOL(ath9k_hw_gettsf32);

struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
void (*trigger)(void *),
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index d032939..e68204a 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -892,7 +892,6 @@ void ath9k_hw_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer);

void ath_gen_timer_free(struct ath_hw *ah, struct ath_gen_timer *timer);
void ath_gen_timer_isr(struct ath_hw *hw);
-u32 ath9k_hw_gettsf32(struct ath_hw *ah);

void ath9k_hw_name(struct ath_hw *ah, char *hw_name, size_t len);

--
1.7.2.2


2010-10-18 01:25:00

by Bruno Randolf

[permalink] [raw]
Subject: Re: [PATCH 4/6] ath9k: optimize/fix ANI RSSI processing

On Sat October 16 2010 03:03:31 Felix Fietkau wrote:
> ANI needs the RSSI average only in station mode, and only for tracking
> the signal strength of beacons of the AP that it is connected to.
> Adjust the code to track on the beacon RSSI, and store the average of that
> in the ath_wiphy struct.
> With these changes, we can get rid of this extra station lookup in the
> rx path, which saves precious CPU cycles.

wouldn't it make sense to have per-station RSSI in IBSS mode?

bruno

> Signed-off-by: Felix Fietkau <[email protected]>
> ---
> drivers/net/wireless/ath/ath9k/ath9k.h | 2 +-
> drivers/net/wireless/ath/ath9k/init.c | 2 +
> drivers/net/wireless/ath/ath9k/main.c | 6 +++-
> drivers/net/wireless/ath/ath9k/recv.c | 38
> +++++++++-------------------- drivers/net/wireless/ath/ath9k/virtual.c |
> 1 +
> 5 files changed, 20 insertions(+), 29 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h
> b/drivers/net/wireless/ath/ath9k/ath9k.h index e435c1d..8f5d4c4 100644
> --- a/drivers/net/wireless/ath/ath9k/ath9k.h
> +++ b/drivers/net/wireless/ath/ath9k/ath9k.h
> @@ -271,7 +271,6 @@ struct ath_node {
> struct ath_atx_ac ac[WME_NUM_AC];
> u16 maxampdu;
> u8 mpdudensity;
> - int last_rssi;
> };
>
> #define AGGR_CLEANUP BIT(1)
> @@ -663,6 +662,7 @@ struct ath_wiphy {
> bool idle;
> int chan_idx;
> int chan_is_ht;
> + int last_rssi;
> };
>
> void ath9k_tasklet(unsigned long data);
> diff --git a/drivers/net/wireless/ath/ath9k/init.c
> b/drivers/net/wireless/ath/ath9k/init.c index bc6c4df..5ebe53d 100644
> --- a/drivers/net/wireless/ath/ath9k/init.c
> +++ b/drivers/net/wireless/ath/ath9k/init.c
> @@ -702,6 +702,7 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc,
> u16 subsysid, const struct ath_bus_ops *bus_ops)
> {
> struct ieee80211_hw *hw = sc->hw;
> + struct ath_wiphy *aphy = hw->priv;
> struct ath_common *common;
> struct ath_hw *ah;
> int error = 0;
> @@ -751,6 +752,7 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc,
> u16 subsysid, INIT_WORK(&sc->chan_work, ath9k_wiphy_chan_work);
> INIT_DELAYED_WORK(&sc->wiphy_work, ath9k_wiphy_work);
> sc->wiphy_scheduler_int = msecs_to_jiffies(500);
> + aphy->last_rssi = ATH_RSSI_DUMMY_MARKER;
>
> ath_init_leds(sc);
> ath_start_rfkill_poll(sc);
> diff --git a/drivers/net/wireless/ath/ath9k/main.c
> b/drivers/net/wireless/ath/ath9k/main.c index 029c6d2..e70dc383 100644
> --- a/drivers/net/wireless/ath/ath9k/main.c
> +++ b/drivers/net/wireless/ath/ath9k/main.c
> @@ -535,7 +535,6 @@ static void ath_node_attach(struct ath_softc *sc,
> struct ieee80211_sta *sta) an->maxampdu = 1 <<
> (IEEE80211_HT_MAX_AMPDU_FACTOR +
> sta->ht_cap.ampdu_factor);
> an->mpdudensity = parse_mpdudensity(sta->ht_cap.ampdu_density);
> - an->last_rssi = ATH_RSSI_DUMMY_MARKER;
> }
> }
>
> @@ -804,9 +803,11 @@ static u32 ath_get_extchanmode(struct ath_softc *sc,
> }
>
> static void ath9k_bss_assoc_info(struct ath_softc *sc,
> + struct ieee80211_hw *hw,
> struct ieee80211_vif *vif,
> struct ieee80211_bss_conf *bss_conf)
> {
> + struct ath_wiphy *aphy = hw->priv;
> struct ath_hw *ah = sc->sc_ah;
> struct ath_common *common = ath9k_hw_common(ah);
>
> @@ -830,6 +831,7 @@ static void ath9k_bss_assoc_info(struct ath_softc *sc,
> ath_beacon_config(sc, vif);
>
> /* Reset rssi stats */
> + aphy->last_rssi = ATH_RSSI_DUMMY_MARKER;
> sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
>
> sc->sc_flags |= SC_OP_ANI_RUN;
> @@ -1951,7 +1953,7 @@ static void ath9k_bss_info_changed(struct
> ieee80211_hw *hw, if (changed & BSS_CHANGED_ASSOC) {
> ath_print(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
> bss_conf->assoc);
> - ath9k_bss_assoc_info(sc, vif, bss_conf);
> + ath9k_bss_assoc_info(sc, hw, vif, bss_conf);
> }
>
> mutex_unlock(&sc->mutex);
> diff --git a/drivers/net/wireless/ath/ath9k/recv.c
> b/drivers/net/wireless/ath/ath9k/recv.c index 7c90eaf..925b327 100644
> --- a/drivers/net/wireless/ath/ath9k/recv.c
> +++ b/drivers/net/wireless/ath/ath9k/recv.c
> @@ -960,36 +960,23 @@ static void ath9k_process_rssi(struct ath_common
> *common, struct ieee80211_hdr *hdr,
> struct ath_rx_status *rx_stats)
> {
> + struct ath_wiphy *aphy = hw->priv;
> struct ath_hw *ah = common->ah;
> - struct ieee80211_sta *sta;
> - struct ath_node *an;
> - int last_rssi = ATH_RSSI_DUMMY_MARKER;
> + int last_rssi;
> __le16 fc;
>
> + if (ah->opmode != NL80211_IFTYPE_STATION)
> + return;
> +
> fc = hdr->frame_control;
> + if (!ieee80211_is_beacon(fc) ||
> + compare_ether_addr(hdr->addr3, common->curbssid))
> + return;
>
> - rcu_read_lock();
> - /*
> - * XXX: use ieee80211_find_sta! This requires quite a bit of work
> - * under the current ath9k virtual wiphy implementation as we have
> - * no way of tying a vif to wiphy. Typically vifs are attached to
> - * at least one sdata of a wiphy on mac80211 but with ath9k virtual
> - * wiphy you'd have to iterate over every wiphy and each sdata.
> - */
> - if (is_multicast_ether_addr(hdr->addr1))
> - sta = ieee80211_find_sta_by_ifaddr(hw, hdr->addr2, NULL);
> - else
> - sta = ieee80211_find_sta_by_ifaddr(hw, hdr->addr2, hdr->addr1);
> -
> - if (sta) {
> - an = (struct ath_node *) sta->drv_priv;
> - if (rx_stats->rs_rssi != ATH9K_RSSI_BAD &&
> - !rx_stats->rs_moreaggr)
> - ATH_RSSI_LPF(an->last_rssi, rx_stats->rs_rssi);
> - last_rssi = an->last_rssi;
> - }
> - rcu_read_unlock();
> + if (rx_stats->rs_rssi != ATH9K_RSSI_BAD && !rx_stats->rs_moreaggr)
> + ATH_RSSI_LPF(aphy->last_rssi, rx_stats->rs_rssi);
>
> + last_rssi = aphy->last_rssi;
> if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER))
> rx_stats->rs_rssi = ATH_EP_RND(last_rssi,
> ATH_RSSI_EP_MULTIPLIER);
> @@ -997,8 +984,7 @@ static void ath9k_process_rssi(struct ath_common
> *common, rx_stats->rs_rssi = 0;
>
> /* Update Beacon RSSI, this is used by ANI. */
> - if (ieee80211_is_beacon(fc))
> - ah->stats.avgbrssi = rx_stats->rs_rssi;
> + ah->stats.avgbrssi = rx_stats->rs_rssi;
> }
>
> /*
> diff --git a/drivers/net/wireless/ath/ath9k/virtual.c
> b/drivers/net/wireless/ath/ath9k/virtual.c index ec7cf5e..cb6c48b 100644
> --- a/drivers/net/wireless/ath/ath9k/virtual.c
> +++ b/drivers/net/wireless/ath/ath9k/virtual.c
> @@ -107,6 +107,7 @@ int ath9k_wiphy_add(struct ath_softc *sc)
> aphy->sc = sc;
> aphy->hw = hw;
> sc->sec_wiphy[i] = aphy;
> + aphy->last_rssi = ATH_RSSI_DUMMY_MARKER;
> spin_unlock_bh(&sc->wiphy_lock);
>
> memcpy(addr, common->macaddr, ETH_ALEN);

2010-10-18 01:52:24

by Felix Fietkau

[permalink] [raw]
Subject: Re: [PATCH 4/6] ath9k: optimize/fix ANI RSSI processing

On 2010-10-18 3:24 AM, Bruno Randolf wrote:
> On Sat October 16 2010 03:03:31 Felix Fietkau wrote:
>> ANI needs the RSSI average only in station mode, and only for tracking
>> the signal strength of beacons of the AP that it is connected to.
>> Adjust the code to track on the beacon RSSI, and store the average of that
>> in the ath_wiphy struct.
>> With these changes, we can get rid of this extra station lookup in the
>> rx path, which saves precious CPU cycles.
>
> wouldn't it make sense to have per-station RSSI in IBSS mode?
ath9k ANI can't deal with per-STA RSSI, and it's not something that the
driver should normally keep track of, as it's easier to do that in mac80211.

- Felix

2010-10-15 18:03:40

by Felix Fietkau

[permalink] [raw]
Subject: [PATCH 3/6] ath9k_hw: optimize tx status descriptor processing

Disassembly shows, that at least on MIPS, the compiler generates a lot of
memory accesses to the same location in the descriptor field parsing.
Since it is operating on uncached memory, this can be quite expensive in
this hot path.
Change the code a bit to help the compiler optimize it properly, and get
rid of some unused fields in the ath_tx_status struct.

Signed-off-by: Felix Fietkau <[email protected]>
---
drivers/net/wireless/ath/ath9k/ar9002_mac.c | 81 ++++++++++++---------------
drivers/net/wireless/ath/ath9k/ar9003_mac.c | 69 ++++++++++++-----------
drivers/net/wireless/ath/ath9k/mac.h | 3 -
3 files changed, 72 insertions(+), 81 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ar9002_mac.c b/drivers/net/wireless/ath/ath9k/ar9002_mac.c
index f5ed73d..3b4c52c 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c
@@ -208,77 +208,68 @@ static int ar9002_hw_proc_txdesc(struct ath_hw *ah, void *ds,
struct ath_tx_status *ts)
{
struct ar5416_desc *ads = AR5416DESC(ds);
+ u32 status;

- if ((ads->ds_txstatus9 & AR_TxDone) == 0)
+ status = ACCESS_ONCE(ads->ds_txstatus9);
+ if ((status & AR_TxDone) == 0)
return -EINPROGRESS;

- ts->ts_seqnum = MS(ads->ds_txstatus9, AR_SeqNum);
ts->ts_tstamp = ads->AR_SendTimestamp;
ts->ts_status = 0;
ts->ts_flags = 0;

- if (ads->ds_txstatus1 & AR_FrmXmitOK)
+ if (status & AR_TxOpExceeded)
+ ts->ts_status |= ATH9K_TXERR_XTXOP;
+ ts->tid = MS(status, AR_TxTid);
+ ts->ts_rateindex = MS(status, AR_FinalTxIdx);
+ ts->ts_seqnum = MS(status, AR_SeqNum);
+
+ status = ACCESS_ONCE(ads->ds_txstatus0);
+ ts->ts_rssi_ctl0 = MS(status, AR_TxRSSIAnt00);
+ ts->ts_rssi_ctl1 = MS(status, AR_TxRSSIAnt01);
+ ts->ts_rssi_ctl2 = MS(status, AR_TxRSSIAnt02);
+ if (status & AR_TxBaStatus) {
+ ts->ts_flags |= ATH9K_TX_BA;
+ ts->ba_low = ads->AR_BaBitmapLow;
+ ts->ba_high = ads->AR_BaBitmapHigh;
+ }
+
+ status = ACCESS_ONCE(ads->ds_txstatus1);
+ if (status & AR_FrmXmitOK)
ts->ts_status |= ATH9K_TX_ACKED;
- if (ads->ds_txstatus1 & AR_ExcessiveRetries)
+ if (status & AR_ExcessiveRetries)
ts->ts_status |= ATH9K_TXERR_XRETRY;
- if (ads->ds_txstatus1 & AR_Filtered)
+ if (status & AR_Filtered)
ts->ts_status |= ATH9K_TXERR_FILT;
- if (ads->ds_txstatus1 & AR_FIFOUnderrun) {
+ if (status & 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)
+ if (status & AR_TxTimerExpired)
ts->ts_status |= ATH9K_TXERR_TIMER_EXPIRED;
-
- if (ads->ds_txstatus1 & AR_DescCfgErr)
+ if (status & AR_DescCfgErr)
ts->ts_flags |= ATH9K_TX_DESC_CFG_ERR;
- if (ads->ds_txstatus1 & AR_TxDataUnderrun) {
+ if (status & AR_TxDataUnderrun) {
ts->ts_flags |= ATH9K_TX_DATA_UNDERRUN;
ath9k_hw_updatetxtriglevel(ah, true);
}
- if (ads->ds_txstatus1 & AR_TxDelimUnderrun) {
+ if (status & AR_TxDelimUnderrun) {
ts->ts_flags |= ATH9K_TX_DELIM_UNDERRUN;
ath9k_hw_updatetxtriglevel(ah, true);
}
- if (ads->ds_txstatus0 & AR_TxBaStatus) {
- ts->ts_flags |= ATH9K_TX_BA;
- ts->ba_low = ads->AR_BaBitmapLow;
- ts->ba_high = ads->AR_BaBitmapHigh;
- }
+ ts->ts_shortretry = MS(status, AR_RTSFailCnt);
+ ts->ts_longretry = MS(status, AR_DataFailCnt);
+ ts->ts_virtcol = MS(status, AR_VirtRetryCnt);

- ts->ts_rateindex = MS(ads->ds_txstatus9, AR_FinalTxIdx);
- switch (ts->ts_rateindex) {
- case 0:
- ts->ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate0);
- break;
- case 1:
- ts->ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate1);
- break;
- case 2:
- ts->ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate2);
- break;
- case 3:
- ts->ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate3);
- break;
- }
+ status = ACCESS_ONCE(ads->ds_txstatus5);
+ ts->ts_rssi = MS(status, AR_TxRSSICombined);
+ ts->ts_rssi_ext0 = MS(status, AR_TxRSSIAnt10);
+ ts->ts_rssi_ext1 = MS(status, AR_TxRSSIAnt11);
+ ts->ts_rssi_ext2 = MS(status, AR_TxRSSIAnt12);

- ts->ts_rssi = MS(ads->ds_txstatus5, AR_TxRSSICombined);
- ts->ts_rssi_ctl0 = MS(ads->ds_txstatus0, AR_TxRSSIAnt00);
- ts->ts_rssi_ctl1 = MS(ads->ds_txstatus0, AR_TxRSSIAnt01);
- ts->ts_rssi_ctl2 = MS(ads->ds_txstatus0, AR_TxRSSIAnt02);
- ts->ts_rssi_ext0 = MS(ads->ds_txstatus5, AR_TxRSSIAnt10);
- ts->ts_rssi_ext1 = MS(ads->ds_txstatus5, AR_TxRSSIAnt11);
- ts->ts_rssi_ext2 = MS(ads->ds_txstatus5, AR_TxRSSIAnt12);
ts->evm0 = ads->AR_TxEVM0;
ts->evm1 = ads->AR_TxEVM1;
ts->evm2 = ads->AR_TxEVM2;
- ts->ts_shortretry = MS(ads->ds_txstatus1, AR_RTSFailCnt);
- ts->ts_longretry = MS(ads->ds_txstatus1, AR_DataFailCnt);
- ts->ts_virtcol = MS(ads->ds_txstatus1, AR_VirtRetryCnt);
- ts->tid = MS(ads->ds_txstatus9, AR_TxTid);
- ts->ts_antenna = 0;

return 0;
}
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
index 3b424ca..10c812e 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c
@@ -237,10 +237,12 @@ static int ar9003_hw_proc_txdesc(struct ath_hw *ah, void *ds,
struct ath_tx_status *ts)
{
struct ar9003_txs *ads;
+ u32 status;

ads = &ah->ts_ring[ah->ts_tail];

- if ((ads->status8 & AR_TxDone) == 0)
+ status = ACCESS_ONCE(ads->status8);
+ if ((status & AR_TxDone) == 0)
return -EINPROGRESS;

ah->ts_tail = (ah->ts_tail + 1) % ah->ts_size;
@@ -253,57 +255,58 @@ static int ar9003_hw_proc_txdesc(struct ath_hw *ah, void *ds,
return -EIO;
}

+ if (status & AR_TxOpExceeded)
+ ts->ts_status |= ATH9K_TXERR_XTXOP;
+ ts->ts_rateindex = MS(status, AR_FinalTxIdx);
+ ts->ts_seqnum = MS(status, AR_SeqNum);
+ ts->tid = MS(status, AR_TxTid);
+
ts->qid = MS(ads->ds_info, AR_TxQcuNum);
ts->desc_id = MS(ads->status1, AR_TxDescId);
- ts->ts_seqnum = MS(ads->status8, AR_SeqNum);
ts->ts_tstamp = ads->status4;
ts->ts_status = 0;
ts->ts_flags = 0;

- if (ads->status3 & AR_ExcessiveRetries)
+ status = ACCESS_ONCE(ads->status2);
+ ts->ts_rssi_ctl0 = MS(status, AR_TxRSSIAnt00);
+ ts->ts_rssi_ctl1 = MS(status, AR_TxRSSIAnt01);
+ ts->ts_rssi_ctl2 = MS(status, AR_TxRSSIAnt02);
+ if (status & AR_TxBaStatus) {
+ ts->ts_flags |= ATH9K_TX_BA;
+ ts->ba_low = ads->status5;
+ ts->ba_high = ads->status6;
+ }
+
+ status = ACCESS_ONCE(ads->status3);
+ if (status & AR_ExcessiveRetries)
ts->ts_status |= ATH9K_TXERR_XRETRY;
- if (ads->status3 & AR_Filtered)
+ if (status & AR_Filtered)
ts->ts_status |= ATH9K_TXERR_FILT;
- if (ads->status3 & AR_FIFOUnderrun) {
+ if (status & AR_FIFOUnderrun) {
ts->ts_status |= ATH9K_TXERR_FIFO;
ath9k_hw_updatetxtriglevel(ah, true);
}
- if (ads->status8 & AR_TxOpExceeded)
- ts->ts_status |= ATH9K_TXERR_XTXOP;
- if (ads->status3 & AR_TxTimerExpired)
+ if (status & AR_TxTimerExpired)
ts->ts_status |= ATH9K_TXERR_TIMER_EXPIRED;
-
- if (ads->status3 & AR_DescCfgErr)
+ if (status & AR_DescCfgErr)
ts->ts_flags |= ATH9K_TX_DESC_CFG_ERR;
- if (ads->status3 & AR_TxDataUnderrun) {
+ if (status & AR_TxDataUnderrun) {
ts->ts_flags |= ATH9K_TX_DATA_UNDERRUN;
ath9k_hw_updatetxtriglevel(ah, true);
}
- if (ads->status3 & AR_TxDelimUnderrun) {
+ if (status & AR_TxDelimUnderrun) {
ts->ts_flags |= ATH9K_TX_DELIM_UNDERRUN;
ath9k_hw_updatetxtriglevel(ah, true);
}
- if (ads->status2 & AR_TxBaStatus) {
- ts->ts_flags |= ATH9K_TX_BA;
- ts->ba_low = ads->status5;
- ts->ba_high = ads->status6;
- }
-
- ts->ts_rateindex = MS(ads->status8, AR_FinalTxIdx);
-
- ts->ts_rssi = MS(ads->status7, AR_TxRSSICombined);
- ts->ts_rssi_ctl0 = MS(ads->status2, AR_TxRSSIAnt00);
- ts->ts_rssi_ctl1 = MS(ads->status2, AR_TxRSSIAnt01);
- ts->ts_rssi_ctl2 = MS(ads->status2, AR_TxRSSIAnt02);
- ts->ts_rssi_ext0 = MS(ads->status7, AR_TxRSSIAnt10);
- ts->ts_rssi_ext1 = MS(ads->status7, AR_TxRSSIAnt11);
- ts->ts_rssi_ext2 = MS(ads->status7, AR_TxRSSIAnt12);
- ts->ts_shortretry = MS(ads->status3, AR_RTSFailCnt);
- ts->ts_longretry = MS(ads->status3, AR_DataFailCnt);
- ts->ts_virtcol = MS(ads->status3, AR_VirtRetryCnt);
- ts->ts_antenna = 0;
-
- ts->tid = MS(ads->status8, AR_TxTid);
+ ts->ts_shortretry = MS(status, AR_RTSFailCnt);
+ ts->ts_longretry = MS(status, AR_DataFailCnt);
+ ts->ts_virtcol = MS(status, AR_VirtRetryCnt);
+
+ status = ACCESS_ONCE(ads->status7);
+ ts->ts_rssi = MS(status, AR_TxRSSICombined);
+ ts->ts_rssi_ext0 = MS(status, AR_TxRSSIAnt10);
+ ts->ts_rssi_ext1 = MS(status, AR_TxRSSIAnt11);
+ ts->ts_rssi_ext2 = MS(status, AR_TxRSSIAnt12);

memset(ads, 0, sizeof(*ads));

diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h
index 538c676..fdc2507 100644
--- a/drivers/net/wireless/ath/ath9k/mac.h
+++ b/drivers/net/wireless/ath/ath9k/mac.h
@@ -104,13 +104,11 @@ struct ath_tx_status {
u32 ts_tstamp;
u16 ts_seqnum;
u8 ts_status;
- u8 ts_ratecode;
u8 ts_rateindex;
int8_t ts_rssi;
u8 ts_shortretry;
u8 ts_longretry;
u8 ts_virtcol;
- u8 ts_antenna;
u8 ts_flags;
int8_t ts_rssi_ctl0;
int8_t ts_rssi_ctl1;
@@ -121,7 +119,6 @@ struct ath_tx_status {
u8 qid;
u16 desc_id;
u8 tid;
- u8 pad[2];
u32 ba_low;
u32 ba_high;
u32 evm0;
--
1.7.2.2


2010-10-15 18:03:39

by Felix Fietkau

[permalink] [raw]
Subject: [PATCH 5/6] ath9k: remove a redundant call to ath9k_hw_gettsf32

When the timer_next argument to ath9k_gen_timer_start is behind the tsf value,
tsf + timer_period is used, which is what ath_btcoex_period_timer was setting
it to.

Signed-off-by: Felix Fietkau <[email protected]>
---
drivers/net/wireless/ath/ath9k/gpio.c | 6 ++----
1 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c
index db9c6fe..6a1a482 100644
--- a/drivers/net/wireless/ath/ath9k/gpio.c
+++ b/drivers/net/wireless/ath/ath9k/gpio.c
@@ -310,10 +310,8 @@ static void ath_btcoex_period_timer(unsigned long data)

timer_period = is_btscan ? btcoex->btscan_no_stomp :
btcoex->btcoex_no_stomp;
- ath9k_gen_timer_start(ah,
- btcoex->no_stomp_timer,
- (ath9k_hw_gettsf32(ah) +
- timer_period), timer_period * 10);
+ ath9k_gen_timer_start(ah, btcoex->no_stomp_timer, 0,
+ timer_period * 10);
btcoex->hw_timer_enabled = true;
}

--
1.7.2.2


2010-10-15 18:03:40

by Felix Fietkau

[permalink] [raw]
Subject: [PATCH 4/6] ath9k: optimize/fix ANI RSSI processing

ANI needs the RSSI average only in station mode, and only for tracking
the signal strength of beacons of the AP that it is connected to.
Adjust the code to track on the beacon RSSI, and store the average of that
in the ath_wiphy struct.
With these changes, we can get rid of this extra station lookup in the
rx path, which saves precious CPU cycles.

Signed-off-by: Felix Fietkau <[email protected]>
---
drivers/net/wireless/ath/ath9k/ath9k.h | 2 +-
drivers/net/wireless/ath/ath9k/init.c | 2 +
drivers/net/wireless/ath/ath9k/main.c | 6 +++-
drivers/net/wireless/ath/ath9k/recv.c | 38 +++++++++--------------------
drivers/net/wireless/ath/ath9k/virtual.c | 1 +
5 files changed, 20 insertions(+), 29 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index e435c1d..8f5d4c4 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -271,7 +271,6 @@ struct ath_node {
struct ath_atx_ac ac[WME_NUM_AC];
u16 maxampdu;
u8 mpdudensity;
- int last_rssi;
};

#define AGGR_CLEANUP BIT(1)
@@ -663,6 +662,7 @@ struct ath_wiphy {
bool idle;
int chan_idx;
int chan_is_ht;
+ int last_rssi;
};

void ath9k_tasklet(unsigned long data);
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index bc6c4df..5ebe53d 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -702,6 +702,7 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, u16 subsysid,
const struct ath_bus_ops *bus_ops)
{
struct ieee80211_hw *hw = sc->hw;
+ struct ath_wiphy *aphy = hw->priv;
struct ath_common *common;
struct ath_hw *ah;
int error = 0;
@@ -751,6 +752,7 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, u16 subsysid,
INIT_WORK(&sc->chan_work, ath9k_wiphy_chan_work);
INIT_DELAYED_WORK(&sc->wiphy_work, ath9k_wiphy_work);
sc->wiphy_scheduler_int = msecs_to_jiffies(500);
+ aphy->last_rssi = ATH_RSSI_DUMMY_MARKER;

ath_init_leds(sc);
ath_start_rfkill_poll(sc);
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 029c6d2..e70dc383 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -535,7 +535,6 @@ static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta)
an->maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
sta->ht_cap.ampdu_factor);
an->mpdudensity = parse_mpdudensity(sta->ht_cap.ampdu_density);
- an->last_rssi = ATH_RSSI_DUMMY_MARKER;
}
}

@@ -804,9 +803,11 @@ static u32 ath_get_extchanmode(struct ath_softc *sc,
}

static void ath9k_bss_assoc_info(struct ath_softc *sc,
+ struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf)
{
+ struct ath_wiphy *aphy = hw->priv;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);

@@ -830,6 +831,7 @@ static void ath9k_bss_assoc_info(struct ath_softc *sc,
ath_beacon_config(sc, vif);

/* Reset rssi stats */
+ aphy->last_rssi = ATH_RSSI_DUMMY_MARKER;
sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;

sc->sc_flags |= SC_OP_ANI_RUN;
@@ -1951,7 +1953,7 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_ASSOC) {
ath_print(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
bss_conf->assoc);
- ath9k_bss_assoc_info(sc, vif, bss_conf);
+ ath9k_bss_assoc_info(sc, hw, vif, bss_conf);
}

mutex_unlock(&sc->mutex);
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index 7c90eaf..925b327 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -960,36 +960,23 @@ static void ath9k_process_rssi(struct ath_common *common,
struct ieee80211_hdr *hdr,
struct ath_rx_status *rx_stats)
{
+ struct ath_wiphy *aphy = hw->priv;
struct ath_hw *ah = common->ah;
- struct ieee80211_sta *sta;
- struct ath_node *an;
- int last_rssi = ATH_RSSI_DUMMY_MARKER;
+ int last_rssi;
__le16 fc;

+ if (ah->opmode != NL80211_IFTYPE_STATION)
+ return;
+
fc = hdr->frame_control;
+ if (!ieee80211_is_beacon(fc) ||
+ compare_ether_addr(hdr->addr3, common->curbssid))
+ return;

- rcu_read_lock();
- /*
- * XXX: use ieee80211_find_sta! This requires quite a bit of work
- * under the current ath9k virtual wiphy implementation as we have
- * no way of tying a vif to wiphy. Typically vifs are attached to
- * at least one sdata of a wiphy on mac80211 but with ath9k virtual
- * wiphy you'd have to iterate over every wiphy and each sdata.
- */
- if (is_multicast_ether_addr(hdr->addr1))
- sta = ieee80211_find_sta_by_ifaddr(hw, hdr->addr2, NULL);
- else
- sta = ieee80211_find_sta_by_ifaddr(hw, hdr->addr2, hdr->addr1);
-
- if (sta) {
- an = (struct ath_node *) sta->drv_priv;
- if (rx_stats->rs_rssi != ATH9K_RSSI_BAD &&
- !rx_stats->rs_moreaggr)
- ATH_RSSI_LPF(an->last_rssi, rx_stats->rs_rssi);
- last_rssi = an->last_rssi;
- }
- rcu_read_unlock();
+ if (rx_stats->rs_rssi != ATH9K_RSSI_BAD && !rx_stats->rs_moreaggr)
+ ATH_RSSI_LPF(aphy->last_rssi, rx_stats->rs_rssi);

+ last_rssi = aphy->last_rssi;
if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER))
rx_stats->rs_rssi = ATH_EP_RND(last_rssi,
ATH_RSSI_EP_MULTIPLIER);
@@ -997,8 +984,7 @@ static void ath9k_process_rssi(struct ath_common *common,
rx_stats->rs_rssi = 0;

/* Update Beacon RSSI, this is used by ANI. */
- if (ieee80211_is_beacon(fc))
- ah->stats.avgbrssi = rx_stats->rs_rssi;
+ ah->stats.avgbrssi = rx_stats->rs_rssi;
}

/*
diff --git a/drivers/net/wireless/ath/ath9k/virtual.c b/drivers/net/wireless/ath/ath9k/virtual.c
index ec7cf5e..cb6c48b 100644
--- a/drivers/net/wireless/ath/ath9k/virtual.c
+++ b/drivers/net/wireless/ath/ath9k/virtual.c
@@ -107,6 +107,7 @@ int ath9k_wiphy_add(struct ath_softc *sc)
aphy->sc = sc;
aphy->hw = hw;
sc->sec_wiphy[i] = aphy;
+ aphy->last_rssi = ATH_RSSI_DUMMY_MARKER;
spin_unlock_bh(&sc->wiphy_lock);

memcpy(addr, common->macaddr, ETH_ALEN);
--
1.7.2.2


2010-10-15 18:03:40

by Felix Fietkau

[permalink] [raw]
Subject: [PATCH 2/6] ath9k_hw: small optimization in ar9002_hw_get_isr

ah->config.rx_intr_mitigation does not need to be checked before checking
the rx interrupt mask for AR_ISR_RXMINTR or AR_ISR_RXINTM, as those
interrupts will be masked out if rx interrupt mitigation is disabled.

Avoid reading AR_ISR_S5_S twice by reordering the code to be more concise.

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

diff --git a/drivers/net/wireless/ath/ath9k/ar9002_mac.c b/drivers/net/wireless/ath/ath9k/ar9002_mac.c
index 50dda39..f5ed73d 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c
@@ -90,13 +90,10 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked)

*masked = isr & ATH9K_INT_COMMON;

- if (ah->config.rx_intr_mitigation) {
- if (isr & (AR_ISR_RXMINTR | AR_ISR_RXINTM))
- *masked |= ATH9K_INT_RX;
- }
-
- if (isr & (AR_ISR_RXOK | AR_ISR_RXERR))
+ if (isr & (AR_ISR_RXMINTR | AR_ISR_RXINTM |
+ AR_ISR_RXOK | AR_ISR_RXERR))
*masked |= ATH9K_INT_RX;
+
if (isr &
(AR_ISR_TXOK | AR_ISR_TXDESC | AR_ISR_TXERR |
AR_ISR_TXEOL)) {
@@ -118,14 +115,6 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked)
"receive FIFO overrun interrupt\n");
}

- if (!AR_SREV_9100(ah)) {
- if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) {
- u32 isr5 = REG_READ(ah, AR_ISR_S5_S);
- if (isr5 & AR_ISR_S5_TIM_TIMER)
- *masked |= ATH9K_INT_TIM_TIMER;
- }
- }
-
*masked |= mask2;
}

@@ -136,17 +125,18 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked)
u32 s5_s;

s5_s = REG_READ(ah, AR_ISR_S5_S);
- if (isr & AR_ISR_GENTMR) {
- ah->intr_gen_timer_trigger =
+ ah->intr_gen_timer_trigger =
MS(s5_s, AR_ISR_S5_GENTIMER_TRIG);

- ah->intr_gen_timer_thresh =
- MS(s5_s, AR_ISR_S5_GENTIMER_THRESH);
+ ah->intr_gen_timer_thresh =
+ MS(s5_s, AR_ISR_S5_GENTIMER_THRESH);

- if (ah->intr_gen_timer_trigger)
- *masked |= ATH9K_INT_GENTIMER;
+ if (ah->intr_gen_timer_trigger)
+ *masked |= ATH9K_INT_GENTIMER;

- }
+ if ((s5_s & AR_ISR_S5_TIM_TIMER) &&
+ !(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP))
+ *masked |= ATH9K_INT_TIM_TIMER;
}

if (sync_cause) {
--
1.7.2.2


2010-10-16 13:10:15

by Felix Fietkau

[permalink] [raw]
Subject: Re: [PATCH 1/6] ath9k_hw: optimize interrupt mask changes

On 2010-10-16 1:51 PM, Rajkumar Manoharan wrote:
> On Fri, Oct 15, 2010 at 11:33:28PM +0530, Felix Fietkau wrote:
>> @@ -751,8 +751,8 @@ chip_reset:
>> ath_debug_stat_interrupt(sc, status);
>>
>> if (sched) {
>> - /* turn off every interrupt except SWBA */
>> - ath9k_hw_set_interrupts(ah, (ah->imask & ATH9K_INT_SWBA));
>> + /* turn off every interrupt */
>> + ath9k_hw_disable_interrupts(ah);
>
> won't it affect beacon generation?
No, because the original code did not leave SWBA interrupts enabled
either, as it was masking out ATH9K_INT_GLOBAL. So far I haven't seen
issues with beacons under high load, even on slow embedded hardware.

- Felix

2010-10-16 11:45:09

by Rajkumar Manoharan

[permalink] [raw]
Subject: Re: [PATCH 1/6] ath9k_hw: optimize interrupt mask changes

On Fri, Oct 15, 2010 at 11:33:28PM +0530, Felix Fietkau wrote:
> @@ -751,8 +751,8 @@ chip_reset:
> ath_debug_stat_interrupt(sc, status);
>
> if (sched) {
> - /* turn off every interrupt except SWBA */
> - ath9k_hw_set_interrupts(ah, (ah->imask & ATH9K_INT_SWBA));
> + /* turn off every interrupt */
> + ath9k_hw_disable_interrupts(ah);

won't it affect beacon generation?

- Rajkumar

2010-11-08 19:54:57

by Felix Fietkau

[permalink] [raw]
Subject: [PATCH v2 1/6] ath9k_hw: optimize interrupt mask changes

OProfile showed that ath9k was spending way too much time in
ath9k_hw_set_interrupts. Since most of the interrupt mask changes only
need to globally enable/disable interrupts, it makes sense to split
this part into separate functions, replacing all calls to
ath9k_hw_set_interrupts(ah, 0) with ath9k_hw_disable_interrupts(ah).

ath9k_hw_set_interrupts(ah, ah->imask) only gets changed to
ath9k_hw_enable_interrupts(ah), whenever ah->imask was not changed
since the point where interrupts were disabled.

Signed-off-by: Felix Fietkau <[email protected]>
---
v2: leave a few ath9k_hw_set_interrupts(ah, ah->imask) calls, because
the interrupt mask is changed earlier in the code.

drivers/net/wireless/ath/ath9k/beacon.c | 6 +-
drivers/net/wireless/ath/ath9k/gpio.c | 4 +-
drivers/net/wireless/ath/ath9k/mac.c | 86 ++++++++++++++++++-------------
drivers/net/wireless/ath/ath9k/mac.h | 6 ++-
drivers/net/wireless/ath/ath9k/main.c | 18 +++---
5 files changed, 68 insertions(+), 52 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index 19891e7..333da7b 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -503,7 +503,7 @@ static void ath_beacon_config_ap(struct ath_softc *sc,

/* Set the computed AP beacon timers */

- ath9k_hw_set_interrupts(ah, 0);
+ ath9k_hw_disable_interrupts(ah);
ath9k_beacon_init(sc, nexttbtt, intval);
sc->beacon.bmisscnt = 0;
ath9k_hw_set_interrupts(ah, ah->imask);
@@ -638,7 +638,7 @@ static void ath_beacon_config_sta(struct ath_softc *sc,

/* Set the computed STA beacon timers */

- ath9k_hw_set_interrupts(ah, 0);
+ ath9k_hw_disable_interrupts(ah);
ath9k_hw_set_sta_beacon_timers(ah, &bs);
ah->imask |= ATH9K_INT_BMISS;
ath9k_hw_set_interrupts(ah, ah->imask);
@@ -686,7 +686,7 @@ static void ath_beacon_config_adhoc(struct ath_softc *sc,

/* Set the computed ADHOC beacon timers */

- ath9k_hw_set_interrupts(ah, 0);
+ ath9k_hw_disable_interrupts(ah);
ath9k_beacon_init(sc, nexttbtt, intval);
sc->beacon.bmisscnt = 0;
ath9k_hw_set_interrupts(ah, ah->imask);
diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c
index 4a9a68b..db9c6fe 100644
--- a/drivers/net/wireless/ath/ath9k/gpio.c
+++ b/drivers/net/wireless/ath/ath9k/gpio.c
@@ -259,7 +259,7 @@ static void ath9k_gen_timer_start(struct ath_hw *ah,
ath9k_hw_gen_timer_start(ah, timer, timer_next, timer_period);

if ((ah->imask & ATH9K_INT_GENTIMER) == 0) {
- ath9k_hw_set_interrupts(ah, 0);
+ ath9k_hw_disable_interrupts(ah);
ah->imask |= ATH9K_INT_GENTIMER;
ath9k_hw_set_interrupts(ah, ah->imask);
}
@@ -273,7 +273,7 @@ static void ath9k_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer)

/* if no timer is enabled, turn off interrupt mask */
if (timer_table->timer_mask.val == 0) {
- ath9k_hw_set_interrupts(ah, 0);
+ ath9k_hw_disable_interrupts(ah);
ah->imask &= ~ATH9K_INT_GENTIMER;
ath9k_hw_set_interrupts(ah, ah->imask);
}
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index 8c13479..65b1ee2 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -117,12 +117,11 @@ EXPORT_SYMBOL(ath9k_hw_numtxpending);
bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel)
{
u32 txcfg, curLevel, newLevel;
- enum ath9k_int omask;

if (ah->tx_trig_level >= ah->config.max_txtrig_level)
return false;

- omask = ath9k_hw_set_interrupts(ah, ah->imask & ~ATH9K_INT_GLOBAL);
+ ath9k_hw_disable_interrupts(ah);

txcfg = REG_READ(ah, AR_TXCFG);
curLevel = MS(txcfg, AR_FTRIG);
@@ -136,7 +135,7 @@ bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel)
REG_WRITE(ah, AR_TXCFG,
(txcfg & ~AR_FTRIG) | SM(newLevel, AR_FTRIG));

- ath9k_hw_set_interrupts(ah, omask);
+ ath9k_hw_enable_interrupts(ah);

ah->tx_trig_level = newLevel;

@@ -849,28 +848,59 @@ bool ath9k_hw_intrpend(struct ath_hw *ah)
}
EXPORT_SYMBOL(ath9k_hw_intrpend);

-enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah,
- enum ath9k_int ints)
+void ath9k_hw_disable_interrupts(struct ath_hw *ah)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ ath_print(common, ATH_DBG_INTERRUPT, "disable IER\n");
+ REG_WRITE(ah, AR_IER, AR_IER_DISABLE);
+ (void) REG_READ(ah, AR_IER);
+ if (!AR_SREV_9100(ah)) {
+ REG_WRITE(ah, AR_INTR_ASYNC_ENABLE, 0);
+ (void) REG_READ(ah, AR_INTR_ASYNC_ENABLE);
+
+ REG_WRITE(ah, AR_INTR_SYNC_ENABLE, 0);
+ (void) REG_READ(ah, AR_INTR_SYNC_ENABLE);
+ }
+}
+EXPORT_SYMBOL(ath9k_hw_disable_interrupts);
+
+void ath9k_hw_enable_interrupts(struct ath_hw *ah)
+{
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ if (!(ah->imask & ATH9K_INT_GLOBAL))
+ return;
+
+ ath_print(common, ATH_DBG_INTERRUPT, "enable IER\n");
+ REG_WRITE(ah, AR_IER, AR_IER_ENABLE);
+ if (!AR_SREV_9100(ah)) {
+ REG_WRITE(ah, AR_INTR_ASYNC_ENABLE,
+ AR_INTR_MAC_IRQ);
+ REG_WRITE(ah, AR_INTR_ASYNC_MASK, AR_INTR_MAC_IRQ);
+
+
+ REG_WRITE(ah, AR_INTR_SYNC_ENABLE,
+ AR_INTR_SYNC_DEFAULT);
+ REG_WRITE(ah, AR_INTR_SYNC_MASK,
+ AR_INTR_SYNC_DEFAULT);
+ }
+ ath_print(common, ATH_DBG_INTERRUPT, "AR_IMR 0x%x IER 0x%x\n",
+ REG_READ(ah, AR_IMR), REG_READ(ah, AR_IER));
+}
+EXPORT_SYMBOL(ath9k_hw_enable_interrupts);
+
+void ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints)
{
enum ath9k_int omask = ah->imask;
u32 mask, mask2;
struct ath9k_hw_capabilities *pCap = &ah->caps;
struct ath_common *common = ath9k_hw_common(ah);

- ath_print(common, ATH_DBG_INTERRUPT, "0x%x => 0x%x\n", omask, ints);
-
- if (omask & ATH9K_INT_GLOBAL) {
- ath_print(common, ATH_DBG_INTERRUPT, "disable IER\n");
- REG_WRITE(ah, AR_IER, AR_IER_DISABLE);
- (void) REG_READ(ah, AR_IER);
- if (!AR_SREV_9100(ah)) {
- REG_WRITE(ah, AR_INTR_ASYNC_ENABLE, 0);
- (void) REG_READ(ah, AR_INTR_ASYNC_ENABLE);
+ if (!(ints & ATH9K_INT_GLOBAL))
+ ath9k_hw_enable_interrupts(ah);

- REG_WRITE(ah, AR_INTR_SYNC_ENABLE, 0);
- (void) REG_READ(ah, AR_INTR_SYNC_ENABLE);
- }
- }
+ ath_print(common, ATH_DBG_INTERRUPT, "0x%x => 0x%x\n", omask, ints);

/* TODO: global int Ref count */
mask = ints & ATH9K_INT_COMMON;
@@ -946,24 +976,8 @@ enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah,
REG_CLR_BIT(ah, AR_IMR_S5, AR_IMR_S5_TIM_TIMER);
}

- if (ints & ATH9K_INT_GLOBAL) {
- ath_print(common, ATH_DBG_INTERRUPT, "enable IER\n");
- REG_WRITE(ah, AR_IER, AR_IER_ENABLE);
- if (!AR_SREV_9100(ah)) {
- REG_WRITE(ah, AR_INTR_ASYNC_ENABLE,
- AR_INTR_MAC_IRQ);
- REG_WRITE(ah, AR_INTR_ASYNC_MASK, AR_INTR_MAC_IRQ);
-
-
- REG_WRITE(ah, AR_INTR_SYNC_ENABLE,
- AR_INTR_SYNC_DEFAULT);
- REG_WRITE(ah, AR_INTR_SYNC_MASK,
- AR_INTR_SYNC_DEFAULT);
- }
- ath_print(common, ATH_DBG_INTERRUPT, "AR_IMR 0x%x IER 0x%x\n",
- REG_READ(ah, AR_IMR), REG_READ(ah, AR_IER));
- }
+ ath9k_hw_enable_interrupts(ah);

- return omask;
+ return;
}
EXPORT_SYMBOL(ath9k_hw_set_interrupts);
diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h
index 7c1a34d..538c676 100644
--- a/drivers/net/wireless/ath/ath9k/mac.h
+++ b/drivers/net/wireless/ath/ath9k/mac.h
@@ -669,6 +669,7 @@ enum ath9k_key_type {

struct ath_hw;
struct ath9k_channel;
+enum ath9k_int;

u32 ath9k_hw_gettxbuf(struct ath_hw *ah, u32 q);
void ath9k_hw_puttxbuf(struct ath_hw *ah, u32 q, u32 txdp);
@@ -700,8 +701,9 @@ int ath9k_hw_beaconq_setup(struct ath_hw *ah);

/* Interrupt Handling */
bool ath9k_hw_intrpend(struct ath_hw *ah);
-enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah,
- enum ath9k_int ints);
+void ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints);
+void ath9k_hw_enable_interrupts(struct ath_hw *ah);
+void ath9k_hw_disable_interrupts(struct ath_hw *ah);

void ar9002_hw_attach_mac_ops(struct ath_hw *ah);

diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index b52f1cf..ade9d7c 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -239,7 +239,7 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
* hardware at the new frequency, and then re-enable
* the relevant bits of the h/w.
*/
- ath9k_hw_set_interrupts(ah, 0);
+ ath9k_hw_disable_interrupts(ah);
ath_drain_all_txq(sc, false);

spin_lock_bh(&sc->rx.pcu_lock);
@@ -653,7 +653,7 @@ void ath9k_tasklet(unsigned long data)
ath_gen_timer_isr(sc->sc_ah);

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

@@ -752,7 +752,7 @@ irqreturn_t ath_isr(int irq, void *dev)
* interrupt; otherwise it will continue to
* fire.
*/
- ath9k_hw_set_interrupts(ah, 0);
+ ath9k_hw_disable_interrupts(ah);
/*
* Let the hal handle the event. We assume
* it will clear whatever condition caused
@@ -761,7 +761,7 @@ irqreturn_t ath_isr(int irq, void *dev)
spin_lock(&common->cc_lock);
ath9k_hw_proc_mib_event(ah);
spin_unlock(&common->cc_lock);
- ath9k_hw_set_interrupts(ah, ah->imask);
+ ath9k_hw_enable_interrupts(ah);
}

if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP))
@@ -778,8 +778,8 @@ chip_reset:
ath_debug_stat_interrupt(sc, status);

if (sched) {
- /* turn off every interrupt except SWBA */
- ath9k_hw_set_interrupts(ah, (ah->imask & ATH9K_INT_SWBA));
+ /* turn off every interrupt */
+ ath9k_hw_disable_interrupts(ah);
tasklet_schedule(&sc->intr_tq);
}

@@ -937,7 +937,7 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
}

/* Disable interrupts */
- ath9k_hw_set_interrupts(ah, 0);
+ ath9k_hw_disable_interrupts(ah);

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

@@ -980,7 +980,7 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)

ieee80211_stop_queues(hw);

- ath9k_hw_set_interrupts(ah, 0);
+ ath9k_hw_disable_interrupts(ah);
ath_drain_all_txq(sc, retry_tx);

spin_lock_bh(&sc->rx.pcu_lock);
@@ -1394,7 +1394,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)

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

spin_lock_bh(&sc->rx.pcu_lock);
if (!(sc->sc_flags & SC_OP_INVALID)) {