Commit f02e1cc2 authored by Benjamin Li's avatar Benjamin Li Committed by Kalle Valo
Browse files

wcn36xx: implement flush op to speed up connected scan



Without ieee80211_ops->flush implemented to empty HW queues, mac80211 will
do a 100ms dead wait after stopping SW queues, before leaving the operating
channel to resume a software connected scan[1].
(see ieee80211_scan_state_resume)

This wait is correctly included in the calculation for whether or not
we've exceeded max off-channel time, as it occurs after sending the null
frame with PS bit set. Thus, with 125 ms max off-channel time we only
have 25 ms of scan time, which technically isn't even enough to scan one
channel (although mac80211 always scans at least one channel per off-
channel window).

Moreover, for passive probes we end up spending at least 100 ms + 111 ms
(IEEE80211_PASSIVE_CHANNEL_TIME) "off-channel"[2], which exceeds the listen
interval of 200 ms that we provide in our association request frame. That's
technically out-of-spec.

[1]: Until recently, wcn36xx performed software (rather than FW-offloaded)
scanning when 5GHz channels are requested. This apparent limitation is now
resolved -- see commit 1395f8a6a4d5 ("wcn36xx: Enable hardware scan offload
for 5Ghz band").
[2]: in quotes because about 100 ms of it is still on-channel but with PS
set

Signed-off-by: default avatarBenjamin Li <benl@squareup.com>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20211027170306.555535-3-benl@squareup.com
parent df008741
Loading
Loading
Loading
Loading
+47 −0
Original line number Diff line number Diff line
@@ -834,6 +834,53 @@ int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
	return ret;
}

static bool _wcn36xx_dxe_tx_channel_is_empty(struct wcn36xx_dxe_ch *ch)
{
	unsigned long flags;
	struct wcn36xx_dxe_ctl *ctl_bd_start, *ctl_skb_start;
	struct wcn36xx_dxe_ctl *ctl_bd, *ctl_skb;
	bool ret = true;

	spin_lock_irqsave(&ch->lock, flags);

	/* Loop through ring buffer looking for nonempty entries. */
	ctl_bd_start = ch->head_blk_ctl;
	ctl_bd = ctl_bd_start;
	ctl_skb_start = ctl_bd_start->next;
	ctl_skb = ctl_skb_start;
	do {
		if (ctl_skb->skb) {
			ret = false;
			goto unlock;
		}
		ctl_bd = ctl_skb->next;
		ctl_skb = ctl_bd->next;
	} while (ctl_skb != ctl_skb_start);

unlock:
	spin_unlock_irqrestore(&ch->lock, flags);
	return ret;
}

int wcn36xx_dxe_tx_flush(struct wcn36xx *wcn)
{
	int i = 0;

	/* Called with mac80211 queues stopped. Wait for empty HW queues. */
	do {
		if (_wcn36xx_dxe_tx_channel_is_empty(&wcn->dxe_tx_l_ch) &&
		    _wcn36xx_dxe_tx_channel_is_empty(&wcn->dxe_tx_h_ch)) {
			return 0;
		}
		/* This ieee80211_ops callback is specifically allowed to
		 * sleep.
		 */
		usleep_range(1000, 1100);
	} while (++i < 100);

	return -EBUSY;
}

int wcn36xx_dxe_init(struct wcn36xx *wcn)
{
	int reg_data = 0, ret;
+1 −0
Original line number Diff line number Diff line
@@ -466,5 +466,6 @@ int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
			 struct wcn36xx_tx_bd *bd,
			 struct sk_buff *skb,
			 bool is_low);
int wcn36xx_dxe_tx_flush(struct wcn36xx *wcn);
void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status);
#endif	/* _DXE_H_ */
+11 −0
Original line number Diff line number Diff line
@@ -1281,6 +1281,16 @@ static void wcn36xx_ipv6_addr_change(struct ieee80211_hw *hw,
}
#endif

static void wcn36xx_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
			  u32 queues, bool drop)
{
	struct wcn36xx *wcn = hw->priv;

	if (wcn36xx_dxe_tx_flush(wcn)) {
		wcn36xx_err("Failed to flush hardware tx queues\n");
	}
}

static const struct ieee80211_ops wcn36xx_ops = {
	.start			= wcn36xx_start,
	.stop			= wcn36xx_stop,
@@ -1308,6 +1318,7 @@ static const struct ieee80211_ops wcn36xx_ops = {
#if IS_ENABLED(CONFIG_IPV6)
	.ipv6_addr_change	= wcn36xx_ipv6_addr_change,
#endif
	.flush			= wcn36xx_flush,

	CFG80211_TESTMODE_CMD(wcn36xx_tm_cmd)
};