mac80211: add patch that gives the driver more control over netdev offloads
This can be used to selectively disable checksum, SG or GSO offloads Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
		| @@ -0,0 +1,513 @@ | |||||||
|  | From: Felix Fietkau <nbd@nbd.name> | ||||||
|  | Date: Sun, 9 Oct 2022 20:15:46 +0200 | ||||||
|  | Subject: [PATCH] mac80211: add support for restricting netdev features per vif | ||||||
|  |  | ||||||
|  | This can be used to selectively disable feature flags for checksum offload, | ||||||
|  | scatter/gather or GSO by changing vif->netdev_features. | ||||||
|  | Removing features from vif->netdev_features does not affect the netdev | ||||||
|  | features themselves, but instead fixes up skbs in the tx path so that the | ||||||
|  | offloads are not needed in the driver. | ||||||
|  |  | ||||||
|  | Aside from making it easier to deal with vif type based hardware limitations, | ||||||
|  | this also makes it possible to optimize performance on hardware without native | ||||||
|  | GSO support by declaring GSO support in hw->netdev_features and removing it | ||||||
|  | from vif->netdev_features. This allows mac80211 to handle GSO segmentation | ||||||
|  | after the sta lookup, but before itxq enqueue, thus reducing the number of | ||||||
|  | unnecessary sta lookups, as well as some other per-packet processing. | ||||||
|  |  | ||||||
|  | Signed-off-by: Felix Fietkau <nbd@nbd.name> | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | --- a/include/net/fq_impl.h | ||||||
|  | +++ b/include/net/fq_impl.h | ||||||
|  | @@ -200,6 +200,7 @@ static void fq_tin_enqueue(struct fq *fq | ||||||
|  |  			   fq_skb_free_t free_func) | ||||||
|  |  { | ||||||
|  |  	struct fq_flow *flow; | ||||||
|  | +	struct sk_buff *next; | ||||||
|  |  	bool oom; | ||||||
|  |   | ||||||
|  |  	lockdep_assert_held(&fq->lock); | ||||||
|  | @@ -214,11 +215,15 @@ static void fq_tin_enqueue(struct fq *fq | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  |  	flow->tin = tin; | ||||||
|  | -	flow->backlog += skb->len; | ||||||
|  | -	tin->backlog_bytes += skb->len; | ||||||
|  | -	tin->backlog_packets++; | ||||||
|  | -	fq->memory_usage += skb->truesize; | ||||||
|  | -	fq->backlog++; | ||||||
|  | +	skb_list_walk_safe(skb, skb, next) { | ||||||
|  | +		skb_mark_not_on_list(skb); | ||||||
|  | +		flow->backlog += skb->len; | ||||||
|  | +		tin->backlog_bytes += skb->len; | ||||||
|  | +		tin->backlog_packets++; | ||||||
|  | +		fq->memory_usage += skb->truesize; | ||||||
|  | +		fq->backlog++; | ||||||
|  | +		__skb_queue_tail(&flow->queue, skb); | ||||||
|  | +	} | ||||||
|  |   | ||||||
|  |  	if (list_empty(&flow->flowchain)) { | ||||||
|  |  		flow->deficit = fq->quantum; | ||||||
|  | @@ -226,7 +231,6 @@ static void fq_tin_enqueue(struct fq *fq | ||||||
|  |  			      &tin->new_flows); | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | -	__skb_queue_tail(&flow->queue, skb); | ||||||
|  |  	oom = (fq->memory_usage > fq->memory_limit); | ||||||
|  |  	while (fq->backlog > fq->limit || oom) { | ||||||
|  |  		flow = fq_find_fattest_flow(fq); | ||||||
|  | --- a/include/net/mac80211.h | ||||||
|  | +++ b/include/net/mac80211.h | ||||||
|  | @@ -1685,6 +1685,10 @@ enum ieee80211_offload_flags { | ||||||
|  |   *	write-protected by sdata_lock and local->mtx so holding either is fine | ||||||
|  |   *	for read access. | ||||||
|  |   * @mu_mimo_owner: indicates interface owns MU-MIMO capability | ||||||
|  | + * @netdev_features: tx netdev features supported by the hardware for this | ||||||
|  | + *	vif. mac80211 initializes this to hw->netdev_features, and the driver | ||||||
|  | + *	can mask out specific tx features. mac80211 will handle software fixup | ||||||
|  | + *	for masked offloads (GSO, CSUM) | ||||||
|  |   * @driver_flags: flags/capabilities the driver has for this interface, | ||||||
|  |   *	these need to be set (or cleared) when the interface is added | ||||||
|  |   *	or, if supported by the driver, the interface type is changed | ||||||
|  | @@ -1736,6 +1740,7 @@ struct ieee80211_vif { | ||||||
|  |   | ||||||
|  |  	struct ieee80211_chanctx_conf __rcu *chanctx_conf; | ||||||
|  |   | ||||||
|  | +	netdev_features_t netdev_features; | ||||||
|  |  	u32 driver_flags; | ||||||
|  |  	u32 offload_flags; | ||||||
|  |   | ||||||
|  | --- a/net/mac80211/iface.c | ||||||
|  | +++ b/net/mac80211/iface.c | ||||||
|  | @@ -2209,6 +2209,7 @@ int ieee80211_if_add(struct ieee80211_lo | ||||||
|  |  		ndev->features |= local->hw.netdev_features; | ||||||
|  |  		ndev->hw_features |= ndev->features & | ||||||
|  |  					MAC80211_SUPPORTED_FEATURES_TX; | ||||||
|  | +		sdata->vif.netdev_features = local->hw.netdev_features; | ||||||
|  |   | ||||||
|  |  		netdev_set_default_ethtool_ops(ndev, &ieee80211_ethtool_ops); | ||||||
|  |   | ||||||
|  | --- a/net/mac80211/tx.c | ||||||
|  | +++ b/net/mac80211/tx.c | ||||||
|  | @@ -1310,7 +1310,11 @@ static struct txq_info *ieee80211_get_tx | ||||||
|  |   | ||||||
|  |  static void ieee80211_set_skb_enqueue_time(struct sk_buff *skb) | ||||||
|  |  { | ||||||
|  | -	IEEE80211_SKB_CB(skb)->control.enqueue_time = codel_get_time(); | ||||||
|  | +	struct sk_buff *next; | ||||||
|  | +	codel_time_t now = codel_get_time(); | ||||||
|  | + | ||||||
|  | +	skb_list_walk_safe(skb, skb, next) | ||||||
|  | +		IEEE80211_SKB_CB(skb)->control.enqueue_time = now; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static u32 codel_skb_len_func(const struct sk_buff *skb) | ||||||
|  | @@ -3499,47 +3503,71 @@ ieee80211_xmit_fast_finish(struct ieee80 | ||||||
|  |  	return TX_CONTINUE; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata, | ||||||
|  | -				struct sta_info *sta, | ||||||
|  | -				struct ieee80211_fast_tx *fast_tx, | ||||||
|  | -				struct sk_buff *skb) | ||||||
|  | +static netdev_features_t | ||||||
|  | +ieee80211_sdata_netdev_features(struct ieee80211_sub_if_data *sdata) | ||||||
|  |  { | ||||||
|  | -	struct ieee80211_local *local = sdata->local; | ||||||
|  | -	u16 ethertype = (skb->data[12] << 8) | skb->data[13]; | ||||||
|  | -	int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2); | ||||||
|  | -	int hw_headroom = sdata->local->hw.extra_tx_headroom; | ||||||
|  | -	struct ethhdr eth; | ||||||
|  | -	struct ieee80211_tx_info *info; | ||||||
|  | -	struct ieee80211_hdr *hdr = (void *)fast_tx->hdr; | ||||||
|  | -	struct ieee80211_tx_data tx; | ||||||
|  | -	ieee80211_tx_result r; | ||||||
|  | -	struct tid_ampdu_tx *tid_tx = NULL; | ||||||
|  | -	u8 tid = IEEE80211_NUM_TIDS; | ||||||
|  | +	if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN) | ||||||
|  | +		return sdata->vif.netdev_features; | ||||||
|  |   | ||||||
|  | -	/* control port protocol needs a lot of special handling */ | ||||||
|  | -	if (cpu_to_be16(ethertype) == sdata->control_port_protocol) | ||||||
|  | -		return false; | ||||||
|  | +	if (!sdata->bss) | ||||||
|  | +		return 0; | ||||||
|  |   | ||||||
|  | -	/* only RFC 1042 SNAP */ | ||||||
|  | -	if (ethertype < ETH_P_802_3_MIN) | ||||||
|  | -		return false; | ||||||
|  | +	sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); | ||||||
|  | +	return sdata->vif.netdev_features; | ||||||
|  | +} | ||||||
|  |   | ||||||
|  | -	/* don't handle TX status request here either */ | ||||||
|  | -	if (skb->sk && skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS) | ||||||
|  | -		return false; | ||||||
|  | +static struct sk_buff * | ||||||
|  | +ieee80211_tx_skb_fixup(struct sk_buff *skb, netdev_features_t features) | ||||||
|  | +{ | ||||||
|  | +	if (skb_is_gso(skb)) { | ||||||
|  | +		struct sk_buff *segs; | ||||||
|  |   | ||||||
|  | -	if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) { | ||||||
|  | -		tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; | ||||||
|  | -		tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]); | ||||||
|  | -		if (tid_tx) { | ||||||
|  | -			if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) | ||||||
|  | -				return false; | ||||||
|  | -			if (tid_tx->timeout) | ||||||
|  | -				tid_tx->last_tx = jiffies; | ||||||
|  | -		} | ||||||
|  | +		segs = skb_gso_segment(skb, features); | ||||||
|  | +		if (!segs) | ||||||
|  | +			return skb; | ||||||
|  | +		if (IS_ERR(segs)) | ||||||
|  | +			goto free; | ||||||
|  | + | ||||||
|  | +		consume_skb(skb); | ||||||
|  | +		return segs; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	if (skb_needs_linearize(skb, features) && __skb_linearize(skb)) | ||||||
|  | +		goto free; | ||||||
|  | + | ||||||
|  | +	if (skb->ip_summed == CHECKSUM_PARTIAL) { | ||||||
|  | +		int ofs = skb_checksum_start_offset(skb); | ||||||
|  | + | ||||||
|  | +		if (skb->encapsulation) | ||||||
|  | +			skb_set_inner_transport_header(skb, ofs); | ||||||
|  | +		else | ||||||
|  | +			skb_set_transport_header(skb, ofs); | ||||||
|  | + | ||||||
|  | +		if (skb_csum_hwoffload_help(skb, features)) | ||||||
|  | +			goto free; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | -	/* after this point (skb is modified) we cannot return false */ | ||||||
|  | +	skb_mark_not_on_list(skb); | ||||||
|  | +	return skb; | ||||||
|  | + | ||||||
|  | +free: | ||||||
|  | +	kfree_skb(skb); | ||||||
|  | +	return NULL; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static void __ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata, | ||||||
|  | +				  struct sta_info *sta, | ||||||
|  | +				  struct ieee80211_fast_tx *fast_tx, | ||||||
|  | +				  struct sk_buff *skb, u8 tid, bool ampdu) | ||||||
|  | +{ | ||||||
|  | +	struct ieee80211_local *local = sdata->local; | ||||||
|  | +	struct ieee80211_hdr *hdr = (void *)fast_tx->hdr; | ||||||
|  | +	struct ieee80211_tx_info *info; | ||||||
|  | +	struct ieee80211_tx_data tx; | ||||||
|  | +	ieee80211_tx_result r; | ||||||
|  | +	int hw_headroom = sdata->local->hw.extra_tx_headroom; | ||||||
|  | +	int extra_head = fast_tx->hdr_len - (ETH_HLEN - 2); | ||||||
|  | +	struct ethhdr eth; | ||||||
|  |   | ||||||
|  |  	if (skb_shared(skb)) { | ||||||
|  |  		struct sk_buff *tmp_skb = skb; | ||||||
|  | @@ -3548,12 +3576,12 @@ static bool ieee80211_xmit_fast(struct i | ||||||
|  |  		kfree_skb(tmp_skb); | ||||||
|  |   | ||||||
|  |  		if (!skb) | ||||||
|  | -			return true; | ||||||
|  | +			return; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  |  	if ((hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) && | ||||||
|  |  	    ieee80211_amsdu_aggregate(sdata, sta, fast_tx, skb)) | ||||||
|  | -		return true; | ||||||
|  | +		return; | ||||||
|  |   | ||||||
|  |  	/* will not be crypto-handled beyond what we do here, so use false | ||||||
|  |  	 * as the may-encrypt argument for the resize to not account for | ||||||
|  | @@ -3562,10 +3590,8 @@ static bool ieee80211_xmit_fast(struct i | ||||||
|  |  	if (unlikely(ieee80211_skb_resize(sdata, skb, | ||||||
|  |  					  max_t(int, extra_head + hw_headroom - | ||||||
|  |  						     skb_headroom(skb), 0), | ||||||
|  | -					  ENCRYPT_NO))) { | ||||||
|  | -		kfree_skb(skb); | ||||||
|  | -		return true; | ||||||
|  | -	} | ||||||
|  | +					  ENCRYPT_NO))) | ||||||
|  | +		goto free; | ||||||
|  |   | ||||||
|  |  	memcpy(ð, skb->data, ETH_HLEN - 2); | ||||||
|  |  	hdr = skb_push(skb, extra_head); | ||||||
|  | @@ -3579,7 +3605,7 @@ static bool ieee80211_xmit_fast(struct i | ||||||
|  |  	info->control.vif = &sdata->vif; | ||||||
|  |  	info->flags = IEEE80211_TX_CTL_FIRST_FRAGMENT | | ||||||
|  |  		      IEEE80211_TX_CTL_DONTFRAG | | ||||||
|  | -		      (tid_tx ? IEEE80211_TX_CTL_AMPDU : 0); | ||||||
|  | +		      (ampdu ? IEEE80211_TX_CTL_AMPDU : 0); | ||||||
|  |  	info->control.flags = IEEE80211_TX_CTRL_FAST_XMIT; | ||||||
|  |   | ||||||
|  |  #ifdef CPTCFG_MAC80211_DEBUGFS | ||||||
|  | @@ -3601,16 +3627,14 @@ static bool ieee80211_xmit_fast(struct i | ||||||
|  |  	tx.key = fast_tx->key; | ||||||
|  |   | ||||||
|  |  	if (ieee80211_queue_skb(local, sdata, sta, skb)) | ||||||
|  | -		return true; | ||||||
|  | +		return; | ||||||
|  |   | ||||||
|  |  	tx.skb = skb; | ||||||
|  |  	r = ieee80211_xmit_fast_finish(sdata, sta, fast_tx->pn_offs, | ||||||
|  |  				       fast_tx->key, &tx); | ||||||
|  |  	tx.skb = NULL; | ||||||
|  | -	if (r == TX_DROP) { | ||||||
|  | -		kfree_skb(skb); | ||||||
|  | -		return true; | ||||||
|  | -	} | ||||||
|  | +	if (r == TX_DROP) | ||||||
|  | +		goto free; | ||||||
|  |   | ||||||
|  |  	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) | ||||||
|  |  		sdata = container_of(sdata->bss, | ||||||
|  | @@ -3618,6 +3642,55 @@ static bool ieee80211_xmit_fast(struct i | ||||||
|  |   | ||||||
|  |  	__skb_queue_tail(&tx.skbs, skb); | ||||||
|  |  	ieee80211_tx_frags(local, &sdata->vif, sta, &tx.skbs, false); | ||||||
|  | +	return; | ||||||
|  | + | ||||||
|  | +free: | ||||||
|  | +	kfree_skb(skb); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata, | ||||||
|  | +				struct sta_info *sta, | ||||||
|  | +				struct ieee80211_fast_tx *fast_tx, | ||||||
|  | +				struct sk_buff *skb) | ||||||
|  | +{ | ||||||
|  | +	u16 ethertype = (skb->data[12] << 8) | skb->data[13]; | ||||||
|  | +	struct ieee80211_hdr *hdr = (void *)fast_tx->hdr; | ||||||
|  | +	struct tid_ampdu_tx *tid_tx = NULL; | ||||||
|  | +	struct sk_buff *next; | ||||||
|  | +	u8 tid = IEEE80211_NUM_TIDS; | ||||||
|  | + | ||||||
|  | +	/* control port protocol needs a lot of special handling */ | ||||||
|  | +	if (cpu_to_be16(ethertype) == sdata->control_port_protocol) | ||||||
|  | +		return false; | ||||||
|  | + | ||||||
|  | +	/* only RFC 1042 SNAP */ | ||||||
|  | +	if (ethertype < ETH_P_802_3_MIN) | ||||||
|  | +		return false; | ||||||
|  | + | ||||||
|  | +	/* don't handle TX status request here either */ | ||||||
|  | +	if (skb->sk && skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS) | ||||||
|  | +		return false; | ||||||
|  | + | ||||||
|  | +	if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) { | ||||||
|  | +		tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; | ||||||
|  | +		tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]); | ||||||
|  | +		if (tid_tx) { | ||||||
|  | +			if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) | ||||||
|  | +				return false; | ||||||
|  | +			if (tid_tx->timeout) | ||||||
|  | +				tid_tx->last_tx = jiffies; | ||||||
|  | +		} | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	skb = ieee80211_tx_skb_fixup(skb, ieee80211_sdata_netdev_features(sdata)); | ||||||
|  | +	if (!skb) | ||||||
|  | +		return true; | ||||||
|  | + | ||||||
|  | +	skb_list_walk_safe(skb, skb, next) { | ||||||
|  | +		skb_mark_not_on_list(skb); | ||||||
|  | +		__ieee80211_xmit_fast(sdata, sta, fast_tx, skb, tid, tid_tx); | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  |  	return true; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | @@ -4123,31 +4196,14 @@ void __ieee80211_subif_start_xmit(struct | ||||||
|  |  			goto out; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | -	if (skb_is_gso(skb)) { | ||||||
|  | -		struct sk_buff *segs; | ||||||
|  | - | ||||||
|  | -		segs = skb_gso_segment(skb, 0); | ||||||
|  | -		if (IS_ERR(segs)) { | ||||||
|  | -			goto out_free; | ||||||
|  | -		} else if (segs) { | ||||||
|  | -			consume_skb(skb); | ||||||
|  | -			skb = segs; | ||||||
|  | -		} | ||||||
|  | -	} else { | ||||||
|  | -		/* we cannot process non-linear frames on this path */ | ||||||
|  | -		if (skb_linearize(skb)) | ||||||
|  | -			goto out_free; | ||||||
|  | - | ||||||
|  | -		/* the frame could be fragmented, software-encrypted, and other | ||||||
|  | -		 * things so we cannot really handle checksum offload with it - | ||||||
|  | -		 * fix it up in software before we handle anything else. | ||||||
|  | -		 */ | ||||||
|  | -		if (skb->ip_summed == CHECKSUM_PARTIAL) { | ||||||
|  | -			skb_set_transport_header(skb, | ||||||
|  | -						 skb_checksum_start_offset(skb)); | ||||||
|  | -			if (skb_checksum_help(skb)) | ||||||
|  | -				goto out_free; | ||||||
|  | -		} | ||||||
|  | +	/* the frame could be fragmented, software-encrypted, and other | ||||||
|  | +	 * things so we cannot really handle checksum or GSO offload. | ||||||
|  | +	 * fix it up in software before we handle anything else. | ||||||
|  | +	 */ | ||||||
|  | +	skb = ieee80211_tx_skb_fixup(skb, 0); | ||||||
|  | +	if (!skb) { | ||||||
|  | +		len = 0; | ||||||
|  | +		goto out; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  |  	skb_list_walk_safe(skb, skb, next) { | ||||||
|  | @@ -4310,9 +4366,11 @@ netdev_tx_t ieee80211_subif_start_xmit(s | ||||||
|  |  	return NETDEV_TX_OK; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata, | ||||||
|  | -			      struct sk_buff *skb, struct sta_info *sta, | ||||||
|  | -			      bool txpending) | ||||||
|  | + | ||||||
|  | + | ||||||
|  | +static bool __ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata, | ||||||
|  | +				struct sk_buff *skb, struct sta_info *sta, | ||||||
|  | +				bool txpending) | ||||||
|  |  { | ||||||
|  |  	struct ieee80211_local *local = sdata->local; | ||||||
|  |  	struct ieee80211_tx_control control = {}; | ||||||
|  | @@ -4321,14 +4379,6 @@ static bool ieee80211_tx_8023(struct iee | ||||||
|  |  	unsigned long flags; | ||||||
|  |  	int q = info->hw_queue; | ||||||
|  |   | ||||||
|  | -	if (sta) | ||||||
|  | -		sk_pacing_shift_update(skb->sk, local->hw.tx_sk_pacing_shift); | ||||||
|  | - | ||||||
|  | -	ieee80211_tpt_led_trig_tx(local, skb->len); | ||||||
|  | - | ||||||
|  | -	if (ieee80211_queue_skb(local, sdata, sta, skb)) | ||||||
|  | -		return true; | ||||||
|  | - | ||||||
|  |  	spin_lock_irqsave(&local->queue_stop_reason_lock, flags); | ||||||
|  |   | ||||||
|  |  	if (local->queue_stop_reasons[q] || | ||||||
|  | @@ -4355,27 +4405,50 @@ static bool ieee80211_tx_8023(struct iee | ||||||
|  |  	return true; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | +static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata, | ||||||
|  | +			      struct sk_buff *skb, struct sta_info *sta, | ||||||
|  | +			      bool txpending) | ||||||
|  | +{ | ||||||
|  | +	struct ieee80211_local *local = sdata->local; | ||||||
|  | +	struct sk_buff *next; | ||||||
|  | +	bool ret = true; | ||||||
|  | + | ||||||
|  | +	if (ieee80211_queue_skb(local, sdata, sta, skb)) | ||||||
|  | +		return true; | ||||||
|  | + | ||||||
|  | +	skb_list_walk_safe(skb, skb, next) { | ||||||
|  | +		skb_mark_not_on_list(skb); | ||||||
|  | +		if (!__ieee80211_tx_8023(sdata, skb, sta, txpending)) | ||||||
|  | +			ret = false; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	return ret; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  |  static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, | ||||||
|  |  				struct net_device *dev, struct sta_info *sta, | ||||||
|  |  				struct ieee80211_key *key, struct sk_buff *skb) | ||||||
|  |  { | ||||||
|  | -	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||||||
|  | +	struct ieee80211_tx_info *info; | ||||||
|  |  	struct ieee80211_local *local = sdata->local; | ||||||
|  |  	struct tid_ampdu_tx *tid_tx; | ||||||
|  | +	struct sk_buff *seg, *next; | ||||||
|  | +	unsigned int skbs = 0, len = 0; | ||||||
|  | +	u16 queue; | ||||||
|  |  	u8 tid; | ||||||
|  |   | ||||||
|  |  	if (local->ops->wake_tx_queue) { | ||||||
|  | -		u16 queue = __ieee80211_select_queue(sdata, sta, skb); | ||||||
|  | +		queue = __ieee80211_select_queue(sdata, sta, skb); | ||||||
|  |  		skb_set_queue_mapping(skb, queue); | ||||||
|  |  		skb_get_hash(skb); | ||||||
|  | +	} else { | ||||||
|  | +		queue = skb_get_queue_mapping(skb); | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  |  	if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)) && | ||||||
|  |  	    test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) | ||||||
|  |  		goto out_free; | ||||||
|  |   | ||||||
|  | -	memset(info, 0, sizeof(*info)); | ||||||
|  | - | ||||||
|  |  	ieee80211_aggr_check(sdata, sta, skb); | ||||||
|  |   | ||||||
|  |  	tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; | ||||||
|  | @@ -4387,22 +4460,20 @@ static void ieee80211_8023_xmit(struct i | ||||||
|  |  			return; | ||||||
|  |  		} | ||||||
|  |   | ||||||
|  | -		info->flags |= IEEE80211_TX_CTL_AMPDU; | ||||||
|  |  		if (tid_tx->timeout) | ||||||
|  |  			tid_tx->last_tx = jiffies; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | -	if (unlikely(skb->sk && | ||||||
|  | -		     skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) | ||||||
|  | -		info->ack_frame_id = ieee80211_store_ack_skb(local, skb, | ||||||
|  | -							     &info->flags, NULL); | ||||||
|  | +	skb = ieee80211_tx_skb_fixup(skb, ieee80211_sdata_netdev_features(sdata)); | ||||||
|  | +	if (!skb) | ||||||
|  | +		return; | ||||||
|  |   | ||||||
|  | -	info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)]; | ||||||
|  | +	info = IEEE80211_SKB_CB(skb); | ||||||
|  | +	memset(info, 0, sizeof(*info)); | ||||||
|  | +	if (tid_tx) | ||||||
|  | +		info->flags |= IEEE80211_TX_CTL_AMPDU; | ||||||
|  |   | ||||||
|  | -	dev_sw_netstats_tx_add(dev, 1, skb->len); | ||||||
|  | - | ||||||
|  | -	sta->tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len; | ||||||
|  | -	sta->tx_stats.packets[skb_get_queue_mapping(skb)]++; | ||||||
|  | +	info->hw_queue = sdata->vif.hw_queue[queue]; | ||||||
|  |   | ||||||
|  |  	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) | ||||||
|  |  		sdata = container_of(sdata->bss, | ||||||
|  | @@ -4414,6 +4485,24 @@ static void ieee80211_8023_xmit(struct i | ||||||
|  |  	if (key) | ||||||
|  |  		info->control.hw_key = &key->conf; | ||||||
|  |   | ||||||
|  | +	skb_list_walk_safe(skb, seg, next) { | ||||||
|  | +		skbs++; | ||||||
|  | +		len += seg->len; | ||||||
|  | +		if (seg != skb) | ||||||
|  | +			memcpy(IEEE80211_SKB_CB(seg), info, sizeof(*info)); | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	if (unlikely(skb->sk && | ||||||
|  | +		     skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) | ||||||
|  | +		info->ack_frame_id = ieee80211_store_ack_skb(local, skb, | ||||||
|  | +							     &info->flags, NULL); | ||||||
|  | + | ||||||
|  | +	dev_sw_netstats_tx_add(dev, skbs, len); | ||||||
|  | +	sta->tx_stats.packets[queue] += skbs; | ||||||
|  | +	sta->tx_stats.bytes[queue] += len; | ||||||
|  | + | ||||||
|  | +	ieee80211_tpt_led_trig_tx(local, len); | ||||||
|  | + | ||||||
|  |  	ieee80211_tx_8023(sdata, skb, sta, false); | ||||||
|  |   | ||||||
|  |  	return; | ||||||
|  | @@ -4455,6 +4544,7 @@ netdev_tx_t ieee80211_subif_start_xmit_8 | ||||||
|  |  		    key->conf.cipher == WLAN_CIPHER_SUITE_TKIP)) | ||||||
|  |  		goto skip_offload; | ||||||
|  |   | ||||||
|  | +	sk_pacing_shift_update(skb->sk, sdata->local->hw.tx_sk_pacing_shift); | ||||||
|  |  	ieee80211_8023_xmit(sdata, dev, sta, key, skb); | ||||||
|  |  	goto out; | ||||||
|  |   | ||||||
		Reference in New Issue
	
	Block a user
	 Felix Fietkau
					Felix Fietkau