提交 caf1eae2 编写于 作者: C Christian Lamparter 提交者: John W. Linville

carl9170: improve unicast PS buffering

Using the ieee80211_sta_block allows the PS code
to handle awake->doze->awake transitions of our
clients in a race-free manner.
Signed-off-by: NChristian Lamparter <chunkeey@googlemail.com>
Signed-off-by: NJohn W. Linville <linville@tuxdriver.com>
上级 be8d98ea
...@@ -448,6 +448,8 @@ struct carl9170_ba_stats { ...@@ -448,6 +448,8 @@ struct carl9170_ba_stats {
struct carl9170_sta_info { struct carl9170_sta_info {
bool ht_sta; bool ht_sta;
bool sleeping;
atomic_t pending_frames;
unsigned int ampdu_max_len; unsigned int ampdu_max_len;
struct carl9170_sta_tid *agg[CARL9170_NUM_TID]; struct carl9170_sta_tid *agg[CARL9170_NUM_TID];
struct carl9170_ba_stats stats[CARL9170_NUM_TID]; struct carl9170_ba_stats stats[CARL9170_NUM_TID];
......
...@@ -1193,6 +1193,8 @@ static int carl9170_op_sta_add(struct ieee80211_hw *hw, ...@@ -1193,6 +1193,8 @@ static int carl9170_op_sta_add(struct ieee80211_hw *hw,
struct carl9170_sta_info *sta_info = (void *) sta->drv_priv; struct carl9170_sta_info *sta_info = (void *) sta->drv_priv;
unsigned int i; unsigned int i;
atomic_set(&sta_info->pending_frames, 0);
if (sta->ht_cap.ht_supported) { if (sta->ht_cap.ht_supported) {
if (sta->ht_cap.ampdu_density > 6) { if (sta->ht_cap.ampdu_density > 6) {
/* /*
...@@ -1467,99 +1469,17 @@ static void carl9170_op_sta_notify(struct ieee80211_hw *hw, ...@@ -1467,99 +1469,17 @@ static void carl9170_op_sta_notify(struct ieee80211_hw *hw,
enum sta_notify_cmd cmd, enum sta_notify_cmd cmd,
struct ieee80211_sta *sta) struct ieee80211_sta *sta)
{ {
struct ar9170 *ar = hw->priv;
struct carl9170_sta_info *sta_info = (void *) sta->drv_priv; struct carl9170_sta_info *sta_info = (void *) sta->drv_priv;
struct sk_buff *skb, *tmp;
struct sk_buff_head free;
int i;
switch (cmd) { switch (cmd) {
case STA_NOTIFY_SLEEP: case STA_NOTIFY_SLEEP:
/* sta_info->sleeping = true;
* Since the peer is no longer listening, we have to return if (atomic_read(&sta_info->pending_frames))
* as many SKBs as possible back to the mac80211 stack. ieee80211_sta_block_awake(hw, sta, true);
* It will deal with the retry procedure, once the peer
* has become available again.
*
* NB: Ideally, the driver should return the all frames in
* the correct, ascending order. However, I think that this
* functionality should be implemented in the stack and not
* here...
*/
__skb_queue_head_init(&free);
if (sta->ht_cap.ht_supported) {
rcu_read_lock();
for (i = 0; i < CARL9170_NUM_TID; i++) {
struct carl9170_sta_tid *tid_info;
tid_info = rcu_dereference(sta_info->agg[i]);
if (!tid_info)
continue;
spin_lock_bh(&ar->tx_ampdu_list_lock);
if (tid_info->state >
CARL9170_TID_STATE_SUSPEND)
tid_info->state =
CARL9170_TID_STATE_SUSPEND;
spin_unlock_bh(&ar->tx_ampdu_list_lock);
spin_lock_bh(&tid_info->lock);
while ((skb = __skb_dequeue(&tid_info->queue)))
__skb_queue_tail(&free, skb);
spin_unlock_bh(&tid_info->lock);
}
rcu_read_unlock();
}
for (i = 0; i < ar->hw->queues; i++) {
spin_lock_bh(&ar->tx_pending[i].lock);
skb_queue_walk_safe(&ar->tx_pending[i], skb, tmp) {
struct _carl9170_tx_superframe *super;
struct ieee80211_hdr *hdr;
struct ieee80211_tx_info *info;
super = (void *) skb->data;
hdr = (void *) super->frame_data;
if (compare_ether_addr(hdr->addr1, sta->addr))
continue;
__skb_unlink(skb, &ar->tx_pending[i]);
info = IEEE80211_SKB_CB(skb);
if (info->flags & IEEE80211_TX_CTL_AMPDU)
atomic_dec(&ar->tx_ampdu_upload);
carl9170_tx_status(ar, skb, false);
}
spin_unlock_bh(&ar->tx_pending[i].lock);
}
while ((skb = __skb_dequeue(&free)))
carl9170_tx_status(ar, skb, false);
break; break;
case STA_NOTIFY_AWAKE: case STA_NOTIFY_AWAKE:
if (!sta->ht_cap.ht_supported) sta_info->sleeping = false;
return;
rcu_read_lock();
for (i = 0; i < CARL9170_NUM_TID; i++) {
struct carl9170_sta_tid *tid_info;
tid_info = rcu_dereference(sta_info->agg[i]);
if (!tid_info)
continue;
if ((tid_info->state == CARL9170_TID_STATE_SUSPEND))
tid_info->state = CARL9170_TID_STATE_IDLE;
}
rcu_read_unlock();
break; break;
} }
} }
......
...@@ -104,6 +104,56 @@ static void carl9170_tx_accounting(struct ar9170 *ar, struct sk_buff *skb) ...@@ -104,6 +104,56 @@ static void carl9170_tx_accounting(struct ar9170 *ar, struct sk_buff *skb)
spin_unlock_bh(&ar->tx_stats_lock); spin_unlock_bh(&ar->tx_stats_lock);
} }
/* needs rcu_read_lock */
static struct ieee80211_sta *__carl9170_get_tx_sta(struct ar9170 *ar,
struct sk_buff *skb)
{
struct _carl9170_tx_superframe *super = (void *) skb->data;
struct ieee80211_hdr *hdr = (void *) super->frame_data;
struct ieee80211_vif *vif;
unsigned int vif_id;
vif_id = (super->s.misc & CARL9170_TX_SUPER_MISC_VIF_ID) >>
CARL9170_TX_SUPER_MISC_VIF_ID_S;
if (WARN_ON_ONCE(vif_id >= AR9170_MAX_VIRTUAL_MAC))
return NULL;
vif = rcu_dereference(ar->vif_priv[vif_id].vif);
if (unlikely(!vif))
return NULL;
/*
* Normally we should use wrappers like ieee80211_get_DA to get
* the correct peer ieee80211_sta.
*
* But there is a problem with indirect traffic (broadcasts, or
* data which is designated for other stations) in station mode.
* The frame will be directed to the AP for distribution and not
* to the actual destination.
*/
return ieee80211_find_sta(vif, hdr->addr1);
}
static void carl9170_tx_ps_unblock(struct ar9170 *ar, struct sk_buff *skb)
{
struct ieee80211_sta *sta;
struct carl9170_sta_info *sta_info;
rcu_read_lock();
sta = __carl9170_get_tx_sta(ar, skb);
if (unlikely(!sta))
goto out_rcu;
sta_info = (struct carl9170_sta_info *) sta->drv_priv;
if (atomic_dec_return(&sta_info->pending_frames) == 0)
ieee80211_sta_block_awake(ar->hw, sta, false);
out_rcu:
rcu_read_unlock();
}
static void carl9170_tx_accounting_free(struct ar9170 *ar, struct sk_buff *skb) static void carl9170_tx_accounting_free(struct ar9170 *ar, struct sk_buff *skb)
{ {
struct ieee80211_tx_info *txinfo; struct ieee80211_tx_info *txinfo;
...@@ -135,6 +185,7 @@ static void carl9170_tx_accounting_free(struct ar9170 *ar, struct sk_buff *skb) ...@@ -135,6 +185,7 @@ static void carl9170_tx_accounting_free(struct ar9170 *ar, struct sk_buff *skb)
} }
spin_unlock_bh(&ar->tx_stats_lock); spin_unlock_bh(&ar->tx_stats_lock);
if (atomic_dec_and_test(&ar->tx_total_queued)) if (atomic_dec_and_test(&ar->tx_total_queued))
complete(&ar->tx_flush); complete(&ar->tx_flush);
} }
...@@ -329,13 +380,10 @@ static void carl9170_tx_status_process_ampdu(struct ar9170 *ar, ...@@ -329,13 +380,10 @@ static void carl9170_tx_status_process_ampdu(struct ar9170 *ar,
{ {
struct _carl9170_tx_superframe *super = (void *) skb->data; struct _carl9170_tx_superframe *super = (void *) skb->data;
struct ieee80211_hdr *hdr = (void *) super->frame_data; struct ieee80211_hdr *hdr = (void *) super->frame_data;
struct ieee80211_tx_info *tx_info;
struct carl9170_tx_info *ar_info; struct carl9170_tx_info *ar_info;
struct carl9170_sta_info *sta_info;
struct ieee80211_sta *sta; struct ieee80211_sta *sta;
struct carl9170_sta_info *sta_info;
struct carl9170_sta_tid *tid_info; struct carl9170_sta_tid *tid_info;
struct ieee80211_vif *vif;
unsigned int vif_id;
u8 tid; u8 tid;
if (!(txinfo->flags & IEEE80211_TX_CTL_AMPDU) || if (!(txinfo->flags & IEEE80211_TX_CTL_AMPDU) ||
...@@ -343,30 +391,10 @@ static void carl9170_tx_status_process_ampdu(struct ar9170 *ar, ...@@ -343,30 +391,10 @@ static void carl9170_tx_status_process_ampdu(struct ar9170 *ar,
(!(super->f.mac_control & cpu_to_le16(AR9170_TX_MAC_AGGR)))) (!(super->f.mac_control & cpu_to_le16(AR9170_TX_MAC_AGGR))))
return; return;
tx_info = IEEE80211_SKB_CB(skb); ar_info = (void *) txinfo->rate_driver_data;
ar_info = (void *) tx_info->rate_driver_data;
vif_id = (super->s.misc & CARL9170_TX_SUPER_MISC_VIF_ID) >>
CARL9170_TX_SUPER_MISC_VIF_ID_S;
if (WARN_ON_ONCE(vif_id >= AR9170_MAX_VIRTUAL_MAC))
return;
rcu_read_lock(); rcu_read_lock();
vif = rcu_dereference(ar->vif_priv[vif_id].vif); sta = __carl9170_get_tx_sta(ar, skb);
if (unlikely(!vif))
goto out_rcu;
/*
* Normally we should use wrappers like ieee80211_get_DA to get
* the correct peer ieee80211_sta.
*
* But there is a problem with indirect traffic (broadcasts, or
* data which is designated for other stations) in station mode.
* The frame will be directed to the AP for distribution and not
* to the actual destination.
*/
sta = ieee80211_find_sta(vif, hdr->addr1);
if (unlikely(!sta)) if (unlikely(!sta))
goto out_rcu; goto out_rcu;
...@@ -427,6 +455,7 @@ void carl9170_tx_status(struct ar9170 *ar, struct sk_buff *skb, ...@@ -427,6 +455,7 @@ void carl9170_tx_status(struct ar9170 *ar, struct sk_buff *skb,
if (txinfo->flags & IEEE80211_TX_CTL_AMPDU) if (txinfo->flags & IEEE80211_TX_CTL_AMPDU)
carl9170_tx_status_process_ampdu(ar, skb, txinfo); carl9170_tx_status_process_ampdu(ar, skb, txinfo);
carl9170_tx_ps_unblock(ar, skb);
carl9170_tx_put_skb(skb); carl9170_tx_put_skb(skb);
} }
...@@ -540,11 +569,7 @@ static void carl9170_tx_ampdu_timeout(struct ar9170 *ar) ...@@ -540,11 +569,7 @@ static void carl9170_tx_ampdu_timeout(struct ar9170 *ar)
struct sk_buff *skb; struct sk_buff *skb;
struct ieee80211_tx_info *txinfo; struct ieee80211_tx_info *txinfo;
struct carl9170_tx_info *arinfo; struct carl9170_tx_info *arinfo;
struct _carl9170_tx_superframe *super;
struct ieee80211_sta *sta; struct ieee80211_sta *sta;
struct ieee80211_vif *vif;
struct ieee80211_hdr *hdr;
unsigned int vif_id;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(iter, &ar->tx_ampdu_list, list) { list_for_each_entry_rcu(iter, &ar->tx_ampdu_list, list) {
...@@ -562,20 +587,7 @@ static void carl9170_tx_ampdu_timeout(struct ar9170 *ar) ...@@ -562,20 +587,7 @@ static void carl9170_tx_ampdu_timeout(struct ar9170 *ar)
msecs_to_jiffies(CARL9170_QUEUE_TIMEOUT))) msecs_to_jiffies(CARL9170_QUEUE_TIMEOUT)))
goto unlock; goto unlock;
super = (void *) skb->data; sta = __carl9170_get_tx_sta(ar, skb);
hdr = (void *) super->frame_data;
vif_id = (super->s.misc & CARL9170_TX_SUPER_MISC_VIF_ID) >>
CARL9170_TX_SUPER_MISC_VIF_ID_S;
if (WARN_ON(vif_id >= AR9170_MAX_VIRTUAL_MAC))
goto unlock;
vif = rcu_dereference(ar->vif_priv[vif_id].vif);
if (WARN_ON(!vif))
goto unlock;
sta = ieee80211_find_sta(vif, hdr->addr1);
if (WARN_ON(!sta)) if (WARN_ON(!sta))
goto unlock; goto unlock;
...@@ -1199,15 +1211,6 @@ static struct sk_buff *carl9170_tx_pick_skb(struct ar9170 *ar, ...@@ -1199,15 +1211,6 @@ static struct sk_buff *carl9170_tx_pick_skb(struct ar9170 *ar,
arinfo = (void *) info->rate_driver_data; arinfo = (void *) info->rate_driver_data;
arinfo->timeout = jiffies; arinfo->timeout = jiffies;
/*
* increase ref count to "2".
* Ref counting is the easiest way to solve the race between
* the the urb's completion routine: carl9170_tx_callback and
* wlan tx status functions: carl9170_tx_status/janitor.
*/
carl9170_tx_get_skb(skb);
return skb; return skb;
err_unlock: err_unlock:
...@@ -1228,6 +1231,36 @@ void carl9170_tx_drop(struct ar9170 *ar, struct sk_buff *skb) ...@@ -1228,6 +1231,36 @@ void carl9170_tx_drop(struct ar9170 *ar, struct sk_buff *skb)
__carl9170_tx_process_status(ar, super->s.cookie, q); __carl9170_tx_process_status(ar, super->s.cookie, q);
} }
static bool carl9170_tx_ps_drop(struct ar9170 *ar, struct sk_buff *skb)
{
struct ieee80211_sta *sta;
struct carl9170_sta_info *sta_info;
rcu_read_lock();
sta = __carl9170_get_tx_sta(ar, skb);
if (!sta)
goto out_rcu;
sta_info = (void *) sta->drv_priv;
if (unlikely(sta_info->sleeping)) {
struct ieee80211_tx_info *tx_info;
rcu_read_unlock();
tx_info = IEEE80211_SKB_CB(skb);
if (tx_info->flags & IEEE80211_TX_CTL_AMPDU)
atomic_dec(&ar->tx_ampdu_upload);
tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
carl9170_tx_status(ar, skb, false);
return true;
}
out_rcu:
rcu_read_unlock();
return false;
}
static void carl9170_tx(struct ar9170 *ar) static void carl9170_tx(struct ar9170 *ar)
{ {
struct sk_buff *skb; struct sk_buff *skb;
...@@ -1247,6 +1280,9 @@ static void carl9170_tx(struct ar9170 *ar) ...@@ -1247,6 +1280,9 @@ static void carl9170_tx(struct ar9170 *ar)
if (unlikely(!skb)) if (unlikely(!skb))
break; break;
if (unlikely(carl9170_tx_ps_drop(ar, skb)))
continue;
atomic_inc(&ar->tx_total_pending); atomic_inc(&ar->tx_total_pending);
q = __carl9170_get_queue(ar, i); q = __carl9170_get_queue(ar, i);
...@@ -1256,6 +1292,16 @@ static void carl9170_tx(struct ar9170 *ar) ...@@ -1256,6 +1292,16 @@ static void carl9170_tx(struct ar9170 *ar)
*/ */
skb_queue_tail(&ar->tx_status[q], skb); skb_queue_tail(&ar->tx_status[q], skb);
/*
* increase ref count to "2".
* Ref counting is the easiest way to solve the
* race between the urb's completion routine:
* carl9170_tx_callback
* and wlan tx status functions:
* carl9170_tx_status/janitor.
*/
carl9170_tx_get_skb(skb);
carl9170_usb_tx(ar, skb); carl9170_usb_tx(ar, skb);
schedule_garbagecollector = true; schedule_garbagecollector = true;
} }
...@@ -1368,6 +1414,11 @@ void carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) ...@@ -1368,6 +1414,11 @@ void carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
* all ressouces which are associated with the frame. * all ressouces which are associated with the frame.
*/ */
if (sta) {
struct carl9170_sta_info *stai = (void *) sta->drv_priv;
atomic_inc(&stai->pending_frames);
}
if (info->flags & IEEE80211_TX_CTL_AMPDU) { if (info->flags & IEEE80211_TX_CTL_AMPDU) {
run = carl9170_tx_ampdu_queue(ar, sta, skb); run = carl9170_tx_ampdu_queue(ar, sta, skb);
if (run) if (run)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册