Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 930e1915 authored by Arik Nemtsov's avatar Arik Nemtsov Committed by Johannes Berg
Browse files

wlcore: don't get the hlid from a queued skb



There was a bug hiding here since the hlid was sometimes inferred from
the sta, which might be invalid at this point.

Instead, propagate the hlid from the skb-queue where we got the skb
in the first place.

Signed-off-by: default avatarArik Nemtsov <arik@wizery.com>
Acked-by: default avatarLuciano Coelho <coelho@ti.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent e2176892
Loading
Loading
Loading
Loading
+26 −20
Original line number Diff line number Diff line
@@ -344,13 +344,12 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,

/* caller must hold wl->mutex */
static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
				   struct sk_buff *skb, u32 buf_offset)
				   struct sk_buff *skb, u32 buf_offset, u8 hlid)
{
	struct ieee80211_tx_info *info;
	u32 extra = 0;
	int ret = 0;
	u32 total_len;
	u8 hlid;
	bool is_dummy;
	bool is_gem = false;

@@ -359,9 +358,13 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
		return -EINVAL;
	}

	if (hlid == WL12XX_INVALID_LINK_ID) {
		wl1271_error("invalid hlid. dropping skb 0x%p", skb);
		return -EINVAL;
	}

	info = IEEE80211_SKB_CB(skb);

	/* TODO: handle dummy packets on multi-vifs */
	is_dummy = wl12xx_is_dummy_packet(wl, skb);

	if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) &&
@@ -386,11 +389,6 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,

		is_gem = (cipher == WL1271_CIPHER_SUITE_GEM);
	}
	hlid = wl12xx_tx_get_hlid(wl, wlvif, skb);
	if (hlid == WL12XX_INVALID_LINK_ID) {
		wl1271_error("invalid hlid. dropping skb 0x%p", skb);
		return -EINVAL;
	}

	ret = wl1271_tx_allocate(wl, wlvif, skb, extra, buf_offset, hlid,
				 is_gem);
@@ -517,7 +515,8 @@ static struct sk_buff *wl12xx_lnk_skb_dequeue(struct wl1271 *wl,
}

static struct sk_buff *wl12xx_vif_skb_dequeue(struct wl1271 *wl,
					      struct wl12xx_vif *wlvif)
					      struct wl12xx_vif *wlvif,
					      u8 *hlid)
{
	struct sk_buff *skb = NULL;
	int i, h, start_hlid;
@@ -544,10 +543,11 @@ static struct sk_buff *wl12xx_vif_skb_dequeue(struct wl1271 *wl,
	if (!skb)
		wlvif->last_tx_hlid = 0;

	*hlid = wlvif->last_tx_hlid;
	return skb;
}

static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl, u8 *hlid)
{
	unsigned long flags;
	struct wl12xx_vif *wlvif = wl->last_wlvif;
@@ -556,7 +556,7 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
	/* continue from last wlvif (round robin) */
	if (wlvif) {
		wl12xx_for_each_wlvif_continue(wl, wlvif) {
			skb = wl12xx_vif_skb_dequeue(wl, wlvif);
			skb = wl12xx_vif_skb_dequeue(wl, wlvif, hlid);
			if (skb) {
				wl->last_wlvif = wlvif;
				break;
@@ -565,13 +565,15 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
	}

	/* dequeue from the system HLID before the restarting wlvif list */
	if (!skb)
	if (!skb) {
		skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[wl->system_hlid]);
		*hlid = wl->system_hlid;
	}

	/* do a new pass over the wlvif list */
	if (!skb) {
		wl12xx_for_each_wlvif(wl, wlvif) {
			skb = wl12xx_vif_skb_dequeue(wl, wlvif);
			skb = wl12xx_vif_skb_dequeue(wl, wlvif, hlid);
			if (skb) {
				wl->last_wlvif = wlvif;
				break;
@@ -591,6 +593,7 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
		int q;

		skb = wl->dummy_packet;
		*hlid = wl->system_hlid;
		q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
		spin_lock_irqsave(&wl->wl_lock, flags);
		WARN_ON_ONCE(wl->tx_queue_count[q] <= 0);
@@ -602,7 +605,7 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
}

static void wl1271_skb_queue_head(struct wl1271 *wl, struct wl12xx_vif *wlvif,
				  struct sk_buff *skb)
				  struct sk_buff *skb, u8 hlid)
{
	unsigned long flags;
	int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
@@ -610,7 +613,6 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct wl12xx_vif *wlvif,
	if (wl12xx_is_dummy_packet(wl, skb)) {
		set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags);
	} else {
		u8 hlid = wl12xx_tx_get_hlid(wl, wlvif, skb);
		skb_queue_head(&wl->links[hlid].tx_queue[q], skb);

		/* make sure we dequeue the same packet next time */
@@ -686,26 +688,30 @@ int wlcore_tx_work_locked(struct wl1271 *wl)
	unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0};
	int ret = 0;
	int bus_ret = 0;
	u8 hlid;

	if (unlikely(wl->state == WL1271_STATE_OFF))
		return 0;

	while ((skb = wl1271_skb_dequeue(wl))) {
	while ((skb = wl1271_skb_dequeue(wl, &hlid))) {
		struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
		bool has_data = false;

		wlvif = NULL;
		if (!wl12xx_is_dummy_packet(wl, skb) && info->control.vif)
			wlvif = wl12xx_vif_to_data(info->control.vif);
		else
			hlid = wl->system_hlid;

		has_data = wlvif && wl1271_tx_is_data_present(skb);
		ret = wl1271_prepare_tx_frame(wl, wlvif, skb, buf_offset);
		ret = wl1271_prepare_tx_frame(wl, wlvif, skb, buf_offset,
					      hlid);
		if (ret == -EAGAIN) {
			/*
			 * Aggregation buffer is full.
			 * Flush buffer and try again.
			 */
			wl1271_skb_queue_head(wl, wlvif, skb);
			wl1271_skb_queue_head(wl, wlvif, skb, hlid);

			buf_offset = wlcore_hw_pre_pkt_send(wl, buf_offset,
							    last_len);
@@ -722,7 +728,7 @@ int wlcore_tx_work_locked(struct wl1271 *wl)
			 * Firmware buffer is full.
			 * Queue back last skb, and stop aggregating.
			 */
			wl1271_skb_queue_head(wl, wlvif, skb);
			wl1271_skb_queue_head(wl, wlvif, skb, hlid);
			/* No work left, avoid scheduling redundant tx work */
			set_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);
			goto out_ack;
@@ -732,7 +738,7 @@ int wlcore_tx_work_locked(struct wl1271 *wl)
				 * fw still expects dummy packet,
				 * so re-enqueue it
				 */
				wl1271_skb_queue_head(wl, wlvif, skb);
				wl1271_skb_queue_head(wl, wlvif, skb, hlid);
			else
				ieee80211_free_txskb(wl->hw, skb);
			goto out_ack;