Skip to content

Commit

Permalink
mt76: implement AP_LINK_PS
Browse files Browse the repository at this point in the history
With software A-MPDU reordering in place, frames that notify mac80211 of
powersave changes are reordered as well, which can cause connection
stalls. Fix this by implementing powersave state processing in the
driver.

Signed-off-by: Felix Fietkau <[email protected]>
  • Loading branch information
nbd168 committed Jan 26, 2018
1 parent 9c2e03d commit f515dfc
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 29 deletions.
52 changes: 51 additions & 1 deletion mac80211.c
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ int mt76_register_device(struct mt76_dev *dev, bool vht,
ieee80211_hw_set(hw, TX_AMSDU);
ieee80211_hw_set(hw, TX_FRAG_LIST);
ieee80211_hw_set(hw, MFP_CAPABLE);
ieee80211_hw_set(hw, AP_LINK_PS);

wiphy->flags |= WIPHY_FLAG_IBSS_RSN;

Expand Down Expand Up @@ -470,6 +471,53 @@ mt76_check_ccmp_pn(struct sk_buff *skb)
return 0;
}

static void
mt76_check_ps(struct mt76_dev *dev, struct sk_buff *skb)
{
struct mt76_rx_status *status = (struct mt76_rx_status *) skb->cb;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_sta *sta;
struct mt76_wcid *wcid = status->wcid;
bool ps;

if (!wcid || !wcid->sta)
return;

sta = container_of((void *) wcid, struct ieee80211_sta, drv_priv);

if (!test_bit(MT_WCID_FLAG_CHECK_PS, &wcid->flags))
return;

if (ieee80211_is_pspoll(hdr->frame_control)) {
ieee80211_sta_pspoll(sta);
return;
}

if (ieee80211_has_morefrags(hdr->frame_control) ||
!(ieee80211_is_mgmt(hdr->frame_control) ||
ieee80211_is_data(hdr->frame_control)))
return;

ps = ieee80211_has_pm(hdr->frame_control);

if (ps && (ieee80211_is_data_qos(hdr->frame_control) ||
ieee80211_is_qos_nullfunc(hdr->frame_control)))
ieee80211_sta_uapsd_trigger(sta, status->tid);

if (!!test_bit(MT_WCID_FLAG_PS, &wcid->flags) == ps)
return;

if (ps) {
set_bit(MT_WCID_FLAG_PS, &wcid->flags);
mt76_stop_tx_queues(dev, sta, true);
} else {
clear_bit(MT_WCID_FLAG_PS, &wcid->flags);
}

ieee80211_sta_ps_transition(sta, ps);
dev->drv->sta_ps(dev, sta, ps);
}

void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
int queue)
{
Expand Down Expand Up @@ -498,8 +546,10 @@ void mt76_rx_poll_complete(struct mt76_dev *dev, enum mt76_rxq_id q)

__skb_queue_head_init(&frames);

while ((skb = __skb_dequeue(&dev->rx_skb[q])) != NULL)
while ((skb = __skb_dequeue(&dev->rx_skb[q])) != NULL) {
mt76_check_ps(dev, skb);
mt76_rx_aggr_reorder(skb, &frames);
}

mt76_rx_complete(dev, &frames, q);
}
10 changes: 10 additions & 0 deletions mt76.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,18 @@ struct mt76_queue_ops {
void (*kick)(struct mt76_dev *dev, struct mt76_queue *q);
};

enum mt76_wcid_flags {
MT_WCID_FLAG_CHECK_PS,
MT_WCID_FLAG_PS,
};

struct mt76_wcid {
struct mt76_rx_tid __rcu *aggr[IEEE80211_NUM_TIDS];

struct work_struct aggr_work;

unsigned long flags;

u8 idx;
u8 hw_key_idx;

Expand Down Expand Up @@ -206,6 +213,9 @@ struct mt76_driver_ops {
struct sk_buff *skb);

void (*rx_poll_complete)(struct mt76_dev *dev, enum mt76_rxq_id q);

void (*sta_ps)(struct mt76_dev *dev, struct ieee80211_sta *sta,
bool ps);
};

struct mt76_channel_state {
Expand Down
1 change: 1 addition & 0 deletions mt7603.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ void mt7603_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue *q,
void mt7603_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
struct sk_buff *skb);
void mt7603_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q);
void mt7603_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps);

void mt7603_tbtt(struct mt7603_dev *dev);
void mt7603_pre_tbtt_tasklet(unsigned long arg);
Expand Down
1 change: 1 addition & 0 deletions mt7603_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ struct mt7603_dev *mt7603_alloc_device(struct device *pdev)
.tx_complete_skb = mt7603_tx_complete_skb,
.rx_skb = mt7603_queue_rx_skb,
.rx_poll_complete = mt7603_rx_poll_complete,
.sta_ps = mt7603_sta_ps,
};
struct ieee80211_hw *hw;
struct mt7603_dev *dev;
Expand Down
28 changes: 14 additions & 14 deletions mt7603_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,9 @@ mt7603_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
mt7603_wtbl_init(dev, idx, mvif->idx, sta->addr);
mt7603_wtbl_update_cap(dev, sta);

if (vif->type == NL80211_IFTYPE_AP)
set_bit(MT_WCID_FLAG_CHECK_PS, &msta->wcid.flags);

rcu_assign_pointer(dev->wcid[idx], &msta->wcid);

out:
Expand Down Expand Up @@ -318,23 +321,14 @@ mt7603_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
return 0;
}

static void
mt7603_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
enum sta_notify_cmd cmd, struct ieee80211_sta *sta)
void
mt7603_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps)
{
struct mt7603_dev *dev = hw->priv;
struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76);
struct mt7603_sta *msta = (struct mt7603_sta *) sta->drv_priv;
int idx = msta->wcid.idx;

switch (cmd) {
case STA_NOTIFY_SLEEP:
mt7603_wtbl_set_ps(dev, idx, true);
mt76_stop_tx_queues(&dev->mt76, sta, false);
break;
case STA_NOTIFY_AWAKE:
mt7603_wtbl_set_ps(dev, idx, false);
break;
}
mt7603_wtbl_set_ps(dev, idx, ps);
}

static int
Expand Down Expand Up @@ -543,6 +537,12 @@ static void mt7603_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *cont
mt76_tx(&dev->mt76, control->sta, wcid, skb);
}

static int
mt7603_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set)
{
return 0;
}

const struct ieee80211_ops mt7603_ops = {
.tx = mt7603_tx,
.start = mt7603_start,
Expand All @@ -554,7 +554,6 @@ const struct ieee80211_ops mt7603_ops = {
.bss_info_changed = mt7603_bss_info_changed,
.sta_add = mt7603_sta_add,
.sta_remove = mt7603_sta_remove,
.sta_notify = mt7603_sta_notify,
.set_key = mt7603_set_key,
.conf_tx = mt7603_conf_tx,
.sw_scan_start = mt7603_sw_scan,
Expand All @@ -566,6 +565,7 @@ const struct ieee80211_ops mt7603_ops = {
.sta_rate_tbl_update = mt7603_sta_rate_tbl_update,
.release_buffered_frames = mt76_release_buffered_frames,
.set_coverage_class = mt7603_set_coverage_class,
.set_tim = mt7603_set_tim,
};

MODULE_LICENSE("Dual BSD/GPL");
Expand Down
2 changes: 2 additions & 0 deletions mt76x2.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ void mt76x2_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q);
void mt76x2_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q,
struct sk_buff *skb);

void mt76x2_sta_ps(struct mt76_dev *dev, struct ieee80211_sta *sta, bool ps);

void mt76x2_update_channel(struct mt76_dev *mdev);

s8 mt76x2_tx_get_max_txpwr_adj(struct mt76x2_dev *dev,
Expand Down
1 change: 1 addition & 0 deletions mt76x2_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,7 @@ struct mt76x2_dev *mt76x2_alloc_device(struct device *pdev)
.tx_complete_skb = mt76x2_tx_complete_skb,
.rx_skb = mt76x2_queue_rx_skb,
.rx_poll_complete = mt76x2_rx_poll_complete,
.sta_ps = mt76x2_sta_ps,
};
struct ieee80211_hw *hw;
struct mt76x2_dev *dev;
Expand Down
28 changes: 14 additions & 14 deletions mt76x2_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,9 @@ mt76x2_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
mt76x2_txq_init(dev, sta->txq[i]);

if (vif->type == NL80211_IFTYPE_AP)
set_bit(MT_WCID_FLAG_CHECK_PS, &msta->wcid.flags);

rcu_assign_pointer(dev->wcid[idx], &msta->wcid);

out:
Expand Down Expand Up @@ -311,23 +314,14 @@ mt76x2_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
return 0;
}

static void
mt76x2_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
enum sta_notify_cmd cmd, struct ieee80211_sta *sta)
void
mt76x2_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps)
{
struct mt76x2_sta *msta = (struct mt76x2_sta *) sta->drv_priv;
struct mt76x2_dev *dev = hw->priv;
struct mt76x2_dev *dev = container_of(mdev, struct mt76x2_dev, mt76);
int idx = msta->wcid.idx;

switch (cmd) {
case STA_NOTIFY_SLEEP:
mt76x2_mac_wcid_set_drop(dev, idx, true);
mt76_stop_tx_queues(&dev->mt76, sta, true);
break;
case STA_NOTIFY_AWAKE:
mt76x2_mac_wcid_set_drop(dev, idx, false);
break;
}
mt76x2_mac_wcid_set_drop(dev, idx, ps);
}

static int
Expand Down Expand Up @@ -549,6 +543,12 @@ static void mt76x2_set_coverage_class(struct ieee80211_hw *hw,
mutex_unlock(&dev->mutex);
}

static int
mt76x2_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set)
{
return 0;
}

const struct ieee80211_ops mt76x2_ops = {
.tx = mt76x2_tx,
.start = mt76x2_start,
Expand All @@ -560,7 +560,6 @@ const struct ieee80211_ops mt76x2_ops = {
.bss_info_changed = mt76x2_bss_info_changed,
.sta_add = mt76x2_sta_add,
.sta_remove = mt76x2_sta_remove,
.sta_notify = mt76x2_sta_notify,
.set_key = mt76x2_set_key,
.conf_tx = mt76x2_conf_tx,
.sw_scan_start = mt76x2_sw_scan,
Expand All @@ -573,5 +572,6 @@ const struct ieee80211_ops mt76x2_ops = {
.release_buffered_frames = mt76_release_buffered_frames,
.set_coverage_class = mt76x2_set_coverage_class,
.get_survey = mt76_get_survey,
.set_tim = mt76x2_set_tim,
};

0 comments on commit f515dfc

Please sign in to comment.