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

Commit a729cff8 authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville
Browse files

mac80211: implement wifi TX status



Implement the socket wifi TX status error
queue reflection in mac80211.

Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 1f074bd8
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
@@ -518,7 +518,7 @@ struct ieee80211_tx_rate {
 * @flags: transmit info flags, defined above
 * @band: the band to transmit on (use for checking for races)
 * @antenna_sel_tx: antenna to use, 0 for automatic diversity
 * @pad: padding, ignore
 * @ack_frame_id: internal frame ID for TX status, used internally
 * @control: union for control data
 * @status: union for status data
 * @driver_data: array of driver_data pointers
@@ -535,8 +535,7 @@ struct ieee80211_tx_info {

	u8 antenna_sel_tx;

	/* 2 byte hole */
	u8 pad[2];
	u16 ack_frame_id;

	union {
		struct {
+4 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include <linux/spinlock.h>
#include <linux/etherdevice.h>
#include <linux/leds.h>
#include <linux/idr.h>
#include <net/ieee80211_radiotap.h>
#include <net/cfg80211.h>
#include <net/mac80211.h>
@@ -1017,6 +1018,9 @@ struct ieee80211_local {
	u32 hw_roc_cookie;
	bool hw_roc_for_tx;

	struct idr ack_status_frames;
	spinlock_t ack_status_lock;

	/* dummy netdev for use w/ NAPI */
	struct net_device napi_dev;

+18 −0
Original line number Diff line number Diff line
@@ -596,6 +596,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
			WIPHY_FLAG_4ADDR_STATION |
			WIPHY_FLAG_REPORTS_OBSS;

	wiphy->features = NL80211_FEATURE_SK_TX_STATUS;

	if (!ops->set_key)
		wiphy->flags |= WIPHY_FLAG_IBSS_RSN;

@@ -669,6 +671,11 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
	INIT_WORK(&local->sched_scan_stopped_work,
		  ieee80211_sched_scan_stopped_work);

	spin_lock_init(&local->ack_status_lock);
	idr_init(&local->ack_status_frames);
	/* preallocate at least one entry */
	idr_pre_get(&local->ack_status_frames, GFP_KERNEL);

	sta_info_init(local);

	for (i = 0; i < IEEE80211_MAX_QUEUES; i++) {
@@ -1044,6 +1051,13 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
}
EXPORT_SYMBOL(ieee80211_unregister_hw);

static int ieee80211_free_ack_frame(int id, void *p, void *data)
{
	WARN_ONCE(1, "Have pending ack frames!\n");
	kfree_skb(p);
	return 0;
}

void ieee80211_free_hw(struct ieee80211_hw *hw)
{
	struct ieee80211_local *local = hw_to_local(hw);
@@ -1054,6 +1068,10 @@ void ieee80211_free_hw(struct ieee80211_hw *hw)
	if (local->wiphy_ciphers_allocated)
		kfree(local->hw.wiphy->cipher_suites);

	idr_for_each(&local->ack_status_frames,
		     ieee80211_free_ack_frame, NULL);
	idr_destroy(&local->ack_status_frames);

	wiphy_free(local->hw.wiphy);
}
EXPORT_SYMBOL(ieee80211_free_hw);
+38 −0
Original line number Diff line number Diff line
@@ -548,6 +548,24 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
		}
	}

	if (unlikely(info->ack_frame_id)) {
		struct sk_buff *ack_skb;
		unsigned long flags;

		spin_lock_irqsave(&local->ack_status_lock, flags);
		ack_skb = idr_find(&local->ack_status_frames,
				   info->ack_frame_id);
		if (ack_skb)
			idr_remove(&local->ack_status_frames,
				   info->ack_frame_id);
		spin_unlock_irqrestore(&local->ack_status_lock, flags);

		/* consumes ack_skb */
		if (ack_skb)
			skb_complete_wifi_ack(ack_skb,
				info->flags & IEEE80211_TX_STAT_ACK);
	}

	/* this was a transmitted frame, but now we want to reuse it */
	skb_orphan(skb);

@@ -621,6 +639,26 @@ EXPORT_SYMBOL(ieee80211_report_low_ack);

void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb)
{
	struct ieee80211_local *local = hw_to_local(hw);
	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);

	if (unlikely(info->ack_frame_id)) {
		struct sk_buff *ack_skb;
		unsigned long flags;

		spin_lock_irqsave(&local->ack_status_lock, flags);
		ack_skb = idr_find(&local->ack_status_frames,
				   info->ack_frame_id);
		if (ack_skb)
			idr_remove(&local->ack_status_frames,
				   info->ack_frame_id);
		spin_unlock_irqrestore(&local->ack_status_lock, flags);

		/* consumes ack_skb */
		if (ack_skb)
			dev_kfree_skb_any(ack_skb);
	}

	dev_kfree_skb_any(skb);
}
EXPORT_SYMBOL(ieee80211_free_txskb);
+53 −3
Original line number Diff line number Diff line
@@ -1684,8 +1684,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
	int nh_pos, h_pos;
	struct sta_info *sta = NULL;
	bool wme_sta = false, authorized = false, tdls_auth = false;
	struct sk_buff *tmp_skb;
	bool tdls_direct = false;
	bool multicast;
	u32 info_flags = 0;
	u16 info_id = 0;

	if (unlikely(skb->len < ETH_HLEN)) {
		ret = NETDEV_TX_OK;
@@ -1872,7 +1874,8 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
	 * if it is a multicast address (which can only happen
	 * in AP mode)
	 */
	if (!is_multicast_ether_addr(hdr.addr1)) {
	multicast = is_multicast_ether_addr(hdr.addr1);
	if (!multicast) {
		rcu_read_lock();
		sta = sta_info_get(sdata, hdr.addr1);
		if (sta) {
@@ -1913,11 +1916,54 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
		goto fail;
	}

	if (unlikely(!multicast && skb->sk &&
		     skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) {
		struct sk_buff *orig_skb = skb;

		skb = skb_clone(skb, GFP_ATOMIC);
		if (skb) {
			unsigned long flags;
			int id, r;

			spin_lock_irqsave(&local->ack_status_lock, flags);
			r = idr_get_new_above(&local->ack_status_frames,
					      orig_skb, 1, &id);
			if (r == -EAGAIN) {
				idr_pre_get(&local->ack_status_frames,
					    GFP_ATOMIC);
				r = idr_get_new_above(&local->ack_status_frames,
						      orig_skb, 1, &id);
			}
			if (WARN_ON(!id) || id > 0xffff) {
				idr_remove(&local->ack_status_frames, id);
				r = -ERANGE;
			}
			spin_unlock_irqrestore(&local->ack_status_lock, flags);

			if (!r) {
				info_id = id;
				info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
			} else if (skb_shared(skb)) {
				kfree_skb(orig_skb);
			} else {
				kfree_skb(skb);
				skb = orig_skb;
			}
		} else {
			/* couldn't clone -- lose tx status ... */
			skb = orig_skb;
		}
	}

	/*
	 * If the skb is shared we need to obtain our own copy.
	 */
	if (skb_shared(skb)) {
		tmp_skb = skb;
		struct sk_buff *tmp_skb = skb;

		/* can't happen -- skb is a clone if info_id != 0 */
		WARN_ON(info_id);

		skb = skb_clone(skb, GFP_ATOMIC);
		kfree_skb(tmp_skb);

@@ -2018,6 +2064,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
	memset(info, 0, sizeof(*info));

	dev->trans_start = jiffies;

	info->flags = info_flags;
	info->ack_frame_id = info_id;

	ieee80211_xmit(sdata, skb);

	return NETDEV_TX_OK;