From 8dbf0822728423475733c21d42a19c52479d812e Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:15:23 +0100 Subject: cw1200: Dynamic BA. Patch implements enabling of block ACK only if valuable data traffic is detected on the interface. BA is disabled for small packets (in assumption of voice/video) and low-thoughput data traffic. ST-Ericsson ID: 405634, 407777 ST-Ericsson FOSS-OUT ID: NA Change-Id: Ic30b65a5b8ea83fe6c865866209a786f26d00c18 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/41129 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/ap.c | 7 ------ drivers/staging/cw1200/cw1200.h | 12 +++++++++ drivers/staging/cw1200/debug.c | 14 +++++++++++ drivers/staging/cw1200/debug.h | 14 +++++++++++ drivers/staging/cw1200/main.c | 8 ++++++ drivers/staging/cw1200/pm.c | 10 ++++++++ drivers/staging/cw1200/sta.c | 56 +++++++++++++++++++++++++++++++++++++++++ drivers/staging/cw1200/sta.h | 2 ++ drivers/staging/cw1200/txrx.c | 25 +++++++++++++++++- 9 files changed, 140 insertions(+), 8 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index ce82ba5e87f..9f7f38681c1 100644 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -496,13 +496,6 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, priv->beacon_int * priv->join_dtim_period > MAX_BEACON_SKIP_TIME_MS ? 1 : priv->join_dtim_period, 0)); - if (sta && cw1200_is_ht(&priv->ht_info)) { - ap_printk(KERN_DEBUG - "[STA] Enabling Block ACK\n"); - WARN_ON(wsm_set_block_ack_policy(priv, - priv->ba_tid_mask, - priv->ba_tid_mask)); - } cw1200_set_pm(priv, &priv->powersave_mode); if (priv->vif->p2p) { diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index ff69098c8a7..3ae1b915566 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -52,6 +52,11 @@ #define CW1200_MAX_TID (8) +#define CW1200_BLOCK_ACK_CNT (30) +#define CW1200_BLOCK_ACK_THLD (800) +#define CW1200_BLOCK_ACK_HIST (3) +#define CW1200_BLOCK_ACK_INTERVAL (1 * HZ / CW1200_BLOCK_ACK_HIST) + /* Please keep order */ enum cw1200_join_status { CW1200_JOIN_STATUS_PASSIVE = 0, @@ -156,6 +161,13 @@ struct cw1200_common { bool disable_beacon_filter; struct work_struct update_filtering_work; u8 ba_tid_mask; + int ba_acc; + int ba_cnt; + int ba_hist; + struct timer_list ba_timer; + spinlock_t ba_lock; + bool ba_ena; + struct work_struct ba_work; struct cw1200_pm_state pm_state; struct wsm_p2p_ps_modeinfo p2p_ps_modeinfo; struct wsm_uapsd_info uapsd_info; diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index 2056101a3f4..43b2c257c57 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -105,6 +105,17 @@ static int cw1200_status_show(struct seq_file *seq, void *v) struct list_head *item; struct cw1200_common *priv = seq->private; struct cw1200_debug_priv *d = priv->debug; + int ba_cnt, ba_acc, ba_avg = 0; + bool ba_ena; + + spin_lock_bh(&priv->ba_lock); + ba_cnt = priv->debug->ba_cnt; + ba_acc = priv->debug->ba_acc; + ba_ena = priv->ba_ena; + if (ba_cnt) + ba_avg = ba_acc / ba_cnt; + spin_unlock_bh(&priv->ba_lock); + seq_puts(seq, "CW1200 Wireless LAN driver status\n"); seq_printf(seq, "Hardware: %d.%d\n", priv->wsm_caps.hardwareId, @@ -216,6 +227,9 @@ static int cw1200_status_show(struct seq_file *seq, void *v) ++i; spin_unlock_bh(&priv->tx_policy_cache.lock); seq_printf(seq, "RC in use: %d\n", i); + seq_printf(seq, "BA stat: %d, %d (%d)\n", + ba_cnt, ba_acc, ba_avg); + seq_printf(seq, "Block ACK: %s\n", ba_ena ? "on" : "off"); seq_puts(seq, "\n"); for (i = 0; i < 4; ++i) { diff --git a/drivers/staging/cw1200/debug.h b/drivers/staging/cw1200/debug.h index 02943e699b8..c901a6a64e9 100644 --- a/drivers/staging/cw1200/debug.h +++ b/drivers/staging/cw1200/debug.h @@ -29,6 +29,8 @@ struct cw1200_debug_priv { int tx_ttl; int tx_burst; int rx_burst; + int ba_cnt; + int ba_acc; }; int cw1200_debug_init(struct cw1200_common *priv); @@ -86,6 +88,13 @@ static inline void cw1200_debug_rx_burst(struct cw1200_common *priv) ++priv->debug->rx_burst; } +static inline void cw1200_debug_ba(struct cw1200_common *priv, + int ba_cnt, int ba_acc) +{ + priv->debug->ba_cnt = ba_cnt; + priv->debug->ba_acc = ba_acc; +} + #else /* CONFIG_CW1200_DEBUGFS */ static inline int cw1200_debug_init(struct cw1200_common *priv) @@ -138,6 +147,11 @@ static inline void cw1200_debug_rx_burst(struct cw1200_common *priv) { } +static inline void cw1200_debug_ba(struct cw1200_common *priv, + int ba_cnt, int ba_acc) +{ +} + #endif /* CONFIG_CW1200_DEBUGFS */ #endif /* CW1200_DEBUG_H_INCLUDED */ diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index f6ddac68823..a6f920e3399 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -348,9 +348,14 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) INIT_WORK(&priv->linkid_reset_work, cw1200_link_id_reset); #endif INIT_WORK(&priv->update_filtering_work, cw1200_update_filtering_work); + INIT_WORK(&priv->ba_work, cw1200_ba_work); init_timer(&priv->mcast_timeout); priv->mcast_timeout.data = (unsigned long)priv; priv->mcast_timeout.function = cw1200_mcast_timeout; + spin_lock_init(&priv->ba_lock); + init_timer(&priv->ba_timer); + priv->ba_timer.data = (unsigned long)priv; + priv->ba_timer.function = cw1200_ba_timer; if (unlikely(cw1200_pm_init(&priv->pm_state, priv))) { ieee80211_free_hw(hw); @@ -430,6 +435,9 @@ void cw1200_unregister_common(struct ieee80211_hw *dev) ieee80211_unregister_hw(dev); + del_timer_sync(&priv->mcast_timeout); + del_timer_sync(&priv->ba_timer); + priv->sbus_ops->irq_unsubscribe(priv->sbus_priv); cw1200_unregister_bh(priv); diff --git a/drivers/staging/cw1200/pm.c b/drivers/staging/cw1200/pm.c index 550ee1482b1..fc9c2354758 100644 --- a/drivers/staging/cw1200/pm.c +++ b/drivers/staging/cw1200/pm.c @@ -274,6 +274,9 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) if (ret) goto revert5; + /* Cancel block ack stat timer */ + del_timer_sync(&priv->ba_timer); + /* Store suspend state */ pm_state->suspend_state = state; @@ -352,6 +355,13 @@ int cw1200_wow_resume(struct ieee80211_hw *hw) cw1200_resume_work(priv, &priv->link_id_gc_work, state->link_id_gc); + /* Restart block ack stat */ + spin_lock_bh(&priv->ba_lock); + if (priv->ba_cnt) + mod_timer(&priv->ba_timer, + jiffies + CW1200_BLOCK_ACK_INTERVAL); + spin_unlock_bh(&priv->ba_lock); + /* Unlock datapath */ wsm_unlock_tx(priv); diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 85e57d2f7b3..c5b79f25060 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -123,6 +123,7 @@ void cw1200_stop(struct ieee80211_hw *dev) cancel_delayed_work_sync(&priv->link_id_gc_work); flush_workqueue(priv->workqueue); del_timer_sync(&priv->mcast_timeout); + del_timer_sync(&priv->ba_timer); mutex_lock(&priv->conf_mutex); priv->mode = NL80211_IFTYPE_UNSPECIFIED; @@ -1360,6 +1361,13 @@ void cw1200_join_work(struct work_struct *work) WARN_ON(wsm_set_block_ack_policy(priv, 0, priv->ba_tid_mask)); + spin_lock_bh(&priv->ba_lock); + priv->ba_ena = false; + priv->ba_cnt = 0; + priv->ba_acc = 0; + priv->ba_hist = 0; + spin_unlock_bh(&priv->ba_lock); + mgmt_policy.protectedMgmtEnable = 0; mgmt_policy.unprotectedMgmtFramesAllowed = 1; mgmt_policy.encryptionForAuthFrame = 1; @@ -1409,6 +1417,7 @@ void cw1200_unjoin_work(struct work_struct *work) .reset_statistics = true, }; + del_timer_sync(&priv->ba_timer); mutex_lock(&priv->conf_mutex); if (unlikely(atomic_read(&priv->scan.in_progress))) { if (priv->delayed_unjoin) { @@ -1543,6 +1552,53 @@ int cw1200_set_uapsd_param(struct cw1200_common *priv, return ret; } +void cw1200_ba_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, ba_work); + u8 tx_ba_tid_mask; + + if (priv->join_status != CW1200_JOIN_STATUS_STA) + return; + if (!priv->setbssparams_done) + return; + + spin_lock_bh(&priv->ba_lock); + tx_ba_tid_mask = priv->ba_ena ? priv->ba_tid_mask : 0; + spin_unlock_bh(&priv->ba_lock); + + WARN_ON(wsm_set_block_ack_policy(priv, + tx_ba_tid_mask, priv->ba_tid_mask)); +} + +void cw1200_ba_timer(unsigned long arg) +{ + bool ba_ena; + struct cw1200_common *priv = + (struct cw1200_common *)arg; + + spin_lock_bh(&priv->ba_lock); + cw1200_debug_ba(priv, priv->ba_cnt, priv->ba_acc); + + ba_ena = (priv->ba_cnt >= CW1200_BLOCK_ACK_CNT && + priv->ba_acc / priv->ba_cnt >= CW1200_BLOCK_ACK_THLD); + priv->ba_cnt = 0; + priv->ba_acc = 0; + + if (ba_ena != priv->ba_ena) { + if (ba_ena || ++priv->ba_hist >= CW1200_BLOCK_ACK_HIST) { + priv->ba_ena = ba_ena; + priv->ba_hist = 0; + sta_printk(KERN_DEBUG "[STA] %s block ACK:\n", + ba_ena ? "enable" : "disable"); + queue_work(priv->workqueue, &priv->ba_work); + } + } else if (priv->ba_hist) + --priv->ba_hist; + + spin_unlock_bh(&priv->ba_lock); +} + /* ******************************************************************** */ /* STA privates */ diff --git a/drivers/staging/cw1200/sta.h b/drivers/staging/cw1200/sta.h index e1257828004..4e4833afcf7 100644 --- a/drivers/staging/cw1200/sta.h +++ b/drivers/staging/cw1200/sta.h @@ -81,5 +81,7 @@ int cw1200_enable_listening(struct cw1200_common *priv); int cw1200_disable_listening(struct cw1200_common *priv); int cw1200_set_uapsd_param(struct cw1200_common *priv, const struct wsm_edca_params *arg); +void cw1200_ba_work(struct work_struct *work); +void cw1200_ba_timer(unsigned long arg); #endif diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index fb80d218dfa..39470c46f23 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -738,7 +738,7 @@ cw1200_tx_h_rate_policy(struct cw1200_common *priv, static bool cw1200_tx_h_pm_state(struct cw1200_common *priv, - struct cw1200_txinfo *t) + struct cw1200_txinfo *t) { int was_buffered = 1; @@ -757,6 +757,28 @@ cw1200_tx_h_pm_state(struct cw1200_common *priv, return !was_buffered; } +static void +cw1200_tx_h_ba_stat(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + if (priv->join_status != CW1200_JOIN_STATUS_STA) + return; + if (!cw1200_is_ht(&priv->ht_info)) + return; + if (!priv->setbssparams_done) + return; + if (!ieee80211_is_data(t->hdr->frame_control)) + return; + + spin_lock_bh(&priv->ba_lock); + priv->ba_acc += t->skb->len - t->hdrlen; + if (!priv->ba_cnt++) { + mod_timer(&priv->ba_timer, + jiffies + CW1200_BLOCK_ACK_INTERVAL); + } + spin_unlock_bh(&priv->ba_lock); +} + /* ******************************************************************** */ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) @@ -818,6 +840,7 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) rcu_read_lock(); sta = rcu_dereference(t.tx_info->control.sta); + cw1200_tx_h_ba_stat(priv, &t); spin_lock_bh(&priv->ps_state_lock); { tid_update = cw1200_tx_h_pm_state(priv, &t); -- cgit v1.2.3