netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC/RFT 0/4] mac80211 QoS-related enhancements
@ 2008-04-30 12:40 Johannes Berg
  2008-04-30 12:40 ` [RFC/RFT 1/4] mac80211: use rate index in TX control Johannes Berg
                   ` (3 more replies)
  0 siblings, 4 replies; 41+ messages in thread
From: Johannes Berg @ 2008-04-30 12:40 UTC (permalink / raw)
  To: linux-wireless
  Cc: netdev, Ron Rindjunsky, Tomas Winkler, Ivo van Doorn,
	Peter P Waskiewicz Jr

Note: This patch series requires a number of other patches, notably Ivo's
rt2x00 update and Ivo's mac80211 patch
	"mac80211: Replace ieee80211_tx_control->key_idx with ieee80211_key_conf"

Any testing of this series is welcome, even just the first patch by itself
(especially with drivers other than b43), it doesn't make sense to test with
patch 2 and not 3 though, so if you're going to test one by one treat 2/3 as
a set.

This series of patches reworks mac80211 step-by-step to use more generic
code instead of having implementations of everything built-in.

The first patch (mac80211: use rate index in TX control) is just preparation
(but I'm not entirely sure about the iwlwifi changes, haven't tested them yet.)

The second patch I want input from netdev from, what do people think about
using the GSO code that way? It does seem like a bit of an abuse to fill the
skb->next pointer from within ->hard_start_xmit() but it works and allows
me to get rid of all the weird retry logic in mac80211 (patch three). Finally,
the fourth patch converts mac80211 and drivers to be multiqueue aware.

There are still a number of things missing in this series:
 * we need to rework the classification function to be a real classifier
   that we install on our wme qdisc
 * we should allow tuning the medium access parameters via the wme qdisc

johannes


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [RFC/RFT 1/4] mac80211: use rate index in TX control
  2008-04-30 12:40 [RFC/RFT 0/4] mac80211 QoS-related enhancements Johannes Berg
@ 2008-04-30 12:40 ` Johannes Berg
  2008-04-30 12:40 ` [RFC/RFT 2/4] GSO: generalize for mac80211 Johannes Berg
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 41+ messages in thread
From: Johannes Berg @ 2008-04-30 12:40 UTC (permalink / raw)
  To: linux-wireless-u79uwXL29TY76Z2rM5mHXA
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA, Ron Rindjunsky, Tomas Winkler,
	Ivo van Doorn, Peter P Waskiewicz Jr

[-- Attachment #1: 036-mac80211-tx-rate-idx.patch --]
[-- Type: text/plain, Size: 40019 bytes --]

This patch modifies struct ieee80211_tx_control to give band
info and the rate index (instead of rate pointers) to drivers.
This mostly serves to reduce the TX control structure size to
make it fit into skb->cb so that the fragmentation code can
put it there and we can think about passing it to drivers that
way in the future.

Signed-off-by: Johannes Berg <johannes-cdvu00un1VgdHxzADdlk8Q@public.gmane.org>
---
 drivers/net/wireless/adm8211.c              |    6 -
 drivers/net/wireless/at76_usb.c             |    2 
 drivers/net/wireless/ath5k/base.c           |    8 +-
 drivers/net/wireless/b43/main.c             |    2 
 drivers/net/wireless/b43/xmit.c             |   13 ++-
 drivers/net/wireless/b43legacy/xmit.c       |    9 +-
 drivers/net/wireless/iwlwifi/iwl-3945-rs.c  |    6 -
 drivers/net/wireless/iwlwifi/iwl-3945.c     |    7 +
 drivers/net/wireless/iwlwifi/iwl-4965-rs.c  |   12 +--
 drivers/net/wireless/iwlwifi/iwl-4965.c     |   13 +--
 drivers/net/wireless/iwlwifi/iwl3945-base.c |    4 -
 drivers/net/wireless/iwlwifi/iwl4965-base.c |    4 -
 drivers/net/wireless/p54/p54common.c        |    2 
 drivers/net/wireless/rt2x00/rt2x00dev.c     |    6 -
 drivers/net/wireless/rtl8180_dev.c          |   19 ++---
 drivers/net/wireless/rtl8187_dev.c          |   10 --
 drivers/net/wireless/zd1211rw/zd_mac.c      |    7 +
 include/net/mac80211.h                      |   51 ++++++++++---
 net/mac80211/ieee80211_i.h                  |    8 +-
 net/mac80211/mlme.c                         |    6 -
 net/mac80211/rate.c                         |   12 ++-
 net/mac80211/rate.h                         |   25 ++----
 net/mac80211/rc80211_pid_algo.c             |    6 -
 net/mac80211/tx.c                           |  104 +++++++++++++++-------------
 net/mac80211/util.c                         |   10 ++
 25 files changed, 200 insertions(+), 152 deletions(-)

--- everything.orig/include/net/mac80211.h	2008-04-29 22:49:27.000000000 +0200
+++ everything/include/net/mac80211.h	2008-04-29 23:56:43.000000000 +0200
@@ -266,27 +266,26 @@ enum mac80211_tx_control_flags {
  * ieee80211_ops->remove_interface() callback funtion.
  * The hw_key pointer is valid until it has been removed with the
  * ieee80211_ops->set_key() callback function.
- * The tx_rate and alt_retry_rate pointers are valid until the phy is
- * deregistered.
  */
 struct ieee80211_tx_control {
-	struct ieee80211_vif *vif;
-	struct ieee80211_rate *tx_rate;
+	u32 flags;		/* tx control flags defined above */
+
+	s8 tx_rate_idx,		/* Transmit rate (indexes registered rates) */
+	   rts_cts_rate_idx,	/* Transmit rate for RTS/CTS frame */
+	   alt_retry_rate_idx;	/* retry rate for the last retries */
 
-	/* Transmit rate for RTS/CTS frame */
-	struct ieee80211_rate *rts_cts_rate;
+	s8 retry_limit;		/* 1 = only first attempt, 2 = one retry, ..
+				 * This could be used when set_retry_limit
+				 * is not implemented by the driver */
 
-	/* retry rate for the last retries */
-	struct ieee80211_rate *alt_retry_rate;
+	struct ieee80211_vif *vif;
 
 	/* Key used for hardware encryption
 	 * NULL if IEEE80211_TXCTL_DO_NOT_ENCRYPT is set */
 	struct ieee80211_key_conf *hw_key;
 
-	u32 flags;		/* tx control flags defined above */
-	u8 retry_limit;		/* 1 = only first attempt, 2 = one retry, ..
-				 * This could be used when set_retry_limit
-				 * is not implemented by the driver */
+	enum ieee80211_band band;
+
 	u8 antenna_sel_tx; 	/* 0 = default/diversity, otherwise bit
 				 * position represents antenna number used */
 	u8 icv_len;		/* length of the ICV/MIC field in octets */
@@ -298,6 +297,7 @@ struct ieee80211_tx_control {
 };
 
 
+
 /**
  * enum mac80211_rx_flags - receive flags
  *
@@ -799,6 +799,33 @@ static inline void SET_IEEE80211_PERM_AD
 	memcpy(hw->wiphy->perm_addr, addr, ETH_ALEN);
 }
 
+static inline struct ieee80211_rate *
+ieee80211_get_tx_rate(const struct ieee80211_hw *hw,
+		      const struct ieee80211_tx_control *c)
+{
+	if (WARN_ON(c->tx_rate_idx < 0))
+		return NULL;
+	return &hw->wiphy->bands[c->band]->bitrates[c->tx_rate_idx];
+}
+
+static inline struct ieee80211_rate *
+ieee80211_get_rts_cts_rate(const struct ieee80211_hw *hw,
+			   const struct ieee80211_tx_control *c)
+{
+	if (c->rts_cts_rate_idx < 0)
+		return NULL;
+	return &hw->wiphy->bands[c->band]->bitrates[c->rts_cts_rate_idx];
+}
+
+static inline struct ieee80211_rate *
+ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw,
+			     const struct ieee80211_tx_control *c)
+{
+	if (c->alt_retry_rate_idx < 0)
+		return NULL;
+	return &hw->wiphy->bands[c->band]->bitrates[c->alt_retry_rate_idx];
+}
+
 /**
  * DOC: Hardware crypto acceleration
  *
--- everything.orig/net/mac80211/rate.h	2008-04-29 22:56:34.000000000 +0200
+++ everything/net/mac80211/rate.h	2008-04-29 23:15:36.000000000 +0200
@@ -19,14 +19,15 @@
 #include "ieee80211_i.h"
 #include "sta_info.h"
 
-/* TODO: kdoc */
+/**
+ * struct rate_selection - rate selection for rate control algos
+ * @rate: selected transmission rate index
+ * @nonerp: Non-ERP rate to use instead if ERP cannot be used
+ * @probe: rate for probing (or -1)
+ *
+ */
 struct rate_selection {
-	/* Selected transmission rate */
-	struct ieee80211_rate *rate;
-	/* Non-ERP rate to use if mac80211 decides it cannot use an ERP rate */
-	struct ieee80211_rate *nonerp;
-	/* probe with this rate, or NULL for no probing */
-	struct ieee80211_rate *probe;
+	s8 rate_idx, nonerp_idx, probe_idx;
 };
 
 struct rate_control_ops {
@@ -138,7 +139,7 @@ static inline int rate_supported(struct 
 	return (sta == NULL || sta->supp_rates[band] & BIT(index));
 }
 
-static inline int
+static inline s8
 rate_lowest_index(struct ieee80211_local *local,
 		  struct ieee80211_supported_band *sband,
 		  struct sta_info *sta)
@@ -155,14 +156,6 @@ rate_lowest_index(struct ieee80211_local
 	return 0;
 }
 
-static inline struct ieee80211_rate *
-rate_lowest(struct ieee80211_local *local,
-	    struct ieee80211_supported_band *sband,
-	    struct sta_info *sta)
-{
-	return &sband->bitrates[rate_lowest_index(local, sband, sta)];
-}
-
 
 /* functions for rate control related to a device */
 int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
--- everything.orig/net/mac80211/mlme.c	2008-04-29 22:56:00.000000000 +0200
+++ everything/net/mac80211/mlme.c	2008-04-29 23:20:08.000000000 +0200
@@ -2371,15 +2371,15 @@ static int ieee80211_sta_join_ibss(struc
 
 		memset(&control, 0, sizeof(control));
 		rate_control_get_rate(dev, sband, skb, &ratesel);
-		if (!ratesel.rate) {
+		if (ratesel.rate_idx < 0) {
 			printk(KERN_DEBUG "%s: Failed to determine TX rate "
 			       "for IBSS beacon\n", dev->name);
 			break;
 		}
 		control.vif = &sdata->vif;
-		control.tx_rate = ratesel.rate;
+		control.tx_rate_idx = ratesel.rate_idx;
 		if (sdata->bss_conf.use_short_preamble &&
-		    ratesel.rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
+		    sband->bitrates[ratesel.rate_idx].flags & IEEE80211_RATE_SHORT_PREAMBLE)
 			control.flags |= IEEE80211_TXCTL_SHORT_PREAMBLE;
 		control.antenna_sel_tx = local->hw.conf.antenna_sel_tx;
 		control.flags |= IEEE80211_TXCTL_NO_ACK;
--- everything.orig/net/mac80211/rate.c	2008-04-29 22:59:56.000000000 +0200
+++ everything/net/mac80211/rate.c	2008-04-29 23:16:53.000000000 +0200
@@ -176,20 +176,24 @@ void rate_control_get_rate(struct net_de
 	rcu_read_lock();
 	sta = sta_info_get(local, hdr->addr1);
 
-	memset(sel, 0, sizeof(struct rate_selection));
+	sel->rate_idx = -1;
+	sel->nonerp_idx = -1;
+	sel->probe_idx = -1;
 
 	ref->ops->get_rate(ref->priv, dev, sband, skb, sel);
 
+	BUG_ON(sel->rate_idx < 0);
+
 	/* Select a non-ERP backup rate. */
-	if (!sel->nonerp) {
+	if (sel->nonerp_idx < 0) {
 		for (i = 0; i < sband->n_bitrates; i++) {
 			struct ieee80211_rate *rate = &sband->bitrates[i];
-			if (sel->rate->bitrate < rate->bitrate)
+			if (sband->bitrates[sel->rate_idx].bitrate < rate->bitrate)
 				break;
 
 			if (rate_supported(sta, sband->band, i) &&
 			    !(rate->flags & IEEE80211_RATE_ERP_G))
-				sel->nonerp = rate;
+				sel->nonerp_idx = i;
 		}
 	}
 
--- everything.orig/net/mac80211/ieee80211_i.h	2008-04-29 22:55:37.000000000 +0200
+++ everything/net/mac80211/ieee80211_i.h	2008-04-29 23:33:09.000000000 +0200
@@ -159,11 +159,11 @@ struct ieee80211_tx_data {
 
 	struct ieee80211_tx_control *control;
 	struct ieee80211_channel *channel;
-	struct ieee80211_rate *rate;
+	s8 rate_idx;
 	/* use this rate (if set) for last fragment; rate can
 	 * be set to lower rate for the first fragments, e.g.,
 	 * when using CTS protection with IEEE 802.11g. */
-	struct ieee80211_rate *last_frag_rate;
+	s8 last_frag_rate_idx;
 
 	/* Extra fragments (in addition to the first fragment
 	 * in skb) */
@@ -225,9 +225,9 @@ struct ieee80211_tx_stored_packet {
 	struct ieee80211_tx_control control;
 	struct sk_buff *skb;
 	struct sk_buff **extra_frag;
-	struct ieee80211_rate *last_frag_rate;
+	s8 last_frag_rate_idx;
 	int num_extra_frag;
-	unsigned int last_frag_rate_ctrl_probe;
+	bool last_frag_rate_ctrl_probe;
 };
 
 struct beacon_data {
--- everything.orig/net/mac80211/rc80211_pid_algo.c	2008-04-29 23:11:25.000000000 +0200
+++ everything/net/mac80211/rc80211_pid_algo.c	2008-04-29 23:26:07.000000000 +0200
@@ -266,7 +266,7 @@ static void rate_control_pid_tx_status(v
 
 	/* Ignore all frames that were sent with a different rate than the rate
 	 * we currently advise mac80211 to use. */
-	if (status->control.tx_rate != &sband->bitrates[sta->txrate_idx])
+	if (status->control.tx_rate_idx != sta->txrate_idx)
 		goto unlock;
 
 	spinfo = sta->rate_ctrl_priv;
@@ -330,7 +330,7 @@ static void rate_control_pid_get_rate(vo
 	fc = le16_to_cpu(hdr->frame_control);
 	if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
 	    is_multicast_ether_addr(hdr->addr1) || !sta) {
-		sel->rate = rate_lowest(local, sband, sta);
+		sel->rate_idx = rate_lowest_index(local, sband, sta);
 		rcu_read_unlock();
 		return;
 	}
@@ -349,7 +349,7 @@ static void rate_control_pid_get_rate(vo
 
 	rcu_read_unlock();
 
-	sel->rate = &sband->bitrates[rateidx];
+	sel->rate_idx = rateidx;
 
 #ifdef CONFIG_MAC80211_DEBUGFS
 	rate_control_pid_event_tx_rate(
--- everything.orig/net/mac80211/tx.c	2008-04-29 23:00:43.000000000 +0200
+++ everything/net/mac80211/tx.c	2008-04-29 23:38:40.000000000 +0200
@@ -91,11 +91,12 @@ static u16 ieee80211_duration(struct iee
 			      int next_frag_len)
 {
 	int rate, mrate, erp, dur, i;
-	struct ieee80211_rate *txrate = tx->rate;
+	struct ieee80211_rate *txrate;
 	struct ieee80211_local *local = tx->local;
 	struct ieee80211_supported_band *sband;
 
-	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+	sband = local->hw.wiphy->bands[tx->channel->band];
+	txrate = &sband->bitrates[tx->rate_idx];
 
 	erp = 0;
 	if (tx->sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
@@ -610,40 +611,40 @@ ieee80211_tx_h_rate_ctrl(struct ieee8021
 	struct rate_selection rsel;
 	struct ieee80211_supported_band *sband;
 
-	sband = tx->local->hw.wiphy->bands[tx->local->hw.conf.channel->band];
+	sband = tx->local->hw.wiphy->bands[tx->channel->band];
 
-	if (likely(!tx->rate)) {
+	if (likely(tx->rate_idx < 0)) {
 		rate_control_get_rate(tx->dev, sband, tx->skb, &rsel);
-		tx->rate = rsel.rate;
-		if (unlikely(rsel.probe)) {
+		tx->rate_idx = rsel.rate_idx;
+		if (unlikely(rsel.probe_idx >= 0)) {
 			tx->control->flags |=
 				IEEE80211_TXCTL_RATE_CTRL_PROBE;
 			tx->flags |= IEEE80211_TX_PROBE_LAST_FRAG;
-			tx->control->alt_retry_rate = tx->rate;
-			tx->rate = rsel.probe;
+			tx->control->alt_retry_rate_idx = tx->rate_idx;
+			tx->rate_idx = rsel.probe_idx;
 		} else
-			tx->control->alt_retry_rate = NULL;
+			tx->control->alt_retry_rate_idx = -1;
 
-		if (!tx->rate)
+		if (unlikely(tx->rate_idx < 0))
 			return TX_DROP;
 	} else
-		tx->control->alt_retry_rate = NULL;
+		tx->control->alt_retry_rate_idx = -1;
 
 	if (tx->sdata->bss_conf.use_cts_prot &&
-	    (tx->flags & IEEE80211_TX_FRAGMENTED) && rsel.nonerp) {
-		tx->last_frag_rate = tx->rate;
-		if (rsel.probe)
+	    (tx->flags & IEEE80211_TX_FRAGMENTED) && (rsel.nonerp_idx >= 0)) {
+		tx->last_frag_rate_idx = tx->rate_idx;
+		if (rsel.probe_idx >= 0)
 			tx->flags &= ~IEEE80211_TX_PROBE_LAST_FRAG;
 		else
 			tx->flags |= IEEE80211_TX_PROBE_LAST_FRAG;
-		tx->rate = rsel.nonerp;
-		tx->control->tx_rate = rsel.nonerp;
+		tx->rate_idx = rsel.nonerp_idx;
+		tx->control->tx_rate_idx = rsel.nonerp_idx;
 		tx->control->flags &= ~IEEE80211_TXCTL_RATE_CTRL_PROBE;
 	} else {
-		tx->last_frag_rate = tx->rate;
-		tx->control->tx_rate = tx->rate;
+		tx->last_frag_rate_idx = tx->rate_idx;
+		tx->control->tx_rate_idx = tx->rate_idx;
 	}
-	tx->control->tx_rate = tx->rate;
+	tx->control->tx_rate_idx = tx->rate_idx;
 
 	return TX_CONTINUE;
 }
@@ -655,6 +656,9 @@ ieee80211_tx_h_misc(struct ieee80211_tx_
 	u16 fc = le16_to_cpu(hdr->frame_control);
 	u16 dur;
 	struct ieee80211_tx_control *control = tx->control;
+	struct ieee80211_supported_band *sband;
+
+	sband = tx->local->hw.wiphy->bands[tx->channel->band];
 
 	if (!control->retry_limit) {
 		if (!is_multicast_ether_addr(hdr->addr1)) {
@@ -681,14 +685,14 @@ ieee80211_tx_h_misc(struct ieee80211_tx_
 		 * frames.
 		 * TODO: The last fragment could still use multiple retry
 		 * rates. */
-		control->alt_retry_rate = NULL;
+		control->alt_retry_rate_idx = -1;
 	}
 
 	/* Use CTS protection for unicast frames sent using extended rates if
 	 * there are associated non-ERP stations and RTS/CTS is not configured
 	 * for the frame. */
 	if ((tx->sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) &&
-	    (tx->rate->flags & IEEE80211_RATE_ERP_G) &&
+	    (sband->bitrates[tx->rate_idx].flags & IEEE80211_RATE_ERP_G) &&
 	    (tx->flags & IEEE80211_TX_UNICAST) &&
 	    tx->sdata->bss_conf.use_cts_prot &&
 	    !(control->flags & IEEE80211_TXCTL_USE_RTS_CTS))
@@ -698,7 +702,7 @@ ieee80211_tx_h_misc(struct ieee80211_tx_
 	 * short preambles at the selected rate and short preambles are
 	 * available on the network at the current point in time. */
 	if (((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) &&
-	    (tx->rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
+	    (sband->bitrates[tx->rate_idx].flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
 	    tx->sdata->bss_conf.use_short_preamble &&
 	    (!tx->sta || test_sta_flags(tx->sta, WLAN_STA_SHORT_PREAMBLE))) {
 		tx->control->flags |= IEEE80211_TXCTL_SHORT_PREAMBLE;
@@ -715,32 +719,32 @@ ieee80211_tx_h_misc(struct ieee80211_tx_
 	if ((control->flags & IEEE80211_TXCTL_USE_RTS_CTS) ||
 	    (control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)) {
 		struct ieee80211_supported_band *sband;
-		struct ieee80211_rate *rate, *baserate;
+		struct ieee80211_rate *rate;
+		s8 baserate = -1;
 		int idx;
 
-		sband = tx->local->hw.wiphy->bands[
-				tx->local->hw.conf.channel->band];
+		sband = tx->local->hw.wiphy->bands[tx->channel->band];
 
 		/* Do not use multiple retry rates when using RTS/CTS */
-		control->alt_retry_rate = NULL;
+		control->alt_retry_rate_idx = -1;
 
 		/* Use min(data rate, max base rate) as CTS/RTS rate */
-		rate = tx->rate;
-		baserate = NULL;
+		rate = &sband->bitrates[tx->rate_idx];
 
 		for (idx = 0; idx < sband->n_bitrates; idx++) {
 			if (sband->bitrates[idx].bitrate > rate->bitrate)
 				continue;
 			if (tx->sdata->basic_rates & BIT(idx) &&
-			    (!baserate ||
-			     (baserate->bitrate < sband->bitrates[idx].bitrate)))
-				baserate = &sband->bitrates[idx];
+			    (baserate < 0 ||
+			     (sband->bitrates[baserate].bitrate
+			      < sband->bitrates[idx].bitrate)))
+				baserate = idx;
 		}
 
-		if (baserate)
-			control->rts_cts_rate = baserate;
+		if (baserate >= 0)
+			control->rts_cts_rate_idx = baserate;
 		else
-			control->rts_cts_rate = &sband->bitrates[0];
+			control->rts_cts_rate_idx = 0;
 	}
 
 	if (tx->sta) {
@@ -768,7 +772,11 @@ ieee80211_tx_h_load_stats(struct ieee802
 	struct sk_buff *skb = tx->skb;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
 	u32 load = 0, hdrtime;
-	struct ieee80211_rate *rate = tx->rate;
+	struct ieee80211_rate *rate;
+	struct ieee80211_supported_band *sband;
+
+	sband = tx->local->hw.wiphy->bands[tx->channel->band];
+	rate = &sband->bitrates[tx->rate_idx];
 
 	/* TODO: this could be part of tx_status handling, so that the number
 	 * of retries would be known; TX rate should in that case be stored
@@ -803,7 +811,7 @@ ieee80211_tx_h_load_stats(struct ieee802
 		for (i = 0; i < tx->num_extra_frag; i++) {
 			load += 2 * hdrtime;
 			load += tx->extra_frag[i]->len *
-				tx->rate->bitrate;
+				rate->bitrate;
 		}
 	}
 
@@ -859,7 +867,7 @@ __ieee80211_parse_tx_radiotap(struct iee
 	int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len);
 	struct ieee80211_tx_control *control = tx->control;
 
-	sband = tx->local->hw.wiphy->bands[tx->local->hw.conf.channel->band];
+	sband = tx->local->hw.wiphy->bands[tx->channel->band];
 
 	control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
 	tx->flags |= IEEE80211_TX_INJECTED;
@@ -899,7 +907,7 @@ __ieee80211_parse_tx_radiotap(struct iee
 				r = &sband->bitrates[i];
 
 				if (r->bitrate == target_rate) {
-					tx->rate = r;
+					tx->rate_idx = i;
 					break;
 				}
 			}
@@ -1098,7 +1106,7 @@ static int __ieee80211_tx(struct ieee802
 			if (__ieee80211_queue_stopped(local, control->queue))
 				return IEEE80211_TX_FRAG_AGAIN;
 			if (i == tx->num_extra_frag) {
-				control->tx_rate = tx->last_frag_rate;
+				control->tx_rate_idx = tx->last_frag_rate_idx;
 
 				if (tx->flags & IEEE80211_TX_PROBE_LAST_FRAG)
 					control->flags |=
@@ -1156,6 +1164,7 @@ static int ieee80211_tx(struct net_devic
 
 	sta = tx.sta;
 	tx.channel = local->hw.conf.channel;
+	control->band = tx.channel->band;
 
 	for (handler = ieee80211_tx_handlers; *handler != NULL;
 	     handler++) {
@@ -1188,7 +1197,7 @@ static int ieee80211_tx(struct net_devic
 				next_len = tx.extra_frag[i + 1]->len;
 			} else {
 				next_len = 0;
-				tx.rate = tx.last_frag_rate;
+				tx.rate_idx = tx.last_frag_rate_idx;
 			}
 			dur = ieee80211_duration(&tx, 0, next_len);
 			hdr->duration_id = cpu_to_le16(dur);
@@ -1225,7 +1234,7 @@ retry:
 		store->skb = skb;
 		store->extra_frag = tx.extra_frag;
 		store->num_extra_frag = tx.num_extra_frag;
-		store->last_frag_rate = tx.last_frag_rate;
+		store->last_frag_rate_idx = tx.last_frag_rate_idx;
 		store->last_frag_rate_ctrl_probe =
 			!!(tx.flags & IEEE80211_TX_PROBE_LAST_FRAG);
 	}
@@ -1681,7 +1690,7 @@ void ieee80211_tx_pending(unsigned long 
 		tx.control = &store->control;
 		tx.extra_frag = store->extra_frag;
 		tx.num_extra_frag = store->num_extra_frag;
-		tx.last_frag_rate = store->last_frag_rate;
+		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;
@@ -1785,9 +1794,10 @@ struct sk_buff *ieee80211_beacon_get(str
 	struct ieee80211_mgmt *mgmt;
 	int *num_beacons;
 	bool err = true;
+	enum ieee80211_band band = local->hw.conf.channel->band;
 	u8 *pos;
 
-	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+	sband = local->hw.wiphy->bands[band];
 
 	rcu_read_lock();
 
@@ -1881,8 +1891,9 @@ struct sk_buff *ieee80211_beacon_get(str
 	}
 
 	if (control) {
+		control->band = band;
 		rate_control_get_rate(local->mdev, sband, skb, &rsel);
-		if (!rsel.rate) {
+		if (unlikely(rsel.rate_idx < 0)) {
 			if (net_ratelimit()) {
 				printk(KERN_DEBUG "%s: ieee80211_beacon_get: "
 				       "no rate found\n",
@@ -1894,9 +1905,9 @@ struct sk_buff *ieee80211_beacon_get(str
 		}
 
 		control->vif = vif;
-		control->tx_rate = rsel.rate;
+		control->tx_rate_idx = rsel.rate_idx;
 		if (sdata->bss_conf.use_short_preamble &&
-		    rsel.rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
+		    sband->bitrates[rsel.rate_idx].flags & IEEE80211_RATE_SHORT_PREAMBLE)
 			control->flags |= IEEE80211_TXCTL_SHORT_PREAMBLE;
 		control->antenna_sel_tx = local->hw.conf.antenna_sel_tx;
 		control->flags |= IEEE80211_TXCTL_NO_ACK;
@@ -2001,6 +2012,7 @@ ieee80211_get_buffered_bc(struct ieee802
 	sta = tx.sta;
 	tx.flags |= IEEE80211_TX_PS_BUFFERED;
 	tx.channel = local->hw.conf.channel;
+	control->band = tx.channel->band;
 
 	for (handler = ieee80211_tx_handlers; *handler != NULL; handler++) {
 		res = (*handler)(&tx);
--- everything.orig/net/mac80211/util.c	2008-04-29 23:13:24.000000000 +0200
+++ everything/net/mac80211/util.c	2008-04-29 23:25:15.000000000 +0200
@@ -266,10 +266,13 @@ __le16 ieee80211_rts_duration(struct iee
 	bool short_preamble;
 	int erp;
 	u16 dur;
+	struct ieee80211_supported_band *sband;
+
+	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
 
 	short_preamble = sdata->bss_conf.use_short_preamble;
 
-	rate = frame_txctl->rts_cts_rate;
+	rate = &sband->bitrates[frame_txctl->rts_cts_rate_idx];
 
 	erp = 0;
 	if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
@@ -300,10 +303,13 @@ __le16 ieee80211_ctstoself_duration(stru
 	bool short_preamble;
 	int erp;
 	u16 dur;
+	struct ieee80211_supported_band *sband;
+
+	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
 
 	short_preamble = sdata->bss_conf.use_short_preamble;
 
-	rate = frame_txctl->rts_cts_rate;
+	rate = &sband->bitrates[frame_txctl->rts_cts_rate_idx];
 	erp = 0;
 	if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
 		erp = rate->flags & IEEE80211_RATE_ERP_G;
--- everything.orig/drivers/net/wireless/adm8211.c	2008-04-29 23:49:15.000000000 +0200
+++ everything/drivers/net/wireless/adm8211.c	2008-04-29 23:50:11.000000000 +0200
@@ -1693,10 +1693,10 @@ static int adm8211_tx(struct ieee80211_h
 	size_t payload_len, hdrlen;
 	int plcp, dur, len, plcp_signal, short_preamble;
 	struct ieee80211_hdr *hdr;
+	struct ieee80211_rate *txrate = ieee80211_get_tx_rate(dev, control);
 
-	short_preamble = !!(control->tx_rate->flags &
-					IEEE80211_TXCTL_SHORT_PREAMBLE);
-	plcp_signal = control->tx_rate->bitrate;
+	short_preamble = !!(txrate->flags & IEEE80211_TXCTL_SHORT_PREAMBLE);
+	plcp_signal = txrate->bitrate;
 
 	hdr = (struct ieee80211_hdr *)skb->data;
 	fc = le16_to_cpu(hdr->frame_control) & ~IEEE80211_FCTL_PROTECTED;
--- everything.orig/drivers/net/wireless/at76_usb.c	2008-04-29 23:44:22.000000000 +0200
+++ everything/drivers/net/wireless/at76_usb.c	2008-04-29 23:44:48.000000000 +0200
@@ -1753,7 +1753,7 @@ static int at76_mac80211_tx(struct ieee8
 	memset(tx_buffer, 0, sizeof(*tx_buffer));
 	tx_buffer->padding = padding;
 	tx_buffer->wlength = cpu_to_le16(skb->len);
-	tx_buffer->tx_rate = control->tx_rate->hw_value;
+	tx_buffer->tx_rate = ieee80211_get_tx_rate(hw, control)->hw_value;
 	memset(tx_buffer->reserved, 0, sizeof(tx_buffer->reserved));
 	memcpy(tx_buffer->packet, skb->data, skb->len);
 
--- everything.orig/drivers/net/wireless/ath5k/base.c	2008-04-29 23:50:17.000000000 +0200
+++ everything/drivers/net/wireless/ath5k/base.c	2008-04-29 23:52:29.000000000 +0200
@@ -1329,7 +1329,8 @@ ath5k_txbuf_setup(struct ath5k_softc *sc
 
 	ret = ah->ah_setup_tx_desc(ah, ds, pktlen,
 		ieee80211_get_hdrlen_from_skb(skb), AR5K_PKT_TYPE_NORMAL,
-		(sc->power_level * 2), ctl->tx_rate->hw_value,
+		(sc->power_level * 2),
+		ieee80211_get_tx_rate(sc->hw, ctl)->hw_value,
 		ctl->retry_limit, keyidx, 0, flags, 0, 0);
 	if (ret)
 		goto err_unmap;
@@ -2061,7 +2062,8 @@ ath5k_beacon_setup(struct ath5k_softc *s
 	ret = ah->ah_setup_tx_desc(ah, ds, skb->len,
 			ieee80211_get_hdrlen_from_skb(skb),
 			AR5K_PKT_TYPE_BEACON, (sc->power_level * 2),
-			ctl->tx_rate->hw_value, 1, AR5K_TXKEYIX_INVALID,
+			ieee80211_get_tx_rate(sc->hw, ctl)->hw_value,
+			1, AR5K_TXKEYIX_INVALID,
 			antenna, flags, 0, 0);
 	if (ret)
 		goto err_unmap;
@@ -2669,7 +2671,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct
 		memmove(skb->data, skb->data+pad, hdrlen);
 	}
 
-	sc->led_txrate = ctl->tx_rate->hw_value;
+	sc->led_txrate = ieee80211_get_tx_rate(hw, ctl)->hw_value;
 
 	spin_lock_irqsave(&sc->txbuflock, flags);
 	if (list_empty(&sc->txbuf)) {
--- everything.orig/drivers/net/wireless/b43/main.c	2008-04-29 23:52:44.000000000 +0200
+++ everything/drivers/net/wireless/b43/main.c	2008-04-29 23:53:58.000000000 +0200
@@ -1365,7 +1365,7 @@ static void b43_write_beacon_template(st
 	bcn = (const struct ieee80211_mgmt *)(dev->wl->current_beacon->data);
 	len = min((size_t) dev->wl->current_beacon->len,
 		  0x200 - sizeof(struct b43_plcp_hdr6));
-	rate = dev->wl->beacon_txctl.tx_rate->hw_value;
+	rate = ieee80211_get_tx_rate(dev->wl->hw, &dev->wl->beacon_txctl)->hw_value;
 
 	b43_write_template_common(dev, (const u8 *)bcn,
 				  len, ram_offset, shm_size_offset, rate);
--- everything.orig/drivers/net/wireless/b43/xmit.c	2008-04-29 23:54:15.000000000 +0200
+++ everything/drivers/net/wireless/b43/xmit.c	2008-04-29 23:58:24.000000000 +0200
@@ -201,13 +201,14 @@ int b43_generate_txhdr(struct b43_wldev 
 	u32 mac_ctl = 0;
 	u16 phy_ctl = 0;
 	u8 extra_ft = 0;
+	struct ieee80211_rate *txrate;
 
 	memset(txhdr, 0, sizeof(*txhdr));
 
-	WARN_ON(!txctl->tx_rate);
-	rate = txctl->tx_rate ? txctl->tx_rate->hw_value : B43_CCK_RATE_1MB;
+	txrate = ieee80211_get_tx_rate(dev->wl->hw, txctl);
+	rate = txrate ? txrate->hw_value : B43_CCK_RATE_1MB;
 	rate_ofdm = b43_is_ofdm_rate(rate);
-	fbrate = txctl->alt_retry_rate ? : txctl->tx_rate;
+	fbrate = ieee80211_get_alt_retry_rate(dev->wl->hw, txctl) ? : txrate;
 	rate_fb = fbrate->hw_value;
 	rate_fb_ofdm = b43_is_ofdm_rate(rate_fb);
 
@@ -336,9 +337,11 @@ int b43_generate_txhdr(struct b43_wldev 
 		int rts_rate, rts_rate_fb;
 		int rts_rate_ofdm, rts_rate_fb_ofdm;
 		struct b43_plcp_hdr6 *plcp;
+		struct ieee80211_rate *rts_cts_rate;
 
-		WARN_ON(!txctl->rts_cts_rate);
-		rts_rate = txctl->rts_cts_rate ? txctl->rts_cts_rate->hw_value : B43_CCK_RATE_1MB;
+		rts_cts_rate = ieee80211_get_rts_cts_rate(dev->wl->hw, txctl);
+
+		rts_rate = rts_cts_rate ? rts_cts_rate->hw_value : B43_CCK_RATE_1MB;
 		rts_rate_ofdm = b43_is_ofdm_rate(rts_rate);
 		rts_rate_fb = b43_calc_fallback_rate(rts_rate);
 		rts_rate_fb_ofdm = b43_is_ofdm_rate(rts_rate_fb);
--- everything.orig/drivers/net/wireless/b43legacy/xmit.c	2008-04-29 23:58:40.000000000 +0200
+++ everything/drivers/net/wireless/b43legacy/xmit.c	2008-04-30 00:00:33.000000000 +0200
@@ -201,15 +201,18 @@ static int generate_txhdr_fw3(struct b43
 	unsigned int plcp_fragment_len;
 	u32 mac_ctl = 0;
 	u16 phy_ctl = 0;
+	struct ieee80211_rate *tx_rate;
 
 	wlhdr = (const struct ieee80211_hdr *)fragment_data;
 	fctl = le16_to_cpu(wlhdr->frame_control);
 
 	memset(txhdr, 0, sizeof(*txhdr));
 
-	rate = txctl->tx_rate->hw_value;
+	tx_rate = ieee80211_get_tx_rate(dev->wl->hw, txctl);
+
+	rate = tx_rate->hw_value;
 	rate_ofdm = b43legacy_is_ofdm_rate(rate);
-	rate_fb = txctl->alt_retry_rate ? : txctl->tx_rate;
+	rate_fb = ieee80211_get_alt_retry_rate(dev->wl->hw, txctl) ? : tx_rate;
 	rate_fb_ofdm = b43legacy_is_ofdm_rate(rate_fb->hw_value);
 
 	txhdr->mac_frame_ctl = wlhdr->frame_control;
@@ -312,7 +315,7 @@ static int generate_txhdr_fw3(struct b43
 		int rts_rate_ofdm;
 		int rts_rate_fb_ofdm;
 
-		rts_rate = txctl->rts_cts_rate->hw_value;
+		rts_rate = ieee80211_get_rts_cts_rate(dev->wl->hw, txctl)->hw_value;
 		rts_rate_ofdm = b43legacy_is_ofdm_rate(rts_rate);
 		rts_rate_fb = b43legacy_calc_fallback_rate(rts_rate);
 		rts_rate_fb_ofdm = b43legacy_is_ofdm_rate(rts_rate_fb);
--- everything.orig/drivers/net/wireless/iwlwifi/iwl-3945-rs.c	2008-04-30 00:05:11.000000000 +0200
+++ everything/drivers/net/wireless/iwlwifi/iwl-3945-rs.c	2008-04-30 00:08:12.000000000 +0200
@@ -465,7 +465,7 @@ static void rs_tx_status(void *priv_rate
 
 
 	retries = tx_resp->retry_count;
-	first_index = tx_resp->control.tx_rate->hw_value;
+	first_index = sband->bitrates[tx_resp->control.tx_rate_idx].hw_value;
 	if ((first_index < 0) || (first_index >= IWL_RATE_COUNT)) {
 		IWL_DEBUG_RATE("leave: Rate out of bounds: %d\n", first_index);
 		return;
@@ -670,7 +670,7 @@ static void rs_get_rate(void *priv_rate,
 	    is_multicast_ether_addr(hdr->addr1) ||
 	    !sta || !sta->rate_ctrl_priv) {
 		IWL_DEBUG_RATE("leave: No STA priv data to update!\n");
-		sel->rate = rate_lowest(local, sband, sta);
+		sel->rate_idx = rate_lowest_index(local, sband, sta);
 		rcu_read_unlock();
 		return;
 	}
@@ -814,7 +814,7 @@ static void rs_get_rate(void *priv_rate,
 
 	IWL_DEBUG_RATE("leave: %d\n", index);
 
-	sel->rate = &sband->bitrates[sta->txrate_idx];
+	sel->rate_idx = sta->txrate_idx;
 }
 
 static struct rate_control_ops rs_ops = {
--- everything.orig/drivers/net/wireless/iwlwifi/iwl-3945.c	2008-04-30 00:02:07.000000000 +0200
+++ everything/drivers/net/wireless/iwlwifi/iwl-3945.c	2008-04-30 00:13:39.000000000 +0200
@@ -331,7 +331,9 @@ static void iwl3945_rx_reply_tx(struct i
 			tx_resp->rate, tx_resp->failure_frame);
 
 	rate_idx = iwl3945_hwrate_to_plcp_idx(tx_resp->rate);
-	tx_status->control.tx_rate = &priv->ieee_rates[rate_idx];
+	if (tx_status->control.band == IEEE80211_BAND_5GHZ)
+		rate_idx -= IWL_FIRST_OFDM_RATE;
+	tx_status->control.tx_rate_idx = rate_idx;
 	IWL_DEBUG_TX_REPLY("Tx queue reclaim %d\n", index);
 	iwl3945_tx_queue_reclaim(priv, txq_id, index);
 
@@ -966,7 +968,8 @@ void iwl3945_hw_build_tx_cmd_rate(struct
 			      struct ieee80211_hdr *hdr, int sta_id, int tx_id)
 {
 	unsigned long flags;
-	u16 rate_index = min(ctrl->tx_rate->hw_value & 0xffff, IWL_RATE_COUNT - 1);
+	u16 hw_value = ieee80211_get_tx_rate(priv->hw, ctrl)->hw_value;
+	u16 rate_index = min(hw_value & 0xffff, IWL_RATE_COUNT - 1);
 	u16 rate_mask;
 	int rate;
 	u8 rts_retry_limit;
--- everything.orig/drivers/net/wireless/iwlwifi/iwl-4965-rs.c	2008-04-30 00:13:48.000000000 +0200
+++ everything/drivers/net/wireless/iwlwifi/iwl-4965-rs.c	2008-04-30 00:17:28.000000000 +0200
@@ -851,7 +851,7 @@ static void rs_tx_status(void *priv_rate
 	if (priv->band == IEEE80211_BAND_5GHZ)
 		rs_index -= IWL_FIRST_OFDM_RATE;
 
-	if ((tx_resp->control.tx_rate == NULL) ||
+	if ((tx_resp->control.tx_rate_idx < 0) ||
 	    (tbl_type.is_SGI ^
 		!!(tx_resp->control.flags & IEEE80211_TXCTL_SHORT_GI)) ||
 	    (tbl_type.is_fat ^
@@ -865,7 +865,7 @@ static void rs_tx_status(void *priv_rate
 	    (!!(tx_mcs.rate_n_flags & RATE_MCS_GF_MSK) ^
 		!!(tx_resp->control.flags & IEEE80211_TXCTL_GREEN_FIELD)) ||
 	    (hw->wiphy->bands[priv->band]->bitrates[rs_index].bitrate !=
-		tx_resp->control.tx_rate->bitrate)) {
+	     hw->wiphy->bands[tx_resp->control.band]->bitrates[tx_resp->control.tx_rate_idx].bitrate)) {
 		IWL_DEBUG_RATE("initial rate does not match 0x%x\n",
 				tx_mcs.rate_n_flags);
 		goto out;
@@ -2181,7 +2181,7 @@ static void rs_get_rate(void *priv_rate,
 	fc = le16_to_cpu(hdr->frame_control);
 	if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1) ||
 	    !sta || !sta->rate_ctrl_priv) {
-		sel->rate = rate_lowest(local, sband, sta);
+		sel->rate_idx = rate_lowest_index(local, sband, sta);
 		goto out;
 	}
 
@@ -2211,11 +2211,13 @@ static void rs_get_rate(void *priv_rate,
 
 done:
 	if ((i < 0) || (i > IWL_RATE_COUNT)) {
-		sel->rate = rate_lowest(local, sband, sta);
+		sel->rate_idx = rate_lowest_index(local, sband, sta);
 		goto out;
 	}
 
-	sel->rate = &priv->ieee_rates[i];
+	if (sband->band == IEEE80211_BAND_5GHZ)
+		i -= IWL_FIRST_OFDM_RATE;
+	sel->rate_idx = i;
 out:
 	rcu_read_unlock();
 }
--- everything.orig/drivers/net/wireless/iwlwifi/iwl-4965.c	2008-04-30 00:09:05.000000000 +0200
+++ everything/drivers/net/wireless/iwlwifi/iwl-4965.c	2008-04-30 00:12:01.000000000 +0200
@@ -421,14 +421,10 @@ void iwl4965_hwrate_to_tx_control(struct
 		control->flags |= IEEE80211_TXCTL_DUP_DATA;
 	if (rate_n_flags & RATE_MCS_SGI_MSK)
 		control->flags |= IEEE80211_TXCTL_SHORT_GI;
-	/* since iwl4965_hwrate_to_plcp_idx is band indifferent, we always use
-	 * IEEE80211_BAND_2GHZ band as it contains all the rates */
 	rate_index = iwl4965_hwrate_to_plcp_idx(rate_n_flags);
-	if (rate_index == -1)
-		control->tx_rate = NULL;
-	else
-		control->tx_rate =
-			&priv->bands[IEEE80211_BAND_2GHZ].bitrates[rate_index];
+	if (control->band == IEEE80211_BAND_5GHZ)
+		rate_index -= IWL_FIRST_OFDM_RATE;
+	control->tx_rate_idx = rate_index;
 }
 
 /*
@@ -2980,7 +2976,8 @@ void iwl4965_hw_build_tx_cmd_rate(struct
 	u16 fc = le16_to_cpu(hdr->frame_control);
 	u8 rate_plcp;
 	u16 rate_flags = 0;
-	int rate_idx = min(ctrl->tx_rate->hw_value & 0xffff, IWL_RATE_COUNT - 1);
+	u16 hw_value = ieee80211_get_tx_rate(priv->hw, ctrl)->hw_value;
+	int rate_idx = min(hw_value & 0xffff, IWL_RATE_COUNT - 1);
 
 	rate_plcp = iwl4965_rates[rate_idx].plcp;
 
--- everything.orig/drivers/net/wireless/iwlwifi/iwl3945-base.c	2008-04-30 00:00:59.000000000 +0200
+++ everything/drivers/net/wireless/iwlwifi/iwl3945-base.c	2008-04-30 00:01:52.000000000 +0200
@@ -2589,7 +2589,7 @@ static int iwl3945_tx_skb(struct iwl3945
 		goto drop_unlock;
 	}
 
-	if ((ctl->tx_rate->hw_value & 0xFF) == IWL_INVALID_RATE) {
+	if ((ieee80211_get_tx_rate(priv->hw, ctl)->hw_value & 0xFF) == IWL_INVALID_RATE) {
 		IWL_ERROR("ERROR: No TX rate available.\n");
 		goto drop_unlock;
 	}
@@ -6700,7 +6700,7 @@ static int iwl3945_mac_tx(struct ieee802
 	}
 
 	IWL_DEBUG_TX("dev->xmit(%d bytes) at rate 0x%02x\n", skb->len,
-		     ctl->tx_rate->bitrate);
+		     ieee80211_get_tx_rate(hw, ctl)->bitrate);
 
 	if (iwl3945_tx_skb(priv, skb, ctl))
 		dev_kfree_skb_any(skb);
--- everything.orig/drivers/net/wireless/iwlwifi/iwl4965-base.c	2008-04-30 00:08:23.000000000 +0200
+++ everything/drivers/net/wireless/iwlwifi/iwl4965-base.c	2008-04-30 00:08:51.000000000 +0200
@@ -2154,7 +2154,7 @@ static int iwl4965_tx_skb(struct iwl_pri
 		goto drop_unlock;
 	}
 
-	if ((ctl->tx_rate->hw_value & 0xFF) == IWL_INVALID_RATE) {
+	if ((ieee80211_get_tx_rate(priv->hw, ctl)->hw_value & 0xFF) == IWL_INVALID_RATE) {
 		IWL_ERROR("ERROR: No TX rate available.\n");
 		goto drop_unlock;
 	}
@@ -6243,7 +6243,7 @@ static int iwl4965_mac_tx(struct ieee802
 	}
 
 	IWL_DEBUG_TX("dev->xmit(%d bytes) at rate 0x%02x\n", skb->len,
-		     ctl->tx_rate->bitrate);
+		     ieee80211_get_tx_rate(hw, ctl)->bitrate);
 
 	if (iwl4965_tx_skb(priv, skb, ctl))
 		dev_kfree_skb_any(skb);
--- everything.orig/drivers/net/wireless/p54/p54common.c	2008-04-30 00:17:46.000000000 +0200
+++ everything/drivers/net/wireless/p54/p54common.c	2008-04-30 00:18:05.000000000 +0200
@@ -595,7 +595,7 @@ static int p54_tx(struct ieee80211_hw *d
 	txhdr->padding2 = 0;
 
 	/* TODO: add support for alternate retry TX rates */
-	rate = control->tx_rate->hw_value;
+	rate = ieee80211_get_tx_rate(dev, control)->hw_value;
 	if (control->flags & IEEE80211_TXCTL_SHORT_PREAMBLE)
 		rate |= 0x10;
 	if (control->flags & IEEE80211_TXCTL_USE_RTS_CTS)
--- everything.orig/drivers/net/wireless/rt2x00/rt2x00dev.c	2008-04-30 00:18:16.000000000 +0200
+++ everything/drivers/net/wireless/rt2x00/rt2x00dev.c	2008-04-30 00:19:14.000000000 +0200
@@ -648,7 +648,7 @@ void rt2x00lib_write_tx_desc(struct rt2x
 	frame_control = le16_to_cpu(hdr->frame_control);
 	seq_ctrl = le16_to_cpu(hdr->seq_ctrl);
 
-	tx_rate = control->tx_rate->hw_value;
+	tx_rate = ieee80211_get_tx_rate(rt2x00dev->hw, control)->hw_value;
 
 	/*
 	 * Check whether this frame is to be acked
@@ -666,8 +666,8 @@ void rt2x00lib_write_tx_desc(struct rt2x
 			__set_bit(ENTRY_TXD_ACK, &txdesc.flags);
 		} else
 			__clear_bit(ENTRY_TXD_ACK, &txdesc.flags);
-		if (control->rts_cts_rate)
-			tx_rate = control->rts_cts_rate->hw_value;
+		if (control->rts_cts_rate_idx >= 0)
+			tx_rate = ieee80211_get_rts_cts_rate(rt2x00dev->hw, control)->hw_value;
 	}
 
 	rate = rt2x00_get_rate(tx_rate);
--- everything.orig/drivers/net/wireless/rtl8180_dev.c	2008-04-29 23:45:07.000000000 +0200
+++ everything/drivers/net/wireless/rtl8180_dev.c	2008-04-29 23:48:00.000000000 +0200
@@ -257,24 +257,21 @@ static int rtl8180_tx(struct ieee80211_h
 	mapping = pci_map_single(priv->pdev, skb->data,
 				 skb->len, PCI_DMA_TODEVICE);
 
-	BUG_ON(!control->tx_rate);
-
 	tx_flags = RTL8180_TX_DESC_FLAG_OWN | RTL8180_TX_DESC_FLAG_FS |
 		   RTL8180_TX_DESC_FLAG_LS |
-		   (control->tx_rate->hw_value << 24) | skb->len;
+		   (ieee80211_get_tx_rate(dev, control)->hw_value << 24) |
+		   skb->len;
 
 	if (priv->r8185)
 		tx_flags |= RTL8180_TX_DESC_FLAG_DMA |
 			    RTL8180_TX_DESC_FLAG_NO_ENC;
 
 	if (control->flags & IEEE80211_TXCTL_USE_RTS_CTS) {
-		BUG_ON(!control->rts_cts_rate);
 		tx_flags |= RTL8180_TX_DESC_FLAG_RTS;
-		tx_flags |= control->rts_cts_rate->hw_value << 19;
+		tx_flags |= ieee80211_get_rts_cts_rate(dev, control)->hw_value << 19;
 	} else if (control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT) {
-		BUG_ON(!control->rts_cts_rate);
 		tx_flags |= RTL8180_TX_DESC_FLAG_CTS;
-		tx_flags |= control->rts_cts_rate->hw_value << 19;
+		tx_flags |= ieee80211_get_rts_cts_rate(dev, control)->hw_value << 19;
 	}
 
 	*((struct ieee80211_tx_control **) skb->cb) =
@@ -288,9 +285,9 @@ static int rtl8180_tx(struct ieee80211_h
 		unsigned int remainder;
 
 		plcp_len = DIV_ROUND_UP(16 * (skb->len + 4),
-					(control->tx_rate->bitrate * 2) / 10);
+				(ieee80211_get_tx_rate(dev, control)->bitrate * 2) / 10);
 		remainder = (16 * (skb->len + 4)) %
-			    ((control->tx_rate->bitrate * 2) / 10);
+			    ((ieee80211_get_tx_rate(dev, control)->bitrate * 2) / 10);
 		if (remainder > 0 && remainder <= 6)
 			plcp_len |= 1 << 15;
 	}
@@ -303,8 +300,8 @@ static int rtl8180_tx(struct ieee80211_h
 	entry->plcp_len = cpu_to_le16(plcp_len);
 	entry->tx_buf = cpu_to_le32(mapping);
 	entry->frame_len = cpu_to_le32(skb->len);
-	entry->flags2 = control->alt_retry_rate != NULL ?
-			control->alt_retry_rate->bitrate << 4 : 0;
+	entry->flags2 = control->alt_retry_rate_idx >= 0 ?
+		ieee80211_get_alt_retry_rate(dev, control)->bitrate << 4 : 0;
 	entry->retry_limit = control->retry_limit;
 	entry->flags = cpu_to_le32(tx_flags);
 	__skb_queue_tail(&ring->queue, skb);
--- everything.orig/drivers/net/wireless/rtl8187_dev.c	2008-04-29 23:48:15.000000000 +0200
+++ everything/drivers/net/wireless/rtl8187_dev.c	2008-04-29 23:49:04.000000000 +0200
@@ -179,21 +179,17 @@ static int rtl8187_tx(struct ieee80211_h
 	flags = skb->len;
 	flags |= RTL8187_TX_FLAG_NO_ENCRYPT;
 
-	BUG_ON(!control->tx_rate);
-
-	flags |= control->tx_rate->hw_value << 24;
+	flags |= ieee80211_get_tx_rate(dev, control)->hw_value << 24;
 	if (ieee80211_get_morefrag((struct ieee80211_hdr *)skb->data))
 		flags |= RTL8187_TX_FLAG_MORE_FRAG;
 	if (control->flags & IEEE80211_TXCTL_USE_RTS_CTS) {
-		BUG_ON(!control->rts_cts_rate);
 		flags |= RTL8187_TX_FLAG_RTS;
-		flags |= control->rts_cts_rate->hw_value << 19;
+		flags |= ieee80211_get_rts_cts_rate(dev, control)->hw_value << 19;
 		rts_dur = ieee80211_rts_duration(dev, priv->vif,
 						 skb->len, control);
 	} else if (control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT) {
-		BUG_ON(!control->rts_cts_rate);
 		flags |= RTL8187_TX_FLAG_CTS;
-		flags |= control->rts_cts_rate->hw_value << 19;
+		flags |= ieee80211_get_rts_cts_rate(dev, control)->hw_value << 19;
 	}
 
 	hdr = (struct rtl8187_tx_hdr *)skb_push(skb, sizeof(*hdr));
--- everything.orig/drivers/net/wireless/zd1211rw/zd_mac.c	2008-04-30 00:19:42.000000000 +0200
+++ everything/drivers/net/wireless/zd1211rw/zd_mac.c	2008-04-30 00:21:03.000000000 +0200
@@ -523,14 +523,17 @@ static int fill_ctrlset(struct zd_mac *m
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
 	unsigned int frag_len = skb->len + FCS_LEN;
 	unsigned int packet_length;
+	struct ieee80211_rate *txrate;
 	struct zd_ctrlset *cs = (struct zd_ctrlset *)
 		skb_push(skb, sizeof(struct zd_ctrlset));
 
 	ZD_ASSERT(frag_len <= 0xffff);
 
-	cs->modulation = control->tx_rate->hw_value;
+	txrate = ieee80211_get_tx_rate(mac->hw, control);
+
+	cs->modulation = txrate->hw_value;
 	if (control->flags & IEEE80211_TXCTL_SHORT_PREAMBLE)
-		cs->modulation = control->tx_rate->hw_value_short;
+		cs->modulation = txrate->hw_value_short;
 
 	cs->tx_length = cpu_to_le16(frag_len);
 

-- 

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

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [RFC/RFT 2/4] GSO: generalize for mac80211
  2008-04-30 12:40 [RFC/RFT 0/4] mac80211 QoS-related enhancements Johannes Berg
  2008-04-30 12:40 ` [RFC/RFT 1/4] mac80211: use rate index in TX control Johannes Berg
@ 2008-04-30 12:40 ` Johannes Berg
       [not found]   ` <20080430130049.359549000-cdvu00un1VgdHxzADdlk8Q@public.gmane.org>
  2008-04-30 12:40 ` [RFC/RFT 3/4] mac80211: use GSO for fragmentation Johannes Berg
  2008-04-30 12:40 ` [RFC/RFT 4/4] mac80211: use multi-queue master netdevice Johannes Berg
  3 siblings, 1 reply; 41+ messages in thread
From: Johannes Berg @ 2008-04-30 12:40 UTC (permalink / raw)
  To: linux-wireless
  Cc: netdev, Ron Rindjunsky, Tomas Winkler, Ivo van Doorn,
	Peter P Waskiewicz Jr, Herbert Xu

[-- Attachment #1: 037-generalize-gso-functions.patch --]
[-- Type: text/plain, Size: 3793 bytes --]

This patch adds a new function dev_skb_segment() that generalises
the existing dev_gso_segment() by allowing to give a segmentation
function. mac80211 will use that function using the segmentation
function skb_segment().

This patch also changes dev_gso_skb_destructor() to be safe when
the skb no longer has any segments, this will happen when mac80211
has internally passed off all the fragments to the driver instead
of asking dev_hard_start_xmit() to do it (which protects against
this by resetting the destructor if it has sent all fragments.)

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: Herbert Xu <herbert@gondor.apana.org.au>
---
 include/linux/netdevice.h |    1 +
 net/core/dev.c            |   38 ++++++++++++++++++++++++++++++--------
 2 files changed, 31 insertions(+), 8 deletions(-)

--- everything.orig/net/core/dev.c	2008-04-30 01:52:23.000000000 +0200
+++ everything/net/core/dev.c	2008-04-30 03:46:33.000000000 +0200
@@ -1491,13 +1491,13 @@ static void dev_gso_skb_destructor(struc
 {
 	struct dev_gso_cb *cb;
 
-	do {
+	while (skb->next) {
 		struct sk_buff *nskb = skb->next;
 
 		skb->next = nskb->next;
 		nskb->next = NULL;
 		kfree_skb(nskb);
-	} while (skb->next);
+	}
 
 	cb = DEV_GSO_CB(skb);
 	if (cb->destructor)
@@ -1505,22 +1505,31 @@ static void dev_gso_skb_destructor(struc
 }
 
 /**
- *	dev_gso_segment - Perform emulated hardware segmentation on skb.
+ *	dev_skb_segment - Perform segmentation on skb.
  *	@skb: buffer to segment
+ *	@segmfn: function that does the actual segmentation
  *
- *	This function segments the given skb and stores the list of segments
- *	in skb->next.
+ *	This function segments the given skb and stores the list
+ *	of segments in skb->next. The segmentation function is
+ *	used to do the actual segmentation, it must return the
+ *	list of segments as chained via the returned skb's @next
+ *	pointer. The segmentation function is given the skb to
+ *	segment and the features of the device the skb is going
+ *	to. The segmentation function needs to return an ERR_PTR
+ *	or a valid sk_buff pointer (or NULL for no segments.)
+ *
+ *	Note that segmentation needs the skbs @cb data.
  */
-static int dev_gso_segment(struct sk_buff *skb)
+int dev_skb_segment(struct sk_buff *skb, struct sk_buff *(segmfn)(struct sk_buff *skb, int feat))
 {
 	struct net_device *dev = skb->dev;
 	struct sk_buff *segs;
 	int features = dev->features & ~(illegal_highdma(dev, skb) ?
 					 NETIF_F_SG : 0);
 
-	segs = skb_gso_segment(skb, features);
+	segs = segmfn(skb, features);
 
-	/* Verifying header integrity only. */
+	/* Verifying header integrity only/no segments required. */
 	if (!segs)
 		return 0;
 
@@ -1533,6 +1542,19 @@ static int dev_gso_segment(struct sk_buf
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(dev_skb_segment);
+
+/**
+ *	dev_gso_segment - Perform emulated hardware segmentation on skb.
+ *	@skb: buffer to segment
+ *
+ *	This function segments the given skb and stores the list of segments
+ *	in skb->next.
+ */
+static int dev_gso_segment(struct sk_buff *skb)
+{
+	return dev_skb_segment(skb, skb_gso_segment);
+}
 
 int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
--- everything.orig/include/linux/netdevice.h	2008-04-30 01:52:23.000000000 +0200
+++ everything/include/linux/netdevice.h	2008-04-30 01:52:26.000000000 +0200
@@ -1454,6 +1454,7 @@ extern int		weight_p;
 extern int		netdev_set_master(struct net_device *dev, struct net_device *master);
 extern int skb_checksum_help(struct sk_buff *skb);
 extern struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features);
+extern int dev_skb_segment(struct sk_buff *skb, struct sk_buff *(segmfn)(struct sk_buff *skb, int feat));
 #ifdef CONFIG_BUG
 extern void netdev_rx_csum_fault(struct net_device *dev);
 #else

-- 


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [RFC/RFT 3/4] mac80211: use GSO for fragmentation
  2008-04-30 12:40 [RFC/RFT 0/4] mac80211 QoS-related enhancements Johannes Berg
  2008-04-30 12:40 ` [RFC/RFT 1/4] mac80211: use rate index in TX control Johannes Berg
  2008-04-30 12:40 ` [RFC/RFT 2/4] GSO: generalize for mac80211 Johannes Berg
@ 2008-04-30 12:40 ` Johannes Berg
  2008-05-07  7:10   ` Herbert Xu
  2008-04-30 12:40 ` [RFC/RFT 4/4] mac80211: use multi-queue master netdevice Johannes Berg
  3 siblings, 1 reply; 41+ messages in thread
From: Johannes Berg @ 2008-04-30 12:40 UTC (permalink / raw)
  To: linux-wireless
  Cc: netdev, Ron Rindjunsky, Tomas Winkler, Ivo van Doorn,
	Peter P Waskiewicz Jr, Herbert Xu

[-- Attachment #1: 038-mac80211-use-gso-infrastructure.patch --]
[-- Type: text/plain, Size: 39519 bytes --]

This patch makes mac80211 use the GSO infrastructure for segmentation,
but not really all of it because we do not register a protocol handler
(nor can do that easily since a lot of functions need to be done before
segmentation and another bunch afterwards, and we need to keep rcu-
protected structures for both.)

Hence, the way it works is that if a packet is to be fragmented then
we call dev_skb_segment() on it and get a fragment list in skb->next.
We then go to pass this fragment list to the driver one by one but if
at any point the driver is unable to handle more data on its queues
we reject the skb (with the remaining fragments) which will cause it
to be stored into dev->gso_skb and handed to us (once the queue is
woken again) fragment by fragment which we detect and hand on to the
driver.

This has the benefit of removing a lot of code that is rarely used
(since fragmentation in IEEE 802.11 isn't all that common any more)
and is rather complex.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: Herbert Xu <herbert@gondor.apana.org.au>
---
Herbert, would you mind taking a look at ieee80211_tx_h_fragment and
__ieee80211_tx?

The diffstat is negative despite adding lots of comments :)

 include/net/mac80211.h     |   11 
 net/mac80211/ieee80211_i.h |   19 -
 net/mac80211/main.c        |   33 +-
 net/mac80211/mlme.c        |    1 
 net/mac80211/tx.c          |  589 +++++++++++++++++++++------------------------
 net/mac80211/util.c        |   30 --
 net/mac80211/wep.c         |   28 +-
 net/mac80211/wme.c         |    9 
 net/mac80211/wpa.c         |   46 ++-
 9 files changed, 370 insertions(+), 396 deletions(-)

--- everything.orig/net/mac80211/ieee80211_i.h	2008-04-30 03:44:52.000000000 +0200
+++ everything/net/mac80211/ieee80211_i.h	2008-04-30 13:37:40.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;
 };
@@ -213,20 +207,20 @@ struct ieee80211_rx_data {
 #define IEEE80211_TXPD_REQUEUE		BIT(2)
 #define IEEE80211_TXPD_EAPOL_FRAME	BIT(3)
 #define IEEE80211_TXPD_AMPDU		BIT(4)
+/* ALWAYS(!) set the _PRESENT bit */
+#define IEEE80211_TXPD_PRESENT		BIT(19)
 /* 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;
 };
 
@@ -612,8 +606,6 @@ struct ieee80211_local {
 	struct timer_list sta_cleanup;
 
 	unsigned long state[IEEE80211_MAX_AMPDU_QUEUES + IEEE80211_MAX_AMPDU_QUEUES];
-	struct ieee80211_tx_stored_packet pending_packet[IEEE80211_MAX_AMPDU_QUEUES + IEEE80211_MAX_AMPDU_QUEUES];
-	struct tasklet_struct tx_pending_tasklet;
 
 	/* number of interfaces with corresponding IFF_ flags */
 	atomic_t iff_allmultis, iff_promiscs;
@@ -846,7 +838,6 @@ static inline struct ieee80211_hw *local
 
 enum ieee80211_link_state_t {
 	IEEE80211_LINK_STATE_XOFF = 0,
-	IEEE80211_LINK_STATE_PENDING,
 };
 
 struct sta_attribute {
@@ -968,8 +959,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);
--- everything.orig/net/mac80211/tx.c	2008-04-30 03:44:52.000000000 +0200
+++ everything/net/mac80211/tx.c	2008-04-30 13:55:23.000000000 +0200
@@ -2,7 +2,7 @@
  * Copyright 2002-2005, Instant802 Networks, Inc.
  * Copyright 2005-2006, Devicescape Software, Inc.
  * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz>
- * Copyright 2007	Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2007-2008	Johannes Berg <johannes@sipsolutions.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,40 @@ 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;
 
 	if (!(tx->flags & IEEE80211_TX_FRAGMENTED))
 		return TX_CONTINUE;
 
-	first = tx->skb;
-
 	hdrlen = ieee80211_get_hdrlen(tx->fc);
-	payload_len = first->len - hdrlen;
+	payload_len = tx->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;
-
-	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;
-
-		if (left <= 0)
-			goto fail;
+	WARN_ON(num_fragm <= 1);
 
-		/* 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);
+	/* skb_segment will push the mac header back in */
+	skb_reset_mac_header(tx->skb);
+	__skb_pull(tx->skb, hdrlen);
 
-		pos += copylen;
-		left -= copylen;
-	}
-	skb_trim(first, hdrlen + per_fragm);
+	skb_shinfo(tx->skb)->gso_size = per_fragm;
+	skb_shinfo(tx->skb)->nr_frags = 0;
 
-	tx->num_extra_frag = num_fragm - 1;
-	tx->extra_frag = frags;
+	/*
+	 * Now segment (fragment) the frame. This will allocate all the
+	 * segments and 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 reject the skb completely
+	 * (with the remaining fragments still in it) which will make the
+	 * upper layer code store it away specially and try to retransmit
+	 * the fragments one by one.
+	 */
+	if (dev_skb_segment(tx->skb, skb_segment))
+		return TX_DROP;
 
 	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 +682,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,17 +717,17 @@ ieee80211_tx_h_misc(struct ieee80211_tx_
 	}
 
 	if (tx->sta) {
+		struct sk_buff *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;
-			}
+		skb = tx->skb;
+		if (!skb->next) {
+			tx->sta->tx_fragments++;
+			tx->sta->tx_bytes += skb->len;
+		}
+		while ((skb = skb->next)) {
+			tx->sta->tx_fragments++;
+			tx->sta->tx_bytes += skb->len;
 		}
 	}
 
@@ -804,15 +773,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 +836,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;
 
 	/*
@@ -1075,63 +1041,123 @@ static int ieee80211_tx_prepare(struct i
 	return 0;
 }
 
+#ifdef DEBUG_PRINT_CONTROL_TX
+static void print_control(struct ieee80211_tx_control *c, struct sk_buff *skb)
+{
+	printk(KERN_DEBUG "skb: %p (%d) fl: 0x%x / rates: %d,%d,%d / retr: %d / vif: %p / key: %p / band: %d / ant: %d / iv,icv: %d,%d / aid: %d, type: %d\n",
+		skb, skb->len, c->flags, c->tx_rate_idx, c->rts_cts_rate_idx, c->alt_retry_rate_idx,
+		c->retry_limit, c->vif, c->hw_key, c->band, c->antenna_sel_tx, c->iv_len, c->icv_len, c->aid, c->type);
+	print_hex_dump(KERN_DEBUG, "skb: ", DUMP_PREFIX_OFFSET, 32, 1, skb->data, skb->len, 1);
+}
+#else
+static inline void
+print_control(struct ieee80211_tx_control *c, struct sk_buff *skb) {}
+#endif
+
+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;
+
+	print_control(control, skb);
+
+	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.)
+	 *
+	 * Functionally, we could reject the frame here if it has segments
+	 * and wait for the upper layer to hand us each of the fragments
+	 * again, but in the common case we can simply enqueue all of the
+	 * fragments to the hardware right away so optimise for that.
+	 */
+
+	if (skb->next) {
+		origskb = skb;
+		skb = skb->next;
+	}
+
+	while (1) {
+		control->flags &= ~unsetflags;
+
+		/* Try to transmit this fragment */
+
+		next = skb->next;
+		/*
+		 * skb->next must be NULL so the driver (or mac80211 in status
+		 * handling) is able to put it onto a queue
+		 */
+		skb->next = NULL;
+		ret = ___ieee80211_tx(local, skb, control);
+
+		/*
+		 * If we return here, and the skb has fragments, then we're
+		 * never going to see the skb itself (origskb) again since
+		 * the generic xmit code will then just hand us the segments
+		 * one by one.
+		 */
+		if (unlikely(ret != NETDEV_TX_OK)) {
+			/* important: restore skb->next on errors */
+			skb->next = next;
+			BUG_ON(control->flags & IEEE80211_TXCTL_TXPD_PRESENT);
+			/* 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;
 			}
+			return ret;
+		}
 
-			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;
+		/* 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;
+
+		/* all ok if this was the last */
+		if (!skb) {
+			if (origskb)
+				kfree_skb(origskb);
+			return NETDEV_TX_OK;
 		}
-		kfree(tx->extra_frag);
-		tx->extra_frag = NULL;
 	}
-	return IEEE80211_TX_OK;
+
+	return NETDEV_TX_OK;
 }
 
 static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
@@ -1139,12 +1165,11 @@ static int ieee80211_tx(struct net_devic
 {
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct sta_info *sta;
+	struct sk_buff *frag;
 	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, num = 0;
 
 	if (unlikely(skb->len < 10)) {
 		dev_kfree_skb(skb);
@@ -1186,74 +1211,70 @@ static int ieee80211_tx(struct net_devic
 		return 0;
 	}
 
-	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);
+	/* fragmented: update duration/seq/flags of fragments */
+	frag = skb;
+	while ((frag = frag->next)) {
+		struct ieee80211_hdr *hdr = (void *)frag->data;
+		int next_len, dur;
+		__le16 morefrags = cpu_to_le16(IEEE80211_FCTL_MOREFRAGS);
+
+		if (frag->next) {
+			hdr->frame_control |= morefrags;
+			next_len = frag->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(num & IEEE80211_SCTL_FRAG);
+		num++;
 	}
 
-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);
 	rcu_read_unlock();
 	return 0;
 }
 
 /* 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 sta_info *sta;
+	struct ieee80211_key *key;
+	struct ieee80211_hdr *hdr = (void *)skb->data;
+
+	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+		if (ctl->vif != &sdata->vif)
+			continue;
+
+		sta = sta_info_get(local, ieee80211_get_DA(hdr));
+		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;
+}
+
 int ieee80211_master_start_xmit(struct sk_buff *skb,
 				struct net_device *dev)
 {
@@ -1264,11 +1285,38 @@ 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));
+
+	if (!(pkt_data->flags & IEEE80211_TXPD_PRESENT)) {
+		/*
+		 * Not a frame with pkt_data, it has tx_control instead!
+		 * We set the TXPD_PRESENT bit in all tx_packet_data
+		 * instances so we know that we got this frame from the
+		 * subif and not because of fragment requeuing. This here
+		 * is the fragment requeuing path (although it may also
+		 * be entered if somebody injects a packet on the master
+		 * interface... which will be dropped right away because
+		 * it has no useful control data in skb->cb.)
+		 */
+		struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+		rcu_read_lock();
+		if (!validate_control(local, (void *)skb->cb, skb)) {
+			if (net_ratelimit())
+				printk(KERN_DEBUG "dropped re-queued skb %p\n", skb);
+			dev_kfree_skb(skb);
+			rcu_read_unlock();
+			return NETDEV_TX_OK;
+		}
+		memcpy(&control, skb->cb, sizeof(control));
+		memset(skb->cb, 0, sizeof(skb->cb));
+		ret = ___ieee80211_tx(local, skb, &control);
+		if (ret != NETDEV_TX_OK)
+			memcpy(skb->cb, &control, sizeof(control));
+		rcu_read_unlock();
+		return ret;
+	}
+
+	memset(&control, 0, sizeof(control));
 
 	if (pkt_data->ifindex)
 		odev = dev_get_by_index(&init_net, pkt_data->ifindex);
@@ -1296,7 +1344,6 @@ int ieee80211_master_start_xmit(struct s
 	}
 
 	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)
@@ -1309,6 +1356,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);
 
@@ -1553,19 +1601,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);
 
@@ -1625,6 +1660,7 @@ int ieee80211_subif_start_xmit(struct sk
 	pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
 	memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data));
 	pkt_data->ifindex = dev->ifindex;
+	pkt_data->flags = IEEE80211_TXPD_PRESENT;
 	if (ethertype == ETH_P_PAE)
 		pkt_data->flags |= IEEE80211_TXPD_EAPOL_FRAME;
 
@@ -1651,69 +1687,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-04-30 03:44:52.000000000 +0200
+++ everything/net/mac80211/wep.c	2008-04-30 03:46:39.000000000 +0200
@@ -349,23 +349,29 @@ static int wep_encrypt_skb(struct ieee80
 ieee80211_tx_result
 ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx)
 {
+	struct sk_buff *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) {
-		I802_DEBUG_INC(tx->local->tx_handlers_drop_wep);
-		return TX_DROP;
+	/* not fragmented */
+	if (!tx->skb->next) {
+		if (wep_encrypt_skb(tx, tx->skb) < 0) {
+			I802_DEBUG_INC(tx->local->tx_handlers_drop_wep);
+			return TX_DROP;
+		}
+		return TX_CONTINUE;
 	}
 
-	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;
-			}
+	/* fragmented, encrypt fragments */
+
+	skb = tx->skb;
+	/* tx->skb is the non-fragmented one */
+	while ((skb = skb->next)) {
+		if (wep_encrypt_skb(tx, skb) < 0) {
+			I802_DEBUG_INC(tx->local->tx_handlers_drop_wep);
+			return TX_DROP;
 		}
 	}
 
--- everything.orig/net/mac80211/wpa.c	2008-04-30 03:44:52.000000000 +0200
+++ everything/net/mac80211/wpa.c	2008-04-30 03:46:39.000000000 +0200
@@ -260,16 +260,19 @@ ieee80211_crypto_tkip_encrypt(struct iee
 		return TX_CONTINUE;
 	}
 
-	if (tkip_encrypt_skb(tx, skb, test) < 0)
-		return TX_DROP;
+	/* not fragmented */
+	if (!skb->next) {
+		if (tkip_encrypt_skb(tx, skb, test) < 0)
+			return TX_DROP;
+		return TX_CONTINUE;
+	}
 
-	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;
-		}
+	/* fragmented, encrypt fragments */
+
+	/* tx->skb is the non-fragmented one */
+	while ((skb = skb->next)) {
+		if (tkip_encrypt_skb(tx, skb, test) < 0)
+			return TX_DROP;
 	}
 
 	return TX_CONTINUE;
@@ -509,16 +512,23 @@ ieee80211_crypto_ccmp_encrypt(struct iee
 		return TX_CONTINUE;
 	}
 
-	if (ccmp_encrypt_skb(tx, skb, test) < 0)
-		return TX_DROP;
+	tx->control->iv_len = WEP_IV_LEN;
+	tx->control->icv_len = WEP_ICV_LEN;
+	ieee80211_tx_set_protected(tx);
+
+	/* not fragmented */
+	if (!skb->next) {
+		if (ccmp_encrypt_skb(tx, skb, test) < 0)
+			return TX_DROP;
+		return TX_CONTINUE;
+	}
+
+	/* fragmented, encrypt fragments */
 
-	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;
-		}
+	/* tx->skb is the non-fragmented one */
+	while ((skb = skb->next)) {
+		if (ccmp_encrypt_skb(tx, skb, test) < 0)
+			return TX_DROP;
 	}
 
 	return TX_CONTINUE;
--- everything.orig/net/mac80211/util.c	2008-04-30 03:44:52.000000000 +0200
+++ everything/net/mac80211/util.c	2008-04-30 13:37:40.000000000 +0200
@@ -167,17 +167,13 @@ 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;
+	struct sk_buff *skb = tx->skb;
+	struct ieee80211_hdr *hdr;
 
-	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);
-		}
+	while (skb) {
+		hdr = (struct ieee80211_hdr *) skb->data;
+		hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+		skb = skb->next;
 	}
 }
 
@@ -333,15 +329,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-04-30 03:44:52.000000000 +0200
+++ everything/net/mac80211/wme.c	2008-04-30 13:40:09.000000000 +0200
@@ -236,9 +236,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);
@@ -247,7 +244,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) {
@@ -274,9 +271,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-04-30 03:44:52.000000000 +0200
+++ everything/net/mac80211/main.c	2008-04-30 13:40:19.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));
 
@@ -1299,7 +1304,7 @@ static void ieee80211_remove_tx_extra(st
 
 	pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
 	pkt_data->ifindex = vif_to_sdata(control->vif)->dev->ifindex;
-	pkt_data->flags = 0;
+	pkt_data->flags = IEEE80211_TXPD_PRESENT;
 	if (control->flags & IEEE80211_TXCTL_REQ_TX_STATUS)
 		pkt_data->flags |= IEEE80211_TXPD_REQ_TX_STATUS;
 	if (control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT)
@@ -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);
@@ -1852,7 +1853,6 @@ void ieee80211_unregister_hw(struct ieee
 	struct ieee80211_local *local = hw_to_local(hw);
 	struct ieee80211_sub_if_data *sdata, *tmp;
 
-	tasklet_kill(&local->tx_pending_tasklet);
 	tasklet_kill(&local->tasklet);
 
 	rtnl_lock();
@@ -1886,7 +1886,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);
@@ -1921,6 +1920,10 @@ 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);
+	BUILD_BUG_ON(IEEE80211_TXPD_PRESENT != IEEE80211_TXCTL_TXPD_PRESENT);
 
 	ret = rc80211_pid_init();
 	if (ret)
--- everything.orig/include/net/mac80211.h	2008-04-30 03:44:52.000000000 +0200
+++ everything/include/net/mac80211.h	2008-04-30 13:37:40.000000000 +0200
@@ -3,7 +3,7 @@
  *
  * Copyright 2002-2005, Devicescape Software, Inc.
  * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz>
- * Copyright 2007	Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2007-2008	Johannes Berg <johannes@sipsolutions.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,8 @@ 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_TXPD_PRESENT: Reserved, must never be set.
+ * @IEEE80211_TXCTL_INJECTED: Frame was injected (NOT for driver use!)
  */
 enum mac80211_tx_control_flags {
 	IEEE80211_TXCTL_REQ_TX_STATUS		= (1<<0),
@@ -254,6 +256,8 @@ 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_TXPD_PRESENT		= (1<<19),
+	IEEE80211_TXCTL_INJECTED		= (1<<20),
 };
 
 /* Transmit control fields. This data structure is passed to low-level driver
@@ -278,6 +282,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,8 +297,6 @@ 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 */
 };
--- everything.orig/net/mac80211/mlme.c	2008-04-30 03:44:49.000000000 +0200
+++ everything/net/mac80211/mlme.c	2008-04-30 13:37:38.000000000 +0200
@@ -587,6 +587,7 @@ void ieee80211_sta_tx(struct net_device 
 	pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
 	memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data));
 	pkt_data->ifindex = sdata->dev->ifindex;
+	pkt_data->flags = IEEE80211_TXPD_PRESENT;
 	if (!encrypt)
 		pkt_data->flags |= IEEE80211_TXPD_DO_NOT_ENCRYPT;
 

-- 


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [RFC/RFT 4/4] mac80211: use multi-queue master netdevice
  2008-04-30 12:40 [RFC/RFT 0/4] mac80211 QoS-related enhancements Johannes Berg
                   ` (2 preceding siblings ...)
  2008-04-30 12:40 ` [RFC/RFT 3/4] mac80211: use GSO for fragmentation Johannes Berg
@ 2008-04-30 12:40 ` Johannes Berg
       [not found]   ` <20080430130051.397094000-cdvu00un1VgdHxzADdlk8Q@public.gmane.org>
  3 siblings, 1 reply; 41+ messages in thread
From: Johannes Berg @ 2008-04-30 12:40 UTC (permalink / raw)
  To: linux-wireless-u79uwXL29TY76Z2rM5mHXA
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA, Ron Rindjunsky, Tomas Winkler,
	Ivo van Doorn, Peter P Waskiewicz Jr

[-- Attachment #1: 039-mac80211-mq.patch --]
[-- Type: text/plain, Size: 23314 bytes --]

This patch updates mac80211 and drivers to be multi-queue aware and
use that instead of the internal queue mapping.

Signed-off-by: Johannes Berg <johannes-cdvu00un1VgdHxzADdlk8Q@public.gmane.org>
---
This is just ground-work. We'll be able to play with Qos much more now.

 drivers/net/wireless/ath5k/base.c           |    2 -
 drivers/net/wireless/b43/dma.c              |    7 ++-
 drivers/net/wireless/b43/pio.c              |    8 ++--
 drivers/net/wireless/b43legacy/dma.c        |    2 -
 drivers/net/wireless/iwlwifi/iwl3945-base.c |    4 +-
 drivers/net/wireless/iwlwifi/iwl4965-base.c |    4 +-
 drivers/net/wireless/p54/p54common.c        |   10 ++---
 drivers/net/wireless/rt2x00/rt2x00mac.c     |   12 +++---
 drivers/net/wireless/rt2x00/rt2x00pci.c     |    2 -
 drivers/net/wireless/rt2x00/rt2x00queue.h   |   13 ------
 drivers/net/wireless/rtl8180_dev.c          |    4 +-
 include/net/mac80211.h                      |   12 ++++--
 net/mac80211/Kconfig                        |   14 ++++++-
 net/mac80211/Makefile                       |    2 -
 net/mac80211/ieee80211_i.h                  |    7 ---
 net/mac80211/main.c                         |   21 ++++++----
 net/mac80211/tx.c                           |    9 ----
 net/mac80211/util.c                         |   54 +++++++++++++++++++---------
 net/mac80211/wme.c                          |   13 ++----
 net/mac80211/wme.h                          |    2 -
 20 files changed, 109 insertions(+), 93 deletions(-)

--- everything.orig/drivers/net/wireless/ath5k/base.c	2008-04-30 14:02:32.000000000 +0200
+++ everything/drivers/net/wireless/ath5k/base.c	2008-04-30 14:02:34.000000000 +0200
@@ -2677,7 +2677,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct
 	if (list_empty(&sc->txbuf)) {
 		ATH5K_ERR(sc, "no further txbuf available, dropping packet\n");
 		spin_unlock_irqrestore(&sc->txbuflock, flags);
-		ieee80211_stop_queue(hw, ctl->queue);
+		ieee80211_stop_queue(hw, skb_get_queue_mapping(skb));
 		return -1;
 	}
 	bf = list_first_entry(&sc->txbuf, struct ath5k_buf, list);
--- everything.orig/drivers/net/wireless/iwlwifi/iwl3945-base.c	2008-04-30 14:02:32.000000000 +0200
+++ everything/drivers/net/wireless/iwlwifi/iwl3945-base.c	2008-04-30 14:02:34.000000000 +0200
@@ -2562,7 +2562,7 @@ static int iwl3945_tx_skb(struct iwl3945
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 	struct iwl3945_tfd_frame *tfd;
 	u32 *control_flags;
-	int txq_id = ctl->queue;
+	int txq_id = skb_get_queue_mapping(skb);
 	struct iwl3945_tx_queue *txq = NULL;
 	struct iwl3945_queue *q = NULL;
 	dma_addr_t phys_addr;
@@ -2776,7 +2776,7 @@ static int iwl3945_tx_skb(struct iwl3945
 			spin_unlock_irqrestore(&priv->lock, flags);
 		}
 
-		ieee80211_stop_queue(priv->hw, ctl->queue);
+		ieee80211_stop_queue(priv->hw, skb_get_queue_mapping(skb));
 	}
 
 	return 0;
--- everything.orig/drivers/net/wireless/iwlwifi/iwl4965-base.c	2008-04-30 14:02:32.000000000 +0200
+++ everything/drivers/net/wireless/iwlwifi/iwl4965-base.c	2008-04-30 14:02:34.000000000 +0200
@@ -2126,7 +2126,7 @@ static int iwl4965_tx_skb(struct iwl_pri
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 	struct iwl4965_tfd_frame *tfd;
 	u32 *control_flags;
-	int txq_id = ctl->queue;
+	int txq_id = skb_get_queue_mapping(skb);
 	struct iwl4965_tx_queue *txq = NULL;
 	struct iwl4965_queue *q = NULL;
 	dma_addr_t phys_addr;
@@ -2350,7 +2350,7 @@ static int iwl4965_tx_skb(struct iwl_pri
 			spin_unlock_irqrestore(&priv->lock, flags);
 		}
 
-		ieee80211_stop_queue(priv->hw, ctl->queue);
+		ieee80211_stop_queue(priv->hw, skb_get_queue_mapping(skb));
 	}
 
 	return 0;
--- everything.orig/drivers/net/wireless/p54/p54common.c	2008-04-30 14:02:32.000000000 +0200
+++ everything/drivers/net/wireless/p54/p54common.c	2008-04-30 14:02:34.000000000 +0200
@@ -417,7 +417,7 @@ static void p54_rx_frame_sent(struct iee
 			memcpy(&status.control, range->control,
 			       sizeof(status.control));
 			kfree(range->control);
-			priv->tx_stats[status.control.queue].len--;
+			priv->tx_stats[skb_get_queue_mapping(skb)].len--;
 			entry_hdr = (struct p54_control_hdr *) entry->data;
 			entry_data = (struct p54_tx_control_allocdata *) entry_hdr->data;
 			if ((entry_hdr->magic1 & cpu_to_le16(0x4000)) != 0)
@@ -562,13 +562,13 @@ static int p54_tx(struct ieee80211_hw *d
 	size_t padding, len;
 	u8 rate;
 
-	current_queue = &priv->tx_stats[control->queue];
+	current_queue = &priv->tx_stats[skb_get_queue_mapping(skb)];
 	if (unlikely(current_queue->len > current_queue->limit))
 		return NETDEV_TX_BUSY;
 	current_queue->len++;
 	current_queue->count++;
 	if (current_queue->len == current_queue->limit)
-		ieee80211_stop_queue(dev, control->queue);
+		ieee80211_stop_queue(dev, skb_get_queue_mapping(skb));
 
 	padding = (unsigned long)(skb->data - (sizeof(*hdr) + sizeof(*txhdr))) & 3;
 	len = skb->len;
@@ -605,7 +605,7 @@ static int p54_tx(struct ieee80211_hw *d
 	memset(txhdr->rateset, rate, 8);
 	txhdr->wep_key_present = 0;
 	txhdr->wep_key_len = 0;
-	txhdr->frame_type = cpu_to_le32(control->queue + 4);
+	txhdr->frame_type = cpu_to_le32(skb_get_queue_mapping(skb) + 4);
 	txhdr->magic4 = 0;
 	txhdr->antenna = (control->antenna_sel_tx == 0) ?
 		2 : control->antenna_sel_tx - 1;
@@ -944,7 +944,7 @@ static int p54_conf_tx(struct ieee80211_
 	vdcf = (struct p54_tx_control_vdcf *)(((struct p54_control_hdr *)
 		((void *)priv->cached_vdcf + priv->tx_hdr_len))->data);
 
-	if ((params) && !((queue < 0) || (queue > 4))) {
+	if (params && queue < dev->queues) {
 		P54_SET_QUEUE(vdcf->queue[queue], params->aifs,
 			params->cw_min, params->cw_max, params->txop);
 	} else
--- everything.orig/drivers/net/wireless/rtl8180_dev.c	2008-04-30 14:02:31.000000000 +0200
+++ everything/drivers/net/wireless/rtl8180_dev.c	2008-04-30 14:02:34.000000000 +0200
@@ -251,7 +251,7 @@ static int rtl8180_tx(struct ieee80211_h
 	u16 plcp_len = 0;
 	__le16 rts_duration = 0;
 
-	prio = control->queue;
+	prio = skb_get_queue_mapping(skb);
 	ring = &priv->tx_ring[prio];
 
 	mapping = pci_map_single(priv->pdev, skb->data,
@@ -306,7 +306,7 @@ static int rtl8180_tx(struct ieee80211_h
 	entry->flags = cpu_to_le32(tx_flags);
 	__skb_queue_tail(&ring->queue, skb);
 	if (ring->entries - skb_queue_len(&ring->queue) < 2)
-		ieee80211_stop_queue(dev, control->queue);
+		ieee80211_stop_queue(dev, skb_get_queue_mapping(skb));
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING, (1 << (prio + 4)));
--- everything.orig/include/net/mac80211.h	2008-04-30 14:02:31.000000000 +0200
+++ everything/include/net/mac80211.h	2008-04-30 14:02:34.000000000 +0200
@@ -282,9 +282,6 @@ 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
@@ -1575,6 +1572,15 @@ void ieee80211_wake_queue(struct ieee802
 void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue);
 
 /**
+ * ieee80211_start_queue - start specific queue
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ * @queue: queue number (counted from zero).
+ *
+ * Drivers should use this function instead of netif_start_queue.
+ */
+void ieee80211_start_queue(struct ieee80211_hw *hw, int queue);
+
+/**
  * ieee80211_start_queues - start all queues
  * @hw: pointer to as obtained from ieee80211_alloc_hw().
  *
--- everything.orig/net/mac80211/Kconfig	2008-04-30 14:02:31.000000000 +0200
+++ everything/net/mac80211/Kconfig	2008-04-30 14:25:39.000000000 +0200
@@ -7,11 +7,23 @@ config MAC80211
 	select CRC32
 	select WIRELESS_EXT
 	select CFG80211
-	select NET_SCH_FIFO
 	---help---
 	  This option enables the hardware independent IEEE 802.11
 	  networking stack.
 
+config MAC80211_QOS
+	def_bool y
+	depends on MAC80211
+	depends on NET_SCHED
+	depends on NETDEVICES_MULTIQUEUE
+
+comment "QoS/HT support disabled"
+	depends on !MAC80211_QOS
+comment "QoS/HT support needs CONFIG_NET_SCHED"
+	depends on MAC80211 && !NET_SCHED
+comment "QoS/HT support needs CONFIG_NETDEVICES_MULTIQUEUE"
+	depends on MAC80211 && !NETDEVICES_MULTIQUEUE
+
 menu "Rate control algorithm selection"
 	depends on MAC80211 != n
 
--- everything.orig/net/mac80211/Makefile	2008-04-30 14:02:31.000000000 +0200
+++ everything/net/mac80211/Makefile	2008-04-30 14:02:34.000000000 +0200
@@ -29,7 +29,7 @@ mac80211-y := \
 	event.o
 
 mac80211-$(CONFIG_MAC80211_LEDS) += led.o
-mac80211-$(CONFIG_NET_SCHED) += wme.o
+mac80211-$(CONFIG_MAC80211_QOS) += wme.o
 mac80211-$(CONFIG_MAC80211_DEBUGFS) += \
 	debugfs.o \
 	debugfs_sta.o \
--- everything.orig/net/mac80211/ieee80211_i.h	2008-04-30 14:02:31.000000000 +0200
+++ everything/net/mac80211/ieee80211_i.h	2008-04-30 14:02:34.000000000 +0200
@@ -213,7 +213,6 @@ struct ieee80211_rx_data {
 struct ieee80211_tx_packet_data {
 	u32 flags;
 	int ifindex;
-	u16 queue;
 	unsigned long jiffies;
 };
 
@@ -605,8 +604,6 @@ struct ieee80211_local {
 	struct sta_info *sta_hash[STA_HASH_SIZE];
 	struct timer_list sta_cleanup;
 
-	unsigned long state[IEEE80211_MAX_AMPDU_QUEUES + IEEE80211_MAX_AMPDU_QUEUES];
-
 	/* number of interfaces with corresponding IFF_ flags */
 	atomic_t iff_allmultis, iff_promiscs;
 
@@ -836,10 +833,6 @@ static inline struct ieee80211_hw *local
 	return &local->hw;
 }
 
-enum ieee80211_link_state_t {
-	IEEE80211_LINK_STATE_XOFF = 0,
-};
-
 struct sta_attribute {
 	struct attribute attr;
 	ssize_t (*show)(const struct sta_info *, char *buf);
--- everything.orig/net/mac80211/main.c	2008-04-30 14:02:31.000000000 +0200
+++ everything/net/mac80211/main.c	2008-04-30 14:18:53.000000000 +0200
@@ -1313,7 +1313,6 @@ static void ieee80211_remove_tx_extra(st
 		pkt_data->flags |= IEEE80211_TXPD_REQUEUE;
 	if (control->flags & IEEE80211_TXCTL_EAPOL_FRAME)
 		pkt_data->flags |= IEEE80211_TXPD_EAPOL_FRAME;
-	pkt_data->queue = control->queue;
 
 	hdrlen = ieee80211_get_hdrlen_from_skb(skb);
 
@@ -1713,9 +1712,20 @@ int ieee80211_register_hw(struct ieee802
 	if (result < 0)
 		return result;
 
+#ifdef CONFIG_MAC80211_QOS
+	if (hw->queues > IEEE80211_MAX_QUEUES)
+		hw->queues = IEEE80211_MAX_QUEUES;
+	if (hw->ampdu_queues > IEEE80211_MAX_AMPDU_QUEUES)
+		hw->ampdu_queues = IEEE80211_MAX_AMPDU_QUEUES;
+#else
+	hw->queues = 1;
+	hw->ampdu_queues = 0;
+#endif
+
 	/* for now, mdev needs sub_if_data :/ */
-	mdev = alloc_netdev(sizeof(struct ieee80211_sub_if_data),
-			    "wmaster%d", ether_setup);
+	mdev = alloc_netdev_mq(sizeof(struct ieee80211_sub_if_data),
+			       "wmaster%d", ether_setup,
+			       hw->queues + hw->ampdu_queues);
 	if (!mdev)
 		goto fail_mdev_alloc;
 
@@ -1807,11 +1817,6 @@ int ieee80211_register_hw(struct ieee802
 		goto fail_wep;
 	}
 
-	if (hw->queues > IEEE80211_MAX_QUEUES)
-		hw->queues = IEEE80211_MAX_QUEUES;
-	if (hw->ampdu_queues > IEEE80211_MAX_AMPDU_QUEUES)
-		hw->ampdu_queues = IEEE80211_MAX_AMPDU_QUEUES;
-
 	ieee80211_install_qdisc(local->mdev);
 
 	/* add one default STA interface */
--- everything.orig/net/mac80211/tx.c	2008-04-30 14:02:31.000000000 +0200
+++ everything/net/mac80211/tx.c	2008-04-30 14:02:34.000000000 +0200
@@ -210,12 +210,6 @@ static u16 ieee80211_duration(struct iee
 	return dur;
 }
 
-static inline int __ieee80211_queue_stopped(const struct ieee80211_local *local,
-					    int queue)
-{
-	return test_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]);
-}
-
 static int inline is_ieee80211_device(struct net_device *dev,
 				      struct net_device *master)
 {
@@ -1066,7 +1060,7 @@ static int ___ieee80211_tx(struct ieee80
 	print_control(control, skb);
 
 	if (unlikely(netif_queue_stopped(local->mdev) ||
-		     __ieee80211_queue_stopped(local, control->queue)))
+		     netif_subqueue_stopped(local->mdev, skb)))
 		return NETDEV_TX_BUSY;
 
 	ieee80211_dump_frame(wiphy_name(local->hw.wiphy),
@@ -1354,7 +1348,6 @@ int ieee80211_master_start_xmit(struct s
 		control.flags |= IEEE80211_TXCTL_EAPOL_FRAME;
 	if (pkt_data->flags & IEEE80211_TXPD_AMPDU)
 		control.flags |= IEEE80211_TXCTL_AMPDU;
-	control.queue = pkt_data->queue;
 
 	memset(skb->cb, 0, sizeof(skb->cb));
 	ret = ieee80211_tx(odev, skb, &control);
--- everything.orig/net/mac80211/util.c	2008-04-30 14:02:31.000000000 +0200
+++ everything/net/mac80211/util.c	2008-04-30 14:20:06.000000000 +0200
@@ -323,18 +323,28 @@ __le16 ieee80211_ctstoself_duration(stru
 }
 EXPORT_SYMBOL(ieee80211_ctstoself_duration);
 
+void ieee80211_start_queue(struct ieee80211_hw *hw, int queue)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+#ifdef CONFIG_MAC80211_QOS
+	netif_start_subqueue(local->mdev, queue);
+#else
+	WARN_ON(queue != 0);
+	netif_start_queue(local->mdev);
+#endif
+}
+EXPORT_SYMBOL(ieee80211_start_queue);
+
 void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
 {
 	struct ieee80211_local *local = hw_to_local(hw);
 
-	if (test_and_clear_bit(IEEE80211_LINK_STATE_XOFF,
-			       &local->state[queue])) {
-		if (!ieee80211_qdisc_installed(local->mdev)) {
-			if (queue == 0)
-				netif_wake_queue(local->mdev);
-		} else
-			__netif_schedule(local->mdev);
-	}
+#ifdef CONFIG_MAC80211_QOS
+	netif_wake_subqueue(local->mdev, queue);
+#else
+	WARN_ON(queue != 0);
+	netif_wake_queue(local->mdev);
+#endif
 }
 EXPORT_SYMBOL(ieee80211_wake_queue);
 
@@ -342,39 +352,51 @@ void ieee80211_stop_queue(struct ieee802
 {
 	struct ieee80211_local *local = hw_to_local(hw);
 
-	if (!ieee80211_qdisc_installed(local->mdev) && queue == 0)
-		netif_stop_queue(local->mdev);
-	set_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]);
+#ifdef CONFIG_MAC80211_QOS
+	netif_stop_subqueue(local->mdev, queue);
+#else
+	WARN_ON(queue != 0);
+	netif_stop_queue(local->mdev);
+#endif
 }
 EXPORT_SYMBOL(ieee80211_stop_queue);
 
 void ieee80211_start_queues(struct ieee80211_hw *hw)
 {
-	struct ieee80211_local *local = hw_to_local(hw);
+#ifdef CONFIG_MAC80211_QOS
 	int i;
 
-	for (i = 0; i < local->hw.queues; i++)
-		clear_bit(IEEE80211_LINK_STATE_XOFF, &local->state[i]);
-	if (!ieee80211_qdisc_installed(local->mdev))
-		netif_start_queue(local->mdev);
+	for (i = 0; i < hw->queues; i++)
+		ieee80211_start_queue(hw, i);
+#else
+	netif_start_queue(local->mdev);
+#endif
 }
 EXPORT_SYMBOL(ieee80211_start_queues);
 
 void ieee80211_stop_queues(struct ieee80211_hw *hw)
 {
+#ifdef CONFIG_MAC80211_QOS
 	int i;
 
 	for (i = 0; i < hw->queues; i++)
 		ieee80211_stop_queue(hw, i);
+#else
+	netif_stop_queue(local->mdev);
+#endif
 }
 EXPORT_SYMBOL(ieee80211_stop_queues);
 
 void ieee80211_wake_queues(struct ieee80211_hw *hw)
 {
+#ifdef CONFIG_MAC80211_QOS
 	int i;
 
 	for (i = 0; i < hw->queues; i++)
 		ieee80211_wake_queue(hw, i);
+#else
+	netif_wake_queue(local->mdev);
+#endif
 }
 EXPORT_SYMBOL(ieee80211_wake_queues);
 
--- everything.orig/net/mac80211/wme.c	2008-04-30 14:02:31.000000000 +0200
+++ everything/net/mac80211/wme.c	2008-04-30 14:35:44.000000000 +0200
@@ -160,7 +160,7 @@ static int wme_qdiscop_enqueue(struct sk
 	u8 tid;
 
 	if (pkt_data->flags & IEEE80211_TXPD_REQUEUE) {
-		queue = pkt_data->queue;
+		queue = skb_get_queue_mapping(skb);
 		rcu_read_lock();
 		sta = sta_info_get(local, hdr->addr1);
 		tid = skb->priority & QOS_CONTROL_TAG1D_MASK;
@@ -221,7 +221,7 @@ static int wme_qdiscop_enqueue(struct sk
 			err = NET_XMIT_DROP;
 	} else {
 		tid = skb->priority & QOS_CONTROL_TAG1D_MASK;
-		pkt_data->queue = (unsigned int) queue;
+		skb_set_queue_mapping(skb, queue);
 		qdisc = q->queues[queue];
 		err = qdisc->enqueue(skb, qdisc);
 		if (err == NET_XMIT_SUCCESS) {
@@ -239,13 +239,10 @@ static int wme_qdiscop_enqueue(struct sk
 static int wme_qdiscop_requeue(struct sk_buff *skb, struct Qdisc* qd)
 {
 	struct ieee80211_sched_data *q = qdisc_priv(qd);
-	struct ieee80211_tx_packet_data *pkt_data =
-		(struct ieee80211_tx_packet_data *) skb->cb;
 	struct Qdisc *qdisc;
 	int err;
 
-	/* We recorded which queue to use earlier. */
-	qdisc = q->queues[pkt_data->queue];
+	qdisc = q->queues[skb_get_queue_mapping(skb)];
 
 	if ((err = qdisc->ops->requeue(skb, qdisc)) == 0) {
 		qd->q.qlen++;
@@ -269,9 +266,7 @@ static struct sk_buff *wme_qdiscop_deque
 	/* check all the h/w queues in numeric/priority order */
 	for (queue = 0; queue < QD_NUM(hw); queue++) {
 		/* see if there is room in this hardware queue */
-		if ((test_bit(IEEE80211_LINK_STATE_XOFF,
-				&local->state[queue])) ||
-		    (!test_bit(queue, q->qdisc_pool)))
+		if (__netif_subqueue_stopped(local->mdev, queue))
 			continue;
 
 		/* there is space - try and get a frame */
--- everything.orig/net/mac80211/wme.h	2008-04-30 14:02:31.000000000 +0200
+++ everything/net/mac80211/wme.h	2008-04-30 14:02:34.000000000 +0200
@@ -31,7 +31,7 @@ static inline int WLAN_FC_IS_QOS_DATA(u1
 	return (fc & 0x8C) == 0x88;
 }
 
-#ifdef CONFIG_NET_SCHED
+#ifdef CONFIG_MAC80211_QOS
 void ieee80211_install_qdisc(struct net_device *dev);
 int ieee80211_qdisc_installed(struct net_device *dev);
 int ieee80211_ht_agg_queue_add(struct ieee80211_local *local,
--- everything.orig/drivers/net/wireless/b43/dma.c	2008-04-30 14:02:32.000000000 +0200
+++ everything/drivers/net/wireless/b43/dma.c	2008-04-30 14:02:34.000000000 +0200
@@ -1298,7 +1298,8 @@ int b43_dma_tx(struct b43_wldev *dev,
 		hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
 	} else {
 		/* Decide by priority where to put this frame. */
-		ring = select_ring_by_priority(dev, ctl->queue);
+		ring = select_ring_by_priority(
+			dev, skb_get_queue_mapping(skb));
 	}
 
 	spin_lock_irqsave(&ring->lock, flags);
@@ -1316,7 +1317,7 @@ int b43_dma_tx(struct b43_wldev *dev,
 	/* Assign the queue number to the ring (if not already done before)
 	 * so TX status handling can use it. The queue to ring mapping is
 	 * static, so we don't need to store it per frame. */
-	ring->queue_prio = ctl->queue;
+	ring->queue_prio = skb_get_queue_mapping(skb);
 
 	err = dma_tx_fragment(ring, skb, ctl);
 	if (unlikely(err == -ENOKEY)) {
@@ -1334,7 +1335,7 @@ int b43_dma_tx(struct b43_wldev *dev,
 	if ((free_slots(ring) < SLOTS_PER_PACKET) ||
 	    should_inject_overflow(ring)) {
 		/* This TX ring is full. */
-		ieee80211_stop_queue(dev->wl->hw, ctl->queue);
+		ieee80211_stop_queue(dev->wl->hw, skb_get_queue_mapping(skb));
 		ring->stopped = 1;
 		if (b43_debug(dev, B43_DBG_DMAVERBOSE)) {
 			b43dbg(dev->wl, "Stopped TX ring %d\n", ring->index);
--- everything.orig/drivers/net/wireless/b43/pio.c	2008-04-30 14:02:32.000000000 +0200
+++ everything/drivers/net/wireless/b43/pio.c	2008-04-30 14:02:34.000000000 +0200
@@ -510,7 +510,7 @@ int b43_pio_tx(struct b43_wldev *dev,
 		hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
 	} else {
 		/* Decide by priority where to put this frame. */
-		q = select_queue_by_priority(dev, ctl->queue);
+		q = select_queue_by_priority(dev, skb_get_queue_mapping(skb));
 	}
 
 	spin_lock_irqsave(&q->lock, flags);
@@ -533,7 +533,7 @@ int b43_pio_tx(struct b43_wldev *dev,
 	if (total_len > (q->buffer_size - q->buffer_used)) {
 		/* Not enough memory on the queue. */
 		err = -EBUSY;
-		ieee80211_stop_queue(dev->wl->hw, ctl->queue);
+		ieee80211_stop_queue(dev->wl->hw, skb_get_queue_mapping(skb));
 		q->stopped = 1;
 		goto out_unlock;
 	}
@@ -541,7 +541,7 @@ int b43_pio_tx(struct b43_wldev *dev,
 	/* Assign the queue number to the ring (if not already done before)
 	 * so TX status handling can use it. The mac80211-queue to b43-queue
 	 * mapping is static, so we don't need to store it per frame. */
-	q->queue_prio = ctl->queue;
+	q->queue_prio = skb_get_queue_mapping(skb);
 
 	err = pio_tx_frame(q, skb, ctl);
 	if (unlikely(err == -ENOKEY)) {
@@ -561,7 +561,7 @@ int b43_pio_tx(struct b43_wldev *dev,
 	if (((q->buffer_size - q->buffer_used) < roundup(2 + 2 + 6, 4)) ||
 	    (q->free_packet_slots == 0)) {
 		/* The queue is full. */
-		ieee80211_stop_queue(dev->wl->hw, ctl->queue);
+		ieee80211_stop_queue(dev->wl->hw, skb_get_queue_mapping(skb));
 		q->stopped = 1;
 	}
 
--- everything.orig/drivers/net/wireless/b43legacy/dma.c	2008-04-30 14:02:32.000000000 +0200
+++ everything/drivers/net/wireless/b43legacy/dma.c	2008-04-30 14:02:34.000000000 +0200
@@ -1330,7 +1330,7 @@ int b43legacy_dma_tx(struct b43legacy_wl
 	int err = 0;
 	unsigned long flags;
 
-	ring = priority_to_txring(dev, ctl->queue);
+	ring = priority_to_txring(dev, skb_get_queue_mapping(skb));
 	spin_lock_irqsave(&ring->lock, flags);
 	B43legacy_WARN_ON(!ring->tx);
 	if (unlikely(free_slots(ring) < SLOTS_PER_PACKET)) {
--- everything.orig/drivers/net/wireless/rt2x00/rt2x00mac.c	2008-04-30 14:02:32.000000000 +0200
+++ everything/drivers/net/wireless/rt2x00/rt2x00mac.c	2008-04-30 14:02:34.000000000 +0200
@@ -81,7 +81,7 @@ int rt2x00mac_tx(struct ieee80211_hw *hw
 {
 	struct rt2x00_dev *rt2x00dev = hw->priv;
 	struct ieee80211_hdr *ieee80211hdr = (struct ieee80211_hdr *)skb->data;
-	enum data_queue_qid qid = mac80211_queue_to_qid(control->queue);
+	enum data_queue_qid qid = skb_get_queue_mapping(skb);
 	struct data_queue *queue;
 	struct skb_frame_desc *skbdesc;
 	u16 frame_control;
@@ -129,12 +129,14 @@ int rt2x00mac_tx(struct ieee80211_hw *hw
 			       IEEE80211_TXCTL_USE_CTS_PROTECT)) &&
 	    !rt2x00dev->ops->hw->set_rts_threshold) {
 		if (rt2x00queue_available(queue) <= 1) {
-			ieee80211_stop_queue(rt2x00dev->hw, control->queue);
+			ieee80211_stop_queue(
+				rt2x00dev->hw, skb_get_queue_mapping(skb));
 			return NETDEV_TX_BUSY;
 		}
 
 		if (rt2x00mac_tx_rts_cts(rt2x00dev, queue, skb, control)) {
-			ieee80211_stop_queue(rt2x00dev->hw, control->queue);
+			ieee80211_stop_queue(
+				rt2x00dev->hw, skb_get_queue_mapping(skb));
 			return NETDEV_TX_BUSY;
 		}
 	}
@@ -146,12 +148,12 @@ int rt2x00mac_tx(struct ieee80211_hw *hw
 	memset(skbdesc, 0, sizeof(*skbdesc));
 
 	if (rt2x00dev->ops->lib->write_tx_data(rt2x00dev, queue, skb, control)) {
-		ieee80211_stop_queue(rt2x00dev->hw, control->queue);
+		ieee80211_stop_queue(rt2x00dev->hw, skb_get_queue_mapping(skb));
 		return NETDEV_TX_BUSY;
 	}
 
 	if (rt2x00queue_full(queue))
-		ieee80211_stop_queue(rt2x00dev->hw, control->queue);
+		ieee80211_stop_queue(rt2x00dev->hw, skb_get_queue_mapping(skb));
 
 	if (rt2x00dev->ops->lib->kick_tx_queue)
 		rt2x00dev->ops->lib->kick_tx_queue(rt2x00dev, qid);
--- everything.orig/drivers/net/wireless/rt2x00/rt2x00pci.c	2008-04-30 14:02:32.000000000 +0200
+++ everything/drivers/net/wireless/rt2x00/rt2x00pci.c	2008-04-30 14:02:34.000000000 +0200
@@ -178,7 +178,7 @@ void rt2x00pci_txdone(struct rt2x00_dev 
 	 * is reenabled when the txdone handler has finished.
 	 */
 	if (!rt2x00queue_full(entry->queue))
-		ieee80211_wake_queue(rt2x00dev->hw, priv_tx->control.queue);
+		ieee80211_wake_queue(rt2x00dev->hw, entry->queue->qid);
 
 }
 EXPORT_SYMBOL_GPL(rt2x00pci_txdone);
--- everything.orig/drivers/net/wireless/rt2x00/rt2x00queue.h	2008-04-30 14:02:32.000000000 +0200
+++ everything/drivers/net/wireless/rt2x00/rt2x00queue.h	2008-04-30 14:02:34.000000000 +0200
@@ -80,19 +80,6 @@ enum data_queue_qid {
 };
 
 /**
- * mac80211_queue_to_qid - Convert mac80211 queue to rt2x00 qid
- * @queue: mac80211 queue.
- */
-static inline enum data_queue_qid mac80211_queue_to_qid(unsigned int queue)
-{
-	/* Regular TX queues are mapped directly */
-	if (queue < 4)
-		return queue;
-	WARN_ON(1);
-	return QID_OTHER;
-}

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [RFC/RFT 4/4] mac80211: use multi-queue master netdevice
       [not found]   ` <20080430130051.397094000-cdvu00un1VgdHxzADdlk8Q@public.gmane.org>
@ 2008-04-30 14:37     ` Ivo van Doorn
       [not found]       ` <200804301637.35170.IvDoorn-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
  2008-04-30 19:39     ` Waskiewicz Jr, Peter P
  1 sibling, 1 reply; 41+ messages in thread
From: Ivo van Doorn @ 2008-04-30 14:37 UTC (permalink / raw)
  To: Johannes Berg
  Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA, Ron Rindjunsky, Tomas Winkler,
	Peter P Waskiewicz Jr

Hi,

Overall the rt2x00 changes are good, but I have a few suggestions :)

> --- everything.orig/drivers/net/wireless/rt2x00/rt2x00mac.c	2008-04-30 14:02:32.000000000 +0200
> +++ everything/drivers/net/wireless/rt2x00/rt2x00mac.c	2008-04-30 14:02:34.000000000 +0200
> @@ -81,7 +81,7 @@ int rt2x00mac_tx(struct ieee80211_hw *hw
>  {
>  	struct rt2x00_dev *rt2x00dev = hw->priv;
>  	struct ieee80211_hdr *ieee80211hdr = (struct ieee80211_hdr *)skb->data;
> -	enum data_queue_qid qid = mac80211_queue_to_qid(control->queue);
> +	enum data_queue_qid qid = skb_get_queue_mapping(skb);
>  	struct data_queue *queue;
>  	struct skb_frame_desc *skbdesc;
>  	u16 frame_control;
> @@ -129,12 +129,14 @@ int rt2x00mac_tx(struct ieee80211_hw *hw
>  			       IEEE80211_TXCTL_USE_CTS_PROTECT)) &&
>  	    !rt2x00dev->ops->hw->set_rts_threshold) {
>  		if (rt2x00queue_available(queue) <= 1) {
> -			ieee80211_stop_queue(rt2x00dev->hw, control->queue);
> +			ieee80211_stop_queue(
> +				rt2x00dev->hw, skb_get_queue_mapping(skb));

ieee80211_stop_queue(rt2x00dev->hw, qid);
The result of skb_get_queue_mapping(skb) was already stored in the
'qid' variable at the start of the function. And since this is a 1-1 mapping
from skb queue to rt2x00 queue, it is safe to use 'qid' directly in all instances.

>  			return NETDEV_TX_BUSY;
>  		}
>  
>  		if (rt2x00mac_tx_rts_cts(rt2x00dev, queue, skb, control)) {
> -			ieee80211_stop_queue(rt2x00dev->hw, control->queue);
> +			ieee80211_stop_queue(
> +				rt2x00dev->hw, skb_get_queue_mapping(skb));

ieee80211_stop_queue(rt2x00dev->hw, qid);

>  			return NETDEV_TX_BUSY;
>  		}
>  	}
> @@ -146,12 +148,12 @@ int rt2x00mac_tx(struct ieee80211_hw *hw
>  	memset(skbdesc, 0, sizeof(*skbdesc));
>  
>  	if (rt2x00dev->ops->lib->write_tx_data(rt2x00dev, queue, skb, control)) {
> -		ieee80211_stop_queue(rt2x00dev->hw, control->queue);
> +		ieee80211_stop_queue(rt2x00dev->hw, skb_get_queue_mapping(skb));

ieee80211_stop_queue(rt2x00dev->hw, qid);

>  		return NETDEV_TX_BUSY;
>  	}
>  
>  	if (rt2x00queue_full(queue))
> -		ieee80211_stop_queue(rt2x00dev->hw, control->queue);
> +		ieee80211_stop_queue(rt2x00dev->hw, skb_get_queue_mapping(skb));

ieee80211_stop_queue(rt2x00dev->hw, qid);

>  	if (rt2x00dev->ops->lib->kick_tx_queue)
>  		rt2x00dev->ops->lib->kick_tx_queue(rt2x00dev, qid);

Thanks,

Ivo
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [RFC/RFT 4/4] mac80211: use multi-queue master netdevice
       [not found]       ` <200804301637.35170.IvDoorn-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
@ 2008-04-30 14:45         ` Johannes Berg
       [not found]           ` <1209566743.18659.30.camel-YfaajirXv214zXjbi5bjpg@public.gmane.org>
  0 siblings, 1 reply; 41+ messages in thread
From: Johannes Berg @ 2008-04-30 14:45 UTC (permalink / raw)
  To: Ivo van Doorn
  Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA, Ron Rindjunsky, Tomas Winkler,
	Peter P Waskiewicz Jr

[-- Attachment #1: Type: text/plain, Size: 1030 bytes --]

Hi Ivo,

> Overall the rt2x00 changes are good, but I have a few suggestions :)

> >  	struct rt2x00_dev *rt2x00dev = hw->priv;
> >  	struct ieee80211_hdr *ieee80211hdr = (struct ieee80211_hdr *)skb->data;
> > -	enum data_queue_qid qid = mac80211_queue_to_qid(control->queue);
> > +	enum data_queue_qid qid = skb_get_queue_mapping(skb);
> >  	struct data_queue *queue;

> >  		if (rt2x00queue_available(queue) <= 1) {
> > -			ieee80211_stop_queue(rt2x00dev->hw, control->queue);
> > +			ieee80211_stop_queue(
> > +				rt2x00dev->hw, skb_get_queue_mapping(skb));
> 
> ieee80211_stop_queue(rt2x00dev->hw, qid);
> The result of skb_get_queue_mapping(skb) was already stored in the
> 'qid' variable at the start of the function. And since this is a 1-1 mapping
> from skb queue to rt2x00 queue, it is safe to use 'qid' directly in all instances.

Heh, good point, changed that. Will probably generate the same code
since get_queue_mapping is a static inline, but hey, much nicer to read
that way :)

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [RFC/RFT 4/4] mac80211: use multi-queue master netdevice
       [not found]           ` <1209566743.18659.30.camel-YfaajirXv214zXjbi5bjpg@public.gmane.org>
@ 2008-04-30 15:00             ` Johannes Berg
       [not found]               ` <1209567609.18659.33.camel-YfaajirXv214zXjbi5bjpg@public.gmane.org>
  0 siblings, 1 reply; 41+ messages in thread
From: Johannes Berg @ 2008-04-30 15:00 UTC (permalink / raw)
  To: Ivo van Doorn
  Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA, Ron Rindjunsky, Tomas Winkler,
	Peter P Waskiewicz Jr

Turns out there was something missing (last hunk of this updated
version) from the rt2x00 part of the patch that I accidentally didn't
quilt edit, here's an updated version if anybody wants to test on rt2x00
hw.

johannes

Subject: mac80211: use multi-queue master netdevice

This patch updates mac80211 and drivers to be multi-queue aware and
use that instead of the internal queue mapping.

Signed-off-by: Johannes Berg <johannes-cdvu00un1VgdHxzADdlk8Q@public.gmane.org>
---
This is just ground-work. We'll be able to play with Qos much more now.

 drivers/net/wireless/ath5k/base.c           |    2 -
 drivers/net/wireless/b43/dma.c              |    7 ++-
 drivers/net/wireless/b43/pio.c              |    8 ++--
 drivers/net/wireless/b43legacy/dma.c        |    2 -
 drivers/net/wireless/iwlwifi/iwl3945-base.c |    4 +-
 drivers/net/wireless/iwlwifi/iwl4965-base.c |    4 +-
 drivers/net/wireless/p54/p54common.c        |   10 ++---
 drivers/net/wireless/rt2x00/rt2x00mac.c     |   10 ++---
 drivers/net/wireless/rt2x00/rt2x00pci.c     |    2 -
 drivers/net/wireless/rt2x00/rt2x00queue.h   |   13 ------
 drivers/net/wireless/rt2x00/rt2x00usb.c     |    2 -
 drivers/net/wireless/rtl8180_dev.c          |    4 +-
 include/net/mac80211.h                      |   12 ++++--
 net/mac80211/Kconfig                        |   14 ++++++-
 net/mac80211/Makefile                       |    2 -
 net/mac80211/ieee80211_i.h                  |    7 ---
 net/mac80211/main.c                         |   21 ++++++----
 net/mac80211/tx.c                           |    9 ----
 net/mac80211/util.c                         |   54 +++++++++++++++++++---------
 net/mac80211/wme.c                          |   13 ++----
 net/mac80211/wme.h                          |    2 -
 21 files changed, 108 insertions(+), 94 deletions(-)

--- everything.orig/drivers/net/wireless/ath5k/base.c	2008-04-30 16:55:36.000000000 +0200
+++ everything/drivers/net/wireless/ath5k/base.c	2008-04-30 16:55:37.000000000 +0200
@@ -2677,7 +2677,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct
 	if (list_empty(&sc->txbuf)) {
 		ATH5K_ERR(sc, "no further txbuf available, dropping packet\n");
 		spin_unlock_irqrestore(&sc->txbuflock, flags);
-		ieee80211_stop_queue(hw, ctl->queue);
+		ieee80211_stop_queue(hw, skb_get_queue_mapping(skb));
 		return -1;
 	}
 	bf = list_first_entry(&sc->txbuf, struct ath5k_buf, list);
--- everything.orig/drivers/net/wireless/iwlwifi/iwl3945-base.c	2008-04-30 16:55:36.000000000 +0200
+++ everything/drivers/net/wireless/iwlwifi/iwl3945-base.c	2008-04-30 16:55:37.000000000 +0200
@@ -2562,7 +2562,7 @@ static int iwl3945_tx_skb(struct iwl3945
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 	struct iwl3945_tfd_frame *tfd;
 	u32 *control_flags;
-	int txq_id = ctl->queue;
+	int txq_id = skb_get_queue_mapping(skb);
 	struct iwl3945_tx_queue *txq = NULL;
 	struct iwl3945_queue *q = NULL;
 	dma_addr_t phys_addr;
@@ -2776,7 +2776,7 @@ static int iwl3945_tx_skb(struct iwl3945
 			spin_unlock_irqrestore(&priv->lock, flags);
 		}
 
-		ieee80211_stop_queue(priv->hw, ctl->queue);
+		ieee80211_stop_queue(priv->hw, skb_get_queue_mapping(skb));
 	}
 
 	return 0;
--- everything.orig/drivers/net/wireless/iwlwifi/iwl4965-base.c	2008-04-30 16:55:36.000000000 +0200
+++ everything/drivers/net/wireless/iwlwifi/iwl4965-base.c	2008-04-30 16:55:37.000000000 +0200
@@ -2126,7 +2126,7 @@ static int iwl4965_tx_skb(struct iwl_pri
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 	struct iwl4965_tfd_frame *tfd;
 	u32 *control_flags;
-	int txq_id = ctl->queue;
+	int txq_id = skb_get_queue_mapping(skb);
 	struct iwl4965_tx_queue *txq = NULL;
 	struct iwl4965_queue *q = NULL;
 	dma_addr_t phys_addr;
@@ -2350,7 +2350,7 @@ static int iwl4965_tx_skb(struct iwl_pri
 			spin_unlock_irqrestore(&priv->lock, flags);
 		}
 
-		ieee80211_stop_queue(priv->hw, ctl->queue);
+		ieee80211_stop_queue(priv->hw, skb_get_queue_mapping(skb));
 	}
 
 	return 0;
--- everything.orig/drivers/net/wireless/p54/p54common.c	2008-04-30 16:55:36.000000000 +0200
+++ everything/drivers/net/wireless/p54/p54common.c	2008-04-30 16:55:37.000000000 +0200
@@ -417,7 +417,7 @@ static void p54_rx_frame_sent(struct iee
 			memcpy(&status.control, range->control,
 			       sizeof(status.control));
 			kfree(range->control);
-			priv->tx_stats[status.control.queue].len--;
+			priv->tx_stats[skb_get_queue_mapping(skb)].len--;
 			entry_hdr = (struct p54_control_hdr *) entry->data;
 			entry_data = (struct p54_tx_control_allocdata *) entry_hdr->data;
 			if ((entry_hdr->magic1 & cpu_to_le16(0x4000)) != 0)
@@ -562,13 +562,13 @@ static int p54_tx(struct ieee80211_hw *d
 	size_t padding, len;
 	u8 rate;
 
-	current_queue = &priv->tx_stats[control->queue];
+	current_queue = &priv->tx_stats[skb_get_queue_mapping(skb)];
 	if (unlikely(current_queue->len > current_queue->limit))
 		return NETDEV_TX_BUSY;
 	current_queue->len++;
 	current_queue->count++;
 	if (current_queue->len == current_queue->limit)
-		ieee80211_stop_queue(dev, control->queue);
+		ieee80211_stop_queue(dev, skb_get_queue_mapping(skb));
 
 	padding = (unsigned long)(skb->data - (sizeof(*hdr) + sizeof(*txhdr))) & 3;
 	len = skb->len;
@@ -605,7 +605,7 @@ static int p54_tx(struct ieee80211_hw *d
 	memset(txhdr->rateset, rate, 8);
 	txhdr->wep_key_present = 0;
 	txhdr->wep_key_len = 0;
-	txhdr->frame_type = cpu_to_le32(control->queue + 4);
+	txhdr->frame_type = cpu_to_le32(skb_get_queue_mapping(skb) + 4);
 	txhdr->magic4 = 0;
 	txhdr->antenna = (control->antenna_sel_tx == 0) ?
 		2 : control->antenna_sel_tx - 1;
@@ -944,7 +944,7 @@ static int p54_conf_tx(struct ieee80211_
 	vdcf = (struct p54_tx_control_vdcf *)(((struct p54_control_hdr *)
 		((void *)priv->cached_vdcf + priv->tx_hdr_len))->data);
 
-	if ((params) && !((queue < 0) || (queue > 4))) {
+	if (params && queue < dev->queues) {
 		P54_SET_QUEUE(vdcf->queue[queue], params->aifs,
 			params->cw_min, params->cw_max, params->txop);
 	} else
--- everything.orig/drivers/net/wireless/rtl8180_dev.c	2008-04-30 16:55:36.000000000 +0200
+++ everything/drivers/net/wireless/rtl8180_dev.c	2008-04-30 16:55:37.000000000 +0200
@@ -251,7 +251,7 @@ static int rtl8180_tx(struct ieee80211_h
 	u16 plcp_len = 0;
 	__le16 rts_duration = 0;
 
-	prio = control->queue;
+	prio = skb_get_queue_mapping(skb);
 	ring = &priv->tx_ring[prio];
 
 	mapping = pci_map_single(priv->pdev, skb->data,
@@ -306,7 +306,7 @@ static int rtl8180_tx(struct ieee80211_h
 	entry->flags = cpu_to_le32(tx_flags);
 	__skb_queue_tail(&ring->queue, skb);
 	if (ring->entries - skb_queue_len(&ring->queue) < 2)
-		ieee80211_stop_queue(dev, control->queue);
+		ieee80211_stop_queue(dev, skb_get_queue_mapping(skb));
 	spin_unlock_irqrestore(&priv->lock, flags);
 
 	rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING, (1 << (prio + 4)));
--- everything.orig/include/net/mac80211.h	2008-04-30 16:55:37.000000000 +0200
+++ everything/include/net/mac80211.h	2008-04-30 16:55:37.000000000 +0200
@@ -282,9 +282,6 @@ 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
@@ -1575,6 +1572,15 @@ void ieee80211_wake_queue(struct ieee802
 void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue);
 
 /**
+ * ieee80211_start_queue - start specific queue
+ * @hw: pointer as obtained from ieee80211_alloc_hw().
+ * @queue: queue number (counted from zero).
+ *
+ * Drivers should use this function instead of netif_start_queue.
+ */
+void ieee80211_start_queue(struct ieee80211_hw *hw, int queue);
+
+/**
  * ieee80211_start_queues - start all queues
  * @hw: pointer to as obtained from ieee80211_alloc_hw().
  *
--- everything.orig/net/mac80211/Kconfig	2008-04-30 16:53:47.000000000 +0200
+++ everything/net/mac80211/Kconfig	2008-04-30 16:55:37.000000000 +0200
@@ -7,11 +7,23 @@ config MAC80211
 	select CRC32
 	select WIRELESS_EXT
 	select CFG80211
-	select NET_SCH_FIFO
 	---help---
 	  This option enables the hardware independent IEEE 802.11
 	  networking stack.
 
+config MAC80211_QOS
+	def_bool y
+	depends on MAC80211
+	depends on NET_SCHED
+	depends on NETDEVICES_MULTIQUEUE
+
+comment "QoS/HT support disabled"
+	depends on !MAC80211_QOS
+comment "QoS/HT support needs CONFIG_NET_SCHED"
+	depends on MAC80211 && !NET_SCHED
+comment "QoS/HT support needs CONFIG_NETDEVICES_MULTIQUEUE"
+	depends on MAC80211 && !NETDEVICES_MULTIQUEUE
+
 menu "Rate control algorithm selection"
 	depends on MAC80211 != n
 
--- everything.orig/net/mac80211/Makefile	2008-04-30 16:55:30.000000000 +0200
+++ everything/net/mac80211/Makefile	2008-04-30 16:55:37.000000000 +0200
@@ -29,7 +29,7 @@ mac80211-y := \
 	event.o
 
 mac80211-$(CONFIG_MAC80211_LEDS) += led.o
-mac80211-$(CONFIG_NET_SCHED) += wme.o
+mac80211-$(CONFIG_MAC80211_QOS) += wme.o
 mac80211-$(CONFIG_MAC80211_DEBUGFS) += \
 	debugfs.o \
 	debugfs_sta.o \
--- everything.orig/net/mac80211/ieee80211_i.h	2008-04-30 16:55:37.000000000 +0200
+++ everything/net/mac80211/ieee80211_i.h	2008-04-30 16:55:37.000000000 +0200
@@ -213,7 +213,6 @@ struct ieee80211_rx_data {
 struct ieee80211_tx_packet_data {
 	u32 flags;
 	int ifindex;
-	u16 queue;
 	unsigned long jiffies;
 };
 
@@ -605,8 +604,6 @@ struct ieee80211_local {
 	struct sta_info *sta_hash[STA_HASH_SIZE];
 	struct timer_list sta_cleanup;
 
-	unsigned long state[IEEE80211_MAX_AMPDU_QUEUES + IEEE80211_MAX_AMPDU_QUEUES];
-
 	/* number of interfaces with corresponding IFF_ flags */
 	atomic_t iff_allmultis, iff_promiscs;
 
@@ -836,10 +833,6 @@ static inline struct ieee80211_hw *local
 	return &local->hw;
 }
 
-enum ieee80211_link_state_t {
-	IEEE80211_LINK_STATE_XOFF = 0,
-};
-
 struct sta_attribute {
 	struct attribute attr;
 	ssize_t (*show)(const struct sta_info *, char *buf);
--- everything.orig/net/mac80211/main.c	2008-04-30 16:55:37.000000000 +0200
+++ everything/net/mac80211/main.c	2008-04-30 16:55:37.000000000 +0200
@@ -1313,7 +1313,6 @@ static void ieee80211_remove_tx_extra(st
 		pkt_data->flags |= IEEE80211_TXPD_REQUEUE;
 	if (control->flags & IEEE80211_TXCTL_EAPOL_FRAME)
 		pkt_data->flags |= IEEE80211_TXPD_EAPOL_FRAME;
-	pkt_data->queue = control->queue;
 
 	hdrlen = ieee80211_get_hdrlen_from_skb(skb);
 
@@ -1713,9 +1712,20 @@ int ieee80211_register_hw(struct ieee802
 	if (result < 0)
 		return result;
 
+#ifdef CONFIG_MAC80211_QOS
+	if (hw->queues > IEEE80211_MAX_QUEUES)
+		hw->queues = IEEE80211_MAX_QUEUES;
+	if (hw->ampdu_queues > IEEE80211_MAX_AMPDU_QUEUES)
+		hw->ampdu_queues = IEEE80211_MAX_AMPDU_QUEUES;
+#else
+	hw->queues = 1;
+	hw->ampdu_queues = 0;
+#endif
+
 	/* for now, mdev needs sub_if_data :/ */
-	mdev = alloc_netdev(sizeof(struct ieee80211_sub_if_data),
-			    "wmaster%d", ether_setup);
+	mdev = alloc_netdev_mq(sizeof(struct ieee80211_sub_if_data),
+			       "wmaster%d", ether_setup,
+			       hw->queues + hw->ampdu_queues);
 	if (!mdev)
 		goto fail_mdev_alloc;
 
@@ -1807,11 +1817,6 @@ int ieee80211_register_hw(struct ieee802
 		goto fail_wep;
 	}
 
-	if (hw->queues > IEEE80211_MAX_QUEUES)
-		hw->queues = IEEE80211_MAX_QUEUES;
-	if (hw->ampdu_queues > IEEE80211_MAX_AMPDU_QUEUES)
-		hw->ampdu_queues = IEEE80211_MAX_AMPDU_QUEUES;
-
 	ieee80211_install_qdisc(local->mdev);
 
 	/* add one default STA interface */
--- everything.orig/net/mac80211/tx.c	2008-04-30 16:55:37.000000000 +0200
+++ everything/net/mac80211/tx.c	2008-04-30 16:55:37.000000000 +0200
@@ -210,12 +210,6 @@ static u16 ieee80211_duration(struct iee
 	return dur;
 }
 
-static inline int __ieee80211_queue_stopped(const struct ieee80211_local *local,
-					    int queue)
-{
-	return test_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]);
-}
-
 static int inline is_ieee80211_device(struct net_device *dev,
 				      struct net_device *master)
 {
@@ -1066,7 +1060,7 @@ static int ___ieee80211_tx(struct ieee80
 	print_control(control, skb);
 
 	if (unlikely(netif_queue_stopped(local->mdev) ||
-		     __ieee80211_queue_stopped(local, control->queue)))
+		     netif_subqueue_stopped(local->mdev, skb)))
 		return NETDEV_TX_BUSY;
 
 	ieee80211_dump_frame(wiphy_name(local->hw.wiphy),
@@ -1354,7 +1348,6 @@ int ieee80211_master_start_xmit(struct s
 		control.flags |= IEEE80211_TXCTL_EAPOL_FRAME;
 	if (pkt_data->flags & IEEE80211_TXPD_AMPDU)
 		control.flags |= IEEE80211_TXCTL_AMPDU;
-	control.queue = pkt_data->queue;
 
 	memset(skb->cb, 0, sizeof(skb->cb));
 	ret = ieee80211_tx(odev, skb, &control);
--- everything.orig/net/mac80211/util.c	2008-04-30 16:55:37.000000000 +0200
+++ everything/net/mac80211/util.c	2008-04-30 16:55:37.000000000 +0200
@@ -323,18 +323,28 @@ __le16 ieee80211_ctstoself_duration(stru
 }
 EXPORT_SYMBOL(ieee80211_ctstoself_duration);
 
+void ieee80211_start_queue(struct ieee80211_hw *hw, int queue)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+#ifdef CONFIG_MAC80211_QOS
+	netif_start_subqueue(local->mdev, queue);
+#else
+	WARN_ON(queue != 0);
+	netif_start_queue(local->mdev);
+#endif
+}
+EXPORT_SYMBOL(ieee80211_start_queue);
+
 void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
 {
 	struct ieee80211_local *local = hw_to_local(hw);
 
-	if (test_and_clear_bit(IEEE80211_LINK_STATE_XOFF,
-			       &local->state[queue])) {
-		if (!ieee80211_qdisc_installed(local->mdev)) {
-			if (queue == 0)
-				netif_wake_queue(local->mdev);
-		} else
-			__netif_schedule(local->mdev);
-	}
+#ifdef CONFIG_MAC80211_QOS
+	netif_wake_subqueue(local->mdev, queue);
+#else
+	WARN_ON(queue != 0);
+	netif_wake_queue(local->mdev);
+#endif
 }
 EXPORT_SYMBOL(ieee80211_wake_queue);
 
@@ -342,39 +352,51 @@ void ieee80211_stop_queue(struct ieee802
 {
 	struct ieee80211_local *local = hw_to_local(hw);
 
-	if (!ieee80211_qdisc_installed(local->mdev) && queue == 0)
-		netif_stop_queue(local->mdev);
-	set_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]);
+#ifdef CONFIG_MAC80211_QOS
+	netif_stop_subqueue(local->mdev, queue);
+#else
+	WARN_ON(queue != 0);
+	netif_stop_queue(local->mdev);
+#endif
 }
 EXPORT_SYMBOL(ieee80211_stop_queue);
 
 void ieee80211_start_queues(struct ieee80211_hw *hw)
 {
-	struct ieee80211_local *local = hw_to_local(hw);
+#ifdef CONFIG_MAC80211_QOS
 	int i;
 
-	for (i = 0; i < local->hw.queues; i++)
-		clear_bit(IEEE80211_LINK_STATE_XOFF, &local->state[i]);
-	if (!ieee80211_qdisc_installed(local->mdev))
-		netif_start_queue(local->mdev);
+	for (i = 0; i < hw->queues; i++)
+		ieee80211_start_queue(hw, i);
+#else
+	netif_start_queue(local->mdev);
+#endif
 }
 EXPORT_SYMBOL(ieee80211_start_queues);
 
 void ieee80211_stop_queues(struct ieee80211_hw *hw)
 {
+#ifdef CONFIG_MAC80211_QOS
 	int i;
 
 	for (i = 0; i < hw->queues; i++)
 		ieee80211_stop_queue(hw, i);
+#else
+	netif_stop_queue(local->mdev);
+#endif
 }
 EXPORT_SYMBOL(ieee80211_stop_queues);
 
 void ieee80211_wake_queues(struct ieee80211_hw *hw)
 {
+#ifdef CONFIG_MAC80211_QOS
 	int i;
 
 	for (i = 0; i < hw->queues; i++)
 		ieee80211_wake_queue(hw, i);
+#else
+	netif_wake_queue(local->mdev);
+#endif
 }
 EXPORT_SYMBOL(ieee80211_wake_queues);
 
--- everything.orig/net/mac80211/wme.c	2008-04-30 16:55:37.000000000 +0200
+++ everything/net/mac80211/wme.c	2008-04-30 16:55:37.000000000 +0200
@@ -160,7 +160,7 @@ static int wme_qdiscop_enqueue(struct sk
 	u8 tid;
 
 	if (pkt_data->flags & IEEE80211_TXPD_REQUEUE) {
-		queue = pkt_data->queue;
+		queue = skb_get_queue_mapping(skb);
 		rcu_read_lock();
 		sta = sta_info_get(local, hdr->addr1);
 		tid = skb->priority & QOS_CONTROL_TAG1D_MASK;
@@ -221,7 +221,7 @@ static int wme_qdiscop_enqueue(struct sk
 			err = NET_XMIT_DROP;
 	} else {
 		tid = skb->priority & QOS_CONTROL_TAG1D_MASK;
-		pkt_data->queue = (unsigned int) queue;
+		skb_set_queue_mapping(skb, queue);
 		qdisc = q->queues[queue];
 		err = qdisc->enqueue(skb, qdisc);
 		if (err == NET_XMIT_SUCCESS) {
@@ -239,13 +239,10 @@ static int wme_qdiscop_enqueue(struct sk
 static int wme_qdiscop_requeue(struct sk_buff *skb, struct Qdisc* qd)
 {
 	struct ieee80211_sched_data *q = qdisc_priv(qd);
-	struct ieee80211_tx_packet_data *pkt_data =
-		(struct ieee80211_tx_packet_data *) skb->cb;
 	struct Qdisc *qdisc;
 	int err;
 
-	/* We recorded which queue to use earlier. */
-	qdisc = q->queues[pkt_data->queue];
+	qdisc = q->queues[skb_get_queue_mapping(skb)];
 
 	if ((err = qdisc->ops->requeue(skb, qdisc)) == 0) {
 		qd->q.qlen++;
@@ -269,9 +266,7 @@ static struct sk_buff *wme_qdiscop_deque
 	/* check all the h/w queues in numeric/priority order */
 	for (queue = 0; queue < QD_NUM(hw); queue++) {
 		/* see if there is room in this hardware queue */
-		if ((test_bit(IEEE80211_LINK_STATE_XOFF,
-				&local->state[queue])) ||
-		    (!test_bit(queue, q->qdisc_pool)))
+		if (__netif_subqueue_stopped(local->mdev, queue))
 			continue;
 
 		/* there is space - try and get a frame */
--- everything.orig/net/mac80211/wme.h	2008-04-30 16:53:47.000000000 +0200
+++ everything/net/mac80211/wme.h	2008-04-30 16:55:37.000000000 +0200
@@ -31,7 +31,7 @@ static inline int WLAN_FC_IS_QOS_DATA(u1
 	return (fc & 0x8C) == 0x88;
 }
 
-#ifdef CONFIG_NET_SCHED
+#ifdef CONFIG_MAC80211_QOS
 void ieee80211_install_qdisc(struct net_device *dev);
 int ieee80211_qdisc_installed(struct net_device *dev);
 int ieee80211_ht_agg_queue_add(struct ieee80211_local *local,
--- everything.orig/drivers/net/wireless/b43/dma.c	2008-04-30 16:55:34.000000000 +0200
+++ everything/drivers/net/wireless/b43/dma.c	2008-04-30 16:55:37.000000000 +0200
@@ -1298,7 +1298,8 @@ int b43_dma_tx(struct b43_wldev *dev,
 		hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
 	} else {
 		/* Decide by priority where to put this frame. */
-		ring = select_ring_by_priority(dev, ctl->queue);
+		ring = select_ring_by_priority(
+			dev, skb_get_queue_mapping(skb));
 	}
 
 	spin_lock_irqsave(&ring->lock, flags);
@@ -1316,7 +1317,7 @@ int b43_dma_tx(struct b43_wldev *dev,
 	/* Assign the queue number to the ring (if not already done before)
 	 * so TX status handling can use it. The queue to ring mapping is
 	 * static, so we don't need to store it per frame. */
-	ring->queue_prio = ctl->queue;
+	ring->queue_prio = skb_get_queue_mapping(skb);
 
 	err = dma_tx_fragment(ring, skb, ctl);
 	if (unlikely(err == -ENOKEY)) {
@@ -1334,7 +1335,7 @@ int b43_dma_tx(struct b43_wldev *dev,
 	if ((free_slots(ring) < SLOTS_PER_PACKET) ||
 	    should_inject_overflow(ring)) {
 		/* This TX ring is full. */
-		ieee80211_stop_queue(dev->wl->hw, ctl->queue);
+		ieee80211_stop_queue(dev->wl->hw, skb_get_queue_mapping(skb));
 		ring->stopped = 1;
 		if (b43_debug(dev, B43_DBG_DMAVERBOSE)) {
 			b43dbg(dev->wl, "Stopped TX ring %d\n", ring->index);
--- everything.orig/drivers/net/wireless/b43/pio.c	2008-04-30 16:55:34.000000000 +0200
+++ everything/drivers/net/wireless/b43/pio.c	2008-04-30 16:55:37.000000000 +0200
@@ -510,7 +510,7 @@ int b43_pio_tx(struct b43_wldev *dev,
 		hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
 	} else {
 		/* Decide by priority where to put this frame. */
-		q = select_queue_by_priority(dev, ctl->queue);
+		q = select_queue_by_priority(dev, skb_get_queue_mapping(skb));
 	}
 
 	spin_lock_irqsave(&q->lock, flags);
@@ -533,7 +533,7 @@ int b43_pio_tx(struct b43_wldev *dev,
 	if (total_len > (q->buffer_size - q->buffer_used)) {
 		/* Not enough memory on the queue. */
 		err = -EBUSY;
-		ieee80211_stop_queue(dev->wl->hw, ctl->queue);
+		ieee80211_stop_queue(dev->wl->hw, skb_get_queue_mapping(skb));
 		q->stopped = 1;
 		goto out_unlock;
 	}
@@ -541,7 +541,7 @@ int b43_pio_tx(struct b43_wldev *dev,
 	/* Assign the queue number to the ring (if not already done before)
 	 * so TX status handling can use it. The mac80211-queue to b43-queue
 	 * mapping is static, so we don't need to store it per frame. */
-	q->queue_prio = ctl->queue;
+	q->queue_prio = skb_get_queue_mapping(skb);
 
 	err = pio_tx_frame(q, skb, ctl);
 	if (unlikely(err == -ENOKEY)) {
@@ -561,7 +561,7 @@ int b43_pio_tx(struct b43_wldev *dev,
 	if (((q->buffer_size - q->buffer_used) < roundup(2 + 2 + 6, 4)) ||
 	    (q->free_packet_slots == 0)) {
 		/* The queue is full. */
-		ieee80211_stop_queue(dev->wl->hw, ctl->queue);
+		ieee80211_stop_queue(dev->wl->hw, skb_get_queue_mapping(skb));
 		q->stopped = 1;
 	}
 
--- everything.orig/drivers/net/wireless/b43legacy/dma.c	2008-04-30 16:55:34.000000000 +0200
+++ everything/drivers/net/wireless/b43legacy/dma.c	2008-04-30 16:55:37.000000000 +0200
@@ -1330,7 +1330,7 @@ int b43legacy_dma_tx(struct b43legacy_wl
 	int err = 0;
 	unsigned long flags;
 
-	ring = priority_to_txring(dev, ctl->queue);
+	ring = priority_to_txring(dev, skb_get_queue_mapping(skb));
 	spin_lock_irqsave(&ring->lock, flags);
 	B43legacy_WARN_ON(!ring->tx);
 	if (unlikely(free_slots(ring) < SLOTS_PER_PACKET)) {
--- everything.orig/drivers/net/wireless/rt2x00/rt2x00mac.c	2008-04-30 16:55:35.000000000 +0200
+++ everything/drivers/net/wireless/rt2x00/rt2x00mac.c	2008-04-30 16:55:37.000000000 +0200
@@ -81,7 +81,7 @@ int rt2x00mac_tx(struct ieee80211_hw *hw
 {
 	struct rt2x00_dev *rt2x00dev = hw->priv;
 	struct ieee80211_hdr *ieee80211hdr = (struct ieee80211_hdr *)skb->data;
-	enum data_queue_qid qid = mac80211_queue_to_qid(control->queue);
+	enum data_queue_qid qid = skb_get_queue_mapping(skb);
 	struct data_queue *queue;
 	struct skb_frame_desc *skbdesc;
 	u16 frame_control;
@@ -129,12 +129,12 @@ int rt2x00mac_tx(struct ieee80211_hw *hw
 			       IEEE80211_TXCTL_USE_CTS_PROTECT)) &&
 	    !rt2x00dev->ops->hw->set_rts_threshold) {
 		if (rt2x00queue_available(queue) <= 1) {
-			ieee80211_stop_queue(rt2x00dev->hw, control->queue);
+			ieee80211_stop_queue(rt2x00dev->hw, qid);
 			return NETDEV_TX_BUSY;
 		}
 
 		if (rt2x00mac_tx_rts_cts(rt2x00dev, queue, skb, control)) {
-			ieee80211_stop_queue(rt2x00dev->hw, control->queue);
+			ieee80211_stop_queue(rt2x00dev->hw, qid);
 			return NETDEV_TX_BUSY;
 		}
 	}
@@ -146,12 +146,12 @@ int rt2x00mac_tx(struct ieee80211_hw *hw
 	memset(skbdesc, 0, sizeof(*skbdesc));
 
 	if (rt2x00dev->ops->lib->write_tx_data(rt2x00dev, queue, skb, control)) {
-		ieee80211_stop_queue(rt2x00dev->hw, control->queue);
+		ieee80211_stop_queue(rt2x00dev->hw, qid);
 		return NETDEV_TX_BUSY;
 	}
 
 	if (rt2x00queue_full(queue))
-		ieee80211_stop_queue(rt2x00dev->hw, control->queue);
+		ieee80211_stop_queue(rt2x00dev->hw, qid);
 
 	if (rt2x00dev->ops->lib->kick_tx_queue)
 		rt2x00dev->ops->lib->kick_tx_queue(rt2x00dev, qid);
--- everything.orig/drivers/net/wireless/rt2x00/rt2x00pci.c	2008-04-30 16:55:33.000000000 +0200
+++ everything/drivers/net/wireless/rt2x00/rt2x00pci.c	2008-04-30 16:55:37.000000000 +0200
@@ -178,7 +178,7 @@ void rt2x00pci_txdone(struct rt2x00_dev 
 	 * is reenabled when the txdone handler has finished.
 	 */
 	if (!rt2x00queue_full(entry->queue))
-		ieee80211_wake_queue(rt2x00dev->hw, priv_tx->control.queue);
+		ieee80211_wake_queue(rt2x00dev->hw, entry->queue->qid);
 
 }
 EXPORT_SYMBOL_GPL(rt2x00pci_txdone);
--- everything.orig/drivers/net/wireless/rt2x00/rt2x00queue.h	2008-04-30 16:55:35.000000000 +0200
+++ everything/drivers/net/wireless/rt2x00/rt2x00queue.h	2008-04-30 16:55:37.000000000 +0200
@@ -80,19 +80,6 @@ enum data_queue_qid {
 };
 
 /**
- * mac80211_queue_to_qid - Convert mac80211 queue to rt2x00 qid
- * @queue: mac80211 queue.
- */
-static inline enum data_queue_qid mac80211_queue_to_qid(unsigned int queue)
-{
-	/* Regular TX queues are mapped directly */
-	if (queue < 4)
-		return queue;
-	WARN_ON(1);
-	return QID_OTHER;
-}

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [RFC/RFT 4/4] mac80211: use multi-queue master netdevice
       [not found]               ` <1209567609.18659.33.camel-YfaajirXv214zXjbi5bjpg@public.gmane.org>
@ 2008-04-30 15:34                 ` Ivo van Doorn
  2008-04-30 15:38                   ` Johannes Berg
  2008-05-01  8:21                 ` Ivo van Doorn
  1 sibling, 1 reply; 41+ messages in thread
From: Ivo van Doorn @ 2008-04-30 15:34 UTC (permalink / raw)
  To: Johannes Berg
  Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA, Ron Rindjunsky, Tomas Winkler,
	Peter P Waskiewicz Jr

Hi,

Don't want to spoil the fun, but the following part doesn't compile
when CONFIG_MAC80211_QOS is not set.

In the following occurences struct ieee80211_local *local needs to be defined.

>  void ieee80211_start_queues(struct ieee80211_hw *hw)
>  {
> -	struct ieee80211_local *local = hw_to_local(hw);
> +#ifdef CONFIG_MAC80211_QOS
>  	int i;
>  
> -	for (i = 0; i < local->hw.queues; i++)
> -		clear_bit(IEEE80211_LINK_STATE_XOFF, &local->state[i]);
> -	if (!ieee80211_qdisc_installed(local->mdev))
> -		netif_start_queue(local->mdev);
> +	for (i = 0; i < hw->queues; i++)
> +		ieee80211_start_queue(hw, i);
> +#else

	struct ieee80211_local *local = hw_to_local(hw);

> +	netif_start_queue(local->mdev);
> +#endif
>  }
>  EXPORT_SYMBOL(ieee80211_start_queues);
>  
>  void ieee80211_stop_queues(struct ieee80211_hw *hw)
>  {
> +#ifdef CONFIG_MAC80211_QOS
>  	int i;
>  
>  	for (i = 0; i < hw->queues; i++)
>  		ieee80211_stop_queue(hw, i);
> +#else

	struct ieee80211_local *local = hw_to_local(hw);

> +	netif_stop_queue(local->mdev);
> +#endif
>  }
>  EXPORT_SYMBOL(ieee80211_stop_queues);
>  
>  void ieee80211_wake_queues(struct ieee80211_hw *hw)
>  {
> +#ifdef CONFIG_MAC80211_QOS
>  	int i;
>  
>  	for (i = 0; i < hw->queues; i++)
>  		ieee80211_wake_queue(hw, i);
> +#else

	struct ieee80211_local *local = hw_to_local(hw);

> +	netif_wake_queue(local->mdev);
> +#endif
>  }
>  EXPORT_SYMBOL(ieee80211_wake_queues);

Ivo
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [RFC/RFT 4/4] mac80211: use multi-queue master netdevice
  2008-04-30 15:34                 ` Ivo van Doorn
@ 2008-04-30 15:38                   ` Johannes Berg
  0 siblings, 0 replies; 41+ messages in thread
From: Johannes Berg @ 2008-04-30 15:38 UTC (permalink / raw)
  To: Ivo van Doorn
  Cc: linux-wireless, netdev, Ron Rindjunsky, Tomas Winkler,
	Peter P Waskiewicz Jr

[-- Attachment #1: Type: text/plain, Size: 323 bytes --]

On Wed, 2008-04-30 at 17:34 +0200, Ivo van Doorn wrote:
> Hi,
> 
> Don't want to spoil the fun, but the following part doesn't compile
> when CONFIG_MAC80211_QOS is not set.

Oh! I hadn't tried that yet, thanks. I had tried not configuring it and
tested the Kconfig for it, but evidently not compiling.

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [RFC/RFT 4/4] mac80211: use multi-queue master netdevice
       [not found]   ` <20080430130051.397094000-cdvu00un1VgdHxzADdlk8Q@public.gmane.org>
  2008-04-30 14:37     ` Ivo van Doorn
@ 2008-04-30 19:39     ` Waskiewicz Jr, Peter P
       [not found]       ` <D5C1322C3E673F459512FB59E0DDC32904FE144F-O6kdQIuPh0Q64kNsxIetb7fspsVTdybXVpNB7YpNyf8@public.gmane.org>
  1 sibling, 1 reply; 41+ messages in thread
From: Waskiewicz Jr, Peter P @ 2008-04-30 19:39 UTC (permalink / raw)
  To: Johannes Berg, linux-wireless-u79uwXL29TY76Z2rM5mHXA
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA, Rindjunsky, Ron, Tomas Winkler,
	Ivo van Doorn

> --- everything.orig/net/mac80211/util.c	2008-04-30 
> 14:02:31.000000000 +0200
> +++ everything/net/mac80211/util.c	2008-04-30 
> 14:20:06.000000000 +0200
> @@ -323,18 +323,28 @@ __le16 ieee80211_ctstoself_duration(stru
>  }
>  EXPORT_SYMBOL(ieee80211_ctstoself_duration);
>  
> +void ieee80211_start_queue(struct ieee80211_hw *hw, int queue)
> +{
> +	struct ieee80211_local *local = hw_to_local(hw);
> +#ifdef CONFIG_MAC80211_QOS
> +	netif_start_subqueue(local->mdev, queue);
> +#else
> +	WARN_ON(queue != 0);
> +	netif_start_queue(local->mdev);
> +#endif
> +}
> +EXPORT_SYMBOL(ieee80211_start_queue);
> +

I would suggest that you enable the netdev feature flag for
NETIF_F_MULTI_QUEUE on devices when you create them.  That way you can
have things like ieee80211_start_queue() key on that instead of a
compile-time option, in case wireless devices come along that won't
support multiple queues, if that's possible.  So something like this:

+void ieee80211_start_queue(struct ieee80211_hw *hw, int queue)
+{
+	struct ieee80211_local *local = hw_to_local(hw);
+	if (netif_is_multiqueue(local->mdev) {
+		netif_start_subqueue(local->mdev, queue);
+	} else {
+		WARN_ON(queue != 0);
+		netif_start_queue(local->mdev);
+	}
+}
+EXPORT_SYMBOL(ieee80211_start_queue);
+

If you think this is a decent idea, I'd suggest that any function that
has a compile-time check for multiqueue being changed to use the runtime
check.  Then in your device setup, where you call netdev_alloc_mq(),
there you set the flag NETIF_F_MULTI_QUEUE based on the device features.

Other than that, this patch looks great.  Exciting to see this starting
to take flight.

Cheers,
-PJ Waskiewicz
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 41+ messages in thread

* RE: [RFC/RFT 4/4] mac80211: use multi-queue master netdevice
       [not found]       ` <D5C1322C3E673F459512FB59E0DDC32904FE144F-O6kdQIuPh0Q64kNsxIetb7fspsVTdybXVpNB7YpNyf8@public.gmane.org>
@ 2008-04-30 20:07         ` Johannes Berg
  0 siblings, 0 replies; 41+ messages in thread
From: Johannes Berg @ 2008-04-30 20:07 UTC (permalink / raw)
  To: Waskiewicz Jr, Peter P
  Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA, Rindjunsky, Ron, Tomas Winkler,
	Ivo van Doorn

[-- Attachment #1: Type: text/plain, Size: 1288 bytes --]


> > +void ieee80211_start_queue(struct ieee80211_hw *hw, int queue)
> > +{
> > +	struct ieee80211_local *local = hw_to_local(hw);
> > +#ifdef CONFIG_MAC80211_QOS
> > +	netif_start_subqueue(local->mdev, queue);
> > +#else
> > +	WARN_ON(queue != 0);
> > +	netif_start_queue(local->mdev);
> > +#endif
> > +}
> > +EXPORT_SYMBOL(ieee80211_start_queue);
> > +
> 
> I would suggest that you enable the netdev feature flag for
> NETIF_F_MULTI_QUEUE on devices when you create them.  That way you can
> have things like ieee80211_start_queue() key on that instead of a
> compile-time option, in case wireless devices come along that won't
> support multiple queues, if that's possible. 

Ah. I thought that then I'd just create a device with alloc_mq with a
single queue.

> If you think this is a decent idea, I'd suggest that any function that
> has a compile-time check for multiqueue being changed to use the runtime
> check.  Then in your device setup, where you call netdev_alloc_mq(),
> there you set the flag NETIF_F_MULTI_QUEUE based on the device features.

Hah, indeed, I don't currently set it at all. Yes, I guess I should do
it that way.

> Other than that, this patch looks great.  Exciting to see this starting
> to take flight.

:)

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [RFC/RFT 4/4] mac80211: use multi-queue master netdevice
       [not found]               ` <1209567609.18659.33.camel-YfaajirXv214zXjbi5bjpg@public.gmane.org>
  2008-04-30 15:34                 ` Ivo van Doorn
@ 2008-05-01  8:21                 ` Ivo van Doorn
       [not found]                   ` <200805011021.04435.IvDoorn-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
  1 sibling, 1 reply; 41+ messages in thread
From: Ivo van Doorn @ 2008-05-01  8:21 UTC (permalink / raw)
  To: Johannes Berg
  Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA, Ron Rindjunsky, Tomas Winkler,
	Peter P Waskiewicz Jr

On Wednesday 30 April 2008, Johannes Berg wrote:
> Turns out there was something missing (last hunk of this updated
> version) from the rt2x00 part of the patch that I accidentally didn't
> quilt edit, here's an updated version if anybody wants to test on rt2x00
> hw.

I've tested the following patch series:

mac80211: clean up get_tx_stats callback
mac80211: remove queue info from ieee80211_tx_status
mac80211: QoS related cleanups
mac80211: use rate index in TX control
GSO: generalize for mac80211
mac80211: use GSO for fragmentation
mac80211: use multi-queue master netdevice

My rt61 card (4 TX queues) seems to work correctly, it uses
queue 3 as default queue, and a few packets seem to go
over queue 1.

Ivo

> johannes
> 
> Subject: mac80211: use multi-queue master netdevice
> 
> This patch updates mac80211 and drivers to be multi-queue aware and
> use that instead of the internal queue mapping.
> 
> Signed-off-by: Johannes Berg <johannes-cdvu00un1VgdHxzADdlk8Q@public.gmane.org>
> ---
> This is just ground-work. We'll be able to play with Qos much more now.
> 
>  drivers/net/wireless/ath5k/base.c           |    2 -
>  drivers/net/wireless/b43/dma.c              |    7 ++-
>  drivers/net/wireless/b43/pio.c              |    8 ++--
>  drivers/net/wireless/b43legacy/dma.c        |    2 -
>  drivers/net/wireless/iwlwifi/iwl3945-base.c |    4 +-
>  drivers/net/wireless/iwlwifi/iwl4965-base.c |    4 +-
>  drivers/net/wireless/p54/p54common.c        |   10 ++---
>  drivers/net/wireless/rt2x00/rt2x00mac.c     |   10 ++---
>  drivers/net/wireless/rt2x00/rt2x00pci.c     |    2 -
>  drivers/net/wireless/rt2x00/rt2x00queue.h   |   13 ------
>  drivers/net/wireless/rt2x00/rt2x00usb.c     |    2 -
>  drivers/net/wireless/rtl8180_dev.c          |    4 +-
>  include/net/mac80211.h                      |   12 ++++--
>  net/mac80211/Kconfig                        |   14 ++++++-
>  net/mac80211/Makefile                       |    2 -
>  net/mac80211/ieee80211_i.h                  |    7 ---
>  net/mac80211/main.c                         |   21 ++++++----
>  net/mac80211/tx.c                           |    9 ----
>  net/mac80211/util.c                         |   54 +++++++++++++++++++---------
>  net/mac80211/wme.c                          |   13 ++----
>  net/mac80211/wme.h                          |    2 -
>  21 files changed, 108 insertions(+), 94 deletions(-)
> 
> --- everything.orig/drivers/net/wireless/ath5k/base.c	2008-04-30 16:55:36.000000000 +0200
> +++ everything/drivers/net/wireless/ath5k/base.c	2008-04-30 16:55:37.000000000 +0200
> @@ -2677,7 +2677,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct
>  	if (list_empty(&sc->txbuf)) {
>  		ATH5K_ERR(sc, "no further txbuf available, dropping packet\n");
>  		spin_unlock_irqrestore(&sc->txbuflock, flags);
> -		ieee80211_stop_queue(hw, ctl->queue);
> +		ieee80211_stop_queue(hw, skb_get_queue_mapping(skb));
>  		return -1;
>  	}
>  	bf = list_first_entry(&sc->txbuf, struct ath5k_buf, list);
> --- everything.orig/drivers/net/wireless/iwlwifi/iwl3945-base.c	2008-04-30 16:55:36.000000000 +0200
> +++ everything/drivers/net/wireless/iwlwifi/iwl3945-base.c	2008-04-30 16:55:37.000000000 +0200
> @@ -2562,7 +2562,7 @@ static int iwl3945_tx_skb(struct iwl3945
>  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
>  	struct iwl3945_tfd_frame *tfd;
>  	u32 *control_flags;
> -	int txq_id = ctl->queue;
> +	int txq_id = skb_get_queue_mapping(skb);
>  	struct iwl3945_tx_queue *txq = NULL;
>  	struct iwl3945_queue *q = NULL;
>  	dma_addr_t phys_addr;
> @@ -2776,7 +2776,7 @@ static int iwl3945_tx_skb(struct iwl3945
>  			spin_unlock_irqrestore(&priv->lock, flags);
>  		}
>  
> -		ieee80211_stop_queue(priv->hw, ctl->queue);
> +		ieee80211_stop_queue(priv->hw, skb_get_queue_mapping(skb));
>  	}
>  
>  	return 0;
> --- everything.orig/drivers/net/wireless/iwlwifi/iwl4965-base.c	2008-04-30 16:55:36.000000000 +0200
> +++ everything/drivers/net/wireless/iwlwifi/iwl4965-base.c	2008-04-30 16:55:37.000000000 +0200
> @@ -2126,7 +2126,7 @@ static int iwl4965_tx_skb(struct iwl_pri
>  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
>  	struct iwl4965_tfd_frame *tfd;
>  	u32 *control_flags;
> -	int txq_id = ctl->queue;
> +	int txq_id = skb_get_queue_mapping(skb);
>  	struct iwl4965_tx_queue *txq = NULL;
>  	struct iwl4965_queue *q = NULL;
>  	dma_addr_t phys_addr;
> @@ -2350,7 +2350,7 @@ static int iwl4965_tx_skb(struct iwl_pri
>  			spin_unlock_irqrestore(&priv->lock, flags);
>  		}
>  
> -		ieee80211_stop_queue(priv->hw, ctl->queue);
> +		ieee80211_stop_queue(priv->hw, skb_get_queue_mapping(skb));
>  	}
>  
>  	return 0;
> --- everything.orig/drivers/net/wireless/p54/p54common.c	2008-04-30 16:55:36.000000000 +0200
> +++ everything/drivers/net/wireless/p54/p54common.c	2008-04-30 16:55:37.000000000 +0200
> @@ -417,7 +417,7 @@ static void p54_rx_frame_sent(struct iee
>  			memcpy(&status.control, range->control,
>  			       sizeof(status.control));
>  			kfree(range->control);
> -			priv->tx_stats[status.control.queue].len--;
> +			priv->tx_stats[skb_get_queue_mapping(skb)].len--;
>  			entry_hdr = (struct p54_control_hdr *) entry->data;
>  			entry_data = (struct p54_tx_control_allocdata *) entry_hdr->data;
>  			if ((entry_hdr->magic1 & cpu_to_le16(0x4000)) != 0)
> @@ -562,13 +562,13 @@ static int p54_tx(struct ieee80211_hw *d
>  	size_t padding, len;
>  	u8 rate;
>  
> -	current_queue = &priv->tx_stats[control->queue];
> +	current_queue = &priv->tx_stats[skb_get_queue_mapping(skb)];
>  	if (unlikely(current_queue->len > current_queue->limit))
>  		return NETDEV_TX_BUSY;
>  	current_queue->len++;
>  	current_queue->count++;
>  	if (current_queue->len == current_queue->limit)
> -		ieee80211_stop_queue(dev, control->queue);
> +		ieee80211_stop_queue(dev, skb_get_queue_mapping(skb));
>  
>  	padding = (unsigned long)(skb->data - (sizeof(*hdr) + sizeof(*txhdr))) & 3;
>  	len = skb->len;
> @@ -605,7 +605,7 @@ static int p54_tx(struct ieee80211_hw *d
>  	memset(txhdr->rateset, rate, 8);
>  	txhdr->wep_key_present = 0;
>  	txhdr->wep_key_len = 0;
> -	txhdr->frame_type = cpu_to_le32(control->queue + 4);
> +	txhdr->frame_type = cpu_to_le32(skb_get_queue_mapping(skb) + 4);
>  	txhdr->magic4 = 0;
>  	txhdr->antenna = (control->antenna_sel_tx == 0) ?
>  		2 : control->antenna_sel_tx - 1;
> @@ -944,7 +944,7 @@ static int p54_conf_tx(struct ieee80211_
>  	vdcf = (struct p54_tx_control_vdcf *)(((struct p54_control_hdr *)
>  		((void *)priv->cached_vdcf + priv->tx_hdr_len))->data);
>  
> -	if ((params) && !((queue < 0) || (queue > 4))) {
> +	if (params && queue < dev->queues) {
>  		P54_SET_QUEUE(vdcf->queue[queue], params->aifs,
>  			params->cw_min, params->cw_max, params->txop);
>  	} else
> --- everything.orig/drivers/net/wireless/rtl8180_dev.c	2008-04-30 16:55:36.000000000 +0200
> +++ everything/drivers/net/wireless/rtl8180_dev.c	2008-04-30 16:55:37.000000000 +0200
> @@ -251,7 +251,7 @@ static int rtl8180_tx(struct ieee80211_h
>  	u16 plcp_len = 0;
>  	__le16 rts_duration = 0;
>  
> -	prio = control->queue;
> +	prio = skb_get_queue_mapping(skb);
>  	ring = &priv->tx_ring[prio];
>  
>  	mapping = pci_map_single(priv->pdev, skb->data,
> @@ -306,7 +306,7 @@ static int rtl8180_tx(struct ieee80211_h
>  	entry->flags = cpu_to_le32(tx_flags);
>  	__skb_queue_tail(&ring->queue, skb);
>  	if (ring->entries - skb_queue_len(&ring->queue) < 2)
> -		ieee80211_stop_queue(dev, control->queue);
> +		ieee80211_stop_queue(dev, skb_get_queue_mapping(skb));
>  	spin_unlock_irqrestore(&priv->lock, flags);
>  
>  	rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING, (1 << (prio + 4)));
> --- everything.orig/include/net/mac80211.h	2008-04-30 16:55:37.000000000 +0200
> +++ everything/include/net/mac80211.h	2008-04-30 16:55:37.000000000 +0200
> @@ -282,9 +282,6 @@ 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
> @@ -1575,6 +1572,15 @@ void ieee80211_wake_queue(struct ieee802
>  void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue);
>  
>  /**
> + * ieee80211_start_queue - start specific queue
> + * @hw: pointer as obtained from ieee80211_alloc_hw().
> + * @queue: queue number (counted from zero).
> + *
> + * Drivers should use this function instead of netif_start_queue.
> + */
> +void ieee80211_start_queue(struct ieee80211_hw *hw, int queue);
> +
> +/**
>   * ieee80211_start_queues - start all queues
>   * @hw: pointer to as obtained from ieee80211_alloc_hw().
>   *
> --- everything.orig/net/mac80211/Kconfig	2008-04-30 16:53:47.000000000 +0200
> +++ everything/net/mac80211/Kconfig	2008-04-30 16:55:37.000000000 +0200
> @@ -7,11 +7,23 @@ config MAC80211
>  	select CRC32
>  	select WIRELESS_EXT
>  	select CFG80211
> -	select NET_SCH_FIFO
>  	---help---
>  	  This option enables the hardware independent IEEE 802.11
>  	  networking stack.
>  
> +config MAC80211_QOS
> +	def_bool y
> +	depends on MAC80211
> +	depends on NET_SCHED
> +	depends on NETDEVICES_MULTIQUEUE
> +
> +comment "QoS/HT support disabled"
> +	depends on !MAC80211_QOS
> +comment "QoS/HT support needs CONFIG_NET_SCHED"
> +	depends on MAC80211 && !NET_SCHED
> +comment "QoS/HT support needs CONFIG_NETDEVICES_MULTIQUEUE"
> +	depends on MAC80211 && !NETDEVICES_MULTIQUEUE
> +
>  menu "Rate control algorithm selection"
>  	depends on MAC80211 != n
>  
> --- everything.orig/net/mac80211/Makefile	2008-04-30 16:55:30.000000000 +0200
> +++ everything/net/mac80211/Makefile	2008-04-30 16:55:37.000000000 +0200
> @@ -29,7 +29,7 @@ mac80211-y := \
>  	event.o
>  
>  mac80211-$(CONFIG_MAC80211_LEDS) += led.o
> -mac80211-$(CONFIG_NET_SCHED) += wme.o
> +mac80211-$(CONFIG_MAC80211_QOS) += wme.o
>  mac80211-$(CONFIG_MAC80211_DEBUGFS) += \
>  	debugfs.o \
>  	debugfs_sta.o \
> --- everything.orig/net/mac80211/ieee80211_i.h	2008-04-30 16:55:37.000000000 +0200
> +++ everything/net/mac80211/ieee80211_i.h	2008-04-30 16:55:37.000000000 +0200
> @@ -213,7 +213,6 @@ struct ieee80211_rx_data {
>  struct ieee80211_tx_packet_data {
>  	u32 flags;
>  	int ifindex;
> -	u16 queue;
>  	unsigned long jiffies;
>  };
>  
> @@ -605,8 +604,6 @@ struct ieee80211_local {
>  	struct sta_info *sta_hash[STA_HASH_SIZE];
>  	struct timer_list sta_cleanup;
>  
> -	unsigned long state[IEEE80211_MAX_AMPDU_QUEUES + IEEE80211_MAX_AMPDU_QUEUES];
> -
>  	/* number of interfaces with corresponding IFF_ flags */
>  	atomic_t iff_allmultis, iff_promiscs;
>  
> @@ -836,10 +833,6 @@ static inline struct ieee80211_hw *local
>  	return &local->hw;
>  }
>  
> -enum ieee80211_link_state_t {
> -	IEEE80211_LINK_STATE_XOFF = 0,
> -};
> -
>  struct sta_attribute {
>  	struct attribute attr;
>  	ssize_t (*show)(const struct sta_info *, char *buf);
> --- everything.orig/net/mac80211/main.c	2008-04-30 16:55:37.000000000 +0200
> +++ everything/net/mac80211/main.c	2008-04-30 16:55:37.000000000 +0200
> @@ -1313,7 +1313,6 @@ static void ieee80211_remove_tx_extra(st
>  		pkt_data->flags |= IEEE80211_TXPD_REQUEUE;
>  	if (control->flags & IEEE80211_TXCTL_EAPOL_FRAME)
>  		pkt_data->flags |= IEEE80211_TXPD_EAPOL_FRAME;
> -	pkt_data->queue = control->queue;
>  
>  	hdrlen = ieee80211_get_hdrlen_from_skb(skb);
>  
> @@ -1713,9 +1712,20 @@ int ieee80211_register_hw(struct ieee802
>  	if (result < 0)
>  		return result;
>  
> +#ifdef CONFIG_MAC80211_QOS
> +	if (hw->queues > IEEE80211_MAX_QUEUES)
> +		hw->queues = IEEE80211_MAX_QUEUES;
> +	if (hw->ampdu_queues > IEEE80211_MAX_AMPDU_QUEUES)
> +		hw->ampdu_queues = IEEE80211_MAX_AMPDU_QUEUES;
> +#else
> +	hw->queues = 1;
> +	hw->ampdu_queues = 0;
> +#endif
> +
>  	/* for now, mdev needs sub_if_data :/ */
> -	mdev = alloc_netdev(sizeof(struct ieee80211_sub_if_data),
> -			    "wmaster%d", ether_setup);
> +	mdev = alloc_netdev_mq(sizeof(struct ieee80211_sub_if_data),
> +			       "wmaster%d", ether_setup,
> +			       hw->queues + hw->ampdu_queues);
>  	if (!mdev)
>  		goto fail_mdev_alloc;
>  
> @@ -1807,11 +1817,6 @@ int ieee80211_register_hw(struct ieee802
>  		goto fail_wep;
>  	}
>  
> -	if (hw->queues > IEEE80211_MAX_QUEUES)
> -		hw->queues = IEEE80211_MAX_QUEUES;
> -	if (hw->ampdu_queues > IEEE80211_MAX_AMPDU_QUEUES)
> -		hw->ampdu_queues = IEEE80211_MAX_AMPDU_QUEUES;
> -
>  	ieee80211_install_qdisc(local->mdev);
>  
>  	/* add one default STA interface */
> --- everything.orig/net/mac80211/tx.c	2008-04-30 16:55:37.000000000 +0200
> +++ everything/net/mac80211/tx.c	2008-04-30 16:55:37.000000000 +0200
> @@ -210,12 +210,6 @@ static u16 ieee80211_duration(struct iee
>  	return dur;
>  }
>  
> -static inline int __ieee80211_queue_stopped(const struct ieee80211_local *local,
> -					    int queue)
> -{
> -	return test_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]);
> -}
> -
>  static int inline is_ieee80211_device(struct net_device *dev,
>  				      struct net_device *master)
>  {
> @@ -1066,7 +1060,7 @@ static int ___ieee80211_tx(struct ieee80
>  	print_control(control, skb);
>  
>  	if (unlikely(netif_queue_stopped(local->mdev) ||
> -		     __ieee80211_queue_stopped(local, control->queue)))
> +		     netif_subqueue_stopped(local->mdev, skb)))
>  		return NETDEV_TX_BUSY;
>  
>  	ieee80211_dump_frame(wiphy_name(local->hw.wiphy),
> @@ -1354,7 +1348,6 @@ int ieee80211_master_start_xmit(struct s
>  		control.flags |= IEEE80211_TXCTL_EAPOL_FRAME;
>  	if (pkt_data->flags & IEEE80211_TXPD_AMPDU)
>  		control.flags |= IEEE80211_TXCTL_AMPDU;
> -	control.queue = pkt_data->queue;
>  
>  	memset(skb->cb, 0, sizeof(skb->cb));
>  	ret = ieee80211_tx(odev, skb, &control);
> --- everything.orig/net/mac80211/util.c	2008-04-30 16:55:37.000000000 +0200
> +++ everything/net/mac80211/util.c	2008-04-30 16:55:37.000000000 +0200
> @@ -323,18 +323,28 @@ __le16 ieee80211_ctstoself_duration(stru
>  }
>  EXPORT_SYMBOL(ieee80211_ctstoself_duration);
>  
> +void ieee80211_start_queue(struct ieee80211_hw *hw, int queue)
> +{
> +	struct ieee80211_local *local = hw_to_local(hw);
> +#ifdef CONFIG_MAC80211_QOS
> +	netif_start_subqueue(local->mdev, queue);
> +#else
> +	WARN_ON(queue != 0);
> +	netif_start_queue(local->mdev);
> +#endif
> +}
> +EXPORT_SYMBOL(ieee80211_start_queue);
> +
>  void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue)
>  {
>  	struct ieee80211_local *local = hw_to_local(hw);
>  
> -	if (test_and_clear_bit(IEEE80211_LINK_STATE_XOFF,
> -			       &local->state[queue])) {
> -		if (!ieee80211_qdisc_installed(local->mdev)) {
> -			if (queue == 0)
> -				netif_wake_queue(local->mdev);
> -		} else
> -			__netif_schedule(local->mdev);
> -	}
> +#ifdef CONFIG_MAC80211_QOS
> +	netif_wake_subqueue(local->mdev, queue);
> +#else
> +	WARN_ON(queue != 0);
> +	netif_wake_queue(local->mdev);
> +#endif
>  }
>  EXPORT_SYMBOL(ieee80211_wake_queue);
>  
> @@ -342,39 +352,51 @@ void ieee80211_stop_queue(struct ieee802
>  {
>  	struct ieee80211_local *local = hw_to_local(hw);
>  
> -	if (!ieee80211_qdisc_installed(local->mdev) && queue == 0)
> -		netif_stop_queue(local->mdev);
> -	set_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]);
> +#ifdef CONFIG_MAC80211_QOS
> +	netif_stop_subqueue(local->mdev, queue);
> +#else
> +	WARN_ON(queue != 0);
> +	netif_stop_queue(local->mdev);
> +#endif
>  }
>  EXPORT_SYMBOL(ieee80211_stop_queue);
>  
>  void ieee80211_start_queues(struct ieee80211_hw *hw)
>  {
> -	struct ieee80211_local *local = hw_to_local(hw);
> +#ifdef CONFIG_MAC80211_QOS
>  	int i;
>  
> -	for (i = 0; i < local->hw.queues; i++)
> -		clear_bit(IEEE80211_LINK_STATE_XOFF, &local->state[i]);
> -	if (!ieee80211_qdisc_installed(local->mdev))
> -		netif_start_queue(local->mdev);
> +	for (i = 0; i < hw->queues; i++)
> +		ieee80211_start_queue(hw, i);
> +#else
> +	netif_start_queue(local->mdev);
> +#endif
>  }
>  EXPORT_SYMBOL(ieee80211_start_queues);
>  
>  void ieee80211_stop_queues(struct ieee80211_hw *hw)
>  {
> +#ifdef CONFIG_MAC80211_QOS
>  	int i;
>  
>  	for (i = 0; i < hw->queues; i++)
>  		ieee80211_stop_queue(hw, i);
> +#else
> +	netif_stop_queue(local->mdev);
> +#endif
>  }
>  EXPORT_SYMBOL(ieee80211_stop_queues);
>  
>  void ieee80211_wake_queues(struct ieee80211_hw *hw)
>  {
> +#ifdef CONFIG_MAC80211_QOS
>  	int i;
>  
>  	for (i = 0; i < hw->queues; i++)
>  		ieee80211_wake_queue(hw, i);
> +#else
> +	netif_wake_queue(local->mdev);
> +#endif
>  }
>  EXPORT_SYMBOL(ieee80211_wake_queues);
>  
> --- everything.orig/net/mac80211/wme.c	2008-04-30 16:55:37.000000000 +0200
> +++ everything/net/mac80211/wme.c	2008-04-30 16:55:37.000000000 +0200
> @@ -160,7 +160,7 @@ static int wme_qdiscop_enqueue(struct sk
>  	u8 tid;
>  
>  	if (pkt_data->flags & IEEE80211_TXPD_REQUEUE) {
> -		queue = pkt_data->queue;
> +		queue = skb_get_queue_mapping(skb);
>  		rcu_read_lock();
>  		sta = sta_info_get(local, hdr->addr1);
>  		tid = skb->priority & QOS_CONTROL_TAG1D_MASK;
> @@ -221,7 +221,7 @@ static int wme_qdiscop_enqueue(struct sk
>  			err = NET_XMIT_DROP;
>  	} else {
>  		tid = skb->priority & QOS_CONTROL_TAG1D_MASK;
> -		pkt_data->queue = (unsigned int) queue;
> +		skb_set_queue_mapping(skb, queue);
>  		qdisc = q->queues[queue];
>  		err = qdisc->enqueue(skb, qdisc);
>  		if (err == NET_XMIT_SUCCESS) {
> @@ -239,13 +239,10 @@ static int wme_qdiscop_enqueue(struct sk
>  static int wme_qdiscop_requeue(struct sk_buff *skb, struct Qdisc* qd)
>  {
>  	struct ieee80211_sched_data *q = qdisc_priv(qd);
> -	struct ieee80211_tx_packet_data *pkt_data =
> -		(struct ieee80211_tx_packet_data *) skb->cb;
>  	struct Qdisc *qdisc;
>  	int err;
>  
> -	/* We recorded which queue to use earlier. */
> -	qdisc = q->queues[pkt_data->queue];
> +	qdisc = q->queues[skb_get_queue_mapping(skb)];
>  
>  	if ((err = qdisc->ops->requeue(skb, qdisc)) == 0) {
>  		qd->q.qlen++;
> @@ -269,9 +266,7 @@ static struct sk_buff *wme_qdiscop_deque
>  	/* check all the h/w queues in numeric/priority order */
>  	for (queue = 0; queue < QD_NUM(hw); queue++) {
>  		/* see if there is room in this hardware queue */
> -		if ((test_bit(IEEE80211_LINK_STATE_XOFF,
> -				&local->state[queue])) ||
> -		    (!test_bit(queue, q->qdisc_pool)))
> +		if (__netif_subqueue_stopped(local->mdev, queue))
>  			continue;
>  
>  		/* there is space - try and get a frame */
> --- everything.orig/net/mac80211/wme.h	2008-04-30 16:53:47.000000000 +0200
> +++ everything/net/mac80211/wme.h	2008-04-30 16:55:37.000000000 +0200
> @@ -31,7 +31,7 @@ static inline int WLAN_FC_IS_QOS_DATA(u1
>  	return (fc & 0x8C) == 0x88;
>  }
>  
> -#ifdef CONFIG_NET_SCHED
> +#ifdef CONFIG_MAC80211_QOS
>  void ieee80211_install_qdisc(struct net_device *dev);
>  int ieee80211_qdisc_installed(struct net_device *dev);
>  int ieee80211_ht_agg_queue_add(struct ieee80211_local *local,
> --- everything.orig/drivers/net/wireless/b43/dma.c	2008-04-30 16:55:34.000000000 +0200
> +++ everything/drivers/net/wireless/b43/dma.c	2008-04-30 16:55:37.000000000 +0200
> @@ -1298,7 +1298,8 @@ int b43_dma_tx(struct b43_wldev *dev,
>  		hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
>  	} else {
>  		/* Decide by priority where to put this frame. */
> -		ring = select_ring_by_priority(dev, ctl->queue);
> +		ring = select_ring_by_priority(
> +			dev, skb_get_queue_mapping(skb));
>  	}
>  
>  	spin_lock_irqsave(&ring->lock, flags);
> @@ -1316,7 +1317,7 @@ int b43_dma_tx(struct b43_wldev *dev,
>  	/* Assign the queue number to the ring (if not already done before)
>  	 * so TX status handling can use it. The queue to ring mapping is
>  	 * static, so we don't need to store it per frame. */
> -	ring->queue_prio = ctl->queue;
> +	ring->queue_prio = skb_get_queue_mapping(skb);
>  
>  	err = dma_tx_fragment(ring, skb, ctl);
>  	if (unlikely(err == -ENOKEY)) {
> @@ -1334,7 +1335,7 @@ int b43_dma_tx(struct b43_wldev *dev,
>  	if ((free_slots(ring) < SLOTS_PER_PACKET) ||
>  	    should_inject_overflow(ring)) {
>  		/* This TX ring is full. */
> -		ieee80211_stop_queue(dev->wl->hw, ctl->queue);
> +		ieee80211_stop_queue(dev->wl->hw, skb_get_queue_mapping(skb));
>  		ring->stopped = 1;
>  		if (b43_debug(dev, B43_DBG_DMAVERBOSE)) {
>  			b43dbg(dev->wl, "Stopped TX ring %d\n", ring->index);
> --- everything.orig/drivers/net/wireless/b43/pio.c	2008-04-30 16:55:34.000000000 +0200
> +++ everything/drivers/net/wireless/b43/pio.c	2008-04-30 16:55:37.000000000 +0200
> @@ -510,7 +510,7 @@ int b43_pio_tx(struct b43_wldev *dev,
>  		hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
>  	} else {
>  		/* Decide by priority where to put this frame. */
> -		q = select_queue_by_priority(dev, ctl->queue);
> +		q = select_queue_by_priority(dev, skb_get_queue_mapping(skb));
>  	}
>  
>  	spin_lock_irqsave(&q->lock, flags);
> @@ -533,7 +533,7 @@ int b43_pio_tx(struct b43_wldev *dev,
>  	if (total_len > (q->buffer_size - q->buffer_used)) {
>  		/* Not enough memory on the queue. */
>  		err = -EBUSY;
> -		ieee80211_stop_queue(dev->wl->hw, ctl->queue);
> +		ieee80211_stop_queue(dev->wl->hw, skb_get_queue_mapping(skb));
>  		q->stopped = 1;
>  		goto out_unlock;
>  	}
> @@ -541,7 +541,7 @@ int b43_pio_tx(struct b43_wldev *dev,
>  	/* Assign the queue number to the ring (if not already done before)
>  	 * so TX status handling can use it. The mac80211-queue to b43-queue
>  	 * mapping is static, so we don't need to store it per frame. */
> -	q->queue_prio = ctl->queue;
> +	q->queue_prio = skb_get_queue_mapping(skb);
>  
>  	err = pio_tx_frame(q, skb, ctl);
>  	if (unlikely(err == -ENOKEY)) {
> @@ -561,7 +561,7 @@ int b43_pio_tx(struct b43_wldev *dev,
>  	if (((q->buffer_size - q->buffer_used) < roundup(2 + 2 + 6, 4)) ||
>  	    (q->free_packet_slots == 0)) {
>  		/* The queue is full. */
> -		ieee80211_stop_queue(dev->wl->hw, ctl->queue);
> +		ieee80211_stop_queue(dev->wl->hw, skb_get_queue_mapping(skb));
>  		q->stopped = 1;
>  	}
>  
> --- everything.orig/drivers/net/wireless/b43legacy/dma.c	2008-04-30 16:55:34.000000000 +0200
> +++ everything/drivers/net/wireless/b43legacy/dma.c	2008-04-30 16:55:37.000000000 +0200
> @@ -1330,7 +1330,7 @@ int b43legacy_dma_tx(struct b43legacy_wl
>  	int err = 0;
>  	unsigned long flags;
>  
> -	ring = priority_to_txring(dev, ctl->queue);
> +	ring = priority_to_txring(dev, skb_get_queue_mapping(skb));
>  	spin_lock_irqsave(&ring->lock, flags);
>  	B43legacy_WARN_ON(!ring->tx);
>  	if (unlikely(free_slots(ring) < SLOTS_PER_PACKET)) {
> --- everything.orig/drivers/net/wireless/rt2x00/rt2x00mac.c	2008-04-30 16:55:35.000000000 +0200
> +++ everything/drivers/net/wireless/rt2x00/rt2x00mac.c	2008-04-30 16:55:37.000000000 +0200
> @@ -81,7 +81,7 @@ int rt2x00mac_tx(struct ieee80211_hw *hw
>  {
>  	struct rt2x00_dev *rt2x00dev = hw->priv;
>  	struct ieee80211_hdr *ieee80211hdr = (struct ieee80211_hdr *)skb->data;
> -	enum data_queue_qid qid = mac80211_queue_to_qid(control->queue);
> +	enum data_queue_qid qid = skb_get_queue_mapping(skb);
>  	struct data_queue *queue;
>  	struct skb_frame_desc *skbdesc;
>  	u16 frame_control;
> @@ -129,12 +129,12 @@ int rt2x00mac_tx(struct ieee80211_hw *hw
>  			       IEEE80211_TXCTL_USE_CTS_PROTECT)) &&
>  	    !rt2x00dev->ops->hw->set_rts_threshold) {
>  		if (rt2x00queue_available(queue) <= 1) {
> -			ieee80211_stop_queue(rt2x00dev->hw, control->queue);
> +			ieee80211_stop_queue(rt2x00dev->hw, qid);
>  			return NETDEV_TX_BUSY;
>  		}
>  
>  		if (rt2x00mac_tx_rts_cts(rt2x00dev, queue, skb, control)) {
> -			ieee80211_stop_queue(rt2x00dev->hw, control->queue);
> +			ieee80211_stop_queue(rt2x00dev->hw, qid);
>  			return NETDEV_TX_BUSY;
>  		}
>  	}
> @@ -146,12 +146,12 @@ int rt2x00mac_tx(struct ieee80211_hw *hw
>  	memset(skbdesc, 0, sizeof(*skbdesc));
>  
>  	if (rt2x00dev->ops->lib->write_tx_data(rt2x00dev, queue, skb, control)) {
> -		ieee80211_stop_queue(rt2x00dev->hw, control->queue);
> +		ieee80211_stop_queue(rt2x00dev->hw, qid);
>  		return NETDEV_TX_BUSY;
>  	}
>  
>  	if (rt2x00queue_full(queue))
> -		ieee80211_stop_queue(rt2x00dev->hw, control->queue);
> +		ieee80211_stop_queue(rt2x00dev->hw, qid);
>  
>  	if (rt2x00dev->ops->lib->kick_tx_queue)
>  		rt2x00dev->ops->lib->kick_tx_queue(rt2x00dev, qid);
> --- everything.orig/drivers/net/wireless/rt2x00/rt2x00pci.c	2008-04-30 16:55:33.000000000 +0200
> +++ everything/drivers/net/wireless/rt2x00/rt2x00pci.c	2008-04-30 16:55:37.000000000 +0200
> @@ -178,7 +178,7 @@ void rt2x00pci_txdone(struct rt2x00_dev 
>  	 * is reenabled when the txdone handler has finished.
>  	 */
>  	if (!rt2x00queue_full(entry->queue))
> -		ieee80211_wake_queue(rt2x00dev->hw, priv_tx->control.queue);
> +		ieee80211_wake_queue(rt2x00dev->hw, entry->queue->qid);
>  
>  }
>  EXPORT_SYMBOL_GPL(rt2x00pci_txdone);
> --- everything.orig/drivers/net/wireless/rt2x00/rt2x00queue.h	2008-04-30 16:55:35.000000000 +0200
> +++ everything/drivers/net/wireless/rt2x00/rt2x00queue.h	2008-04-30 16:55:37.000000000 +0200
> @@ -80,19 +80,6 @@ enum data_queue_qid {
>  };
>  
>  /**
> - * mac80211_queue_to_qid - Convert mac80211 queue to rt2x00 qid
> - * @queue: mac80211 queue.
> - */
> -static inline enum data_queue_qid mac80211_queue_to_qid(unsigned int queue)
> -{
> -	/* Regular TX queues are mapped directly */
> -	if (queue < 4)
> -		return queue;
> -	WARN_ON(1);
> -	return QID_OTHER;
> -}
> -
> -/**
>   * enum skb_frame_desc_flags: Flags for &struct skb_frame_desc
>   *
>   * @FRAME_DESC_DRIVER_GENERATED: Frame was generated inside driver
> --- everything.orig/drivers/net/wireless/rt2x00/rt2x00usb.c	2008-04-30 16:56:02.000000000 +0200
> +++ everything/drivers/net/wireless/rt2x00/rt2x00usb.c	2008-04-30 16:56:16.000000000 +0200
> @@ -166,7 +166,7 @@ static void rt2x00usb_interrupt_txdone(s
>  	 * is reenabled when the txdone handler has finished.
>  	 */
>  	if (!rt2x00queue_full(entry->queue))
> -		ieee80211_wake_queue(rt2x00dev->hw, priv_tx->control.queue);
> +		ieee80211_wake_queue(rt2x00dev->hw, entry->queue->qid);
>  }
>  
>  int rt2x00usb_write_tx_data(struct rt2x00_dev *rt2x00dev,
> 
> 
> 


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

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [RFC/RFT 4/4] mac80211: use multi-queue master netdevice
       [not found]                   ` <200805011021.04435.IvDoorn-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
@ 2008-05-01  8:54                     ` Johannes Berg
  0 siblings, 0 replies; 41+ messages in thread
From: Johannes Berg @ 2008-05-01  8:54 UTC (permalink / raw)
  To: Ivo van Doorn
  Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA, Ron Rindjunsky, Tomas Winkler,
	Peter P Waskiewicz Jr

[-- Attachment #1: Type: text/plain, Size: 525 bytes --]


> I've tested the following patch series:
> 
> mac80211: clean up get_tx_stats callback
> mac80211: remove queue info from ieee80211_tx_status
> mac80211: QoS related cleanups
> mac80211: use rate index in TX control
> GSO: generalize for mac80211
> mac80211: use GSO for fragmentation
> mac80211: use multi-queue master netdevice
> 
> My rt61 card (4 TX queues) seems to work correctly, it uses
> queue 3 as default queue, and a few packets seem to go
> over queue 1.

Nice, thanks for testing!

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [RFC/RFT 2/4] GSO: generalize for mac80211
       [not found]   ` <20080430130049.359549000-cdvu00un1VgdHxzADdlk8Q@public.gmane.org>
@ 2008-05-06 16:12     ` Johannes Berg
  0 siblings, 0 replies; 41+ messages in thread
From: Johannes Berg @ 2008-05-06 16:12 UTC (permalink / raw)
  To: linux-wireless-u79uwXL29TY76Z2rM5mHXA
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA, Ron Rindjunsky, Tomas Winkler,
	Ivo van Doorn, Peter P Waskiewicz Jr, Herbert Xu, David S. Miller

[-- Attachment #1: Type: text/plain, Size: 1076 bytes --]

On Wed, 2008-04-30 at 14:40 +0200, Johannes Berg wrote:
> plain text document attachment (037-generalize-gso-functions.patch)
> This patch adds a new function dev_skb_segment() that generalises
> the existing dev_gso_segment() by allowing to give a segmentation
> function. mac80211 will use that function using the segmentation
> function skb_segment().
> 
> This patch also changes dev_gso_skb_destructor() to be safe when
> the skb no longer has any segments, this will happen when mac80211
> has internally passed off all the fragments to the driver instead
> of asking dev_hard_start_xmit() to do it (which protects against
> this by resetting the destructor if it has sent all fragments.)

Any comments on this? I'd like to include this (well, more importantly
the mac80211 patch that needs it) for testing for 2.6.27, should I send
it via John or directly to you, Dave?

Also, I'm not sure, should I allow for a segmentation function at all?
I'm only going to use the generic skb_segment and the code there only
uses skb_gso_segment.

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [RFC/RFT 3/4] mac80211: use GSO for fragmentation
  2008-04-30 12:40 ` [RFC/RFT 3/4] mac80211: use GSO for fragmentation Johannes Berg
@ 2008-05-07  7:10   ` Herbert Xu
  2008-05-07  8:50     ` Johannes Berg
  0 siblings, 1 reply; 41+ messages in thread
From: Herbert Xu @ 2008-05-07  7:10 UTC (permalink / raw)
  To: Johannes Berg
  Cc: linux-wireless, netdev, Ron Rindjunsky, Tomas Winkler,
	Ivo van Doorn, Peter P Waskiewicz Jr

On Wed, Apr 30, 2008 at 02:40:58PM +0200, Johannes Berg wrote:
> This patch makes mac80211 use the GSO infrastructure for segmentation,
> but not really all of it because we do not register a protocol handler
> (nor can do that easily since a lot of functions need to be done before
> segmentation and another bunch afterwards, and we need to keep rcu-
> protected structures for both.)

Your idea of using skb_segment to remove duplicate code is great.

However, using skb->gso_skb for the push-back doesn't work.  For
example, what is going to happen when I enable software GSO on a
wireless device and then send a real GSO packet to it where each
GSO fragment also required wireless fragmentation?

I suggest that you just use skb_segment for the actual fragmentation
but keep the original infrastructure for handling the fragments.

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [RFC/RFT 3/4] mac80211: use GSO for fragmentation
  2008-05-07  7:10   ` Herbert Xu
@ 2008-05-07  8:50     ` Johannes Berg
  2008-05-07  9:00       ` Herbert Xu
  0 siblings, 1 reply; 41+ messages in thread
From: Johannes Berg @ 2008-05-07  8:50 UTC (permalink / raw)
  To: Herbert Xu
  Cc: linux-wireless, netdev, Ron Rindjunsky, Tomas Winkler,
	Ivo van Doorn, Peter P Waskiewicz Jr

[-- Attachment #1: Type: text/plain, Size: 2147 bytes --]

On Wed, 2008-05-07 at 15:10 +0800, Herbert Xu wrote:
> On Wed, Apr 30, 2008 at 02:40:58PM +0200, Johannes Berg wrote:
> > This patch makes mac80211 use the GSO infrastructure for segmentation,
> > but not really all of it because we do not register a protocol handler
> > (nor can do that easily since a lot of functions need to be done before
> > segmentation and another bunch afterwards, and we need to keep rcu-
> > protected structures for both.)
> 
> Your idea of using skb_segment to remove duplicate code is great.
> 
> However, using skb->gso_skb for the push-back doesn't work.  For
> example, what is going to happen when I enable software GSO on a
> wireless device and then send a real GSO packet to it where each
> GSO fragment also required wireless fragmentation?

Good point. Somehow I thought this couldn't happen but of course it can
since we don't do hw GSO.

> I suggest that you just use skb_segment for the actual fragmentation
> but keep the original infrastructure for handling the fragments.

But I want to get rid of that much more than I want to get rid of the
fragmentation code :/ Also, if I need to keep that code, I will
absolutely not use skb_segment either as it's more efficient to just
trim the existing skb to make the first segment out of it (like the code
does now) rather than reallocate that too.

However, I'm sure it's buggy under certain circumstances we never
understood (as indicated by a WARN_ON triggering that shouldn't and lots
of reports about that) and I don't like the extra tasklet. I guess I
could remove it and copy the existing code into the wireless qdisc, but
that would be weird.

But you're right, it doesn't work this way. I'll have to think of
something. Maybe I can rip out all the retry logic and just store an skb
myself (like dev->gso_skb) and return NETDEV_TX_BUSY when I used that
one and the queue got full because of it. That'll requeue a lot when
non-gso skbs are coming in, but mind you this is not a common case, most
of the time fragmentation will not be enabled and even when it is device
queues should be long enough.

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [RFC/RFT 3/4] mac80211: use GSO for fragmentation
  2008-05-07  8:50     ` Johannes Berg
@ 2008-05-07  9:00       ` Herbert Xu
       [not found]         ` <20080507090040.GA25186-lOAM2aK0SrRLBo1qDEOMRrpzq4S04n8Q@public.gmane.org>
  0 siblings, 1 reply; 41+ messages in thread
From: Herbert Xu @ 2008-05-07  9:00 UTC (permalink / raw)
  To: Johannes Berg
  Cc: linux-wireless, netdev, Ron Rindjunsky, Tomas Winkler,
	Ivo van Doorn, Peter P Waskiewicz Jr

On Wed, May 07, 2008 at 10:50:17AM +0200, Johannes Berg wrote:
>
> But you're right, it doesn't work this way. I'll have to think of
> something. Maybe I can rip out all the retry logic and just store an skb
> myself (like dev->gso_skb) and return NETDEV_TX_BUSY when I used that
> one and the queue got full because of it. That'll requeue a lot when
> non-gso skbs are coming in, but mind you this is not a common case, most
> of the time fragmentation will not be enabled and even when it is device
> queues should be long enough.

Yes keeping your own gso_skb sounds like a good solution.

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH] mac80211: rewrite fragmentation code
       [not found]         ` <20080507090040.GA25186-lOAM2aK0SrRLBo1qDEOMRrpzq4S04n8Q@public.gmane.org>
@ 2008-05-07 11:22           ` Johannes Berg
       [not found]             ` <1210159339.5642.13.camel-YfaajirXv214zXjbi5bjpg@public.gmane.org>
  0 siblings, 1 reply; 41+ messages in thread
From: Johannes Berg @ 2008-05-07 11:22 UTC (permalink / raw)
  To: Herbert Xu
  Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA, Ron Rindjunsky, Tomas Winkler,
	Ivo van Doorn, Peter P Waskiewicz Jr

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-cdvu00un1VgdHxzADdlk8Q@public.gmane.org>
---
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-AlSwsSmVLrQ@public.gmane.org>
- * Copyright 2007	Johannes Berg <johannes-cdvu00un1VgdHxzADdlk8Q@public.gmane.org>
+ * Copyright 2007-2008	Johannes Berg <johannes-cdvu00un1VgdHxzADdlk8Q@public.gmane.org>
  *
  * 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-AlSwsSmVLrQ@public.gmane.org>
- * Copyright 2007	Johannes Berg <johannes-cdvu00un1VgdHxzADdlk8Q@public.gmane.org>
+ * Copyright 2007-2008	Johannes Berg <johannes-cdvu00un1VgdHxzADdlk8Q@public.gmane.org>
  *
  * 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 linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH] mac80211: rewrite fragmentation code
       [not found]             ` <1210159339.5642.13.camel-YfaajirXv214zXjbi5bjpg@public.gmane.org>
@ 2008-05-07 11:41               ` Herbert Xu
  2008-05-07 11:52                 ` Johannes Berg
  0 siblings, 1 reply; 41+ messages in thread
From: Herbert Xu @ 2008-05-07 11:41 UTC (permalink / raw)
  To: Johannes Berg
  Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA, Ron Rindjunsky, Tomas Winkler,
	Ivo van Doorn, Peter P Waskiewicz Jr

On Wed, May 07, 2008 at 01:22:19PM +0200, Johannes Berg wrote:
>
> +	if (unlikely(netif_queue_stopped(local->mdev) ||
> +		     __ieee80211_queue_stopped(local, control->queue)))
> +		return NETDEV_TX_BUSY;

Returning busy should be avoided because not everything expects it.
For example, this can cause tcpdump to see the same packet twice.

Besides, I'm not sure if this will even work if you're fiddling
with skb->next.  Perhaps you can stash it in a pointer local to
your device.

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert-lOAM2aK0SrRLBo1qDEOMRrpzq4S04n8Q@public.gmane.org>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH] mac80211: rewrite fragmentation code
  2008-05-07 11:41               ` Herbert Xu
@ 2008-05-07 11:52                 ` Johannes Berg
       [not found]                   ` <1210161133.5642.19.camel-YfaajirXv214zXjbi5bjpg@public.gmane.org>
  0 siblings, 1 reply; 41+ messages in thread
From: Johannes Berg @ 2008-05-07 11:52 UTC (permalink / raw)
  To: Herbert Xu
  Cc: linux-wireless, netdev, Ron Rindjunsky, Tomas Winkler,
	Ivo van Doorn, Peter P Waskiewicz Jr

[-- Attachment #1: Type: text/plain, Size: 1132 bytes --]

On Wed, 2008-05-07 at 19:41 +0800, Herbert Xu wrote:
> On Wed, May 07, 2008 at 01:22:19PM +0200, Johannes Berg wrote:
> >
> > +	if (unlikely(netif_queue_stopped(local->mdev) ||
> > +		     __ieee80211_queue_stopped(local, control->queue)))
> > +		return NETDEV_TX_BUSY;
> 
> Returning busy should be avoided because not everything expects it.

Hmm. Why does busy exist then? Historical accident? I really really
don't want to keep all that logic that does "if device enables queue
then first check if maybe we have pending packets and try to send them
and then go back to the regular queues and see if ..."

> Besides, I'm not sure if this will even work if you're fiddling
> with skb->next.  Perhaps you can stash it in a pointer local to
> your device.

You're looking at an internal helper function that is never called with
skb->next assigned; if it returns BUSY for a fragmented skb then later
in __ieee80211_tx() I will store the fragmented frame away and return
OK.

But if I really need to avoid returning busy I need to hook into all the
queue management stuff which I'd like to avoid.

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH] mac80211: rewrite fragmentation code
       [not found]                   ` <1210161133.5642.19.camel-YfaajirXv214zXjbi5bjpg@public.gmane.org>
@ 2008-05-07 13:05                     ` Herbert Xu
       [not found]                       ` <20080507130548.GA26977-lOAM2aK0SrRLBo1qDEOMRrpzq4S04n8Q@public.gmane.org>
  0 siblings, 1 reply; 41+ messages in thread
From: Herbert Xu @ 2008-05-07 13:05 UTC (permalink / raw)
  To: Johannes Berg
  Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA, Ron Rindjunsky, Tomas Winkler,
	Ivo van Doorn, Peter P Waskiewicz Jr

On Wed, May 07, 2008 at 01:52:13PM +0200, Johannes Berg wrote:
>
> Hmm. Why does busy exist then? Historical accident? I really really

Yes it's historical baggage.  These days the biggest users would
be LLTX drivers which should all be converted to non-LLTX and
drivers that don't keep track of queue status properly.

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert-lOAM2aK0SrRLBo1qDEOMRrpzq4S04n8Q@public.gmane.org>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH] mac80211: rewrite fragmentation code
       [not found]                       ` <20080507130548.GA26977-lOAM2aK0SrRLBo1qDEOMRrpzq4S04n8Q@public.gmane.org>
@ 2008-05-07 13:48                         ` Michael Buesch
  2008-05-08  3:22                           ` Herbert Xu
  2008-05-07 19:19                         ` Johannes Berg
  1 sibling, 1 reply; 41+ messages in thread
From: Michael Buesch @ 2008-05-07 13:48 UTC (permalink / raw)
  To: Herbert Xu
  Cc: Johannes Berg, linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA, Ron Rindjunsky, Tomas Winkler,
	Ivo van Doorn, Peter P Waskiewicz Jr

On Wednesday 07 May 2008 15:05:48 Herbert Xu wrote:
> On Wed, May 07, 2008 at 01:52:13PM +0200, Johannes Berg wrote:
> >
> > Hmm. Why does busy exist then? Historical accident? I really really
> 
> Yes it's historical baggage.  These days the biggest users would
> be LLTX drivers which should all be converted to non-LLTX and
> drivers that don't keep track of queue status properly.

So there's no way to actually fail in a TX handler? Drivers
are doomed to drop the packet, if they cannot handle it due to
ring overflow?

-- 
Greetings Michael.
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH] mac80211: rewrite fragmentation code
       [not found]                       ` <20080507130548.GA26977-lOAM2aK0SrRLBo1qDEOMRrpzq4S04n8Q@public.gmane.org>
  2008-05-07 13:48                         ` Michael Buesch
@ 2008-05-07 19:19                         ` Johannes Berg
  1 sibling, 0 replies; 41+ messages in thread
From: Johannes Berg @ 2008-05-07 19:19 UTC (permalink / raw)
  To: Herbert Xu
  Cc: linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA, Ron Rindjunsky, Tomas Winkler,
	Ivo van Doorn, Peter P Waskiewicz Jr

[-- Attachment #1: Type: text/plain, Size: 677 bytes --]

On Wed, 2008-05-07 at 21:05 +0800, Herbert Xu wrote:
> On Wed, May 07, 2008 at 01:52:13PM +0200, Johannes Berg wrote:
> >
> > Hmm. Why does busy exist then? Historical accident? I really really
> 
> Yes it's historical baggage.  These days the biggest users would
> be LLTX drivers which should all be converted to non-LLTX and
> drivers that don't keep track of queue status properly.

Hmmm mmmm. I'll clean it up later, it gets easier with my patch that
puts the control info into skb->cb so I'll do that later in a separate
patch. The way I see it, this is actually *correct* from a "what hits
the air" point of view and the current code is crappy.

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH] mac80211: rewrite fragmentation code
  2008-05-07 13:48                         ` Michael Buesch
@ 2008-05-08  3:22                           ` Herbert Xu
       [not found]                             ` <20080508032208.GA401-lOAM2aK0SrRLBo1qDEOMRrpzq4S04n8Q@public.gmane.org>
  2008-05-08 13:00                             ` Michael Buesch
  0 siblings, 2 replies; 41+ messages in thread
From: Herbert Xu @ 2008-05-08  3:22 UTC (permalink / raw)
  To: Michael Buesch
  Cc: Johannes Berg, linux-wireless, netdev, Ron Rindjunsky,
	Tomas Winkler, Ivo van Doorn, Peter P Waskiewicz Jr

On Wed, May 07, 2008 at 03:48:06PM +0200, Michael Buesch wrote:
>
> So there's no way to actually fail in a TX handler? Drivers
> are doomed to drop the packet, if they cannot handle it due to
> ring overflow?

You're supposed to stop the queue before the ring overflows.

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH] mac80211: rewrite fragmentation code
       [not found]                             ` <20080508032208.GA401-lOAM2aK0SrRLBo1qDEOMRrpzq4S04n8Q@public.gmane.org>
@ 2008-05-08  3:26                               ` David Miller
       [not found]                                 ` <20080507.202606.242037993.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
  0 siblings, 1 reply; 41+ messages in thread
From: David Miller @ 2008-05-08  3:26 UTC (permalink / raw)
  To: herbert-lOAM2aK0SrRLBo1qDEOMRrpzq4S04n8Q
  Cc: mb-fseUSCV1ubazQB+pC5nmwQ, johannes-cdvu00un1VgdHxzADdlk8Q,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	ron.rindjunsky-ral2JQCrhuEAvxtiuMwx3w,
	tomasw-Re5JQEeQqe8AvxtiuMwx3w, ivdoorn-Re5JQEeQqe8AvxtiuMwx3w,
	peter.p.waskiewicz.jr-ral2JQCrhuEAvxtiuMwx3w

From: Herbert Xu <herbert-lOAM2aK0SrRLBo1qDEOMRrpzq4S04n8Q@public.gmane.org>
Date: Thu, 8 May 2008 11:22:08 +0800

> On Wed, May 07, 2008 at 03:48:06PM +0200, Michael Buesch wrote:
> >
> > So there's no way to actually fail in a TX handler? Drivers
> > are doomed to drop the packet, if they cannot handle it due to
> > ring overflow?
> 
> You're supposed to stop the queue before the ring overflows.

Right, and this is why drivers choose a TX wakeup threshold such
that they can accept an arbitrarily sized TSO frame.

For example, from drivers/net/tg3.c's ->hard_start_xmit():

	if (unlikely(tg3_tx_avail(tp) <= (MAX_SKB_FRAGS + 1))) {
		netif_stop_queue(dev);
		if (tg3_tx_avail(tp) > TG3_TX_WAKEUP_THRESH(tp))
			netif_wake_queue(tp->dev);
	}

The driver is responsible for stopping the queue _before_ it
enters a state where there is not enough space in the queue
to accept a packet.

This is why most drivers make the following kind of BUG check
at the start of their ->hard_start_xmit()

	if (unlikely(tg3_tx_avail(tp) <= (skb_shinfo(skb)->nr_frags + 1))) {
		if (!netif_queue_stopped(dev)) {
			netif_stop_queue(dev);

			/* This is a hard error, log it. */
			printk(KERN_ERR PFX "%s: BUG! Tx Ring full when "
			       "queue awake!\n", dev->name);
		}
		return NETDEV_TX_BUSY;
	}
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH] mac80211: rewrite fragmentation code
       [not found]                                 ` <20080507.202606.242037993.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
@ 2008-05-08  9:00                                   ` Johannes Berg
  2008-05-16  2:01                                   ` Rusty Russell
  1 sibling, 0 replies; 41+ messages in thread
From: Johannes Berg @ 2008-05-08  9:00 UTC (permalink / raw)
  To: David Miller
  Cc: herbert-lOAM2aK0SrRLBo1qDEOMRrpzq4S04n8Q,
	mb-fseUSCV1ubazQB+pC5nmwQ, linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	ron.rindjunsky-ral2JQCrhuEAvxtiuMwx3w,
	tomasw-Re5JQEeQqe8AvxtiuMwx3w, ivdoorn-Re5JQEeQqe8AvxtiuMwx3w,
	peter.p.waskiewicz.jr-ral2JQCrhuEAvxtiuMwx3w

[-- Attachment #1: Type: text/plain, Size: 1033 bytes --]

On Wed, 2008-05-07 at 20:26 -0700, David Miller wrote:

> > You're supposed to stop the queue before the ring overflows.
> 
> Right, and this is why drivers choose a TX wakeup threshold such
> that they can accept an arbitrarily sized TSO frame.
> 
> For example, from drivers/net/tg3.c's ->hard_start_xmit():
> 
> 	if (unlikely(tg3_tx_avail(tp) <= (MAX_SKB_FRAGS + 1))) {
> 		netif_stop_queue(dev);
> 		if (tg3_tx_avail(tp) > TG3_TX_WAKEUP_THRESH(tp))
> 			netif_wake_queue(tp->dev);
> 	}
> 
> The driver is responsible for stopping the queue _before_ it
> enters a state where there is not enough space in the queue
> to accept a packet.

Yes yes yes I know :) It's just quite awkward to handle when your
hard_start_xmit() actually increases the number of packets, you don't
really know how deep the queue is etc. I'll add an assertion like that
to mac80211 for the non-fragmented case and clean up the fragmented case
to retry by itself without upper layer involvement after the tx->cb
patch.

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH] mac80211: rewrite fragmentation code
  2008-05-08  3:22                           ` Herbert Xu
       [not found]                             ` <20080508032208.GA401-lOAM2aK0SrRLBo1qDEOMRrpzq4S04n8Q@public.gmane.org>
@ 2008-05-08 13:00                             ` Michael Buesch
       [not found]                               ` <200805081500.00682.mb-fseUSCV1ubazQB+pC5nmwQ@public.gmane.org>
  1 sibling, 1 reply; 41+ messages in thread
From: Michael Buesch @ 2008-05-08 13:00 UTC (permalink / raw)
  To: Herbert Xu
  Cc: Johannes Berg, linux-wireless, netdev, Ron Rindjunsky,
	Tomas Winkler, Ivo van Doorn, Peter P Waskiewicz Jr

On Thursday 08 May 2008 05:22:08 Herbert Xu wrote:
> On Wed, May 07, 2008 at 03:48:06PM +0200, Michael Buesch wrote:
> >
> > So there's no way to actually fail in a TX handler? Drivers
> > are doomed to drop the packet, if they cannot handle it due to
> > ring overflow?
> 
> You're supposed to stop the queue before the ring overflows.

Ok, what about DMA error. kmalloc error or something else?
ring overflow was a bad example, of course.

-- 
Greetings Michael.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH] mac80211: rewrite fragmentation code
       [not found]                               ` <200805081500.00682.mb-fseUSCV1ubazQB+pC5nmwQ@public.gmane.org>
@ 2008-05-08 13:08                                 ` Herbert Xu
  2008-05-08 13:13                                   ` Michael Buesch
  0 siblings, 1 reply; 41+ messages in thread
From: Herbert Xu @ 2008-05-08 13:08 UTC (permalink / raw)
  To: Michael Buesch
  Cc: Johannes Berg, linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA, Ron Rindjunsky, Tomas Winkler,
	Ivo van Doorn, Peter P Waskiewicz Jr

On Thu, May 08, 2008 at 03:00:00PM +0200, Michael Buesch wrote:
>
> Ok, what about DMA error. kmalloc error or something else?
> ring overflow was a bad example, of course.

If your NIC gets DMA errors all the time then I suggest you
invest in some new hardware.  Ditto if you fail to kmalloc all
the time.

If they're rare as they should be then dropping the packet is
perfectly reasonable.

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert-lOAM2aK0SrRLBo1qDEOMRrpzq4S04n8Q@public.gmane.org>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH] mac80211: rewrite fragmentation code
  2008-05-08 13:08                                 ` Herbert Xu
@ 2008-05-08 13:13                                   ` Michael Buesch
  2008-05-08 13:15                                     ` Michael Buesch
       [not found]                                     ` <200805081513.56521.mb-fseUSCV1ubazQB+pC5nmwQ@public.gmane.org>
  0 siblings, 2 replies; 41+ messages in thread
From: Michael Buesch @ 2008-05-08 13:13 UTC (permalink / raw)
  To: Herbert Xu
  Cc: Johannes Berg, linux-wireless, netdev, Ron Rindjunsky,
	Tomas Winkler, Ivo van Doorn, Peter P Waskiewicz Jr

On Thursday 08 May 2008 15:08:46 Herbert Xu wrote:
> On Thu, May 08, 2008 at 03:00:00PM +0200, Michael Buesch wrote:
> >
> > Ok, what about DMA error. kmalloc error or something else?
> > ring overflow was a bad example, of course.
> 
> If your NIC gets DMA errors all the time then I suggest you
> invest in some new hardware.  Ditto if you fail to kmalloc all
> the time.
> 
> If they're rare as they should be then dropping the packet is
> perfectly reasonable.

Ok, why is nobody actually answering my _question_? :)
So I'll try to answer it by myself: the hard_start_xmit callback
is actually supposed to return "void", but it returns "int" just
for historical reasons. So there is no way to fail in a TX handler.
If a failure happens, the driver must free and drop the skb.
How likely errors are is a completely different story. One could argue
that errors are unlikely for any part of the kernel, except for very
sone special cases.

So does that also apply to the wireless stack? Should we change drivers
to always return 0. later we can change the function prototype to return void.

-- 
Greetings Michael.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH] mac80211: rewrite fragmentation code
  2008-05-08 13:13                                   ` Michael Buesch
@ 2008-05-08 13:15                                     ` Michael Buesch
       [not found]                                     ` <200805081513.56521.mb-fseUSCV1ubazQB+pC5nmwQ@public.gmane.org>
  1 sibling, 0 replies; 41+ messages in thread
From: Michael Buesch @ 2008-05-08 13:15 UTC (permalink / raw)
  To: Herbert Xu
  Cc: Johannes Berg, linux-wireless, netdev, Ron Rindjunsky,
	Tomas Winkler, Ivo van Doorn, Peter P Waskiewicz Jr

On Thursday 08 May 2008 15:13:56 Michael Buesch wrote:
> that errors are unlikely for any part of the kernel, except for very
> sone special cases.

"..., except for some very special cases."



-- 
Greetings Michael.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH] mac80211: rewrite fragmentation code
       [not found]                                     ` <200805081513.56521.mb-fseUSCV1ubazQB+pC5nmwQ@public.gmane.org>
@ 2008-05-08 13:32                                       ` Herbert Xu
  0 siblings, 0 replies; 41+ messages in thread
From: Herbert Xu @ 2008-05-08 13:32 UTC (permalink / raw)
  To: Michael Buesch
  Cc: Johannes Berg, linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA, Ron Rindjunsky, Tomas Winkler,
	Ivo van Doorn, Peter P Waskiewicz Jr

On Thu, May 08, 2008 at 03:13:56PM +0200, Michael Buesch wrote:
>
> Ok, why is nobody actually answering my _question_? :)
> So I'll try to answer it by myself: the hard_start_xmit callback
> is actually supposed to return "void", but it returns "int" just
> for historical reasons. So there is no way to fail in a TX handler.

Pretty much.  When lockless drivers were the vogue the return
value was useful but as we've since found better ways to scale
NICs in an SMP environment (first the tg3 model, and then multi-
queue TX) that don't have the problems associated with requeueing
the packet, any non-zero return value should only be used by
existing code.

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert-lOAM2aK0SrRLBo1qDEOMRrpzq4S04n8Q@public.gmane.org>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH] mac80211: rewrite fragmentation code
       [not found]                                 ` <20080507.202606.242037993.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
  2008-05-08  9:00                                   ` Johannes Berg
@ 2008-05-16  2:01                                   ` Rusty Russell
  2008-05-16  3:28                                     ` Herbert Xu
  2008-05-16  4:58                                     ` David Miller
  1 sibling, 2 replies; 41+ messages in thread
From: Rusty Russell @ 2008-05-16  2:01 UTC (permalink / raw)
  To: David Miller
  Cc: herbert-lOAM2aK0SrRLBo1qDEOMRrpzq4S04n8Q,
	mb-fseUSCV1ubazQB+pC5nmwQ, johannes-cdvu00un1VgdHxzADdlk8Q,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	ron.rindjunsky-ral2JQCrhuEAvxtiuMwx3w,
	tomasw-Re5JQEeQqe8AvxtiuMwx3w, ivdoorn-Re5JQEeQqe8AvxtiuMwx3w,
	peter.p.waskiewicz.jr-ral2JQCrhuEAvxtiuMwx3w

On Thursday 08 May 2008 13:26:06 David Miller wrote:
> From: Herbert Xu <herbert-lOAM2aK0SrRLBo1qDEOMRrpzq4S04n8Q@public.gmane.org>
> Date: Thu, 8 May 2008 11:22:08 +0800
>
> > On Wed, May 07, 2008 at 03:48:06PM +0200, Michael Buesch wrote:
> > > So there's no way to actually fail in a TX handler? Drivers
> > > are doomed to drop the packet, if they cannot handle it due to
> > > ring overflow?
> >
> > You're supposed to stop the queue before the ring overflows.
>
> Right, and this is why drivers choose a TX wakeup threshold such
> that they can accept an arbitrarily sized TSO frame.

Dave, please allow me to ask a heretical question.  Returning TX_BUSY has some 
appeal for virtio_net: is it fundamentally a flawed idea, or simply a matter 
of coding?

Currently we have no virtio interface to ask how many descriptors are left; 
it's not clear that it's a fair question to ask, since for Xen it's depends 
on the actual buffers we're trying to put in the descirptors.

Thanks,
Rusty.
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH] mac80211: rewrite fragmentation code
  2008-05-16  2:01                                   ` Rusty Russell
@ 2008-05-16  3:28                                     ` Herbert Xu
  2008-05-16  4:58                                     ` David Miller
  1 sibling, 0 replies; 41+ messages in thread
From: Herbert Xu @ 2008-05-16  3:28 UTC (permalink / raw)
  To: Rusty Russell
  Cc: David Miller, mb, johannes, linux-wireless, netdev,
	ron.rindjunsky, tomasw, ivdoorn, peter.p.waskiewicz.jr

On Fri, May 16, 2008 at 12:01:48PM +1000, Rusty Russell wrote:
>
> Dave, please allow me to ask a heretical question.  Returning TX_BUSY has some 
> appeal for virtio_net: is it fundamentally a flawed idea, or simply a matter 
> of coding?

I don't think returning TX_BUSY is fundamentally flawed (unlike
LLTX which is).  However, until somebody (i.e., you :) writes
the code to handle it properly, i.e., not making the requeued
packet go through AF_PCAKET twice, it's something that shouldn't
be encouraged.

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH] mac80211: rewrite fragmentation code
  2008-05-16  2:01                                   ` Rusty Russell
  2008-05-16  3:28                                     ` Herbert Xu
@ 2008-05-16  4:58                                     ` David Miller
       [not found]                                       ` <20080515.215823.28841530.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
  1 sibling, 1 reply; 41+ messages in thread
From: David Miller @ 2008-05-16  4:58 UTC (permalink / raw)
  To: rusty
  Cc: herbert, mb, johannes, linux-wireless, netdev, ron.rindjunsky,
	tomasw, ivdoorn, peter.p.waskiewicz.jr

From: Rusty Russell <rusty@rustcorp.com.au>
Date: Fri, 16 May 2008 12:01:48 +1000

> Dave, please allow me to ask a heretical question.  Returning
> TX_BUSY has some appeal for virtio_net: is it fundamentally a flawed
> idea, or simply a matter of coding?

Allowing TX_BUSY adds a special case to the caller which we'd
like to remove at some point.

> Currently we have no virtio interface to ask how many descriptors are left; 
> it's not clear that it's a fair question to ask, since for Xen it's depends 
> on the actual buffers we're trying to put in the descirptors.

Two things:

1) You can always make sure that you have enough space for a
   TSO frame, with arbitrary page boundaries and thus buffer
   chopping.

   It can even be estimated, and if violated by some corner case
   you can punt and drop.

2) You can queue inside of the driver one packet when you hit
   the limits unexpectedly, netif_stop_queue(), and return
   success.  Spit this packet out right before waking the
   queue again.

Really, there are no hard reasons to ever return TX_BUSY,
it's always a bug.

In fact, I want to move things more and more towards the driver
queueing TX packets internally instead of the networking mid-layer.

That will ahve benefits for things like TX multiqueue, we won't
need any locking at all, nor have any knowledge about multiple
queues at all, if the driver takes care of providing the buffer
between what the kernel gives it and what the device can handle
at the moment.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH] mac80211: rewrite fragmentation code
       [not found]                                       ` <20080515.215823.28841530.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
@ 2008-05-16 10:32                                         ` Rusty Russell
       [not found]                                           ` <200805162032.48469.rusty-8n+1lVoiYb80n/F98K4Iww@public.gmane.org>
                                                             ` (2 more replies)
  0 siblings, 3 replies; 41+ messages in thread
From: Rusty Russell @ 2008-05-16 10:32 UTC (permalink / raw)
  To: David Miller
  Cc: herbert-lOAM2aK0SrRLBo1qDEOMRrpzq4S04n8Q,
	mb-fseUSCV1ubazQB+pC5nmwQ, johannes-cdvu00un1VgdHxzADdlk8Q,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	ron.rindjunsky-ral2JQCrhuEAvxtiuMwx3w,
	tomasw-Re5JQEeQqe8AvxtiuMwx3w, ivdoorn-Re5JQEeQqe8AvxtiuMwx3w,
	peter.p.waskiewicz.jr-ral2JQCrhuEAvxtiuMwx3w

On Friday 16 May 2008 14:58:23 David Miller wrote:
> From: Rusty Russell <rusty-8n+1lVoiYb80n/F98K4Iww@public.gmane.org>
> Date: Fri, 16 May 2008 12:01:48 +1000
>
> > Dave, please allow me to ask a heretical question.  Returning
> > TX_BUSY has some appeal for virtio_net: is it fundamentally a flawed
> > idea, or simply a matter of coding?
>
> Allowing TX_BUSY adds a special case to the caller which we'd
> like to remove at some point.
>
> > Currently we have no virtio interface to ask how many descriptors are
> > left; it's not clear that it's a fair question to ask, since for Xen it's
> > depends on the actual buffers we're trying to put in the descirptors.
>
> Two things:
>
> 1) You can always make sure that you have enough space for a
>    TSO frame, with arbitrary page boundaries and thus buffer
>    chopping.
>
>    It can even be estimated, and if violated by some corner case
>    you can punt and drop.

Yes, this is what we'd have to do.  Wasting room in the ring feels wrong 
though.

> 2) You can queue inside of the driver one packet when you hit
>    the limits unexpectedly, netif_stop_queue(), and return
>    success.  Spit this packet out right before waking the
>    queue again.

I put a patch in to do exactly that at Herbert's prompting, for 2.6.26, but 
it's buggy in (at least) two ways.  I have a fix for this, which adds a new 
tasklet to xmit the packet.  There's still some subtle race, however, since 
I'm still seeing a stuck packet.  I'll have to revert to TX_BUSY for 2.6.26 
if I can't find it (unlikely).

And I haven't measured what it does to performance (should be OK, but still).

> Really, there are no hard reasons to ever return TX_BUSY,
> it's always a bug.

But it's *simple*, and seems like a common thing to want.  Why not change 
everything to use TX_BUSY and rip out the guestimate/buffering hacks?

> In fact, I want to move things more and more towards the driver
> queueing TX packets internally instead of the networking mid-layer.
>
> That will ahve benefits for things like TX multiqueue, we won't
> need any locking at all, nor have any knowledge about multiple
> queues at all, if the driver takes care of providing the buffer
> between what the kernel gives it and what the device can handle
> at the moment.

That would be great: then I could shove the packet back on the queue myself 
and not have to ask you about it.  It's adding a *second* queue inside the 
driver which feels terribly ugly...

Cheers,
Rusty.
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH] mac80211: rewrite fragmentation code
       [not found]                                           ` <200805162032.48469.rusty-8n+1lVoiYb80n/F98K4Iww@public.gmane.org>
@ 2008-05-16 10:38                                             ` Johannes Berg
  0 siblings, 0 replies; 41+ messages in thread
From: Johannes Berg @ 2008-05-16 10:38 UTC (permalink / raw)
  To: Rusty Russell
  Cc: David Miller, herbert-lOAM2aK0SrRLBo1qDEOMRrpzq4S04n8Q,
	mb-fseUSCV1ubazQB+pC5nmwQ, linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	ron.rindjunsky-ral2JQCrhuEAvxtiuMwx3w,
	tomasw-Re5JQEeQqe8AvxtiuMwx3w, ivdoorn-Re5JQEeQqe8AvxtiuMwx3w,
	peter.p.waskiewicz.jr-ral2JQCrhuEAvxtiuMwx3w

[-- Attachment #1: Type: text/plain, Size: 523 bytes --]


> I put a patch in to do exactly that at Herbert's prompting, for 2.6.26, but 
> it's buggy in (at least) two ways.  I have a fix for this, which adds a new 
> tasklet to xmit the packet.  There's still some subtle race, however, since 
> I'm still seeing a stuck packet.  I'll have to revert to TX_BUSY for 2.6.26 
> if I can't find it (unlikely).

Incidentally, mac80211 has a tasklet for this as well, and I'm pretty
sure it's also buggy in the corner cases (rather than inefficient or
something)

johannes

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 828 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH] mac80211: rewrite fragmentation code
  2008-05-16 10:32                                         ` Rusty Russell
       [not found]                                           ` <200805162032.48469.rusty-8n+1lVoiYb80n/F98K4Iww@public.gmane.org>
@ 2008-05-16 12:15                                           ` Herbert Xu
  2008-05-16 19:40                                           ` David Miller
  2 siblings, 0 replies; 41+ messages in thread
From: Herbert Xu @ 2008-05-16 12:15 UTC (permalink / raw)
  To: Rusty Russell
  Cc: David Miller, mb, johannes, linux-wireless, netdev,
	ron.rindjunsky, tomasw, ivdoorn, peter.p.waskiewicz.jr

On Fri, May 16, 2008 at 08:32:48PM +1000, Rusty Russell wrote:
> 
> But it's *simple*, and seems like a common thing to want.  Why not change 
> everything to use TX_BUSY and rip out the guestimate/buffering hacks?

I think this sounds quite reasonable.

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH] mac80211: rewrite fragmentation code
  2008-05-16 10:32                                         ` Rusty Russell
       [not found]                                           ` <200805162032.48469.rusty-8n+1lVoiYb80n/F98K4Iww@public.gmane.org>
  2008-05-16 12:15                                           ` Herbert Xu
@ 2008-05-16 19:40                                           ` David Miller
       [not found]                                             ` <20080516.124039.253626477.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
  2 siblings, 1 reply; 41+ messages in thread
From: David Miller @ 2008-05-16 19:40 UTC (permalink / raw)
  To: rusty
  Cc: herbert, mb, johannes, linux-wireless, netdev, ron.rindjunsky,
	tomasw, ivdoorn, peter.p.waskiewicz.jr

From: Rusty Russell <rusty@rustcorp.com.au>
Date: Fri, 16 May 2008 20:32:48 +1000

> On Friday 16 May 2008 14:58:23 David Miller wrote:
> > In fact, I want to move things more and more towards the driver
> > queueing TX packets internally instead of the networking mid-layer.
> >
> > That will ahve benefits for things like TX multiqueue, we won't
> > need any locking at all, nor have any knowledge about multiple
> > queues at all, if the driver takes care of providing the buffer
> > between what the kernel gives it and what the device can handle
> > at the moment.
> 
> That would be great: then I could shove the packet back on the queue myself 
> and not have to ask you about it.  It's adding a *second* queue inside the 
> driver which feels terribly ugly...

My description describes how I want the mid-layer queue to disappear
entirely.  Queueing would be done by the driver only.

Only the driver knows all of these details about how much space there
is, what packet chopping has to take place and what effects that has
on queue space, multiqueue configurations, how one wants to hash to
multiple queues, how to lock this stuff the most efficiently.

The kernel should just get out of the way and let the driver take care
of everything.

Sure, we can have some standard helpers to deal with the most common
cases.  But anything non-trivial right now is painful because the
mid-layer model tries to be too helpful when it should just get out
of the way.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH] mac80211: rewrite fragmentation code
       [not found]                                             ` <20080516.124039.253626477.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
@ 2008-05-19  3:08                                               ` Rusty Russell
  2008-05-19  7:03                                                 ` David Miller
  0 siblings, 1 reply; 41+ messages in thread
From: Rusty Russell @ 2008-05-19  3:08 UTC (permalink / raw)
  To: David Miller
  Cc: herbert-lOAM2aK0SrRLBo1qDEOMRrpzq4S04n8Q,
	mb-fseUSCV1ubazQB+pC5nmwQ, johannes-cdvu00un1VgdHxzADdlk8Q,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	ron.rindjunsky-ral2JQCrhuEAvxtiuMwx3w,
	tomasw-Re5JQEeQqe8AvxtiuMwx3w, ivdoorn-Re5JQEeQqe8AvxtiuMwx3w,
	peter.p.waskiewicz.jr-ral2JQCrhuEAvxtiuMwx3w

On Saturday 17 May 2008 05:40:39 David Miller wrote:
> My description describes how I want the mid-layer queue to disappear
> entirely.  Queueing would be done by the driver only.

Ok, the more I ponder this, the more I like it.  It has a very nice side 
benefit for virtio_net: we can xmit a whole bunch of packets before notifying 
the host.  Real NICs might gain similarly.

The bit I can't see is what to do about qdisc if the driver manages its own 
queue(s).  Leave the qdisc as currently in place and have the driver call 
dev_dequeue_skb() (or some wrapper) directly?  Modulo locking issues, that 
should be a fairly simple change.

Thanks,
Rusty.
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH] mac80211: rewrite fragmentation code
  2008-05-19  3:08                                               ` Rusty Russell
@ 2008-05-19  7:03                                                 ` David Miller
  0 siblings, 0 replies; 41+ messages in thread
From: David Miller @ 2008-05-19  7:03 UTC (permalink / raw)
  To: rusty
  Cc: herbert, mb, johannes, linux-wireless, netdev, ron.rindjunsky,
	tomasw, ivdoorn, peter.p.waskiewicz.jr

From: Rusty Russell <rusty@rustcorp.com.au>
Date: Mon, 19 May 2008 13:08:13 +1000

> The bit I can't see is what to do about qdisc if the driver manages its own 
> queue(s).  Leave the qdisc as currently in place and have the driver call 
> dev_dequeue_skb() (or some wrapper) directly?  Modulo locking issues, that 
> should be a fairly simple change.

I'd like to approach a state where the device is just a black hole
that the qdisc injects packets into.  At least theoretically, that's
what the network is once the packet leaves the device anyways.  Nobody
really notices as long as flows don't get reordered.

I realize it isn't feasible to retain many of the qualities that some
qdiscs want (rates, qfull handling, etc.), so we'll have to provide
some handling for that, ideally in some cheap slowpath test.

But for things like tx queue backlog overflow the behavior would
be essentially the same.  The only change is that the txqueuelen
parameter is handled inside of the driver (again, perhaps via
helpers).

Our TX path is way too complicated and, frankly, restrictive.

This is why we don't have real parallel TX multiqueue support as a
simple patch to some drivers.  We have fundamental restrictions that
keep that from happening.

^ permalink raw reply	[flat|nested] 41+ messages in thread

end of thread, other threads:[~2008-05-19  7:03 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-04-30 12:40 [RFC/RFT 0/4] mac80211 QoS-related enhancements Johannes Berg
2008-04-30 12:40 ` [RFC/RFT 1/4] mac80211: use rate index in TX control Johannes Berg
2008-04-30 12:40 ` [RFC/RFT 2/4] GSO: generalize for mac80211 Johannes Berg
     [not found]   ` <20080430130049.359549000-cdvu00un1VgdHxzADdlk8Q@public.gmane.org>
2008-05-06 16:12     ` Johannes Berg
2008-04-30 12:40 ` [RFC/RFT 3/4] mac80211: use GSO for fragmentation Johannes Berg
2008-05-07  7:10   ` Herbert Xu
2008-05-07  8:50     ` Johannes Berg
2008-05-07  9:00       ` Herbert Xu
     [not found]         ` <20080507090040.GA25186-lOAM2aK0SrRLBo1qDEOMRrpzq4S04n8Q@public.gmane.org>
2008-05-07 11:22           ` [PATCH] mac80211: rewrite fragmentation code Johannes Berg
     [not found]             ` <1210159339.5642.13.camel-YfaajirXv214zXjbi5bjpg@public.gmane.org>
2008-05-07 11:41               ` Herbert Xu
2008-05-07 11:52                 ` Johannes Berg
     [not found]                   ` <1210161133.5642.19.camel-YfaajirXv214zXjbi5bjpg@public.gmane.org>
2008-05-07 13:05                     ` Herbert Xu
     [not found]                       ` <20080507130548.GA26977-lOAM2aK0SrRLBo1qDEOMRrpzq4S04n8Q@public.gmane.org>
2008-05-07 13:48                         ` Michael Buesch
2008-05-08  3:22                           ` Herbert Xu
     [not found]                             ` <20080508032208.GA401-lOAM2aK0SrRLBo1qDEOMRrpzq4S04n8Q@public.gmane.org>
2008-05-08  3:26                               ` David Miller
     [not found]                                 ` <20080507.202606.242037993.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
2008-05-08  9:00                                   ` Johannes Berg
2008-05-16  2:01                                   ` Rusty Russell
2008-05-16  3:28                                     ` Herbert Xu
2008-05-16  4:58                                     ` David Miller
     [not found]                                       ` <20080515.215823.28841530.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
2008-05-16 10:32                                         ` Rusty Russell
     [not found]                                           ` <200805162032.48469.rusty-8n+1lVoiYb80n/F98K4Iww@public.gmane.org>
2008-05-16 10:38                                             ` Johannes Berg
2008-05-16 12:15                                           ` Herbert Xu
2008-05-16 19:40                                           ` David Miller
     [not found]                                             ` <20080516.124039.253626477.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
2008-05-19  3:08                                               ` Rusty Russell
2008-05-19  7:03                                                 ` David Miller
2008-05-08 13:00                             ` Michael Buesch
     [not found]                               ` <200805081500.00682.mb-fseUSCV1ubazQB+pC5nmwQ@public.gmane.org>
2008-05-08 13:08                                 ` Herbert Xu
2008-05-08 13:13                                   ` Michael Buesch
2008-05-08 13:15                                     ` Michael Buesch
     [not found]                                     ` <200805081513.56521.mb-fseUSCV1ubazQB+pC5nmwQ@public.gmane.org>
2008-05-08 13:32                                       ` Herbert Xu
2008-05-07 19:19                         ` Johannes Berg
2008-04-30 12:40 ` [RFC/RFT 4/4] mac80211: use multi-queue master netdevice Johannes Berg
     [not found]   ` <20080430130051.397094000-cdvu00un1VgdHxzADdlk8Q@public.gmane.org>
2008-04-30 14:37     ` Ivo van Doorn
     [not found]       ` <200804301637.35170.IvDoorn-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2008-04-30 14:45         ` Johannes Berg
     [not found]           ` <1209566743.18659.30.camel-YfaajirXv214zXjbi5bjpg@public.gmane.org>
2008-04-30 15:00             ` Johannes Berg
     [not found]               ` <1209567609.18659.33.camel-YfaajirXv214zXjbi5bjpg@public.gmane.org>
2008-04-30 15:34                 ` Ivo van Doorn
2008-04-30 15:38                   ` Johannes Berg
2008-05-01  8:21                 ` Ivo van Doorn
     [not found]                   ` <200805011021.04435.IvDoorn-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2008-05-01  8:54                     ` Johannes Berg
2008-04-30 19:39     ` Waskiewicz Jr, Peter P
     [not found]       ` <D5C1322C3E673F459512FB59E0DDC32904FE144F-O6kdQIuPh0Q64kNsxIetb7fspsVTdybXVpNB7YpNyf8@public.gmane.org>
2008-04-30 20:07         ` Johannes Berg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).