lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Wed, 07 May 2008 13:22:19 +0200
From:	Johannes Berg <johannes@...solutions.net>
To:	Herbert Xu <herbert@...dor.apana.org.au>
Cc:	linux-wireless@...r.kernel.org, netdev@...r.kernel.org,
	Ron Rindjunsky <ron.rindjunsky@...el.com>,
	Tomas Winkler <tomasw@...il.com>,
	Ivo van Doorn <ivdoorn@...il.com>,
	Peter P Waskiewicz Jr <peter.p.waskiewicz.jr@...el.com>
Subject: [PATCH] mac80211: rewrite fragmentation code

This patch rewrites mac80211's fragmentation handling to
 (a) use skb_segment
 (b) get rid of the tasklet etc. and just push responsibility
     to the qdisc/core code

This can result in excessive requeues when the device only
accepts a fragment at a time or so then we'll always ask the
generic code to try again but then won't accept the skb.
However, devices that only accept a single skb at a time won't
get good performance anyhow so it doesn't matter.

Signed-off-by: Johannes Berg <johannes@...solutions.net>
---
John, this has no more dependencies on other patches except ones I
submitted earlier, please merge unless somebody objects.

 include/net/mac80211.h     |   10 
 net/mac80211/ieee80211_i.h |   32 --
 net/mac80211/main.c        |   46 ++-
 net/mac80211/tx.c          |  629 ++++++++++++++++++++++-----------------------
 net/mac80211/util.c        |   30 --
 net/mac80211/wep.c         |   26 -
 net/mac80211/wme.c         |    9 
 net/mac80211/wpa.c         |   44 +--
 8 files changed, 395 insertions(+), 431 deletions(-)

--- everything.orig/net/mac80211/ieee80211_i.h	2008-05-07 11:13:00.000000000 +0200
+++ everything/net/mac80211/ieee80211_i.h	2008-05-07 11:49:11.000000000 +0200
@@ -147,7 +147,6 @@ typedef unsigned __bitwise__ ieee80211_t
 #define IEEE80211_TX_UNICAST		BIT(1)
 #define IEEE80211_TX_PS_BUFFERED	BIT(2)
 #define IEEE80211_TX_PROBE_LAST_FRAG	BIT(3)
-#define IEEE80211_TX_INJECTED		BIT(4)
 
 struct ieee80211_tx_data {
 	struct sk_buff *skb;
@@ -165,11 +164,6 @@ struct ieee80211_tx_data {
 	 * when using CTS protection with IEEE 802.11g. */
 	s8 last_frag_rate_idx;
 
-	/* Extra fragments (in addition to the first fragment
-	 * in skb) */
-	struct sk_buff **extra_frag;
-	int num_extra_frag;
-
 	u16 fc, ethertype;
 	unsigned int flags;
 };
@@ -215,19 +209,10 @@ struct ieee80211_rx_data {
 #define IEEE80211_TXPD_AMPDU		BIT(4)
 /* Stored in sk_buff->cb */
 struct ieee80211_tx_packet_data {
+	u32 flags;
 	int ifindex;
+	u16 queue;
 	unsigned long jiffies;
-	unsigned int flags;
-	u8 queue;
-};
-
-struct ieee80211_tx_stored_packet {
-	struct ieee80211_tx_control control;
-	struct sk_buff *skb;
-	struct sk_buff **extra_frag;
-	s8 last_frag_rate_idx;
-	int num_extra_frag;
-	bool last_frag_rate_ctrl_probe;
 };
 
 struct beacon_data {
@@ -612,8 +597,7 @@ struct ieee80211_local {
 	struct timer_list sta_cleanup;
 
 	unsigned long state[IEEE80211_MAX_QUEUES + IEEE80211_MAX_AMPDU_QUEUES];
-	struct ieee80211_tx_stored_packet pending_packet[IEEE80211_MAX_QUEUES + IEEE80211_MAX_AMPDU_QUEUES];
-	struct tasklet_struct tx_pending_tasklet;
+	struct sk_buff *pending_packet[IEEE80211_MAX_QUEUES];
 
 	/* number of interfaces with corresponding IFF_ flags */
 	atomic_t iff_allmultis, iff_promiscs;
@@ -846,7 +830,6 @@ static inline struct ieee80211_hw *local
 
 enum ieee80211_link_state_t {
 	IEEE80211_LINK_STATE_XOFF = 0,
-	IEEE80211_LINK_STATE_PENDING,
 };
 
 struct sta_attribute {
@@ -866,7 +849,6 @@ static inline int ieee80211_bssid_match(
 int ieee80211_hw_config(struct ieee80211_local *local);
 int ieee80211_if_config(struct net_device *dev);
 int ieee80211_if_config_beacon(struct net_device *dev);
-void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx);
 void ieee80211_if_setup(struct net_device *dev);
 u32 ieee80211_handle_ht(struct ieee80211_local *local, int enable_ht,
 			struct ieee80211_ht_info *req_ht_cap,
@@ -967,8 +949,6 @@ void ieee80211_if_free(struct net_device
 void ieee80211_if_sdata_init(struct ieee80211_sub_if_data *sdata);
 
 /* tx handling */
-void ieee80211_clear_tx_pending(struct ieee80211_local *local);
-void ieee80211_tx_pending(unsigned long data);
 int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev);
 int ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev);
 int ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev);
@@ -984,4 +964,10 @@ int ieee80211_frame_duration(struct ieee
 void mac80211_ev_michael_mic_failure(struct net_device *dev, int keyidx,
 				     struct ieee80211_hdr *hdr);
 
+static inline void ieee80211_skb_set_protected(struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+	hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+}
+
 #endif /* IEEE80211_I_H */
--- everything.orig/net/mac80211/tx.c	2008-05-07 11:13:00.000000000 +0200
+++ everything/net/mac80211/tx.c	2008-05-07 13:02:02.000000000 +0200
@@ -2,7 +2,7 @@
  * Copyright 2002-2005, Instant802 Networks, Inc.
  * Copyright 2005-2006, Devicescape Software, Inc.
  * Copyright 2006-2007	Jiri Benc <jbenc@...e.cz>
- * Copyright 2007	Johannes Berg <johannes@...solutions.net>
+ * Copyright 2007-2008	Johannes Berg <johannes@...solutions.net>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -32,9 +32,6 @@
 #include "wme.h"
 #include "rate.h"
 
-#define IEEE80211_TX_OK		0
-#define IEEE80211_TX_AGAIN	1
-#define IEEE80211_TX_FRAG_AGAIN	2
 
 /* misc utils */
 
@@ -219,12 +216,6 @@ static inline int __ieee80211_queue_stop
 	return test_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]);
 }
 
-static inline int __ieee80211_queue_pending(const struct ieee80211_local *local,
-					    int queue)
-{
-	return test_bit(IEEE80211_LINK_STATE_PENDING, &local->state[queue]);
-}
-
 static int inline is_ieee80211_device(struct net_device *dev,
 				      struct net_device *master)
 {
@@ -243,7 +234,7 @@ ieee80211_tx_h_check_assoc(struct ieee80
 #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
 	u32 sta_flags;
 
-	if (unlikely(tx->flags & IEEE80211_TX_INJECTED))
+	if (unlikely(tx->control->flags & IEEE80211_TXCTL_INJECTED))
 		return TX_CONTINUE;
 
 	if (unlikely(tx->local->sta_sw_scanning) &&
@@ -457,46 +448,66 @@ ieee80211_tx_h_ps_buf(struct ieee80211_t
 		return ieee80211_tx_h_multicast_ps_buf(tx);
 }
 
+static struct ieee80211_key *
+ieee80211_select_key(struct ieee80211_sub_if_data *sdata,
+		     struct sta_info *sta,
+		     struct ieee80211_tx_control *control,
+		     u16 fc)
+{
+	struct ieee80211_key *key, *result;
+	u16 ftype, stype;
+
+	if (unlikely(control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT))
+		result = NULL;
+	else if (sta && (key = rcu_dereference(sta->key)))
+		result = key;
+	else if ((key = rcu_dereference(sdata->default_key)))
+		result = key;
+	else
+		result = NULL;
+
+	if (!result)
+		return NULL;
+
+	switch (result->conf.alg) {
+	case ALG_WEP:
+		ftype = fc & IEEE80211_FCTL_FTYPE;
+		stype = fc & IEEE80211_FCTL_STYPE;
+
+		if (ftype == IEEE80211_FTYPE_MGMT &&
+		    stype == IEEE80211_STYPE_AUTH)
+			break;
+	case ALG_TKIP:
+	case ALG_CCMP:
+		if (!WLAN_FC_DATA_PRESENT(fc))
+			result = NULL;
+		break;
+	}
+
+	return result;
+}
+
 static ieee80211_tx_result
 ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
 {
-	struct ieee80211_key *key;
 	u16 fc = tx->fc;
 
 	if (unlikely(tx->control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT))
-		tx->key = NULL;
-	else if (tx->sta && (key = rcu_dereference(tx->sta->key)))
-		tx->key = key;
-	else if ((key = rcu_dereference(tx->sdata->default_key)))
-		tx->key = key;
-	else if (tx->sdata->drop_unencrypted &&
-		 !(tx->control->flags & IEEE80211_TXCTL_EAPOL_FRAME) &&
-		 !(tx->flags & IEEE80211_TX_INJECTED)) {
+		return TX_CONTINUE;
+
+	tx->key = ieee80211_select_key(tx->sdata, tx->sta, tx->control, fc);
+
+	if (!tx->key &&
+	    tx->sdata->drop_unencrypted &&
+	    !(tx->control->flags & IEEE80211_TXCTL_EAPOL_FRAME) &&
+	    !(tx->control->flags & IEEE80211_TXCTL_INJECTED)) {
 		I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted);
 		return TX_DROP;
-	} else
-		tx->key = NULL;
+	}
 
 	if (tx->key) {
-		u16 ftype, stype;
-
 		tx->key->tx_rx_count++;
 		/* TODO: add threshold stuff again */
-
-		switch (tx->key->conf.alg) {
-		case ALG_WEP:
-			ftype = fc & IEEE80211_FCTL_FTYPE;
-			stype = fc & IEEE80211_FCTL_STYPE;
-
-			if (ftype == IEEE80211_FTYPE_MGMT &&
-			    stype == IEEE80211_STYPE_AUTH)
-				break;
-		case ALG_TKIP:
-		case ALG_CCMP:
-			if (!WLAN_FC_DATA_PRESENT(fc))
-				tx->key = NULL;
-			break;
-		}
 	}
 
 	if (!tx->key || !(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
@@ -508,81 +519,69 @@ ieee80211_tx_h_select_key(struct ieee802
 static ieee80211_tx_result
 ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
 {
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
-	size_t hdrlen, per_fragm, num_fragm, payload_len, left;
-	struct sk_buff **frags, *first, *frag;
-	int i;
-	u16 seq;
-	u8 *pos;
 	int frag_threshold = tx->local->fragmentation_threshold;
+	int hdrlen, payload_len, per_fragm, num_fragm;
+	struct sk_buff *skb = tx->skb, *frags;
+	int fragnum = 0;
 
 	if (!(tx->flags & IEEE80211_TX_FRAGMENTED))
 		return TX_CONTINUE;
 
-	first = tx->skb;
+	if (WARN_ON(tx->control->flags & IEEE80211_TXCTL_AMPDU ||
+		    tx->control->queue >= tx->local->hw.queues))
+		return TX_DROP;
 
 	hdrlen = ieee80211_get_hdrlen(tx->fc);
-	payload_len = first->len - hdrlen;
+	payload_len = skb->len - hdrlen;
 	per_fragm = frag_threshold - hdrlen - FCS_LEN;
 	num_fragm = DIV_ROUND_UP(payload_len, per_fragm);
 
-	frags = kzalloc(num_fragm * sizeof(struct sk_buff *), GFP_ATOMIC);
-	if (!frags)
-		goto fail;
+	if (WARN_ON(num_fragm <= 1))
+		return TX_DROP;
 
-	hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREFRAGS);
-	seq = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ;
-	pos = first->data + hdrlen + per_fragm;
-	left = payload_len - per_fragm;
-	for (i = 0; i < num_fragm - 1; i++) {
-		struct ieee80211_hdr *fhdr;
-		size_t copylen;
+	/* skb_segment will push the mac header back in */
+	skb_reset_mac_header(skb);
+	__skb_pull(skb, hdrlen);
 
-		if (left <= 0)
-			goto fail;
+	skb_shinfo(skb)->gso_size = per_fragm;
+	skb_shinfo(skb)->nr_frags = 0;
 
-		/* reserve enough extra head and tail room for possible
-		 * encryption */
-		frag = frags[i] =
-			dev_alloc_skb(tx->local->tx_headroom +
-				      frag_threshold +
-				      IEEE80211_ENCRYPT_HEADROOM +
-				      IEEE80211_ENCRYPT_TAILROOM);
-		if (!frag)
-			goto fail;
-		/* Make sure that all fragments use the same priority so
-		 * that they end up using the same TX queue */
-		frag->priority = first->priority;
-		skb_reserve(frag, tx->local->tx_headroom +
-				  IEEE80211_ENCRYPT_HEADROOM);
-		fhdr = (struct ieee80211_hdr *) skb_put(frag, hdrlen);
-		memcpy(fhdr, first->data, hdrlen);
-		if (i == num_fragm - 2)
-			fhdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREFRAGS);
-		fhdr->seq_ctrl = cpu_to_le16(seq | ((i + 1) & IEEE80211_SCTL_FRAG));
-		copylen = left > per_fragm ? per_fragm : left;
-		memcpy(skb_put(frag, copylen), pos, copylen);
+	/*
+	 * Now segment (fragment) the frame. This will allocate all the
+	 * segments, we then assign them to the tx->skb->next pointer.
+	 * During transmission, we will remove the successfully transmitted
+	 * fragments from this list. Iff the low-level driver rejects one
+	 * of the fragments then we will simply pretend to accept the skb
+	 * but store it away in our fragment list and use it preferentially
+	 * when the next skb comes in.
+	 */
+	frags = skb_segment(skb, 0);
+	if (IS_ERR(frags))
+		return TX_DROP;
 
-		pos += copylen;
-		left -= copylen;
-	}
-	skb_trim(first, hdrlen + per_fragm);
+	skb->next = frags;
 
-	tx->num_extra_frag = num_fragm - 1;
-	tx->extra_frag = frags;
+	/* update duration/seq/flags of fragments, not on original */
+	while ((skb = skb->next)) {
+		struct ieee80211_hdr *hdr = (void *)skb->data;
+		int next_len, dur;
+		__le16 morefrags = cpu_to_le16(IEEE80211_FCTL_MOREFRAGS);
+
+		if (skb->next) {
+			hdr->frame_control |= morefrags;
+			next_len = skb->next->len;
+		} else {
+			hdr->frame_control &= ~morefrags;
+			next_len = 0;
+			tx->rate_idx = tx->last_frag_rate_idx;
+		}
+		dur = ieee80211_duration(tx, 0, next_len);
+		hdr->duration_id = cpu_to_le16(dur);
+		hdr->seq_ctrl |= cpu_to_le16(fragnum & IEEE80211_SCTL_FRAG);
+		fragnum++;
+	}
 
 	return TX_CONTINUE;
-
- fail:
-	printk(KERN_DEBUG "%s: failed to fragment frame\n", tx->dev->name);
-	if (frags) {
-		for (i = 0; i < num_fragm - 1; i++)
-			if (frags[i])
-				dev_kfree_skb(frags[i]);
-		kfree(frags);
-	}
-	I802_DEBUG_INC(tx->local->tx_handlers_drop_fragment);
-	return TX_DROP;
 }
 
 static ieee80211_tx_result
@@ -712,8 +711,7 @@ ieee80211_tx_h_misc(struct ieee80211_tx_
 	 * for remaining fragments will be updated when they are being sent
 	 * to low-level driver in ieee80211_tx(). */
 	dur = ieee80211_duration(tx, is_multicast_ether_addr(hdr->addr1),
-				 (tx->flags & IEEE80211_TX_FRAGMENTED) ?
-				 tx->extra_frag[0]->len : 0);
+				 tx->skb->next ? tx->skb->next->len : 0);
 	hdr->duration_id = cpu_to_le16(dur);
 
 	if ((control->flags & IEEE80211_TXCTL_USE_RTS_CTS) ||
@@ -748,18 +746,16 @@ ieee80211_tx_h_misc(struct ieee80211_tx_
 	}
 
 	if (tx->sta) {
+		struct sk_buff *skb = tx->skb;
+
 		control->aid = tx->sta->aid;
 		tx->sta->tx_packets++;
-		tx->sta->tx_fragments++;
-		tx->sta->tx_bytes += tx->skb->len;
-		if (tx->extra_frag) {
-			int i;
-			tx->sta->tx_fragments += tx->num_extra_frag;
-			for (i = 0; i < tx->num_extra_frag; i++) {
-				tx->sta->tx_bytes +=
-					tx->extra_frag[i]->len;
-			}
-		}
+		if (skb->next)
+			skb = skb->next;
+		do {
+			tx->sta->tx_fragments++;
+			tx->sta->tx_bytes += skb->len;
+		} while ((skb = skb->next));
 	}
 
 	return TX_CONTINUE;
@@ -804,15 +800,12 @@ ieee80211_tx_h_load_stats(struct ieee802
 		load += hdrtime;
 
 	/* TODO: optimise again */
-	load += skb->len * CHAN_UTIL_RATE_LCM / rate->bitrate;
+	if (!skb->next)
+		load += skb->len * CHAN_UTIL_RATE_LCM / rate->bitrate;
 
-	if (tx->extra_frag) {
-		int i;
-		for (i = 0; i < tx->num_extra_frag; i++) {
-			load += 2 * hdrtime;
-			load += tx->extra_frag[i]->len *
-				rate->bitrate;
-		}
+	while ((skb = skb->next)) {
+		load += 2 * hdrtime;
+		load += skb->len * CHAN_UTIL_RATE_LCM / rate->bitrate;
 	}
 
 	/* Divide channel_use by 8 to avoid wrapping around the counter */
@@ -870,7 +863,7 @@ __ieee80211_parse_tx_radiotap(struct iee
 	sband = tx->local->hw.wiphy->bands[tx->channel->band];
 
 	control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
-	tx->flags |= IEEE80211_TX_INJECTED;
+	control->flags |= IEEE80211_TXCTL_INJECTED;
 	tx->flags &= ~IEEE80211_TX_FRAGMENTED;
 
 	/*
@@ -1074,63 +1067,106 @@ static int ieee80211_tx_prepare(struct i
 	return 0;
 }
 
+static int ___ieee80211_tx(struct ieee80211_local *local,
+			   struct sk_buff *skb,
+			   struct ieee80211_tx_control *control)
+{
+	int ret;
+
+	if (unlikely(!skb))
+		return NETDEV_TX_OK;
+
+	if (unlikely(netif_queue_stopped(local->mdev) ||
+		     __ieee80211_queue_stopped(local, control->queue)))
+		return NETDEV_TX_BUSY;
+
+	ieee80211_dump_frame(wiphy_name(local->hw.wiphy),
+			     "TX to low-level driver", skb);
+	ret = local->ops->tx(local_to_hw(local), skb, control);
+	if (ret)
+		return NETDEV_TX_BUSY;
+
+	local->mdev->trans_start = jiffies;
+	ieee80211_led_tx(local, 1);
+
+	return NETDEV_TX_OK;
+}
+
 static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
-			  struct ieee80211_tx_data *tx)
+			  struct ieee80211_tx_control *control)
 {
-	struct ieee80211_tx_control *control = tx->control;
-	int ret, i;
+	struct sk_buff *origskb = NULL, *next;
+	int unsetflags = 0, ret;
 
-	if (!ieee80211_qdisc_installed(local->mdev) &&
-	    __ieee80211_queue_stopped(local, 0)) {
-		netif_stop_queue(local->mdev);
-		return IEEE80211_TX_AGAIN;
-	}
-	if (skb) {
-		ieee80211_dump_frame(wiphy_name(local->hw.wiphy),
-				     "TX to low-level driver", skb);
-		ret = local->ops->tx(local_to_hw(local), skb, control);
-		if (ret)
-			return IEEE80211_TX_AGAIN;
-		local->mdev->trans_start = jiffies;
-		ieee80211_led_tx(local, 1);
-	}
-	if (tx->extra_frag) {
-		control->flags &= ~(IEEE80211_TXCTL_USE_RTS_CTS |
-				    IEEE80211_TXCTL_USE_CTS_PROTECT |
-				    IEEE80211_TXCTL_CLEAR_PS_FILT |
-				    IEEE80211_TXCTL_FIRST_FRAGMENT);
-		for (i = 0; i < tx->num_extra_frag; i++) {
-			if (!tx->extra_frag[i])
-				continue;
-			if (__ieee80211_queue_stopped(local, control->queue))
-				return IEEE80211_TX_FRAG_AGAIN;
-			if (i == tx->num_extra_frag) {
-				control->tx_rate_idx = tx->last_frag_rate_idx;
-
-				if (tx->flags & IEEE80211_TX_PROBE_LAST_FRAG)
-					control->flags |=
-						IEEE80211_TXCTL_RATE_CTRL_PROBE;
-				else
-					control->flags &=
-						~IEEE80211_TXCTL_RATE_CTRL_PROBE;
+	/*
+	 * Send skb (or fragments if any.)
+	 */
+
+	if (skb->next) {
+		origskb = skb;
+		skb = skb->next;
+	}
+
+	do {
+		control->flags &= ~unsetflags;
+
+		/* Try to transmit this fragment */
+
+		next = skb->next;
+		/*
+		 * skb->next must be NULL so the driver (or more likely
+		 * mac80211 in status handling) is able to put it onto a
+		 * queue.
+		 */
+		skb->next = NULL;
+		ret = ___ieee80211_tx(local, skb, control);
+
+		if (unlikely(ret != NETDEV_TX_OK)) {
+			/* important: restore skb->next on errors */
+			skb->next = next;
+			/* put control data into skb->cb for all fragments */
+			while (skb) {
+				control->flags &= ~unsetflags;
+				memcpy(skb->cb, control, sizeof(*control));
+				skb = skb->next;
+				unsetflags = IEEE80211_TXCTL_USE_RTS_CTS |
+					     IEEE80211_TXCTL_USE_CTS_PROTECT |
+					     IEEE80211_TXCTL_CLEAR_PS_FILT |
+					     IEEE80211_TXCTL_FIRST_FRAGMENT;
 			}
 
-			ieee80211_dump_frame(wiphy_name(local->hw.wiphy),
-					     "TX to low-level driver",
-					     tx->extra_frag[i]);
-			ret = local->ops->tx(local_to_hw(local),
-					    tx->extra_frag[i],
-					    control);
-			if (ret)
-				return IEEE80211_TX_FRAG_AGAIN;
-			local->mdev->trans_start = jiffies;
-			ieee80211_led_tx(local, 1);
-			tx->extra_frag[i] = NULL;
+			/*
+			 * If this is not fragmented, simply reject it and have
+			 * it requeued at the upper layer.
+			 */
+			if (!origskb)
+				return ret;
+
+			BUG_ON(local->pending_packet[control->queue]);
+			local->pending_packet[control->queue] = origskb;
+			/*
+			 * We stored it locally, pretend all is well,
+			 * let's hope the driver turned off the queue.
+			 */
+			return NETDEV_TX_OK;
 		}
-		kfree(tx->extra_frag);
-		tx->extra_frag = NULL;
-	}
-	return IEEE80211_TX_OK;
+
+		/* successfully transmitted this (only) fragment */
+
+		unsetflags = IEEE80211_TXCTL_USE_RTS_CTS |
+			     IEEE80211_TXCTL_USE_CTS_PROTECT |
+			     IEEE80211_TXCTL_CLEAR_PS_FILT |
+			     IEEE80211_TXCTL_FIRST_FRAGMENT;
+
+		/* remove this one from the segment list */
+		skb = next;
+		if (origskb)
+			origskb->next = skb;
+	} while (skb);
+
+	kfree_skb(origskb);
+
+	return NETDEV_TX_OK;
 }
 
 static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
@@ -1141,13 +1177,11 @@ static int ieee80211_tx(struct net_devic
 	ieee80211_tx_handler *handler;
 	struct ieee80211_tx_data tx;
 	ieee80211_tx_result res = TX_DROP, res_prepare;
-	int ret, i;
-
-	WARN_ON(__ieee80211_queue_pending(local, control->queue));
+	int ret;
 
 	if (unlikely(skb->len < 10)) {
 		dev_kfree_skb(skb);
-		return 0;
+		return NETDEV_TX_OK;
 	}
 
 	rcu_read_lock();
@@ -1155,11 +1189,8 @@ static int ieee80211_tx(struct net_devic
 	/* initialises tx */
 	res_prepare = __ieee80211_tx_prepare(&tx, skb, dev, control);
 
-	if (res_prepare == TX_DROP) {
-		dev_kfree_skb(skb);
-		rcu_read_unlock();
-		return 0;
-	}
+	if (res_prepare == TX_DROP)
+		goto drop;
 
 	sta = tx.sta;
 	tx.channel = local->hw.conf.channel;
@@ -1181,78 +1212,107 @@ static int ieee80211_tx(struct net_devic
 
 	if (unlikely(res == TX_QUEUED)) {
 		I802_DEBUG_INC(local->tx_handlers_queued);
-		rcu_read_unlock();
-		return 0;
+		goto unlock;
 	}
 
-	if (tx.extra_frag) {
-		for (i = 0; i < tx.num_extra_frag; i++) {
-			int next_len, dur;
-			struct ieee80211_hdr *hdr =
-				(struct ieee80211_hdr *)
-				tx.extra_frag[i]->data;
-
-			if (i + 1 < tx.num_extra_frag) {
-				next_len = tx.extra_frag[i + 1]->len;
-			} else {
-				next_len = 0;
-				tx.rate_idx = tx.last_frag_rate_idx;
-			}
-			dur = ieee80211_duration(&tx, 0, next_len);
-			hdr->duration_id = cpu_to_le16(dur);
-		}
-	}
-
-retry:
-	ret = __ieee80211_tx(local, skb, &tx);
-	if (ret) {
-		struct ieee80211_tx_stored_packet *store =
-			&local->pending_packet[control->queue];
-
-		if (ret == IEEE80211_TX_FRAG_AGAIN)
-			skb = NULL;
-		set_bit(IEEE80211_LINK_STATE_PENDING,
-			&local->state[control->queue]);
-		smp_mb();
-		/* When the driver gets out of buffers during sending of
-		 * fragments and calls ieee80211_stop_queue, there is
-		 * a small window between IEEE80211_LINK_STATE_XOFF and
-		 * IEEE80211_LINK_STATE_PENDING flags are set. If a buffer
-		 * gets available in that window (i.e. driver calls
-		 * ieee80211_wake_queue), we would end up with ieee80211_tx
-		 * called with IEEE80211_LINK_STATE_PENDING. Prevent this by
-		 * continuing transmitting here when that situation is
-		 * possible to have happened. */
-		if (!__ieee80211_queue_stopped(local, control->queue)) {
-			clear_bit(IEEE80211_LINK_STATE_PENDING,
-				  &local->state[control->queue]);
-			goto retry;
-		}
-		memcpy(&store->control, control,
-		       sizeof(struct ieee80211_tx_control));
-		store->skb = skb;
-		store->extra_frag = tx.extra_frag;
-		store->num_extra_frag = tx.num_extra_frag;
-		store->last_frag_rate_idx = tx.last_frag_rate_idx;
-		store->last_frag_rate_ctrl_probe =
-			!!(tx.flags & IEEE80211_TX_PROBE_LAST_FRAG);
-	}
+	ret = __ieee80211_tx(local, skb, tx.control);
 	rcu_read_unlock();
-	return 0;
+	return ret;
 
  drop:
 	if (skb)
 		dev_kfree_skb(skb);
-	for (i = 0; i < tx.num_extra_frag; i++)
-		if (tx.extra_frag[i])
-			dev_kfree_skb(tx.extra_frag[i]);
-	kfree(tx.extra_frag);
+ unlock:
 	rcu_read_unlock();
-	return 0;
+	return NETDEV_TX_OK;
 }
 
 /* device xmit handlers */
 
+/*
+ * validate_control verifies that the control pointers are
+ * still valid after the requeue. If they are not then we'll
+ * simply drop the packet.
+ */
+static bool validate_control(struct ieee80211_local *local,
+			     struct ieee80211_tx_control *ctl,
+			     struct sk_buff *skb)
+{
+	struct ieee80211_sub_if_data *sdata;
+	struct ieee80211_key *key;
+	struct ieee80211_hdr *hdr = (void *)skb->data;
+	struct sta_info *sta;
+	u8 *da;
+
+	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+		if (ctl->vif != &sdata->vif)
+			continue;
+
+		da = ieee80211_get_DA(hdr);
+		if (!is_multicast_ether_addr(da))
+			sta = sta_info_get(local, da);
+
+		key = ieee80211_select_key(sdata, sta, ctl,
+					   le16_to_cpu(hdr->frame_control));
+
+		if (!ctl->hw_key && !key)
+			return true;
+		return &key->conf == ctl->hw_key;
+	}
+
+	return false;
+}
+
+static int ieee80211_send_pending_fragments(struct ieee80211_local *local,
+					    struct ieee80211_tx_control *ctl,
+					    int queue)
+{
+	struct sk_buff *orig, *frag, *next;
+	int ret;
+
+	orig = local->pending_packet[queue];
+
+	if (!orig)
+		return NETDEV_TX_OK;
+
+	frag = orig->next;
+
+	/* need RCU protection for key/vif pointers */
+	rcu_read_lock();
+
+	do {
+		next = frag->next;
+		frag->next = NULL;
+
+		/* The interface could have been removed, or the key, or ... */
+		if (!validate_control(local, (void *)frag->cb, frag)) {
+			dev_kfree_skb(frag);
+			/* most likely we'll drop all others, but let's see */
+			orig->next = frag = next;
+			continue;
+		}
+		memcpy(ctl, frag->cb, sizeof(*ctl));
+		memset(frag->cb, 0, sizeof(frag->cb));
+		ret = ___ieee80211_tx(local, frag, ctl);
+		if (ret != NETDEV_TX_OK) {
+			frag->next = next;
+			memcpy(frag->cb, ctl, sizeof(*ctl));
+			rcu_read_unlock();
+			return ret;
+		}
+		/* fragment sent fine, remove from list */
+		orig->next = frag = next;
+	} while (next);
+
+	rcu_read_unlock();
+
+	/* yay, all fragments queued to hw successfully */
+	dev_kfree_skb(local->pending_packet[queue]);
+	local->pending_packet[queue] = NULL;
+
+	return NETDEV_TX_OK;
+}
+
 int ieee80211_master_start_xmit(struct sk_buff *skb,
 				struct net_device *dev)
 {
@@ -1263,11 +1323,16 @@ int ieee80211_master_start_xmit(struct s
 	int headroom;
 	int ret;
 
-	/*
-	 * copy control out of the skb so other people can use skb->cb
-	 */
 	pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
-	memset(&control, 0, sizeof(struct ieee80211_tx_control));
+
+	ret = ieee80211_send_pending_fragments(wdev_priv(dev->ieee80211_ptr),
+					       &control, pkt_data->queue);
+
+	/* Not all pending fragments queued to hw so reject this skb */
+	if (ret != NETDEV_TX_OK)
+		return ret;
+
+	memset(&control, 0, sizeof(control));
 
 	if (pkt_data->ifindex)
 		odev = dev_get_by_index(&init_net, pkt_data->ifindex);
@@ -1290,12 +1355,11 @@ int ieee80211_master_start_xmit(struct s
 		if (pskb_expand_head(skb, headroom, 0, GFP_ATOMIC)) {
 			dev_kfree_skb(skb);
 			dev_put(odev);
-			return 0;
+			return NETDEV_TX_OK;
 		}
 	}
 
 	control.vif = &osdata->vif;
-	control.type = osdata->vif.type;
 	if (pkt_data->flags & IEEE80211_TXPD_REQ_TX_STATUS)
 		control.flags |= IEEE80211_TXCTL_REQ_TX_STATUS;
 	if (pkt_data->flags & IEEE80211_TXPD_DO_NOT_ENCRYPT)
@@ -1308,6 +1372,7 @@ int ieee80211_master_start_xmit(struct s
 		control.flags |= IEEE80211_TXCTL_AMPDU;
 	control.queue = pkt_data->queue;
 
+	memset(skb->cb, 0, sizeof(skb->cb));
 	ret = ieee80211_tx(odev, skb, &control);
 	dev_put(odev);
 
@@ -1552,19 +1617,6 @@ int ieee80211_subif_start_xmit(struct sk
 	nh_pos -= skip_header_bytes;
 	h_pos -= skip_header_bytes;
 
-	/* TODO: implement support for fragments so that there is no need to
-	 * reallocate and copy payload; it might be enough to support one
-	 * extra fragment that would be copied in the beginning of the frame
-	 * data.. anyway, it would be nice to include this into skb structure
-	 * somehow
-	 *
-	 * There are few options for this:
-	 * use skb->cb as an extra space for 802.11 header
-	 * allocate new buffer if not enough headroom
-	 * make sure that there is enough headroom in every skb by increasing
-	 * build in headroom in __dev_alloc_skb() (linux/skbuff.h) and
-	 * alloc_skb() (net/core/skbuff.c)
-	 */
 	head_need = hdrlen + encaps_len + meshhdrlen + local->tx_headroom;
 	head_need -= skb_headroom(skb);
 
@@ -1650,69 +1702,6 @@ int ieee80211_subif_start_xmit(struct sk
 	return ret;
 }
 
-/* helper functions for pending packets for when queues are stopped */
-
-void ieee80211_clear_tx_pending(struct ieee80211_local *local)
-{
-	int i, j;
-	struct ieee80211_tx_stored_packet *store;
-
-	for (i = 0; i < local->hw.queues; i++) {
-		if (!__ieee80211_queue_pending(local, i))
-			continue;
-		store = &local->pending_packet[i];
-		kfree_skb(store->skb);
-		for (j = 0; j < store->num_extra_frag; j++)
-			kfree_skb(store->extra_frag[j]);
-		kfree(store->extra_frag);
-		clear_bit(IEEE80211_LINK_STATE_PENDING, &local->state[i]);
-	}
-}
-
-void ieee80211_tx_pending(unsigned long data)
-{
-	struct ieee80211_local *local = (struct ieee80211_local *)data;
-	struct net_device *dev = local->mdev;
-	struct ieee80211_tx_stored_packet *store;
-	struct ieee80211_tx_data tx;
-	int i, ret, reschedule = 0;
-
-	netif_tx_lock_bh(dev);
-	for (i = 0; i < local->hw.queues; i++) {
-		if (__ieee80211_queue_stopped(local, i))
-			continue;
-		if (!__ieee80211_queue_pending(local, i)) {
-			reschedule = 1;
-			continue;
-		}
-		store = &local->pending_packet[i];
-		tx.control = &store->control;
-		tx.extra_frag = store->extra_frag;
-		tx.num_extra_frag = store->num_extra_frag;
-		tx.last_frag_rate_idx = store->last_frag_rate_idx;
-		tx.flags = 0;
-		if (store->last_frag_rate_ctrl_probe)
-			tx.flags |= IEEE80211_TX_PROBE_LAST_FRAG;
-		ret = __ieee80211_tx(local, store->skb, &tx);
-		if (ret) {
-			if (ret == IEEE80211_TX_FRAG_AGAIN)
-				store->skb = NULL;
-		} else {
-			clear_bit(IEEE80211_LINK_STATE_PENDING,
-				  &local->state[i]);
-			reschedule = 1;
-		}
-	}
-	netif_tx_unlock_bh(dev);
-	if (reschedule) {
-		if (!ieee80211_qdisc_installed(dev)) {
-			if (!__ieee80211_queue_stopped(local, 0))
-				netif_wake_queue(dev);
-		} else
-			netif_schedule(dev);
-	}
-}
-
 /* functions for drivers to get certain frames */
 
 static void ieee80211_beacon_add_tim(struct ieee80211_local *local,
--- everything.orig/net/mac80211/wep.c	2008-05-07 11:09:12.000000000 +0200
+++ everything/net/mac80211/wep.c	2008-05-07 11:13:19.000000000 +0200
@@ -333,6 +333,8 @@ ieee80211_crypto_wep_decrypt(struct ieee
 
 static int wep_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
 {
+	ieee80211_skb_set_protected(skb);
+
 	if (!(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) {
 		if (ieee80211_wep_encrypt(tx->local, skb, tx->key))
 			return -1;
@@ -349,25 +351,21 @@ static int wep_encrypt_skb(struct ieee80
 ieee80211_tx_result
 ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx)
 {
+	struct sk_buff *skb = tx->skb;
+
 	tx->control->iv_len = WEP_IV_LEN;
 	tx->control->icv_len = WEP_ICV_LEN;
-	ieee80211_tx_set_protected(tx);
 
-	if (wep_encrypt_skb(tx, tx->skb) < 0) {
+	/* skip original if fragmented */
+	if (skb->next)
+		skb = skb->next;
+
+	do {
+		if (wep_encrypt_skb(tx, tx->skb) == 0)
+			continue;
 		I802_DEBUG_INC(tx->local->tx_handlers_drop_wep);
 		return TX_DROP;
-	}
-
-	if (tx->extra_frag) {
-		int i;
-		for (i = 0; i < tx->num_extra_frag; i++) {
-			if (wep_encrypt_skb(tx, tx->extra_frag[i]) < 0) {
-				I802_DEBUG_INC(tx->local->
-					       tx_handlers_drop_wep);
-				return TX_DROP;
-			}
-		}
-	}
+	} while ((skb = skb->next));
 
 	return TX_CONTINUE;
 }
--- everything.orig/net/mac80211/wpa.c	2008-05-07 11:09:13.000000000 +0200
+++ everything/net/mac80211/wpa.c	2008-05-07 11:13:19.000000000 +0200
@@ -192,6 +192,8 @@ static int tkip_encrypt_skb(struct ieee8
 	u16 fc;
 	u8 *pos;
 
+	ieee80211_skb_set_protected(skb);
+
 	fc = le16_to_cpu(hdr->frame_control);
 	hdrlen = ieee80211_get_hdrlen(fc);
 	len = skb->len - hdrlen;
@@ -250,7 +252,6 @@ ieee80211_crypto_tkip_encrypt(struct iee
 
 	tx->control->icv_len = TKIP_ICV_LEN;
 	tx->control->iv_len = TKIP_IV_LEN;
-	ieee80211_tx_set_protected(tx);
 
 	if ((tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) &&
 	    !(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) &&
@@ -260,17 +261,14 @@ ieee80211_crypto_tkip_encrypt(struct iee
 		return TX_CONTINUE;
 	}
 
-	if (tkip_encrypt_skb(tx, skb, test) < 0)
-		return TX_DROP;
-
-	if (tx->extra_frag) {
-		int i;
-		for (i = 0; i < tx->num_extra_frag; i++) {
-			if (tkip_encrypt_skb(tx, tx->extra_frag[i], test)
-			    < 0)
-				return TX_DROP;
-		}
-	}
+	/* skip original if segmented */
+	if (skb->next)
+		skb = skb->next;
+
+	do {
+		if (tkip_encrypt_skb(tx, skb, test) < 0)
+			return TX_DROP;
+	} while ((skb = skb->next));
 
 	return TX_CONTINUE;
 }
@@ -439,6 +437,8 @@ static int ccmp_encrypt_skb(struct ieee8
 	u8 *pos, *pn, *b_0, *aad, *scratch;
 	int i;
 
+	ieee80211_skb_set_protected(skb);
+
 	scratch = key->u.ccmp.tx_crypto_buf;
 	b_0 = scratch + 3 * AES_BLOCK_LEN;
 	aad = scratch + 4 * AES_BLOCK_LEN;
@@ -499,7 +499,6 @@ ieee80211_crypto_ccmp_encrypt(struct iee
 
 	tx->control->icv_len = CCMP_MIC_LEN;
 	tx->control->iv_len = CCMP_HDR_LEN;
-	ieee80211_tx_set_protected(tx);
 
 	if ((tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) &&
 	    !(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
@@ -509,17 +508,14 @@ ieee80211_crypto_ccmp_encrypt(struct iee
 		return TX_CONTINUE;
 	}
 
-	if (ccmp_encrypt_skb(tx, skb, test) < 0)
-		return TX_DROP;
-
-	if (tx->extra_frag) {
-		int i;
-		for (i = 0; i < tx->num_extra_frag; i++) {
-			if (ccmp_encrypt_skb(tx, tx->extra_frag[i], test)
-			    < 0)
-				return TX_DROP;
-		}
-	}
+	/* skip original if fragmented */
+	if (skb->next)
+		skb = skb->next;
+
+	do {
+		if (ccmp_encrypt_skb(tx, skb, test) < 0)
+			return TX_DROP;
+	} while ((skb = skb->next));
 
 	return TX_CONTINUE;
 }
--- everything.orig/net/mac80211/util.c	2008-05-07 11:13:00.000000000 +0200
+++ everything/net/mac80211/util.c	2008-05-07 11:13:19.000000000 +0200
@@ -165,22 +165,6 @@ int ieee80211_get_mesh_hdrlen(struct iee
 	}
 }
 
-void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
-{
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
-
-	hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
-	if (tx->extra_frag) {
-		struct ieee80211_hdr *fhdr;
-		int i;
-		for (i = 0; i < tx->num_extra_frag; i++) {
-			fhdr = (struct ieee80211_hdr *)
-				tx->extra_frag[i]->data;
-			fhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
-		}
-	}
-}
-
 int ieee80211_frame_duration(struct ieee80211_local *local, size_t len,
 			     int rate, int erp, int short_preamble)
 {
@@ -333,15 +317,11 @@ void ieee80211_wake_queue(struct ieee802
 
 	if (test_and_clear_bit(IEEE80211_LINK_STATE_XOFF,
 			       &local->state[queue])) {
-		if (test_bit(IEEE80211_LINK_STATE_PENDING,
-			     &local->state[queue]))
-			tasklet_schedule(&local->tx_pending_tasklet);
-		else
-			if (!ieee80211_qdisc_installed(local->mdev)) {
-				if (queue == 0)
-					netif_wake_queue(local->mdev);
-			} else
-				__netif_schedule(local->mdev);
+		if (!ieee80211_qdisc_installed(local->mdev)) {
+			if (queue == 0)
+				netif_wake_queue(local->mdev);
+		} else
+			__netif_schedule(local->mdev);
 	}
 }
 EXPORT_SYMBOL(ieee80211_wake_queue);
--- everything.orig/net/mac80211/wme.c	2008-05-07 11:12:48.000000000 +0200
+++ everything/net/mac80211/wme.c	2008-05-07 11:13:19.000000000 +0200
@@ -235,9 +235,6 @@ static int wme_qdiscop_enqueue(struct sk
 }
 
 
-/* TODO: clean up the cases where master_hard_start_xmit
- * returns non 0 - it shouldn't ever do that. Once done we
- * can remove this function */
 static int wme_qdiscop_requeue(struct sk_buff *skb, struct Qdisc* qd)
 {
 	struct ieee80211_sched_data *q = qdisc_priv(qd);
@@ -246,7 +243,7 @@ static int wme_qdiscop_requeue(struct sk
 	struct Qdisc *qdisc;
 	int err;
 
-	/* we recorded which queue to use earlier! */
+	/* We recorded which queue to use earlier. */
 	qdisc = q->queues[pkt_data->queue];
 
 	if ((err = qdisc->ops->requeue(skb, qdisc)) == 0) {
@@ -273,9 +270,7 @@ static struct sk_buff *wme_qdiscop_deque
 		/* see if there is room in this hardware queue */
 		if ((test_bit(IEEE80211_LINK_STATE_XOFF,
 				&local->state[queue])) ||
-		    (test_bit(IEEE80211_LINK_STATE_PENDING,
-				&local->state[queue])) ||
-			 (!test_bit(queue, q->qdisc_pool)))
+		    (!test_bit(queue, q->qdisc_pool)))
 			continue;
 
 		/* there is space - try and get a frame */
--- everything.orig/net/mac80211/main.c	2008-05-07 11:12:57.000000000 +0200
+++ everything/net/mac80211/main.c	2008-05-07 12:43:17.000000000 +0200
@@ -408,7 +408,6 @@ static int ieee80211_open(struct net_dev
 		WARN_ON(res);
 		if (res)
 			goto err_del_interface;
-		tasklet_enable(&local->tx_pending_tasklet);
 		tasklet_enable(&local->tasklet);
 	}
 
@@ -602,7 +601,6 @@ static int ieee80211_stop(struct net_dev
 
 		ieee80211_led_radio(local, 0);
 
-		tasklet_disable(&local->tx_pending_tasklet);
 		tasklet_disable(&local->tasklet);
 	}
 
@@ -1204,8 +1202,16 @@ void ieee80211_tx_status_irqsafe(struct 
 	struct ieee80211_tx_status *saved;
 	int tmp;
 
+	if (WARN_ON(!skb))
+		return;
+
+	if (WARN_ON(!status)) {
+		dev_kfree_skb(skb);
+		return;
+	}
+
 	skb->dev = local->mdev;
-	saved = kmalloc(sizeof(struct ieee80211_tx_status), GFP_ATOMIC);
+	saved = kmemdup(status, sizeof(struct ieee80211_tx_status), GFP_ATOMIC);
 	if (unlikely(!saved)) {
 		if (net_ratelimit())
 			printk(KERN_WARNING "%s: Not enough memory, "
@@ -1216,7 +1222,6 @@ void ieee80211_tx_status_irqsafe(struct 
 		dev_kfree_skb_any(skb);
 		return;
 	}
-	memcpy(saved, status, sizeof(struct ieee80211_tx_status));
 	/* copy pointer to saved status into skb->cb for use by tasklet */
 	memcpy(skb->cb, &saved, sizeof(saved));
 
@@ -1435,10 +1440,10 @@ void ieee80211_tx_status(struct ieee8021
 	struct ieee80211_sub_if_data *sdata;
 	struct net_device *prev_dev = NULL;
 
-	if (!status) {
-		printk(KERN_ERR
-		       "%s: ieee80211_tx_status called with NULL status\n",
-		       wiphy_name(local->hw.wiphy));
+	if (WARN_ON(!skb))
+		return;
+
+	if (WARN_ON(!status)) {
 		dev_kfree_skb(skb);
 		return;
 	}
@@ -1665,10 +1670,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(
 
 	sta_info_init(local);
 
-	tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending,
-		     (unsigned long)local);
-	tasklet_disable(&local->tx_pending_tasklet);
-
 	tasklet_init(&local->tasklet,
 		     ieee80211_tasklet_handler,
 		     (unsigned long) local);
@@ -1851,8 +1852,8 @@ void ieee80211_unregister_hw(struct ieee
 {
 	struct ieee80211_local *local = hw_to_local(hw);
 	struct ieee80211_sub_if_data *sdata, *tmp;
+	int queue;
 
-	tasklet_kill(&local->tx_pending_tasklet);
 	tasklet_kill(&local->tasklet);
 
 	rtnl_lock();
@@ -1886,7 +1887,6 @@ void ieee80211_unregister_hw(struct ieee
 	rtnl_unlock();
 
 	ieee80211_rx_bss_list_deinit(local->mdev);
-	ieee80211_clear_tx_pending(local);
 	sta_info_stop(local);
 	rate_control_deinitialize(local);
 	debugfs_hw_del(local);
@@ -1898,6 +1898,21 @@ void ieee80211_unregister_hw(struct ieee
 	skb_queue_purge(&local->skb_queue);
 	skb_queue_purge(&local->skb_queue_unreliable);
 
+	/* free possibly queued fragmented skbs */
+	for (queue = 0; queue < hw->queues; queue++) {
+		struct sk_buff *skb = local->pending_packet[queue];
+		struct sk_buff *frag;
+		if (!skb)
+			continue;
+		frag = skb->next;
+		while (frag) {
+			skb->next = frag->next;
+			dev_kfree_skb(frag);
+			frag = skb->next;
+		}
+		dev_kfree_skb(skb);
+	}
+
 	destroy_workqueue(local->hw.workqueue);
 	wiphy_unregister(local->hw.wiphy);
 	ieee80211_wep_free(local);
@@ -1921,6 +1936,9 @@ static int __init ieee80211_init(void)
 	int ret;
 
 	BUILD_BUG_ON(sizeof(struct ieee80211_tx_packet_data) > sizeof(skb->cb));
+	BUILD_BUG_ON(sizeof(struct ieee80211_tx_control) > sizeof(skb->cb));
+	BUILD_BUG_ON(offsetof(struct ieee80211_tx_control, flags) != 0);
+	BUILD_BUG_ON(offsetof(struct ieee80211_tx_packet_data, flags) != 0);
 
 	ret = rc80211_pid_init();
 	if (ret)
--- everything.orig/include/net/mac80211.h	2008-05-07 11:13:00.000000000 +0200
+++ everything/include/net/mac80211.h	2008-05-07 12:01:25.000000000 +0200
@@ -3,7 +3,7 @@
  *
  * Copyright 2002-2005, Devicescape Software, Inc.
  * Copyright 2006-2007	Jiri Benc <jbenc@...e.cz>
- * Copyright 2007	Johannes Berg <johannes@...solutions.net>
+ * Copyright 2007-2008	Johannes Berg <johannes@...solutions.net>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -233,6 +233,7 @@ struct ieee80211_bss_conf {
  * @IEEE80211_TXCTL_40_MHZ_WIDTH: send this frame using 40 Mhz channel width
  * @IEEE80211_TXCTL_DUP_DATA: duplicate data frame on both 20 Mhz channels
  * @IEEE80211_TXCTL_SHORT_GI: send this frame using short guard interval
+ * @IEEE80211_TXCTL_INJECTED: Frame was injected (NOT for driver use!)
  */
 enum mac80211_tx_control_flags {
 	IEEE80211_TXCTL_REQ_TX_STATUS		= (1<<0),
@@ -254,6 +255,7 @@ enum mac80211_tx_control_flags {
 	IEEE80211_TXCTL_40_MHZ_WIDTH		= (1<<16),
 	IEEE80211_TXCTL_DUP_DATA		= (1<<17),
 	IEEE80211_TXCTL_SHORT_GI		= (1<<18),
+	IEEE80211_TXCTL_INJECTED		= (1<<19),
 };
 
 /* Transmit control fields. This data structure is passed to low-level driver
@@ -278,6 +280,9 @@ struct ieee80211_tx_control {
 				 * This could be used when set_retry_limit
 				 * is not implemented by the driver */
 
+	u16 queue;		/* hardware queue to use for this frame;
+				 * 0 = highest, hw->queues-1 = lowest */
+
 	struct ieee80211_vif *vif;
 
 	/* Key used for hardware encryption
@@ -290,10 +295,7 @@ struct ieee80211_tx_control {
 				 * position represents antenna number used */
 	u8 icv_len;		/* length of the ICV/MIC field in octets */
 	u8 iv_len;		/* length of the IV field in octets */
-	u16 queue;		/* hardware queue to use for this frame;
-				 * 0 = highest, hw->queues-1 = lowest */
 	u16 aid;		/* Station AID */
-	int type;	/* internal */
 };
 
 


--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ