void __ieee80211_subif_start_xmit(struct sk_buff *skb,
				  struct net_device *dev,
				  u32 info_flags,
				  u32 ctrl_flags,
				  u64 *cookie)
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	struct ieee80211_local *local = sdata->local;
	struct sta_info *sta;
	struct sk_buff *next;

	if (unlikely(skb->len < ETH_HLEN)) {
		kfree_skb(skb);
		return;
	}

	rcu_read_lock();

	if (ieee80211_lookup_ra_sta(sdata, skb, &sta))
		goto out_free;

	if (IS_ERR(sta))
		sta = NULL;

	if (local->ops->wake_tx_queue) {
		u16 queue = __ieee80211_select_queue(sdata, sta, skb);
		skb_set_queue_mapping(skb, queue);
		skb_get_hash(skb);
	}

	if (sta) {
		struct ieee80211_fast_tx *fast_tx;

		sk_pacing_shift_update(skb->sk, sdata->local->hw.tx_sk_pacing_shift);

		fast_tx = rcu_dereference(sta->fast_tx);

		if (fast_tx &&
		    ieee80211_xmit_fast(sdata, sta, fast_tx, skb))
			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)) {
			kfree_skb(skb);
			goto out;
		}

		/* 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;
		}
	}

	skb_list_walk_safe(skb, skb, next) {
		skb_mark_not_on_list(skb);

		if (skb->protocol == sdata->control_port_protocol)
			ctrl_flags |= IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP;

		skb = ieee80211_build_hdr(sdata, skb, info_flags,
					  sta, ctrl_flags, cookie);
		if (IS_ERR(skb)) {
			kfree_skb_list(next);
			goto out;
		}

		dev_sw_netstats_tx_add(dev, 1, skb->len);

		ieee80211_xmit(sdata, sta, skb);
	}
	goto out;
 out_free:
	kfree_skb(skb);
 out:
	rcu_read_unlock();
}
void __ieee80211_subif_start_xmit(struct sk_buff *skb,
				  struct net_device *dev,
				  u32 info_flags,
				  u32 ctrl_flags,
				  u64 *cookie)
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	struct ieee80211_local *local = sdata->local;
	struct sta_info *sta;
	struct sk_buff *next;

	if (unlikely(skb->len < ETH_HLEN)) {
		kfree_skb(skb);
		return;
	}

	rcu_read_lock();

	if (ieee80211_lookup_ra_sta(sdata, skb, &sta))
		goto out_free;

	if (IS_ERR(sta))
		sta = NULL;

	if (local->ops->wake_tx_queue) {
		u16 queue = __ieee80211_select_queue(sdata, sta, skb);
		skb_set_queue_mapping(skb, queue);
		skb_get_hash(skb);
	}

	if (sta) {
		struct ieee80211_fast_tx *fast_tx;

		sk_pacing_shift_update(skb->sk, sdata->local->hw.tx_sk_pacing_shift);

		fast_tx = rcu_dereference(sta->fast_tx);

		if (fast_tx &&
		    ieee80211_xmit_fast(sdata, sta, fast_tx, skb))
			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 {
		 
		if (skb_linearize(skb)) {
			kfree_skb(skb);
			goto out;
		}

		 
		if (skb->ip_summed == CHECKSUM_PARTIAL) {
			skb_set_transport_header(skb,
						 skb_checksum_start_offset(skb));
			if (skb_checksum_help(skb))
				goto out_free;
		}
	}

	skb_list_walk_safe(skb, skb, next) {
		skb_mark_not_on_list(skb);

		if (skb->protocol == sdata->control_port_protocol)
			ctrl_flags |= IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP;

		skb = ieee80211_build_hdr(sdata, skb, info_flags,
					  sta, ctrl_flags, cookie);
		if (IS_ERR(skb)) {
			kfree_skb_list(next);
			goto out;
		}

		dev_sw_netstats_tx_add(dev, 1, skb->len);

		ieee80211_xmit(sdata, sta, skb);
	}
	goto out;
 out_free:
	kfree_skb(skb);
 out:
	rcu_read_unlock();
}