linux-wireless.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/10] mac80211 locking/sta info work
@ 2008-02-25 15:27 Johannes Berg
  2008-02-25 15:27 ` [PATCH 01/10] mac80211: clarify use of TX status/RX callbacks Johannes Berg
                   ` (9 more replies)
  0 siblings, 10 replies; 16+ messages in thread
From: Johannes Berg @ 2008-02-25 15:27 UTC (permalink / raw)
  To: John Linville; +Cc: linux-wireless, Luis Carlos Cobo

This series of patches contains my current sta_info and
mac80211 locking work, the sta info patches are now based
on top of the mesh networking.

Luis, please verify that I didn't break mesh networking,
I cannot currently test it. Also, I think I got the lifetime
rules for mesh peer links correct, but it could use a check.

Still, I think it should be merged and we can fix any more
problems, it's rather intrusive and clashes with most other
work.

Tested in both AP and STA mode and working fine.

johannes


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

* [PATCH 01/10] mac80211: clarify use of TX status/RX callbacks
  2008-02-25 15:27 [PATCH 00/10] mac80211 locking/sta info work Johannes Berg
@ 2008-02-25 15:27 ` Johannes Berg
  2008-02-25 15:27 ` [PATCH 02/10] mac80211: safely free beacon in ieee80211_if_reinit Johannes Berg
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: Johannes Berg @ 2008-02-25 15:27 UTC (permalink / raw)
  To: John Linville; +Cc: linux-wireless, Luis Carlos Cobo

This patch clarifies the use of the irqsafe vs. non-irq-safe
functions and their respective locking requirements.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
 include/net/mac80211.h |   37 ++++++++++++++++++++++++++++++++++---
 1 file changed, 34 insertions(+), 3 deletions(-)

--- everything.orig/include/net/mac80211.h	2008-02-23 15:13:24.000000000 +0100
+++ everything/include/net/mac80211.h	2008-02-24 10:07:07.000000000 +0100
@@ -38,7 +38,11 @@
  * called in hardware interrupt context. The low-level driver must not call any
  * other functions in hardware interrupt context. If there is a need for such
  * call, the low-level driver should first ACK the interrupt and perform the
- * IEEE 802.11 code call after this, e.g. from a scheduled workqueue function.
+ * IEEE 802.11 code call after this, e.g. from a scheduled workqueue or even
+ * tasklet function.
+ *
+ * NOTE: If the driver opts to use the _irqsafe() functions, it may not also
+ *	 use the non-irqsafe functions!
  */
 
 /**
@@ -1213,7 +1217,10 @@ void __ieee80211_rx(struct ieee80211_hw 
  * buffer in @skb must start with an IEEE 802.11 header or a radiotap
  * header if %RX_FLAG_RADIOTAP is set in the @status flags.
  *
- * This function may not be called in IRQ context.
+ * This function may not be called in IRQ context. Calls to this function
+ * for a single hardware must be synchronized against each other. Calls
+ * to this function and ieee80211_rx_irqsafe() may not be mixed for a
+ * single hardware.
  *
  * @hw: the hardware this frame came in on
  * @skb: the buffer to receive, owned by mac80211 after this call
@@ -1230,7 +1237,10 @@ static inline void ieee80211_rx(struct i
  * ieee80211_rx_irqsafe - receive frame
  *
  * Like ieee80211_rx() but can be called in IRQ context
- * (internally defers to a workqueue.)
+ * (internally defers to a tasklet.)
+ *
+ * Calls to this function and ieee80211_rx() may not be mixed for a
+ * single hardware.
  *
  * @hw: the hardware this frame came in on
  * @skb: the buffer to receive, owned by mac80211 after this call
@@ -1249,6 +1259,11 @@ void ieee80211_rx_irqsafe(struct ieee802
  * transmitted. It is permissible to not call this function for
  * multicast frames but this can affect statistics.
  *
+ * This function may not be called in IRQ context. Calls to this function
+ * for a single hardware must be synchronized against each other. Calls
+ * to this function and ieee80211_tx_status_irqsafe() may not be mixed
+ * for a single hardware.
+ *
  * @hw: the hardware the frame was transmitted by
  * @skb: the frame that was transmitted, owned by mac80211 after this call
  * @status: status information for this frame; the status pointer need not
@@ -1258,6 +1273,22 @@ void ieee80211_rx_irqsafe(struct ieee802
 void ieee80211_tx_status(struct ieee80211_hw *hw,
 			 struct sk_buff *skb,
 			 struct ieee80211_tx_status *status);
+
+/**
+ * ieee80211_tx_status_irqsafe - irq-safe transmit status callback
+ *
+ * Like ieee80211_tx_status() but can be called in IRQ context
+ * (internally defers to a tasklet.)
+ *
+ * Calls to this function and ieee80211_tx_status() may not be mixed for a
+ * single hardware.
+ *
+ * @hw: the hardware the frame was transmitted by
+ * @skb: the frame that was transmitted, owned by mac80211 after this call
+ * @status: status information for this frame; the status pointer need not
+ *	be valid after this function returns and is not freed by mac80211,
+ *	it is recommended that it points to a stack area
+ */
 void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw,
 				 struct sk_buff *skb,
 				 struct ieee80211_tx_status *status);

-- 


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

* [PATCH 02/10] mac80211: safely free beacon in ieee80211_if_reinit
  2008-02-25 15:27 [PATCH 00/10] mac80211 locking/sta info work Johannes Berg
  2008-02-25 15:27 ` [PATCH 01/10] mac80211: clarify use of TX status/RX callbacks Johannes Berg
@ 2008-02-25 15:27 ` Johannes Berg
  2008-02-25 15:27 ` [PATCH 03/10] mac80211: split ieee80211_txrx_data Johannes Berg
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: Johannes Berg @ 2008-02-25 15:27 UTC (permalink / raw)
  To: John Linville; +Cc: linux-wireless, Luis Carlos Cobo

If ieee80211_if_reinit() is called from ieee80211_unregister_hw()
then it is possible that the driver will still request a beacon
(it is allowed to until ieee80211_unregister_hw() has returned.)
This means we need to use an RCU-protected write to the beacon
information even in this function.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
 net/mac80211/ieee80211_iface.c |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

--- everything.orig/net/mac80211/ieee80211_iface.c	2008-02-23 14:59:28.000000000 +0100
+++ everything/net/mac80211/ieee80211_iface.c	2008-02-24 10:07:16.000000000 +0100
@@ -209,6 +209,7 @@ void ieee80211_if_reinit(struct net_devi
 		/* Remove all virtual interfaces that use this BSS
 		 * as their sdata->bss */
 		struct ieee80211_sub_if_data *tsdata, *n;
+		struct beacon_data *beacon;
 
 		list_for_each_entry_safe(tsdata, n, &local->interfaces, list) {
 			if (tsdata != sdata && tsdata->bss == &sdata->u.ap) {
@@ -226,7 +227,10 @@ void ieee80211_if_reinit(struct net_devi
 			}
 		}
 
-		kfree(sdata->u.ap.beacon);
+		beacon = sdata->u.ap.beacon;
+		rcu_assign_pointer(sdata->u.ap.beacon, NULL);
+		synchronize_rcu();
+		kfree(beacon);
 
 		while ((skb = skb_dequeue(&sdata->u.ap.ps_bc_buf))) {
 			local->total_ps_buffered--;

-- 


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

* [PATCH 03/10] mac80211: split ieee80211_txrx_data
  2008-02-25 15:27 [PATCH 00/10] mac80211 locking/sta info work Johannes Berg
  2008-02-25 15:27 ` [PATCH 01/10] mac80211: clarify use of TX status/RX callbacks Johannes Berg
  2008-02-25 15:27 ` [PATCH 02/10] mac80211: safely free beacon in ieee80211_if_reinit Johannes Berg
@ 2008-02-25 15:27 ` Johannes Berg
  2008-02-25 15:27 ` [PATCH 04/10] mac80211: remove STA infos last_ack stuff Johannes Berg
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: Johannes Berg @ 2008-02-25 15:27 UTC (permalink / raw)
  To: John Linville; +Cc: linux-wireless, Luis Carlos Cobo

Split it into ieee80211_tx_data and ieee80211_rx_data to clarify
usage/flag usage and remove the stupid union thing.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
 net/mac80211/ieee80211_i.h |   86 ++++++++-------
 net/mac80211/rx.c          |  163 +++++++++++++++---------------
 net/mac80211/tx.c          |  244 ++++++++++++++++++++++-----------------------
 net/mac80211/util.c        |    8 -
 net/mac80211/wep.c         |   24 ++--
 net/mac80211/wep.h         |    4 
 net/mac80211/wpa.c         |   80 +++++++-------
 net/mac80211/wpa.h         |   12 +-
 8 files changed, 315 insertions(+), 306 deletions(-)

--- everything.orig/net/mac80211/ieee80211_i.h	2008-02-24 10:09:07.000000000 +0100
+++ everything/net/mac80211/ieee80211_i.h	2008-02-24 10:19:17.000000000 +0100
@@ -142,26 +142,51 @@ typedef unsigned __bitwise__ ieee80211_t
 #define TX_DROP		((__force ieee80211_tx_result) 1u)
 #define TX_QUEUED	((__force ieee80211_tx_result) 2u)
 
+#define IEEE80211_TX_FRAGMENTED		BIT(0)
+#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;
+	struct net_device *dev;
+	struct ieee80211_local *local;
+	struct ieee80211_sub_if_data *sdata;
+	struct sta_info *sta;
+	u16 fc, ethertype;
+	struct ieee80211_key *key;
+	unsigned int flags;
+
+	struct ieee80211_tx_control *control;
+	struct ieee80211_channel *channel;
+	struct ieee80211_rate *rate;
+	/* 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;
+
+	/* Extra fragments (in addition to the first fragment
+	 * in skb) */
+	int num_extra_frag;
+	struct sk_buff **extra_frag;
+};
+
+
 typedef unsigned __bitwise__ ieee80211_rx_result;
 #define RX_CONTINUE		((__force ieee80211_rx_result) 0u)
 #define RX_DROP_UNUSABLE	((__force ieee80211_rx_result) 1u)
 #define RX_DROP_MONITOR		((__force ieee80211_rx_result) 2u)
 #define RX_QUEUED		((__force ieee80211_rx_result) 3u)
 
-
-/* flags used in struct ieee80211_txrx_data.flags */
-/* whether the MSDU was fragmented */
-#define IEEE80211_TXRXD_FRAGMENTED		BIT(0)
-#define IEEE80211_TXRXD_TXUNICAST		BIT(1)
-#define IEEE80211_TXRXD_TXPS_BUFFERED		BIT(2)
-#define IEEE80211_TXRXD_TXPROBE_LAST_FRAG	BIT(3)
-#define IEEE80211_TXRXD_RXIN_SCAN		BIT(4)
+#define IEEE80211_RX_IN_SCAN		BIT(0)
 /* frame is destined to interface currently processed (incl. multicast frames) */
-#define IEEE80211_TXRXD_RXRA_MATCH		BIT(5)
-#define IEEE80211_TXRXD_TX_INJECTED		BIT(6)
-#define IEEE80211_TXRXD_RX_AMSDU		BIT(7)
-#define IEEE80211_TXRXD_RX_CMNTR_REPORTED	BIT(8)
-struct ieee80211_txrx_data {
+#define IEEE80211_RX_RA_MATCH		BIT(1)
+#define IEEE80211_RX_AMSDU		BIT(2)
+#define IEEE80211_RX_CMNTR_REPORTED	BIT(3)
+#define IEEE80211_RX_FRAGMENTED		BIT(4)
+
+struct ieee80211_rx_data {
 	struct sk_buff *skb;
 	struct net_device *dev;
 	struct ieee80211_local *local;
@@ -170,31 +195,14 @@ struct ieee80211_txrx_data {
 	u16 fc, ethertype;
 	struct ieee80211_key *key;
 	unsigned int flags;
-	union {
-		struct {
-			struct ieee80211_tx_control *control;
-			struct ieee80211_channel *channel;
-			struct ieee80211_rate *rate;
-			/* 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;
-
-			/* Extra fragments (in addition to the first fragment
-			 * in skb) */
-			int num_extra_frag;
-			struct sk_buff **extra_frag;
-		} tx;
-		struct {
-			struct ieee80211_rx_status *status;
-			struct ieee80211_rate *rate;
-			int sent_ps_buffered;
-			int queue;
-			int load;
-			u32 tkip_iv32;
-			u16 tkip_iv16;
-		} rx;
-	} u;
+
+	struct ieee80211_rx_status *status;
+	struct ieee80211_rate *rate;
+	int sent_ps_buffered;
+	int queue;
+	int load;
+	u32 tkip_iv32;
+	u16 tkip_iv16;
 };
 
 /* flags used in struct ieee80211_tx_packet_data.flags */
@@ -842,7 +850,7 @@ 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_iswep(struct ieee80211_txrx_data *tx);
+void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx);
 int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr);
 void ieee80211_if_setup(struct net_device *dev);
 int ieee80211_hw_config_ht(struct ieee80211_local *local, int enable_ht,
--- everything.orig/net/mac80211/rx.c	2008-02-24 10:15:11.000000000 +0100
+++ everything/net/mac80211/rx.c	2008-02-24 10:20:22.000000000 +0100
@@ -251,7 +251,7 @@ ieee80211_rx_monitor(struct ieee80211_lo
 }
 
 
-static void ieee80211_parse_qos(struct ieee80211_txrx_data *rx)
+static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)
 {
 	u8 *data = rx->skb->data;
 	int tid;
@@ -262,9 +262,9 @@ static void ieee80211_parse_qos(struct i
 		/* frame has qos control */
 		tid = qc[0] & QOS_CONTROL_TID_MASK;
 		if (qc[0] & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT)
-			rx->flags |= IEEE80211_TXRXD_RX_AMSDU;
+			rx->flags |= IEEE80211_RX_AMSDU;
 		else
-			rx->flags &= ~IEEE80211_TXRXD_RX_AMSDU;
+			rx->flags &= ~IEEE80211_RX_AMSDU;
 	} else {
 		if (unlikely((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)) {
 			/* Separate TID for management frames */
@@ -280,13 +280,13 @@ static void ieee80211_parse_qos(struct i
 	if (rx->sta)
 		I802_DEBUG_INC(rx->sta->wme_rx_queue[tid]);
 
-	rx->u.rx.queue = tid;
+	rx->queue = tid;
 	/* Set skb->priority to 1d tag if highest order bit of TID is not set.
 	 * For now, set skb->priority to 0 for other cases. */
 	rx->skb->priority = (tid > 7) ? 0 : tid;
 }
 
-static void ieee80211_verify_ip_alignment(struct ieee80211_txrx_data *rx)
+static void ieee80211_verify_ip_alignment(struct ieee80211_rx_data *rx)
 {
 #ifdef CONFIG_MAC80211_DEBUG_PACKET_ALIGNMENT
 	int hdrlen;
@@ -314,7 +314,7 @@ static void ieee80211_verify_ip_alignmen
 	 * to move the 802.11 header further back in that case.
 	 */
 	hdrlen = ieee80211_get_hdrlen(rx->fc);
-	if (rx->flags & IEEE80211_TXRXD_RX_AMSDU)
+	if (rx->flags & IEEE80211_RX_AMSDU)
 		hdrlen += ETH_HLEN;
 	WARN_ON_ONCE(((unsigned long)(rx->skb->data + hdrlen)) & 3);
 #endif
@@ -357,32 +357,32 @@ static u32 ieee80211_rx_load_stats(struc
 /* rx handlers */
 
 static ieee80211_rx_result
-ieee80211_rx_h_if_stats(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_if_stats(struct ieee80211_rx_data *rx)
 {
 	if (rx->sta)
-		rx->sta->channel_use_raw += rx->u.rx.load;
-	rx->sdata->channel_use_raw += rx->u.rx.load;
+		rx->sta->channel_use_raw += rx->load;
+	rx->sdata->channel_use_raw += rx->load;
 	return RX_CONTINUE;
 }
 
 static ieee80211_rx_result
-ieee80211_rx_h_passive_scan(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx)
 {
 	struct ieee80211_local *local = rx->local;
 	struct sk_buff *skb = rx->skb;
 
 	if (unlikely(local->sta_hw_scanning))
-		return ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status);
+		return ieee80211_sta_rx_scan(rx->dev, skb, rx->status);
 
 	if (unlikely(local->sta_sw_scanning)) {
 		/* drop all the other packets during a software scan anyway */
-		if (ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status)
+		if (ieee80211_sta_rx_scan(rx->dev, skb, rx->status)
 		    != RX_QUEUED)
 			dev_kfree_skb(skb);
 		return RX_QUEUED;
 	}
 
-	if (unlikely(rx->flags & IEEE80211_TXRXD_RXIN_SCAN)) {
+	if (unlikely(rx->flags & IEEE80211_RX_IN_SCAN)) {
 		/* scanning finished during invoking of handlers */
 		I802_DEBUG_INC(local->rx_handlers_drop_passive_scan);
 		return RX_DROP_UNUSABLE;
@@ -392,7 +392,7 @@ ieee80211_rx_h_passive_scan(struct ieee8
 }
 
 static ieee80211_rx_result
-ieee80211_rx_mesh_check(struct ieee80211_txrx_data *rx)
+ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
 {
 	int hdrlen = ieee80211_get_hdrlen(rx->fc);
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
@@ -443,7 +443,7 @@ ieee80211_rx_mesh_check(struct ieee80211
 
 
 static ieee80211_rx_result
-ieee80211_rx_h_check(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
 {
 	struct ieee80211_hdr *hdr;
 
@@ -452,15 +452,15 @@ ieee80211_rx_h_check(struct ieee80211_tx
 	/* Drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.2.9) */
 	if (rx->sta && !is_multicast_ether_addr(hdr->addr1)) {
 		if (unlikely(rx->fc & IEEE80211_FCTL_RETRY &&
-			     rx->sta->last_seq_ctrl[rx->u.rx.queue] ==
+			     rx->sta->last_seq_ctrl[rx->queue] ==
 			     hdr->seq_ctrl)) {
-			if (rx->flags & IEEE80211_TXRXD_RXRA_MATCH) {
+			if (rx->flags & IEEE80211_RX_RA_MATCH) {
 				rx->local->dot11FrameDuplicateCount++;
 				rx->sta->num_duplicates++;
 			}
 			return RX_DROP_MONITOR;
 		} else
-			rx->sta->last_seq_ctrl[rx->u.rx.queue] = hdr->seq_ctrl;
+			rx->sta->last_seq_ctrl[rx->queue] = hdr->seq_ctrl;
 	}
 
 	if (unlikely(rx->skb->len < 16)) {
@@ -488,7 +488,7 @@ ieee80211_rx_h_check(struct ieee80211_tx
 		if ((!(rx->fc & IEEE80211_FCTL_FROMDS) &&
 		     !(rx->fc & IEEE80211_FCTL_TODS) &&
 		     (rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)
-		    || !(rx->flags & IEEE80211_TXRXD_RXRA_MATCH)) {
+		    || !(rx->flags & IEEE80211_RX_RA_MATCH)) {
 			/* Drop IBSS frames and frames for other hosts
 			 * silently. */
 			return RX_DROP_MONITOR;
@@ -502,7 +502,7 @@ ieee80211_rx_h_check(struct ieee80211_tx
 
 
 static ieee80211_rx_result
-ieee80211_rx_h_decrypt(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
 {
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
 	int keyidx;
@@ -543,7 +543,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_
 	 * No point in finding a key and decrypting if the frame is neither
 	 * addressed to us nor a multicast frame.
 	 */
-	if (!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH))
+	if (!(rx->flags & IEEE80211_RX_RA_MATCH))
 		return RX_CONTINUE;
 
 	if (rx->sta)
@@ -561,8 +561,8 @@ ieee80211_rx_h_decrypt(struct ieee80211_
 		 * we somehow allow the driver to tell us which key
 		 * the hardware used if this flag is set?
 		 */
-		if ((rx->u.rx.status->flag & RX_FLAG_DECRYPTED) &&
-		    (rx->u.rx.status->flag & RX_FLAG_IV_STRIPPED))
+		if ((rx->status->flag & RX_FLAG_DECRYPTED) &&
+		    (rx->status->flag & RX_FLAG_IV_STRIPPED))
 			return RX_CONTINUE;
 
 		hdrlen = ieee80211_get_hdrlen(rx->fc);
@@ -603,8 +603,8 @@ ieee80211_rx_h_decrypt(struct ieee80211_
 	/* Check for weak IVs if possible */
 	if (rx->sta && rx->key->conf.alg == ALG_WEP &&
 	    ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) &&
-	    (!(rx->u.rx.status->flag & RX_FLAG_IV_STRIPPED) ||
-	     !(rx->u.rx.status->flag & RX_FLAG_DECRYPTED)) &&
+	    (!(rx->status->flag & RX_FLAG_IV_STRIPPED) ||
+	     !(rx->status->flag & RX_FLAG_DECRYPTED)) &&
 	    ieee80211_wep_is_weak_iv(rx->skb, rx->key))
 		rx->sta->wep_weak_iv_count++;
 
@@ -621,7 +621,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_
 	}
 
 	/* either the frame has been decrypted or will be dropped */
-	rx->u.rx.status->flag |= RX_FLAG_DECRYPTED;
+	rx->status->flag |= RX_FLAG_DECRYPTED;
 
 	return result;
 }
@@ -691,7 +691,7 @@ static int ap_sta_ps_end(struct net_devi
 }
 
 static ieee80211_rx_result
-ieee80211_rx_h_sta_process(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
 {
 	struct sta_info *sta = rx->sta;
 	struct net_device *dev = rx->dev;
@@ -720,20 +720,20 @@ ieee80211_rx_h_sta_process(struct ieee80
 		sta->last_rx = jiffies;
 	}
 
-	if (!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH))
+	if (!(rx->flags & IEEE80211_RX_RA_MATCH))
 		return RX_CONTINUE;
 
 	sta->rx_fragments++;
 	sta->rx_bytes += rx->skb->len;
-	sta->last_rssi = rx->u.rx.status->ssi;
-	sta->last_signal = rx->u.rx.status->signal;
-	sta->last_noise = rx->u.rx.status->noise;
+	sta->last_rssi = rx->status->ssi;
+	sta->last_signal = rx->status->signal;
+	sta->last_noise = rx->status->noise;
 
 	if (!(rx->fc & IEEE80211_FCTL_MOREFRAGS)) {
 		/* Change STA power saving mode only in the end of a frame
 		 * exchange sequence */
 		if ((sta->flags & WLAN_STA_PS) && !(rx->fc & IEEE80211_FCTL_PM))
-			rx->u.rx.sent_ps_buffered += ap_sta_ps_end(dev, sta);
+			rx->sent_ps_buffered += ap_sta_ps_end(dev, sta);
 		else if (!(sta->flags & WLAN_STA_PS) &&
 			 (rx->fc & IEEE80211_FCTL_PM))
 			ap_sta_ps_start(dev, sta);
@@ -838,7 +838,7 @@ ieee80211_reassemble_find(struct ieee802
 }
 
 static ieee80211_rx_result
-ieee80211_rx_h_defragment(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
 {
 	struct ieee80211_hdr *hdr;
 	u16 sc;
@@ -864,14 +864,14 @@ ieee80211_rx_h_defragment(struct ieee802
 	if (frag == 0) {
 		/* This is the first fragment of a new frame. */
 		entry = ieee80211_reassemble_add(rx->sdata, frag, seq,
-						 rx->u.rx.queue, &(rx->skb));
+						 rx->queue, &(rx->skb));
 		if (rx->key && rx->key->conf.alg == ALG_CCMP &&
 		    (rx->fc & IEEE80211_FCTL_PROTECTED)) {
 			/* Store CCMP PN so that we can verify that the next
 			 * fragment has a sequential PN value. */
 			entry->ccmp = 1;
 			memcpy(entry->last_pn,
-			       rx->key->u.ccmp.rx_pn[rx->u.rx.queue],
+			       rx->key->u.ccmp.rx_pn[rx->queue],
 			       CCMP_PN_LEN);
 		}
 		return RX_QUEUED;
@@ -881,7 +881,7 @@ ieee80211_rx_h_defragment(struct ieee802
 	 * fragment cache. Add this fragment to the end of the pending entry.
 	 */
 	entry = ieee80211_reassemble_find(rx->sdata, rx->fc, frag, seq,
-					  rx->u.rx.queue, hdr);
+					  rx->queue, hdr);
 	if (!entry) {
 		I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag);
 		return RX_DROP_MONITOR;
@@ -900,7 +900,7 @@ ieee80211_rx_h_defragment(struct ieee802
 			if (pn[i])
 				break;
 		}
-		rpn = rx->key->u.ccmp.rx_pn[rx->u.rx.queue];
+		rpn = rx->key->u.ccmp.rx_pn[rx->queue];
 		if (memcmp(pn, rpn, CCMP_PN_LEN) != 0) {
 			if (net_ratelimit())
 				printk(KERN_DEBUG "%s: defrag: CCMP PN not "
@@ -941,7 +941,7 @@ ieee80211_rx_h_defragment(struct ieee802
 	}
 
 	/* Complete frame has been reassembled - process it now */
-	rx->flags |= IEEE80211_TXRXD_FRAGMENTED;
+	rx->flags |= IEEE80211_RX_FRAGMENTED;
 
  out:
 	if (rx->sta)
@@ -954,7 +954,7 @@ ieee80211_rx_h_defragment(struct ieee802
 }
 
 static ieee80211_rx_result
-ieee80211_rx_h_ps_poll(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
 {
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
 	struct sk_buff *skb;
@@ -964,7 +964,7 @@ ieee80211_rx_h_ps_poll(struct ieee80211_
 	if (likely(!rx->sta ||
 		   (rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_CTL ||
 		   (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PSPOLL ||
-		   !(rx->flags & IEEE80211_TXRXD_RXRA_MATCH)))
+		   !(rx->flags & IEEE80211_RX_RA_MATCH)))
 		return RX_CONTINUE;
 
 	if ((sdata->vif.type != IEEE80211_IF_TYPE_AP) &&
@@ -1008,7 +1008,7 @@ ieee80211_rx_h_ps_poll(struct ieee80211_
 		if (no_pending_pkts)
 			sta_info_clear_tim_bit(rx->sta);
 #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
-	} else if (!rx->u.rx.sent_ps_buffered) {
+	} else if (!rx->sent_ps_buffered) {
 		/*
 		 * FIXME: This can be the result of a race condition between
 		 *	  us expiring a frame and the station polling for it.
@@ -1029,7 +1029,7 @@ ieee80211_rx_h_ps_poll(struct ieee80211_
 }
 
 static ieee80211_rx_result
-ieee80211_rx_h_remove_qos_control(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_remove_qos_control(struct ieee80211_rx_data *rx)
 {
 	u16 fc = rx->fc;
 	u8 *data = rx->skb->data;
@@ -1049,7 +1049,7 @@ ieee80211_rx_h_remove_qos_control(struct
 }
 
 static int
-ieee80211_802_1x_port_control(struct ieee80211_txrx_data *rx)
+ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx)
 {
 	if (unlikely(!rx->sta || !(rx->sta->flags & WLAN_STA_AUTHORIZED))) {
 #ifdef CONFIG_MAC80211_DEBUG
@@ -1064,13 +1064,13 @@ ieee80211_802_1x_port_control(struct iee
 }
 
 static int
-ieee80211_drop_unencrypted(struct ieee80211_txrx_data *rx)
+ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx)
 {
 	/*
 	 * Pass through unencrypted frames if the hardware has
 	 * decrypted them already.
 	 */
-	if (rx->u.rx.status->flag & RX_FLAG_DECRYPTED)
+	if (rx->status->flag & RX_FLAG_DECRYPTED)
 		return 0;
 
 	/* Drop unencrypted frames if key is set. */
@@ -1087,7 +1087,7 @@ ieee80211_drop_unencrypted(struct ieee80
 }
 
 static int
-ieee80211_data_to_8023(struct ieee80211_txrx_data *rx)
+ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
 {
 	struct net_device *dev = rx->dev;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
@@ -1235,7 +1235,7 @@ ieee80211_data_to_8023(struct ieee80211_
 /*
  * requires that rx->skb is a frame with ethernet header
  */
-static bool ieee80211_frame_allowed(struct ieee80211_txrx_data *rx)
+static bool ieee80211_frame_allowed(struct ieee80211_rx_data *rx)
 {
 	static const u8 pae_group_addr[ETH_ALEN]
 		= { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x03 };
@@ -1261,7 +1261,7 @@ static bool ieee80211_frame_allowed(stru
  * requires that rx->skb is a frame with ethernet header
  */
 static void
-ieee80211_deliver_skb(struct ieee80211_txrx_data *rx)
+ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
 {
 	struct net_device *dev = rx->dev;
 	struct ieee80211_local *local = rx->local;
@@ -1275,7 +1275,7 @@ ieee80211_deliver_skb(struct ieee80211_t
 
 	if (local->bridge_packets && (sdata->vif.type == IEEE80211_IF_TYPE_AP ||
 				      sdata->vif.type == IEEE80211_IF_TYPE_VLAN) &&
-	    (rx->flags & IEEE80211_TXRXD_RXRA_MATCH)) {
+	    (rx->flags & IEEE80211_RX_RA_MATCH)) {
 		if (is_multicast_ether_addr(ehdr->h_dest)) {
 			/*
 			 * send multicast frames both to higher layers in
@@ -1351,7 +1351,7 @@ ieee80211_deliver_skb(struct ieee80211_t
 }
 
 static ieee80211_rx_result
-ieee80211_rx_h_amsdu(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
 {
 	struct net_device *dev = rx->dev;
 	struct ieee80211_local *local = rx->local;
@@ -1371,7 +1371,7 @@ ieee80211_rx_h_amsdu(struct ieee80211_tx
 	if (unlikely(!WLAN_FC_DATA_PRESENT(fc)))
 		return RX_DROP_MONITOR;
 
-	if (!(rx->flags & IEEE80211_TXRXD_RX_AMSDU))
+	if (!(rx->flags & IEEE80211_RX_AMSDU))
 		return RX_CONTINUE;
 
 	err = ieee80211_data_to_8023(rx);
@@ -1468,7 +1468,7 @@ ieee80211_rx_h_amsdu(struct ieee80211_tx
 }
 
 static ieee80211_rx_result
-ieee80211_rx_h_data(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
 {
 	struct net_device *dev = rx->dev;
 	u16 fc;
@@ -1499,7 +1499,7 @@ ieee80211_rx_h_data(struct ieee80211_txr
 }
 
 static ieee80211_rx_result
-ieee80211_rx_h_ctrl(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx)
 {
 	struct ieee80211_local *local = rx->local;
 	struct ieee80211_hw *hw = &local->hw;
@@ -1542,11 +1542,11 @@ ieee80211_rx_h_ctrl(struct ieee80211_txr
 }
 
 static ieee80211_rx_result
-ieee80211_rx_h_mgmt(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
 {
 	struct ieee80211_sub_if_data *sdata;
 
-	if (!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH))
+	if (!(rx->flags & IEEE80211_RX_RA_MATCH))
 		return RX_DROP_MONITOR;
 
 	sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
@@ -1554,7 +1554,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_txr
 	     sdata->vif.type == IEEE80211_IF_TYPE_IBSS ||
 	     sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT) &&
 	    !(sdata->flags & IEEE80211_SDATA_USERSPACE_MLME))
-		ieee80211_sta_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status);
+		ieee80211_sta_rx_mgmt(rx->dev, rx->skb, rx->status);
 	else
 		return RX_DROP_MONITOR;
 
@@ -1563,7 +1563,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_txr
 
 static void ieee80211_rx_michael_mic_report(struct net_device *dev,
 					    struct ieee80211_hdr *hdr,
-					    struct ieee80211_txrx_data *rx)
+					    struct ieee80211_rx_data *rx)
 {
 	int keyidx, hdrlen;
 	DECLARE_MAC_BUF(mac);
@@ -1633,7 +1633,8 @@ static void ieee80211_rx_michael_mic_rep
 	rx->skb = NULL;
 }
 
-static void ieee80211_rx_cooked_monitor(struct ieee80211_txrx_data *rx)
+/* TODO: use IEEE80211_RX_FRAGMENTED */
+static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx)
 {
 	struct ieee80211_sub_if_data *sdata;
 	struct ieee80211_local *local = rx->local;
@@ -1646,9 +1647,9 @@ static void ieee80211_rx_cooked_monitor(
 	} __attribute__ ((packed)) *rthdr;
 	struct sk_buff *skb = rx->skb, *skb2;
 	struct net_device *prev_dev = NULL;
-	struct ieee80211_rx_status *status = rx->u.rx.status;
+	struct ieee80211_rx_status *status = rx->status;
 
-	if (rx->flags & IEEE80211_TXRXD_RX_CMNTR_REPORTED)
+	if (rx->flags & IEEE80211_RX_CMNTR_REPORTED)
 		goto out_free_skb;
 
 	if (skb_headroom(skb) < sizeof(*rthdr) &&
@@ -1663,7 +1664,7 @@ static void ieee80211_rx_cooked_monitor(
 			    (1 << IEEE80211_RADIOTAP_RATE) |
 			    (1 << IEEE80211_RADIOTAP_CHANNEL));
 
-	rthdr->rate = rx->u.rx.rate->bitrate / 5;
+	rthdr->rate = rx->rate->bitrate / 5;
 	rthdr->chan_freq = cpu_to_le16(status->freq);
 
 	if (status->band == IEEE80211_BAND_5GHZ)
@@ -1706,14 +1707,14 @@ static void ieee80211_rx_cooked_monitor(
 	} else
 		goto out_free_skb;
 
-	rx->flags |= IEEE80211_TXRXD_RX_CMNTR_REPORTED;
+	rx->flags |= IEEE80211_RX_CMNTR_REPORTED;
 	return;
 
  out_free_skb:
 	dev_kfree_skb(skb);
 }
 
-typedef ieee80211_rx_result (*ieee80211_rx_handler)(struct ieee80211_txrx_data *);
+typedef ieee80211_rx_result (*ieee80211_rx_handler)(struct ieee80211_rx_data *);
 static ieee80211_rx_handler ieee80211_rx_handlers[] =
 {
 	ieee80211_rx_h_if_stats,
@@ -1737,7 +1738,7 @@ static ieee80211_rx_handler ieee80211_rx
 };
 
 static void ieee80211_invoke_rx_handlers(struct ieee80211_sub_if_data *sdata,
-					 struct ieee80211_txrx_data *rx,
+					 struct ieee80211_rx_data *rx,
 					 struct sk_buff *skb)
 {
 	ieee80211_rx_handler *handler;
@@ -1780,7 +1781,7 @@ static void ieee80211_invoke_rx_handlers
 /* main receive path */
 
 static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
-				u8 *bssid, struct ieee80211_txrx_data *rx,
+				u8 *bssid, struct ieee80211_rx_data *rx,
 				struct ieee80211_hdr *hdr)
 {
 	int multicast = is_multicast_ether_addr(hdr->addr1);
@@ -1790,15 +1791,15 @@ static int prepare_for_handlers(struct i
 		if (!bssid)
 			return 0;
 		if (!ieee80211_bssid_match(bssid, sdata->u.sta.bssid)) {
-			if (!(rx->flags & IEEE80211_TXRXD_RXIN_SCAN))
+			if (!(rx->flags & IEEE80211_RX_IN_SCAN))
 				return 0;
-			rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
+			rx->flags &= ~IEEE80211_RX_RA_MATCH;
 		} else if (!multicast &&
 			   compare_ether_addr(sdata->dev->dev_addr,
 					      hdr->addr1) != 0) {
 			if (!(sdata->dev->flags & IFF_PROMISC))
 				return 0;
-			rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
+			rx->flags &= ~IEEE80211_RX_RA_MATCH;
 		}
 		break;
 	case IEEE80211_IF_TYPE_IBSS:
@@ -1808,15 +1809,15 @@ static int prepare_for_handlers(struct i
 		    (rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON)
 			return 1;
 		else if (!ieee80211_bssid_match(bssid, sdata->u.sta.bssid)) {
-			if (!(rx->flags & IEEE80211_TXRXD_RXIN_SCAN))
+			if (!(rx->flags & IEEE80211_RX_IN_SCAN))
 				return 0;
-			rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
+			rx->flags &= ~IEEE80211_RX_RA_MATCH;
 		} else if (!multicast &&
 			   compare_ether_addr(sdata->dev->dev_addr,
 					      hdr->addr1) != 0) {
 			if (!(sdata->dev->flags & IFF_PROMISC))
 				return 0;
-			rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
+			rx->flags &= ~IEEE80211_RX_RA_MATCH;
 		} else if (!rx->sta)
 			rx->sta = ieee80211_ibss_add_sta(sdata->dev, rx->skb,
 							 bssid, hdr->addr2);
@@ -1828,7 +1829,7 @@ static int prepare_for_handlers(struct i
 			if (!(sdata->dev->flags & IFF_PROMISC))
 				return 0;
 
-			rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
+			rx->flags &= ~IEEE80211_RX_RA_MATCH;
 		}
 		break;
 	case IEEE80211_IF_TYPE_VLAN:
@@ -1839,12 +1840,12 @@ static int prepare_for_handlers(struct i
 				return 0;
 		} else if (!ieee80211_bssid_match(bssid,
 					sdata->dev->dev_addr)) {
-			if (!(rx->flags & IEEE80211_TXRXD_RXIN_SCAN))
+			if (!(rx->flags & IEEE80211_RX_IN_SCAN))
 				return 0;
-			rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
+			rx->flags &= ~IEEE80211_RX_RA_MATCH;
 		}
 		if (sdata->dev == sdata->local->mdev &&
-		    !(rx->flags & IEEE80211_TXRXD_RXIN_SCAN))
+		    !(rx->flags & IEEE80211_RX_IN_SCAN))
 			/* do not receive anything via
 			 * master device when not scanning */
 			return 0;
@@ -1881,7 +1882,7 @@ static void __ieee80211_rx_handle_packet
 	struct ieee80211_local *local = hw_to_local(hw);
 	struct ieee80211_sub_if_data *sdata;
 	struct ieee80211_hdr *hdr;
-	struct ieee80211_txrx_data rx;
+	struct ieee80211_rx_data rx;
 	u16 type;
 	int prepares;
 	struct ieee80211_sub_if_data *prev = NULL;
@@ -1893,9 +1894,9 @@ static void __ieee80211_rx_handle_packet
 	rx.skb = skb;
 	rx.local = local;
 
-	rx.u.rx.status = status;
-	rx.u.rx.load = load;
-	rx.u.rx.rate = rate;
+	rx.status = status;
+	rx.load = load;
+	rx.rate = rate;
 	rx.fc = le16_to_cpu(hdr->frame_control);
 	type = rx.fc & IEEE80211_FCTL_FTYPE;
 
@@ -1914,7 +1915,7 @@ static void __ieee80211_rx_handle_packet
 	}
 
 	if (unlikely(local->sta_sw_scanning || local->sta_hw_scanning))
-		rx.flags |= IEEE80211_TXRXD_RXIN_SCAN;
+		rx.flags |= IEEE80211_RX_IN_SCAN;
 
 	ieee80211_parse_qos(&rx);
 	ieee80211_verify_ip_alignment(&rx);
@@ -1929,7 +1930,7 @@ static void __ieee80211_rx_handle_packet
 			continue;
 
 		bssid = ieee80211_get_bssid(hdr, skb->len, sdata->vif.type);
-		rx.flags |= IEEE80211_TXRXD_RXRA_MATCH;
+		rx.flags |= IEEE80211_RX_RA_MATCH;
 		prepares = prepare_for_handlers(sdata, bssid, &rx, hdr);
 
 		if (!prepares)
--- everything.orig/net/mac80211/tx.c	2008-02-24 10:09:07.000000000 +0100
+++ everything/net/mac80211/tx.c	2008-02-24 10:19:18.000000000 +0100
@@ -87,11 +87,11 @@ static inline void ieee80211_dump_frame(
 }
 #endif /* CONFIG_MAC80211_LOWTX_FRAME_DUMP */
 
-static u16 ieee80211_duration(struct ieee80211_txrx_data *tx, int group_addr,
+static u16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
 			      int next_frag_len)
 {
 	int rate, mrate, erp, dur, i;
-	struct ieee80211_rate *txrate = tx->u.tx.rate;
+	struct ieee80211_rate *txrate = tx->rate;
 	struct ieee80211_local *local = tx->local;
 	struct ieee80211_supported_band *sband;
 
@@ -234,7 +234,7 @@ static int inline is_ieee80211_device(st
 /* tx handlers */
 
 static ieee80211_tx_result
-ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx)
+ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
 {
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
 	struct sk_buff *skb = tx->skb;
@@ -242,7 +242,7 @@ ieee80211_tx_h_check_assoc(struct ieee80
 #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
 	u32 sta_flags;
 
-	if (unlikely(tx->flags & IEEE80211_TXRXD_TX_INJECTED))
+	if (unlikely(tx->flags & IEEE80211_TX_INJECTED))
 		return TX_CONTINUE;
 
 	if (unlikely(tx->local->sta_sw_scanning) &&
@@ -253,12 +253,12 @@ ieee80211_tx_h_check_assoc(struct ieee80
 	if (tx->sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT)
 		return TX_CONTINUE;
 
-	if (tx->flags & IEEE80211_TXRXD_TXPS_BUFFERED)
+	if (tx->flags & IEEE80211_TX_PS_BUFFERED)
 		return TX_CONTINUE;
 
 	sta_flags = tx->sta ? tx->sta->flags : 0;
 
-	if (likely(tx->flags & IEEE80211_TXRXD_TXUNICAST)) {
+	if (likely(tx->flags & IEEE80211_TX_UNICAST)) {
 		if (unlikely(!(sta_flags & WLAN_STA_ASSOC) &&
 			     tx->sdata->vif.type != IEEE80211_IF_TYPE_IBSS &&
 			     (tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)) {
@@ -288,7 +288,7 @@ ieee80211_tx_h_check_assoc(struct ieee80
 }
 
 static ieee80211_tx_result
-ieee80211_tx_h_sequence(struct ieee80211_txrx_data *tx)
+ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
 {
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
 
@@ -346,7 +346,7 @@ static void purge_old_ps_buffers(struct 
 }
 
 static ieee80211_tx_result
-ieee80211_tx_h_multicast_ps_buf(struct ieee80211_txrx_data *tx)
+ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
 {
 	/*
 	 * broadcast/multicast frame
@@ -383,13 +383,13 @@ ieee80211_tx_h_multicast_ps_buf(struct i
 	}
 
 	/* buffered in hardware */
-	tx->u.tx.control->flags |= IEEE80211_TXCTL_SEND_AFTER_DTIM;
+	tx->control->flags |= IEEE80211_TXCTL_SEND_AFTER_DTIM;
 
 	return TX_CONTINUE;
 }
 
 static ieee80211_tx_result
-ieee80211_tx_h_unicast_ps_buf(struct ieee80211_txrx_data *tx)
+ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
 {
 	struct sta_info *sta = tx->sta;
 	DECLARE_MAC_BUF(mac);
@@ -443,32 +443,32 @@ ieee80211_tx_h_unicast_ps_buf(struct iee
 }
 
 static ieee80211_tx_result
-ieee80211_tx_h_ps_buf(struct ieee80211_txrx_data *tx)
+ieee80211_tx_h_ps_buf(struct ieee80211_tx_data *tx)
 {
-	if (unlikely(tx->flags & IEEE80211_TXRXD_TXPS_BUFFERED))
+	if (unlikely(tx->flags & IEEE80211_TX_PS_BUFFERED))
 		return TX_CONTINUE;
 
-	if (tx->flags & IEEE80211_TXRXD_TXUNICAST)
+	if (tx->flags & IEEE80211_TX_UNICAST)
 		return ieee80211_tx_h_unicast_ps_buf(tx);
 	else
 		return ieee80211_tx_h_multicast_ps_buf(tx);
 }
 
 static ieee80211_tx_result
-ieee80211_tx_h_select_key(struct ieee80211_txrx_data *tx)
+ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
 {
 	struct ieee80211_key *key;
 	u16 fc = tx->fc;
 
-	if (unlikely(tx->u.tx.control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT))
+	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->u.tx.control->flags & IEEE80211_TXCTL_EAPOL_FRAME) &&
-		 !(tx->flags & IEEE80211_TXRXD_TX_INJECTED)) {
+		 !(tx->control->flags & IEEE80211_TXCTL_EAPOL_FRAME) &&
+		 !(tx->flags & IEEE80211_TX_INJECTED)) {
 		I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted);
 		return TX_DROP;
 	} else
@@ -497,13 +497,13 @@ ieee80211_tx_h_select_key(struct ieee802
 	}
 
 	if (!tx->key || !(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
-		tx->u.tx.control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
+		tx->control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
 
 	return TX_CONTINUE;
 }
 
 static ieee80211_tx_result
-ieee80211_tx_h_fragment(struct ieee80211_txrx_data *tx)
+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;
@@ -513,7 +513,7 @@ ieee80211_tx_h_fragment(struct ieee80211
 	u8 *pos;
 	int frag_threshold = tx->local->fragmentation_threshold;
 
-	if (!(tx->flags & IEEE80211_TXRXD_FRAGMENTED))
+	if (!(tx->flags & IEEE80211_TX_FRAGMENTED))
 		return TX_CONTINUE;
 
 	first = tx->skb;
@@ -565,8 +565,8 @@ ieee80211_tx_h_fragment(struct ieee80211
 	}
 	skb_trim(first, hdrlen + per_fragm);
 
-	tx->u.tx.num_extra_frag = num_fragm - 1;
-	tx->u.tx.extra_frag = frags;
+	tx->num_extra_frag = num_fragm - 1;
+	tx->extra_frag = frags;
 
 	return TX_CONTINUE;
 
@@ -583,7 +583,7 @@ ieee80211_tx_h_fragment(struct ieee80211
 }
 
 static ieee80211_tx_result
-ieee80211_tx_h_encrypt(struct ieee80211_txrx_data *tx)
+ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx)
 {
 	if (!tx->key)
 		return TX_CONTINUE;
@@ -603,56 +603,56 @@ ieee80211_tx_h_encrypt(struct ieee80211_
 }
 
 static ieee80211_tx_result
-ieee80211_tx_h_rate_ctrl(struct ieee80211_txrx_data *tx)
+ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
 {
 	struct rate_selection rsel;
 	struct ieee80211_supported_band *sband;
 
 	sband = tx->local->hw.wiphy->bands[tx->local->hw.conf.channel->band];
 
-	if (likely(!tx->u.tx.rate)) {
+	if (likely(!tx->rate)) {
 		rate_control_get_rate(tx->dev, sband, tx->skb, &rsel);
-		tx->u.tx.rate = rsel.rate;
+		tx->rate = rsel.rate;
 		if (unlikely(rsel.probe)) {
-			tx->u.tx.control->flags |=
+			tx->control->flags |=
 				IEEE80211_TXCTL_RATE_CTRL_PROBE;
-			tx->flags |= IEEE80211_TXRXD_TXPROBE_LAST_FRAG;
-			tx->u.tx.control->alt_retry_rate = tx->u.tx.rate;
-			tx->u.tx.rate = rsel.probe;
+			tx->flags |= IEEE80211_TX_PROBE_LAST_FRAG;
+			tx->control->alt_retry_rate = tx->rate;
+			tx->rate = rsel.probe;
 		} else
-			tx->u.tx.control->alt_retry_rate = NULL;
+			tx->control->alt_retry_rate = NULL;
 
-		if (!tx->u.tx.rate)
+		if (!tx->rate)
 			return TX_DROP;
 	} else
-		tx->u.tx.control->alt_retry_rate = NULL;
+		tx->control->alt_retry_rate = NULL;
 
 	if (tx->sdata->bss_conf.use_cts_prot &&
-	    (tx->flags & IEEE80211_TXRXD_FRAGMENTED) && rsel.nonerp) {
-		tx->u.tx.last_frag_rate = tx->u.tx.rate;
+	    (tx->flags & IEEE80211_TX_FRAGMENTED) && rsel.nonerp) {
+		tx->last_frag_rate = tx->rate;
 		if (rsel.probe)
-			tx->flags &= ~IEEE80211_TXRXD_TXPROBE_LAST_FRAG;
+			tx->flags &= ~IEEE80211_TX_PROBE_LAST_FRAG;
 		else
-			tx->flags |= IEEE80211_TXRXD_TXPROBE_LAST_FRAG;
-		tx->u.tx.rate = rsel.nonerp;
-		tx->u.tx.control->tx_rate = rsel.nonerp;
-		tx->u.tx.control->flags &= ~IEEE80211_TXCTL_RATE_CTRL_PROBE;
+			tx->flags |= IEEE80211_TX_PROBE_LAST_FRAG;
+		tx->rate = rsel.nonerp;
+		tx->control->tx_rate = rsel.nonerp;
+		tx->control->flags &= ~IEEE80211_TXCTL_RATE_CTRL_PROBE;
 	} else {
-		tx->u.tx.last_frag_rate = tx->u.tx.rate;
-		tx->u.tx.control->tx_rate = tx->u.tx.rate;
+		tx->last_frag_rate = tx->rate;
+		tx->control->tx_rate = tx->rate;
 	}
-	tx->u.tx.control->tx_rate = tx->u.tx.rate;
+	tx->control->tx_rate = tx->rate;
 
 	return TX_CONTINUE;
 }
 
 static ieee80211_tx_result
-ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx)
+ieee80211_tx_h_misc(struct ieee80211_tx_data *tx)
 {
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
 	u16 fc = le16_to_cpu(hdr->frame_control);
 	u16 dur;
-	struct ieee80211_tx_control *control = tx->u.tx.control;
+	struct ieee80211_tx_control *control = tx->control;
 
 	if (!control->retry_limit) {
 		if (!is_multicast_ether_addr(hdr->addr1)) {
@@ -674,7 +674,7 @@ ieee80211_tx_h_misc(struct ieee80211_txr
 		}
 	}
 
-	if (tx->flags & IEEE80211_TXRXD_FRAGMENTED) {
+	if (tx->flags & IEEE80211_TX_FRAGMENTED) {
 		/* Do not use multiple retry rates when sending fragmented
 		 * frames.
 		 * TODO: The last fragment could still use multiple retry
@@ -686,8 +686,8 @@ ieee80211_tx_h_misc(struct ieee80211_txr
 	 * there are associated non-ERP stations and RTS/CTS is not configured
 	 * for the frame. */
 	if ((tx->sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) &&
-	    (tx->u.tx.rate->flags & IEEE80211_RATE_ERP_G) &&
-	    (tx->flags & IEEE80211_TXRXD_TXUNICAST) &&
+	    (tx->rate->flags & IEEE80211_RATE_ERP_G) &&
+	    (tx->flags & IEEE80211_TX_UNICAST) &&
 	    tx->sdata->bss_conf.use_cts_prot &&
 	    !(control->flags & IEEE80211_TXCTL_USE_RTS_CTS))
 		control->flags |= IEEE80211_TXCTL_USE_CTS_PROTECT;
@@ -696,18 +696,18 @@ ieee80211_tx_h_misc(struct ieee80211_txr
 	 * 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->u.tx.rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
+	    (tx->rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
 	    tx->sdata->bss_conf.use_short_preamble &&
 	    (!tx->sta || (tx->sta->flags & WLAN_STA_SHORT_PREAMBLE))) {
-		tx->u.tx.control->flags |= IEEE80211_TXCTL_SHORT_PREAMBLE;
+		tx->control->flags |= IEEE80211_TXCTL_SHORT_PREAMBLE;
 	}
 
 	/* Setup duration field for the first fragment of the frame. Duration
 	 * 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_TXRXD_FRAGMENTED) ?
-				 tx->u.tx.extra_frag[0]->len : 0);
+				 (tx->flags & IEEE80211_TX_FRAGMENTED) ?
+				 tx->extra_frag[0]->len : 0);
 	hdr->duration_id = cpu_to_le16(dur);
 
 	if ((control->flags & IEEE80211_TXCTL_USE_RTS_CTS) ||
@@ -723,7 +723,7 @@ ieee80211_tx_h_misc(struct ieee80211_txr
 		control->alt_retry_rate = NULL;
 
 		/* Use min(data rate, max base rate) as CTS/RTS rate */
-		rate = tx->u.tx.rate;
+		rate = tx->rate;
 		baserate = NULL;
 
 		for (idx = 0; idx < sband->n_bitrates; idx++) {
@@ -745,12 +745,12 @@ ieee80211_tx_h_misc(struct ieee80211_txr
 		tx->sta->tx_packets++;
 		tx->sta->tx_fragments++;
 		tx->sta->tx_bytes += tx->skb->len;
-		if (tx->u.tx.extra_frag) {
+		if (tx->extra_frag) {
 			int i;
-			tx->sta->tx_fragments += tx->u.tx.num_extra_frag;
-			for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
+			tx->sta->tx_fragments += tx->num_extra_frag;
+			for (i = 0; i < tx->num_extra_frag; i++) {
 				tx->sta->tx_bytes +=
-					tx->u.tx.extra_frag[i]->len;
+					tx->extra_frag[i]->len;
 			}
 		}
 	}
@@ -759,13 +759,13 @@ ieee80211_tx_h_misc(struct ieee80211_txr
 }
 
 static ieee80211_tx_result
-ieee80211_tx_h_load_stats(struct ieee80211_txrx_data *tx)
+ieee80211_tx_h_load_stats(struct ieee80211_tx_data *tx)
 {
 	struct ieee80211_local *local = tx->local;
 	struct sk_buff *skb = tx->skb;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
 	u32 load = 0, hdrtime;
-	struct ieee80211_rate *rate = tx->u.tx.rate;
+	struct ieee80211_rate *rate = tx->rate;
 
 	/* 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
@@ -776,8 +776,8 @@ ieee80211_tx_h_load_stats(struct ieee802
 	/* 1 bit at 1 Mbit/s takes 1 usec; in channel_use values,
 	 * 1 usec = 1/8 * (1080 / 10) = 13.5 */
 
-	if (tx->u.tx.channel->band == IEEE80211_BAND_5GHZ ||
-	    (tx->u.tx.channel->band == IEEE80211_BAND_2GHZ &&
+	if (tx->channel->band == IEEE80211_BAND_5GHZ ||
+	    (tx->channel->band == IEEE80211_BAND_2GHZ &&
 	     rate->flags & IEEE80211_RATE_ERP_G))
 		hdrtime = CHAN_UTIL_HDR_SHORT;
 	else
@@ -787,20 +787,20 @@ ieee80211_tx_h_load_stats(struct ieee802
 	if (!is_multicast_ether_addr(hdr->addr1))
 		load += hdrtime;
 
-	if (tx->u.tx.control->flags & IEEE80211_TXCTL_USE_RTS_CTS)
+	if (tx->control->flags & IEEE80211_TXCTL_USE_RTS_CTS)
 		load += 2 * hdrtime;
-	else if (tx->u.tx.control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)
+	else if (tx->control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)
 		load += hdrtime;
 
 	/* TODO: optimise again */
 	load += skb->len * CHAN_UTIL_RATE_LCM / rate->bitrate;
 
-	if (tx->u.tx.extra_frag) {
+	if (tx->extra_frag) {
 		int i;
-		for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
+		for (i = 0; i < tx->num_extra_frag; i++) {
 			load += 2 * hdrtime;
-			load += tx->u.tx.extra_frag[i]->len *
-				tx->u.tx.rate->bitrate;
+			load += tx->extra_frag[i]->len *
+				tx->rate->bitrate;
 		}
 	}
 
@@ -815,7 +815,7 @@ ieee80211_tx_h_load_stats(struct ieee802
 }
 
 
-typedef ieee80211_tx_result (*ieee80211_tx_handler)(struct ieee80211_txrx_data *);
+typedef ieee80211_tx_result (*ieee80211_tx_handler)(struct ieee80211_tx_data *);
 static ieee80211_tx_handler ieee80211_tx_handlers[] =
 {
 	ieee80211_tx_h_check_assoc,
@@ -838,7 +838,7 @@ static ieee80211_tx_handler ieee80211_tx
  * with Radiotap Header -- only called for monitor mode interface
  */
 static ieee80211_tx_result
-__ieee80211_parse_tx_radiotap(struct ieee80211_txrx_data *tx,
+__ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
 			      struct sk_buff *skb)
 {
 	/*
@@ -854,13 +854,13 @@ __ieee80211_parse_tx_radiotap(struct iee
 		(struct ieee80211_radiotap_header *) skb->data;
 	struct ieee80211_supported_band *sband;
 	int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len);
-	struct ieee80211_tx_control *control = tx->u.tx.control;
+	struct ieee80211_tx_control *control = tx->control;
 
 	sband = tx->local->hw.wiphy->bands[tx->local->hw.conf.channel->band];
 
 	control->flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
-	tx->flags |= IEEE80211_TXRXD_TX_INJECTED;
-	tx->flags &= ~IEEE80211_TXRXD_FRAGMENTED;
+	tx->flags |= IEEE80211_TX_INJECTED;
+	tx->flags &= ~IEEE80211_TX_FRAGMENTED;
 
 	/*
 	 * for every radiotap entry that is present
@@ -896,7 +896,7 @@ __ieee80211_parse_tx_radiotap(struct iee
 				r = &sband->bitrates[i];
 
 				if (r->bitrate == target_rate) {
-					tx->u.tx.rate = r;
+					tx->rate = r;
 					break;
 				}
 			}
@@ -934,7 +934,7 @@ __ieee80211_parse_tx_radiotap(struct iee
 				control->flags &=
 					~IEEE80211_TXCTL_DO_NOT_ENCRYPT;
 			if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FRAG)
-				tx->flags |= IEEE80211_TXRXD_FRAGMENTED;
+				tx->flags |= IEEE80211_TX_FRAGMENTED;
 			break;
 
 		/*
@@ -965,7 +965,7 @@ __ieee80211_parse_tx_radiotap(struct iee
  * initialises @tx
  */
 static ieee80211_tx_result
-__ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
+__ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
 		       struct sk_buff *skb,
 		       struct net_device *dev,
 		       struct ieee80211_tx_control *control)
@@ -981,12 +981,12 @@ __ieee80211_tx_prepare(struct ieee80211_
 	tx->dev = dev; /* use original interface */
 	tx->local = local;
 	tx->sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-	tx->u.tx.control = control;
+	tx->control = control;
 	/*
 	 * Set this flag (used below to indicate "automatic fragmentation"),
 	 * it will be cleared/left by radiotap as desired.
 	 */
-	tx->flags |= IEEE80211_TXRXD_FRAGMENTED;
+	tx->flags |= IEEE80211_TX_FRAGMENTED;
 
 	/* process and remove the injection radiotap header */
 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -1007,20 +1007,20 @@ __ieee80211_tx_prepare(struct ieee80211_
 	tx->fc = le16_to_cpu(hdr->frame_control);
 
 	if (is_multicast_ether_addr(hdr->addr1)) {
-		tx->flags &= ~IEEE80211_TXRXD_TXUNICAST;
+		tx->flags &= ~IEEE80211_TX_UNICAST;
 		control->flags |= IEEE80211_TXCTL_NO_ACK;
 	} else {
-		tx->flags |= IEEE80211_TXRXD_TXUNICAST;
+		tx->flags |= IEEE80211_TX_UNICAST;
 		control->flags &= ~IEEE80211_TXCTL_NO_ACK;
 	}
 
-	if (tx->flags & IEEE80211_TXRXD_FRAGMENTED) {
-		if ((tx->flags & IEEE80211_TXRXD_TXUNICAST) &&
+	if (tx->flags & IEEE80211_TX_FRAGMENTED) {
+		if ((tx->flags & IEEE80211_TX_UNICAST) &&
 		    skb->len + FCS_LEN > local->fragmentation_threshold &&
 		    !local->ops->set_frag_threshold)
-			tx->flags |= IEEE80211_TXRXD_FRAGMENTED;
+			tx->flags |= IEEE80211_TX_FRAGMENTED;
 		else
-			tx->flags &= ~IEEE80211_TXRXD_FRAGMENTED;
+			tx->flags &= ~IEEE80211_TX_FRAGMENTED;
 	}
 
 	if (!tx->sta)
@@ -1043,7 +1043,7 @@ __ieee80211_tx_prepare(struct ieee80211_
 /*
  * NB: @tx is uninitialised when passed in here
  */
-static int ieee80211_tx_prepare(struct ieee80211_txrx_data *tx,
+static int ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
 				struct sk_buff *skb,
 				struct net_device *mdev,
 				struct ieee80211_tx_control *control)
@@ -1066,9 +1066,9 @@ static int ieee80211_tx_prepare(struct i
 }
 
 static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
-			  struct ieee80211_txrx_data *tx)
+			  struct ieee80211_tx_data *tx)
 {
-	struct ieee80211_tx_control *control = tx->u.tx.control;
+	struct ieee80211_tx_control *control = tx->control;
 	int ret, i;
 
 	if (!ieee80211_qdisc_installed(local->mdev) &&
@@ -1085,20 +1085,20 @@ static int __ieee80211_tx(struct ieee802
 		local->mdev->trans_start = jiffies;
 		ieee80211_led_tx(local, 1);
 	}
-	if (tx->u.tx.extra_frag) {
+	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->u.tx.num_extra_frag; i++) {
-			if (!tx->u.tx.extra_frag[i])
+		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->u.tx.num_extra_frag) {
-				control->tx_rate = tx->u.tx.last_frag_rate;
+			if (i == tx->num_extra_frag) {
+				control->tx_rate = tx->last_frag_rate;
 
-				if (tx->flags & IEEE80211_TXRXD_TXPROBE_LAST_FRAG)
+				if (tx->flags & IEEE80211_TX_PROBE_LAST_FRAG)
 					control->flags |=
 						IEEE80211_TXCTL_RATE_CTRL_PROBE;
 				else
@@ -1108,18 +1108,18 @@ static int __ieee80211_tx(struct ieee802
 
 			ieee80211_dump_frame(wiphy_name(local->hw.wiphy),
 					     "TX to low-level driver",
-					     tx->u.tx.extra_frag[i]);
+					     tx->extra_frag[i]);
 			ret = local->ops->tx(local_to_hw(local),
-					    tx->u.tx.extra_frag[i],
+					    tx->extra_frag[i],
 					    control);
 			if (ret)
 				return IEEE80211_TX_FRAG_AGAIN;
 			local->mdev->trans_start = jiffies;
 			ieee80211_led_tx(local, 1);
-			tx->u.tx.extra_frag[i] = NULL;
+			tx->extra_frag[i] = NULL;
 		}
-		kfree(tx->u.tx.extra_frag);
-		tx->u.tx.extra_frag = NULL;
+		kfree(tx->extra_frag);
+		tx->extra_frag = NULL;
 	}
 	return IEEE80211_TX_OK;
 }
@@ -1130,7 +1130,7 @@ static int ieee80211_tx(struct net_devic
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct sta_info *sta;
 	ieee80211_tx_handler *handler;
-	struct ieee80211_txrx_data tx;
+	struct ieee80211_tx_data tx;
 	ieee80211_tx_result res = TX_DROP, res_prepare;
 	int ret, i;
 
@@ -1156,7 +1156,7 @@ static int ieee80211_tx(struct net_devic
 	rcu_read_lock();
 
 	sta = tx.sta;
-	tx.u.tx.channel = local->hw.conf.channel;
+	tx.channel = local->hw.conf.channel;
 
 	for (handler = ieee80211_tx_handlers; *handler != NULL;
 	     handler++) {
@@ -1181,18 +1181,18 @@ static int ieee80211_tx(struct net_devic
 		return 0;
 	}
 
-	if (tx.u.tx.extra_frag) {
-		for (i = 0; i < tx.u.tx.num_extra_frag; i++) {
+	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.u.tx.extra_frag[i]->data;
+				tx.extra_frag[i]->data;
 
-			if (i + 1 < tx.u.tx.num_extra_frag) {
-				next_len = tx.u.tx.extra_frag[i + 1]->len;
+			if (i + 1 < tx.num_extra_frag) {
+				next_len = tx.extra_frag[i + 1]->len;
 			} else {
 				next_len = 0;
-				tx.u.tx.rate = tx.u.tx.last_frag_rate;
+				tx.rate = tx.last_frag_rate;
 			}
 			dur = ieee80211_duration(&tx, 0, next_len);
 			hdr->duration_id = cpu_to_le16(dur);
@@ -1227,11 +1227,11 @@ retry:
 		memcpy(&store->control, control,
 		       sizeof(struct ieee80211_tx_control));
 		store->skb = skb;
-		store->extra_frag = tx.u.tx.extra_frag;
-		store->num_extra_frag = tx.u.tx.num_extra_frag;
-		store->last_frag_rate = tx.u.tx.last_frag_rate;
+		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_ctrl_probe =
-			!!(tx.flags & IEEE80211_TXRXD_TXPROBE_LAST_FRAG);
+			!!(tx.flags & IEEE80211_TX_PROBE_LAST_FRAG);
 	}
 	rcu_read_unlock();
 	return 0;
@@ -1239,10 +1239,10 @@ retry:
  drop:
 	if (skb)
 		dev_kfree_skb(skb);
-	for (i = 0; i < tx.u.tx.num_extra_frag; i++)
-		if (tx.u.tx.extra_frag[i])
-			dev_kfree_skb(tx.u.tx.extra_frag[i]);
-	kfree(tx.u.tx.extra_frag);
+	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;
 }
@@ -1670,7 +1670,7 @@ void ieee80211_tx_pending(unsigned long 
 	struct ieee80211_local *local = (struct ieee80211_local *)data;
 	struct net_device *dev = local->mdev;
 	struct ieee80211_tx_stored_packet *store;
-	struct ieee80211_txrx_data tx;
+	struct ieee80211_tx_data tx;
 	int i, ret, reschedule = 0;
 
 	netif_tx_lock_bh(dev);
@@ -1682,13 +1682,13 @@ void ieee80211_tx_pending(unsigned long 
 			continue;
 		}
 		store = &local->pending_packet[i];
-		tx.u.tx.control = &store->control;
-		tx.u.tx.extra_frag = store->extra_frag;
-		tx.u.tx.num_extra_frag = store->num_extra_frag;
-		tx.u.tx.last_frag_rate = store->last_frag_rate;
+		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.flags = 0;
 		if (store->last_frag_rate_ctrl_probe)
-			tx.flags |= IEEE80211_TXRXD_TXPROBE_LAST_FRAG;
+			tx.flags |= IEEE80211_TX_PROBE_LAST_FRAG;
 		ret = __ieee80211_tx(local, store->skb, &tx);
 		if (ret) {
 			if (ret == IEEE80211_TX_FRAG_AGAIN)
@@ -1943,7 +1943,7 @@ ieee80211_get_buffered_bc(struct ieee802
 	struct sk_buff *skb;
 	struct sta_info *sta;
 	ieee80211_tx_handler *handler;
-	struct ieee80211_txrx_data tx;
+	struct ieee80211_tx_data tx;
 	ieee80211_tx_result res = TX_DROP;
 	struct net_device *bdev;
 	struct ieee80211_sub_if_data *sdata;
@@ -1991,8 +1991,8 @@ ieee80211_get_buffered_bc(struct ieee802
 		dev_kfree_skb_any(skb);
 	}
 	sta = tx.sta;
-	tx.flags |= IEEE80211_TXRXD_TXPS_BUFFERED;
-	tx.u.tx.channel = local->hw.conf.channel;
+	tx.flags |= IEEE80211_TX_PS_BUFFERED;
+	tx.channel = local->hw.conf.channel;
 
 	for (handler = ieee80211_tx_handlers; *handler != NULL; handler++) {
 		res = (*handler)(&tx);
--- everything.orig/net/mac80211/wep.c	2008-02-24 10:09:08.000000000 +0100
+++ everything/net/mac80211/wep.c	2008-02-24 10:19:18.000000000 +0100
@@ -306,14 +306,14 @@ u8 * ieee80211_wep_is_weak_iv(struct sk_
 }
 
 ieee80211_rx_result
-ieee80211_crypto_wep_decrypt(struct ieee80211_txrx_data *rx)
+ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx)
 {
 	if ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA &&
 	    ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
 	     (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH))
 		return RX_CONTINUE;
 
-	if (!(rx->u.rx.status->flag & RX_FLAG_DECRYPTED)) {
+	if (!(rx->status->flag & RX_FLAG_DECRYPTED)) {
 		if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key)) {
 #ifdef CONFIG_MAC80211_DEBUG
 			if (net_ratelimit())
@@ -322,7 +322,7 @@ ieee80211_crypto_wep_decrypt(struct ieee
 #endif /* CONFIG_MAC80211_DEBUG */
 			return RX_DROP_UNUSABLE;
 		}
-	} else if (!(rx->u.rx.status->flag & RX_FLAG_IV_STRIPPED)) {
+	} else if (!(rx->status->flag & RX_FLAG_IV_STRIPPED)) {
 		ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key);
 		/* remove ICV */
 		skb_trim(rx->skb, rx->skb->len - 4);
@@ -331,13 +331,13 @@ ieee80211_crypto_wep_decrypt(struct ieee
 	return RX_CONTINUE;
 }
 
-static int wep_encrypt_skb(struct ieee80211_txrx_data *tx, struct sk_buff *skb)
+static int wep_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
 {
 	if (!(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) {
 		if (ieee80211_wep_encrypt(tx->local, skb, tx->key))
 			return -1;
 	} else {
-		tx->u.tx.control->key_idx = tx->key->conf.hw_key_idx;
+		tx->control->key_idx = tx->key->conf.hw_key_idx;
 		if (tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) {
 			if (!ieee80211_wep_add_iv(tx->local, skb, tx->key))
 				return -1;
@@ -347,21 +347,21 @@ static int wep_encrypt_skb(struct ieee80
 }
 
 ieee80211_tx_result
-ieee80211_crypto_wep_encrypt(struct ieee80211_txrx_data *tx)
+ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx)
 {
-	tx->u.tx.control->iv_len = WEP_IV_LEN;
-	tx->u.tx.control->icv_len = WEP_ICV_LEN;
-	ieee80211_tx_set_iswep(tx);
+	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;
 	}
 
-	if (tx->u.tx.extra_frag) {
+	if (tx->extra_frag) {
 		int i;
-		for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
-			if (wep_encrypt_skb(tx, tx->u.tx.extra_frag[i]) < 0) {
+		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;
--- everything.orig/net/mac80211/wep.h	2008-02-24 10:09:07.000000000 +0100
+++ everything/net/mac80211/wep.h	2008-02-24 10:19:18.000000000 +0100
@@ -29,8 +29,8 @@ int ieee80211_wep_decrypt(struct ieee802
 u8 * ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key);
 
 ieee80211_rx_result
-ieee80211_crypto_wep_decrypt(struct ieee80211_txrx_data *rx);
+ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx);
 ieee80211_tx_result
-ieee80211_crypto_wep_encrypt(struct ieee80211_txrx_data *tx);
+ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx);
 
 #endif /* WEP_H */
--- everything.orig/net/mac80211/wpa.c	2008-02-24 10:09:08.000000000 +0100
+++ everything/net/mac80211/wpa.c	2008-02-24 10:19:18.000000000 +0100
@@ -71,7 +71,7 @@ static int ieee80211_get_hdr_info(const 
 
 
 ieee80211_tx_result
-ieee80211_tx_h_michael_mic_add(struct ieee80211_txrx_data *tx)
+ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx)
 {
 	u8 *data, *sa, *da, *key, *mic, qos_tid;
 	size_t data_len;
@@ -90,7 +90,7 @@ ieee80211_tx_h_michael_mic_add(struct ie
 		return TX_DROP;
 
 	if ((tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) &&
-	    !(tx->flags & IEEE80211_TXRXD_FRAGMENTED) &&
+	    !(tx->flags & IEEE80211_TX_FRAGMENTED) &&
 	    !(tx->key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) &&
 	    !wpa_test) {
 		/* hwaccel - with no need for preallocated room for Michael MIC
@@ -124,7 +124,7 @@ ieee80211_tx_h_michael_mic_add(struct ie
 
 
 ieee80211_rx_result
-ieee80211_rx_h_michael_mic_verify(struct ieee80211_txrx_data *rx)
+ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)
 {
 	u8 *data, *sa, *da, *key = NULL, qos_tid;
 	size_t data_len;
@@ -139,7 +139,7 @@ ieee80211_rx_h_michael_mic_verify(struct
 	/*
 	 * No way to verify the MIC if the hardware stripped it
 	 */
-	if (rx->u.rx.status->flag & RX_FLAG_MMIC_STRIPPED)
+	if (rx->status->flag & RX_FLAG_MMIC_STRIPPED)
 		return RX_CONTINUE;
 
 	if (!rx->key || rx->key->conf.alg != ALG_TKIP ||
@@ -161,7 +161,7 @@ ieee80211_rx_h_michael_mic_verify(struct
 				 ALG_TKIP_TEMP_AUTH_TX_MIC_KEY];
 	michael_mic(key, da, sa, qos_tid & 0x0f, data, data_len, mic);
 	if (memcmp(mic, data + data_len, MICHAEL_MIC_LEN) != 0 || wpa_test) {
-		if (!(rx->flags & IEEE80211_TXRXD_RXRA_MATCH))
+		if (!(rx->flags & IEEE80211_RX_RA_MATCH))
 			return RX_DROP_UNUSABLE;
 
 		printk(KERN_DEBUG "%s: invalid Michael MIC in data frame from "
@@ -176,14 +176,14 @@ ieee80211_rx_h_michael_mic_verify(struct
 	skb_trim(skb, skb->len - MICHAEL_MIC_LEN);
 
 	/* update IV in key information to be able to detect replays */
-	rx->key->u.tkip.iv32_rx[rx->u.rx.queue] = rx->u.rx.tkip_iv32;
-	rx->key->u.tkip.iv16_rx[rx->u.rx.queue] = rx->u.rx.tkip_iv16;
+	rx->key->u.tkip.iv32_rx[rx->queue] = rx->tkip_iv32;
+	rx->key->u.tkip.iv16_rx[rx->queue] = rx->tkip_iv16;
 
 	return RX_CONTINUE;
 }
 
 
-static int tkip_encrypt_skb(struct ieee80211_txrx_data *tx,
+static int tkip_encrypt_skb(struct ieee80211_tx_data *tx,
 			    struct sk_buff *skb, int test)
 {
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
@@ -228,7 +228,7 @@ static int tkip_encrypt_skb(struct ieee8
 					    0x7f),
 				      (u8) key->u.tkip.iv16);
 
-		tx->u.tx.control->key_idx = tx->key->conf.hw_key_idx;
+		tx->control->key_idx = tx->key->conf.hw_key_idx;
 		return 0;
 	}
 
@@ -243,30 +243,30 @@ static int tkip_encrypt_skb(struct ieee8
 
 
 ieee80211_tx_result
-ieee80211_crypto_tkip_encrypt(struct ieee80211_txrx_data *tx)
+ieee80211_crypto_tkip_encrypt(struct ieee80211_tx_data *tx)
 {
 	struct sk_buff *skb = tx->skb;
 	int wpa_test = 0, test = 0;
 
-	tx->u.tx.control->icv_len = TKIP_ICV_LEN;
-	tx->u.tx.control->iv_len = TKIP_IV_LEN;
-	ieee80211_tx_set_iswep(tx);
+	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) &&
 	    !wpa_test) {
 		/* hwaccel - with no need for preallocated room for IV/ICV */
-		tx->u.tx.control->key_idx = tx->key->conf.hw_key_idx;
+		tx->control->key_idx = tx->key->conf.hw_key_idx;
 		return TX_CONTINUE;
 	}
 
 	if (tkip_encrypt_skb(tx, skb, test) < 0)
 		return TX_DROP;
 
-	if (tx->u.tx.extra_frag) {
+	if (tx->extra_frag) {
 		int i;
-		for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
-			if (tkip_encrypt_skb(tx, tx->u.tx.extra_frag[i], test)
+		for (i = 0; i < tx->num_extra_frag; i++) {
+			if (tkip_encrypt_skb(tx, tx->extra_frag[i], test)
 			    < 0)
 				return TX_DROP;
 		}
@@ -277,7 +277,7 @@ ieee80211_crypto_tkip_encrypt(struct iee
 
 
 ieee80211_rx_result
-ieee80211_crypto_tkip_decrypt(struct ieee80211_txrx_data *rx)
+ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
 {
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
 	u16 fc;
@@ -295,8 +295,8 @@ ieee80211_crypto_tkip_decrypt(struct iee
 	if (!rx->sta || skb->len - hdrlen < 12)
 		return RX_DROP_UNUSABLE;
 
-	if (rx->u.rx.status->flag & RX_FLAG_DECRYPTED) {
-		if (rx->u.rx.status->flag & RX_FLAG_IV_STRIPPED) {
+	if (rx->status->flag & RX_FLAG_DECRYPTED) {
+		if (rx->status->flag & RX_FLAG_IV_STRIPPED) {
 			/*
 			 * Hardware took care of all processing, including
 			 * replay protection, and stripped the ICV/IV so
@@ -312,9 +312,9 @@ ieee80211_crypto_tkip_decrypt(struct iee
 	res = ieee80211_tkip_decrypt_data(rx->local->wep_rx_tfm,
 					  key, skb->data + hdrlen,
 					  skb->len - hdrlen, rx->sta->addr,
-					  hwaccel, rx->u.rx.queue,
-					  &rx->u.rx.tkip_iv32,
-					  &rx->u.rx.tkip_iv16);
+					  hwaccel, rx->queue,
+					  &rx->tkip_iv32,
+					  &rx->tkip_iv16);
 	if (res != TKIP_DECRYPT_OK || wpa_test) {
 #ifdef CONFIG_MAC80211_DEBUG
 		if (net_ratelimit())
@@ -429,7 +429,7 @@ static inline int ccmp_hdr2pn(u8 *pn, u8
 }
 
 
-static int ccmp_encrypt_skb(struct ieee80211_txrx_data *tx,
+static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx,
 			    struct sk_buff *skb, int test)
 {
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
@@ -478,7 +478,7 @@ static int ccmp_encrypt_skb(struct ieee8
 
 	if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) {
 		/* hwaccel - with preallocated room for CCMP header */
-		tx->u.tx.control->key_idx = key->conf.hw_key_idx;
+		tx->control->key_idx = key->conf.hw_key_idx;
 		return 0;
 	}
 
@@ -492,30 +492,30 @@ static int ccmp_encrypt_skb(struct ieee8
 
 
 ieee80211_tx_result
-ieee80211_crypto_ccmp_encrypt(struct ieee80211_txrx_data *tx)
+ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx)
 {
 	struct sk_buff *skb = tx->skb;
 	int test = 0;
 
-	tx->u.tx.control->icv_len = CCMP_MIC_LEN;
-	tx->u.tx.control->iv_len = CCMP_HDR_LEN;
-	ieee80211_tx_set_iswep(tx);
+	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)) {
 		/* hwaccel - with no need for preallocated room for CCMP "
 		 * header or MIC fields */
-		tx->u.tx.control->key_idx = tx->key->conf.hw_key_idx;
+		tx->control->key_idx = tx->key->conf.hw_key_idx;
 		return TX_CONTINUE;
 	}
 
 	if (ccmp_encrypt_skb(tx, skb, test) < 0)
 		return TX_DROP;
 
-	if (tx->u.tx.extra_frag) {
+	if (tx->extra_frag) {
 		int i;
-		for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
-			if (ccmp_encrypt_skb(tx, tx->u.tx.extra_frag[i], test)
+		for (i = 0; i < tx->num_extra_frag; i++) {
+			if (ccmp_encrypt_skb(tx, tx->extra_frag[i], test)
 			    < 0)
 				return TX_DROP;
 		}
@@ -526,7 +526,7 @@ ieee80211_crypto_ccmp_encrypt(struct iee
 
 
 ieee80211_rx_result
-ieee80211_crypto_ccmp_decrypt(struct ieee80211_txrx_data *rx)
+ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
 {
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data;
 	u16 fc;
@@ -547,15 +547,15 @@ ieee80211_crypto_ccmp_decrypt(struct iee
 	if (!rx->sta || data_len < 0)
 		return RX_DROP_UNUSABLE;
 
-	if ((rx->u.rx.status->flag & RX_FLAG_DECRYPTED) &&
-	    (rx->u.rx.status->flag & RX_FLAG_IV_STRIPPED))
+	if ((rx->status->flag & RX_FLAG_DECRYPTED) &&
+	    (rx->status->flag & RX_FLAG_IV_STRIPPED))
 		return RX_CONTINUE;
 
 	(void) ccmp_hdr2pn(pn, skb->data + hdrlen);
 
-	if (memcmp(pn, key->u.ccmp.rx_pn[rx->u.rx.queue], CCMP_PN_LEN) <= 0) {
+	if (memcmp(pn, key->u.ccmp.rx_pn[rx->queue], CCMP_PN_LEN) <= 0) {
 #ifdef CONFIG_MAC80211_DEBUG
-		u8 *ppn = key->u.ccmp.rx_pn[rx->u.rx.queue];
+		u8 *ppn = key->u.ccmp.rx_pn[rx->queue];
 
 		printk(KERN_DEBUG "%s: CCMP replay detected for RX frame from "
 		       "%s (RX PN %02x%02x%02x%02x%02x%02x <= prev. PN "
@@ -568,7 +568,7 @@ ieee80211_crypto_ccmp_decrypt(struct iee
 		return RX_DROP_UNUSABLE;
 	}
 
-	if (!(rx->u.rx.status->flag & RX_FLAG_DECRYPTED)) {
+	if (!(rx->status->flag & RX_FLAG_DECRYPTED)) {
 		/* hardware didn't decrypt/verify MIC */
 		u8 *scratch, *b_0, *aad;
 
@@ -593,7 +593,7 @@ ieee80211_crypto_ccmp_decrypt(struct iee
 		}
 	}
 
-	memcpy(key->u.ccmp.rx_pn[rx->u.rx.queue], pn, CCMP_PN_LEN);
+	memcpy(key->u.ccmp.rx_pn[rx->queue], pn, CCMP_PN_LEN);
 
 	/* Remove CCMP header and MIC */
 	skb_trim(skb, skb->len - CCMP_MIC_LEN);
--- everything.orig/net/mac80211/wpa.h	2008-02-24 10:09:07.000000000 +0100
+++ everything/net/mac80211/wpa.h	2008-02-24 10:19:18.000000000 +0100
@@ -14,18 +14,18 @@
 #include "ieee80211_i.h"
 
 ieee80211_tx_result
-ieee80211_tx_h_michael_mic_add(struct ieee80211_txrx_data *tx);
+ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx);
 ieee80211_rx_result
-ieee80211_rx_h_michael_mic_verify(struct ieee80211_txrx_data *rx);
+ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx);
 
 ieee80211_tx_result
-ieee80211_crypto_tkip_encrypt(struct ieee80211_txrx_data *tx);
+ieee80211_crypto_tkip_encrypt(struct ieee80211_tx_data *tx);
 ieee80211_rx_result
-ieee80211_crypto_tkip_decrypt(struct ieee80211_txrx_data *rx);
+ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx);
 
 ieee80211_tx_result
-ieee80211_crypto_ccmp_encrypt(struct ieee80211_txrx_data *tx);
+ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx);
 ieee80211_rx_result
-ieee80211_crypto_ccmp_decrypt(struct ieee80211_txrx_data *rx);
+ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx);
 
 #endif /* WPA_H */
--- everything.orig/net/mac80211/util.c	2008-02-24 10:09:07.000000000 +0100
+++ everything/net/mac80211/util.c	2008-02-24 10:22:41.000000000 +0100
@@ -165,17 +165,17 @@ int ieee80211_get_mesh_hdrlen(struct iee
 	}
 }
 
-void ieee80211_tx_set_iswep(struct ieee80211_txrx_data *tx)
+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->u.tx.extra_frag) {
+	if (tx->extra_frag) {
 		struct ieee80211_hdr *fhdr;
 		int i;
-		for (i = 0; i < tx->u.tx.num_extra_frag; i++) {
+		for (i = 0; i < tx->num_extra_frag; i++) {
 			fhdr = (struct ieee80211_hdr *)
-				tx->u.tx.extra_frag[i]->data;
+				tx->extra_frag[i]->data;
 			fhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
 		}
 	}

-- 


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

* [PATCH 04/10] mac80211: remove STA infos last_ack stuff
  2008-02-25 15:27 [PATCH 00/10] mac80211 locking/sta info work Johannes Berg
                   ` (2 preceding siblings ...)
  2008-02-25 15:27 ` [PATCH 03/10] mac80211: split ieee80211_txrx_data Johannes Berg
@ 2008-02-25 15:27 ` Johannes Berg
  2008-02-25 15:27 ` [PATCH 05/10] mac80211: split ieee80211_key_alloc/free Johannes Berg
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: Johannes Berg @ 2008-02-25 15:27 UTC (permalink / raw)
  To: John Linville; +Cc: linux-wireless, Luis Carlos Cobo

These things aren't used and the only possible use is within
rate control algorithms, however those can, if they need it,
keep track of it in their private data. last_ack_ms isn't
even updated so completely useless.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
 net/mac80211/debugfs_sta.c      |   29 -----------------------------
 net/mac80211/rc80211_pid_algo.c |    3 ---
 net/mac80211/rc80211_simple.c   |    3 ---
 net/mac80211/sta_info.h         |    4 ----
 4 files changed, 39 deletions(-)

--- everything.orig/net/mac80211/sta_info.h	2008-02-24 10:15:38.000000000 +0100
+++ everything/net/mac80211/sta_info.h	2008-02-24 10:22:49.000000000 +0100
@@ -189,8 +189,6 @@ struct sta_info {
 	int last_rssi; /* RSSI of last received frame from this STA */
 	int last_signal; /* signal of last received frame from this STA */
 	int last_noise; /* noise of last received frame from this STA */
-	int last_ack_rssi[3]; /* RSSI of last received ACKs from this STA */
-	unsigned long last_ack;
 	int channel_use;
 	int channel_use_raw;
 
@@ -226,8 +224,6 @@ struct sta_info {
 		struct dentry *dir;
 		struct dentry *flags;
 		struct dentry *num_ps_buf_frames;
-		struct dentry *last_ack_rssi;
-		struct dentry *last_ack_ms;
 		struct dentry *inactive_ms;
 		struct dentry *last_seq_ctrl;
 #ifdef CONFIG_MAC80211_DEBUG_COUNTERS
--- everything.orig/net/mac80211/debugfs_sta.c	2008-02-23 14:45:46.000000000 +0100
+++ everything/net/mac80211/debugfs_sta.c	2008-02-24 10:22:49.000000000 +0100
@@ -98,31 +98,6 @@ static ssize_t sta_num_ps_buf_frames_rea
 }
 STA_OPS(num_ps_buf_frames);
 
-static ssize_t sta_last_ack_rssi_read(struct file *file, char __user *userbuf,
-				      size_t count, loff_t *ppos)
-{
-	char buf[100];
-	struct sta_info *sta = file->private_data;
-	int res = scnprintf(buf, sizeof(buf), "%d %d %d\n",
-			    sta->last_ack_rssi[0],
-			    sta->last_ack_rssi[1],
-			    sta->last_ack_rssi[2]);
-	return simple_read_from_buffer(userbuf, count, ppos, buf, res);
-}
-STA_OPS(last_ack_rssi);
-
-static ssize_t sta_last_ack_ms_read(struct file *file, char __user *userbuf,
-				    size_t count, loff_t *ppos)
-{
-	char buf[20];
-	struct sta_info *sta = file->private_data;
-	int res = scnprintf(buf, sizeof(buf), "%d\n",
-			    sta->last_ack ?
-			    jiffies_to_msecs(jiffies - sta->last_ack) : -1);
-	return simple_read_from_buffer(userbuf, count, ppos, buf, res);
-}
-STA_OPS(last_ack_ms);
-
 static ssize_t sta_inactive_ms_read(struct file *file, char __user *userbuf,
 				    size_t count, loff_t *ppos)
 {
@@ -311,8 +286,6 @@ void ieee80211_sta_debugfs_add(struct st
 
 	DEBUGFS_ADD(flags);
 	DEBUGFS_ADD(num_ps_buf_frames);
-	DEBUGFS_ADD(last_ack_rssi);
-	DEBUGFS_ADD(last_ack_ms);
 	DEBUGFS_ADD(inactive_ms);
 	DEBUGFS_ADD(last_seq_ctrl);
 #ifdef CONFIG_MAC80211_DEBUG_COUNTERS
@@ -326,8 +299,6 @@ void ieee80211_sta_debugfs_remove(struct
 {
 	DEBUGFS_DEL(flags);
 	DEBUGFS_DEL(num_ps_buf_frames);
-	DEBUGFS_DEL(last_ack_rssi);
-	DEBUGFS_DEL(last_ack_ms);
 	DEBUGFS_DEL(inactive_ms);
 	DEBUGFS_DEL(last_seq_ctrl);
 #ifdef CONFIG_MAC80211_DEBUG_COUNTERS
--- everything.orig/net/mac80211/rc80211_pid_algo.c	2008-02-23 14:59:28.000000000 +0100
+++ everything/net/mac80211/rc80211_pid_algo.c	2008-02-24 10:22:49.000000000 +0100
@@ -290,9 +290,6 @@ static void rate_control_pid_tx_status(v
 		sta->tx_num_consecutive_failures++;
 		sta->tx_num_mpdu_fail++;
 	} else {
-		sta->last_ack_rssi[0] = sta->last_ack_rssi[1];
-		sta->last_ack_rssi[1] = sta->last_ack_rssi[2];
-		sta->last_ack_rssi[2] = status->ack_signal;
 		sta->tx_num_consecutive_failures = 0;
 		sta->tx_num_mpdu_ok++;
 	}
--- everything.orig/net/mac80211/rc80211_simple.c	2008-02-23 13:03:38.000000000 +0100
+++ everything/net/mac80211/rc80211_simple.c	2008-02-24 10:22:49.000000000 +0100
@@ -131,9 +131,6 @@ static void rate_control_simple_tx_statu
 		sta->tx_num_consecutive_failures++;
 		sta->tx_num_mpdu_fail++;
 	} else {
-		sta->last_ack_rssi[0] = sta->last_ack_rssi[1];
-		sta->last_ack_rssi[1] = sta->last_ack_rssi[2];
-		sta->last_ack_rssi[2] = status->ack_signal;
 		sta->tx_num_consecutive_failures = 0;
 		sta->tx_num_mpdu_ok++;
 	}

-- 


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

* [PATCH 05/10] mac80211: split ieee80211_key_alloc/free
  2008-02-25 15:27 [PATCH 00/10] mac80211 locking/sta info work Johannes Berg
                   ` (3 preceding siblings ...)
  2008-02-25 15:27 ` [PATCH 04/10] mac80211: remove STA infos last_ack stuff Johannes Berg
@ 2008-02-25 15:27 ` Johannes Berg
  2008-02-25 15:27 ` [PATCH 06/10] mac80211: RCU-ify STA info structure access Johannes Berg
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: Johannes Berg @ 2008-02-25 15:27 UTC (permalink / raw)
  To: John Linville; +Cc: linux-wireless, Luis Carlos Cobo

In order to RCU-ify sta_info, we need to be able to allocate
a key without linking it to an sdata/sta structure (because
allocation cannot be done in an rcu critical section). This
patch splits up ieee80211_key_alloc() and updates all users
appropriately.

While at it, this patch fixes a number of race conditions
such as finally making key replacement atomic, unfortunately
at the expense of more complex code.

Note that this patch documents /existing/ bugs with sta info
and key interaction, there is currently a race condition
when a sta info is freed without holding the RTNL. This will
finally be fixed by a followup patch.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
 net/mac80211/cfg.c             |   27 ++++---
 net/mac80211/ieee80211_ioctl.c |   90 ++++++++++++-----------
 net/mac80211/ieee80211_key.h   |   26 +++++-
 net/mac80211/key.c             |  156 +++++++++++++++++++++++++++++------------
 net/mac80211/sta_info.c        |    2 
 5 files changed, 203 insertions(+), 98 deletions(-)

--- everything.orig/net/mac80211/ieee80211_ioctl.c	2008-02-23 15:13:17.000000000 +0100
+++ everything/net/mac80211/ieee80211_ioctl.c	2008-02-24 10:27:18.000000000 +0100
@@ -33,8 +33,8 @@ static int ieee80211_set_encryption(stru
 				    size_t key_len)
 {
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	int ret = 0;
-	struct sta_info *sta;
+	int ret;
+	struct sta_info *sta = NULL;
 	struct ieee80211_key *key;
 	struct ieee80211_sub_if_data *sdata;
 
@@ -46,58 +46,64 @@ static int ieee80211_set_encryption(stru
 		return -EINVAL;
 	}
 
-	if (is_broadcast_ether_addr(sta_addr)) {
-		sta = NULL;
-		key = sdata->keys[idx];
-	} else {
-		set_tx_key = 0;
-		/*
-		 * According to the standard, the key index of a pairwise
-		 * key must be zero. However, some AP are broken when it
-		 * comes to WEP key indices, so we work around this.
-		 */
-		if (idx != 0 && alg != ALG_WEP) {
-			printk(KERN_DEBUG "%s: set_encrypt - non-zero idx for "
-			       "individual key\n", dev->name);
-			return -EINVAL;
+	if (remove) {
+		if (is_broadcast_ether_addr(sta_addr)) {
+			key = sdata->keys[idx];
+		} else {
+			sta = sta_info_get(local, sta_addr);
+			if (!sta) {
+				ret = -ENOENT;
+				key = NULL;
+				goto err_out;
+			}
+
+			key = sta->key;
 		}
 
-		sta = sta_info_get(local, sta_addr);
-		if (!sta) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-			DECLARE_MAC_BUF(mac);
-			printk(KERN_DEBUG "%s: set_encrypt - unknown addr "
-			       "%s\n",
-			       dev->name, print_mac(mac, sta_addr));
-#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
+		if (!key)
+			ret = -ENOENT;
+		else
+			ret = 0;
+	} else {
+		key = ieee80211_key_alloc(alg, idx, key_len, _key);
+		if (!key)
+			return -ENOMEM;
+
+		if (!is_broadcast_ether_addr(sta_addr)) {
+			set_tx_key = 0;
+			/*
+			 * According to the standard, the key index of a
+			 * pairwise key must be zero. However, some AP are
+			 * broken when it comes to WEP key indices, so we
+			 * work around this.
+			 */
+			if (idx != 0 && alg != ALG_WEP) {
+				ret = -EINVAL;
+				goto err_out;
+			}
 
-			return -ENOENT;
+			sta = sta_info_get(local, sta_addr);
+			if (!sta) {
+				ret = -ENOENT;
+				goto err_out;
+			}
 		}
 
-		key = sta->key;
-	}
+		ieee80211_key_link(key, sdata, sta);
 
-	if (remove) {
-		ieee80211_key_free(key);
+		if (set_tx_key || (!sta && !sdata->default_key && key))
+			ieee80211_set_default_key(sdata, idx);
+
+		/* don't free key later */
 		key = NULL;
-	} else {
-		/*
-		 * Automatically frees any old key if present.
-		 */
-		key = ieee80211_key_alloc(sdata, sta, alg, idx, key_len, _key);
-		if (!key) {
-			ret = -ENOMEM;
-			goto err_out;
-		}
-	}
 
-	if (set_tx_key || (!sta && !sdata->default_key && key))
-		ieee80211_set_default_key(sdata, idx);
+		ret = 0;
+	}
 
-	ret = 0;
  err_out:
 	if (sta)
 		sta_info_put(sta);
+	ieee80211_key_free(key);
 	return ret;
 }
 
--- everything.orig/net/mac80211/ieee80211_key.h	2008-02-23 11:37:09.000000000 +0100
+++ everything/net/mac80211/ieee80211_key.h	2008-02-24 10:27:18.000000000 +0100
@@ -13,6 +13,7 @@
 #include <linux/types.h>
 #include <linux/list.h>
 #include <linux/crypto.h>
+#include <linux/rcupdate.h>
 #include <net/mac80211.h>
 
 /* ALG_TKIP
@@ -45,7 +46,19 @@ struct ieee80211_local;
 struct ieee80211_sub_if_data;
 struct sta_info;
 
-#define KEY_FLAG_UPLOADED_TO_HARDWARE	(1<<0)
+/**
+ * enum ieee80211_internal_key_flags - internal key flags
+ *
+ * @KEY_FLAG_UPLOADED_TO_HARDWARE: Indicates that this key is present
+ *	in the hardware for TX crypto hardware acceleration.
+ * @KEY_FLAG_REMOVE_FROM_HARDWARE: Indicates to the key code that this
+ *	key is present in the hardware (but it cannot be used for
+ *	hardware acceleration any more!)
+ */
+enum ieee80211_internal_key_flags {
+	KEY_FLAG_UPLOADED_TO_HARDWARE	= BIT(0),
+	KEY_FLAG_REMOVE_FROM_HARDWARE	= BIT(1),
+};
 
 struct ieee80211_key {
 	struct ieee80211_local *local;
@@ -112,12 +125,17 @@ struct ieee80211_key {
 	struct ieee80211_key_conf conf;
 };
 
-struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
-					  struct sta_info *sta,
-					  enum ieee80211_key_alg alg,
+struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg,
 					  int idx,
 					  size_t key_len,
 					  const u8 *key_data);
+/*
+ * Insert a key into data structures (sdata, sta if necessary)
+ * to make it used, free old key.
+ */
+void ieee80211_key_link(struct ieee80211_key *key,
+			struct ieee80211_sub_if_data *sdata,
+			struct sta_info *sta);
 void ieee80211_key_free(struct ieee80211_key *key);
 void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx);
 void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata);
--- everything.orig/net/mac80211/key.c	2008-02-23 13:03:38.000000000 +0100
+++ everything/net/mac80211/key.c	2008-02-24 10:27:18.000000000 +0100
@@ -13,6 +13,7 @@
 #include <linux/etherdevice.h>
 #include <linux/list.h>
 #include <linux/rcupdate.h>
+#include <linux/rtnetlink.h>
 #include <net/mac80211.h>
 #include "ieee80211_i.h"
 #include "debugfs_key.h"
@@ -34,6 +35,10 @@
  *
  * All operations here are called under RTNL so no extra locking is
  * required.
+ *
+ * NOTE: This code requires that sta info *destruction* is done under
+ *	 RTNL, otherwise it can try to access already freed STA structs
+ *	 when a STA key is being freed.
  */
 
 static const u8 bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
@@ -84,16 +89,25 @@ static void ieee80211_key_enable_hw_acce
 		       key->conf.keyidx, print_mac(mac, addr), ret);
 }
 
+static void ieee80211_key_mark_hw_accel_off(struct ieee80211_key *key)
+{
+	if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) {
+		key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
+		key->flags |= KEY_FLAG_REMOVE_FROM_HARDWARE;
+	}
+}
+
 static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
 {
 	const u8 *addr;
 	int ret;
 	DECLARE_MAC_BUF(mac);
 
-	if (!key->local->ops->set_key)
+	if (!key || !key->local->ops->set_key)
 		return;
 
-	if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
+	if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) &&
+	    !(key->flags & KEY_FLAG_REMOVE_FROM_HARDWARE))
 		return;
 
 	addr = get_mac_for_key(key);
@@ -108,12 +122,11 @@ static void ieee80211_key_disable_hw_acc
 		       wiphy_name(key->local->hw.wiphy),
 		       key->conf.keyidx, print_mac(mac, addr), ret);
 
-	key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
+	key->flags &= ~(KEY_FLAG_UPLOADED_TO_HARDWARE |
+			KEY_FLAG_REMOVE_FROM_HARDWARE);
 }
 
-struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
-					  struct sta_info *sta,
-					  enum ieee80211_key_alg alg,
+struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg,
 					  int idx,
 					  size_t key_len,
 					  const u8 *key_data)
@@ -138,10 +151,6 @@ struct ieee80211_key *ieee80211_key_allo
 	key->conf.keylen = key_len;
 	memcpy(key->conf.key, key_data, key_len);
 
-	key->local = sdata->local;
-	key->sdata = sdata;
-	key->sta = sta;
-
 	if (alg == ALG_CCMP) {
 		/*
 		 * Initialize AES key state here as an optimization so that
@@ -154,13 +163,62 @@ struct ieee80211_key *ieee80211_key_allo
 		}
 	}
 
-	ieee80211_debugfs_key_add(key->local, key);
+	return key;
+}
 
-	/* remove key first */
-	if (sta)
-		ieee80211_key_free(sta->key);
-	else
-		ieee80211_key_free(sdata->keys[idx]);
+static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
+				    struct sta_info *sta,
+				    struct ieee80211_key *key,
+				    struct ieee80211_key *new)
+{
+	int idx, defkey;
+
+	if (sta) {
+		rcu_assign_pointer(sta->key, new);
+	} else {
+		WARN_ON(new && key && new->conf.keyidx != key->conf.keyidx);
+
+		if (key)
+			idx = key->conf.keyidx;
+		else
+			idx = new->conf.keyidx;
+
+		defkey = key && sdata->default_key == key;
+
+		if (defkey && !new)
+			ieee80211_set_default_key(sdata, -1);
+
+		rcu_assign_pointer(sdata->keys[idx], new);
+
+		if (defkey && new)
+			ieee80211_set_default_key(sdata, new->conf.keyidx);
+	}
+
+	if (key) {
+		ieee80211_key_mark_hw_accel_off(key);
+		list_del(&key->list);
+	}
+}
+
+void ieee80211_key_link(struct ieee80211_key *key,
+			struct ieee80211_sub_if_data *sdata,
+			struct sta_info *sta)
+{
+	struct ieee80211_key *old_key;
+	int idx;
+
+	ASSERT_RTNL();
+	might_sleep();
+
+	BUG_ON(!sdata);
+	BUG_ON(!key);
+
+	idx = key->conf.keyidx;
+	key->local = sdata->local;
+	key->sdata = sdata;
+	key->sta = sta;
+
+	ieee80211_debugfs_key_add(key->local, key);
 
 	if (sta) {
 		ieee80211_debugfs_key_sta_link(key, sta);
@@ -186,50 +244,53 @@ struct ieee80211_key *ieee80211_key_allo
 		}
 	}
 
-	/* enable hwaccel if appropriate */
-	if (netif_running(key->sdata->dev))
-		ieee80211_key_enable_hw_accel(key);
-
 	if (sta)
-		rcu_assign_pointer(sta->key, key);
+		old_key = sta->key;
 	else
-		rcu_assign_pointer(sdata->keys[idx], key);
+		old_key = sdata->keys[idx];
+
+	__ieee80211_key_replace(sdata, sta, old_key, key);
 
 	list_add(&key->list, &sdata->key_list);
 
-	return key;
+	synchronize_rcu();
+
+	ieee80211_key_free(old_key);
+	ieee80211_key_enable_hw_accel(key);
 }
 
 void ieee80211_key_free(struct ieee80211_key *key)
 {
+	ASSERT_RTNL();
+	might_sleep();
+
 	if (!key)
 		return;
 
-	if (key->sta) {
-		rcu_assign_pointer(key->sta->key, NULL);
-	} else {
-		if (key->sdata->default_key == key)
-			ieee80211_set_default_key(key->sdata, -1);
-		if (key->conf.keyidx >= 0 &&
-		    key->conf.keyidx < NUM_DEFAULT_KEYS)
-			rcu_assign_pointer(key->sdata->keys[key->conf.keyidx],
-					   NULL);
-		else
-			WARN_ON(1);
-	}
+	if (key->sdata) {
+		/*
+		 * Replace key with nothingness.
+		 *
+		 * Because other code may have key reference (RCU protected)
+		 * right now, we then wait for a grace period before freeing
+		 * it.
+		 */
+		__ieee80211_key_replace(key->sdata, key->sta, key, NULL);
 
-	/* wait for all key users to complete */
-	synchronize_rcu();
+		synchronize_rcu();
 
-	/* remove from hwaccel if appropriate */
-	ieee80211_key_disable_hw_accel(key);
+		/*
+		 * Remove from hwaccel if appropriate, this will
+		 * only happen when the key is actually unlinked,
+		 * it will already be done when the key was replaced.
+		 */
+		ieee80211_key_disable_hw_accel(key);
+	}
 
 	if (key->conf.alg == ALG_CCMP)
 		ieee80211_aes_key_free(key->u.ccmp.tfm);
 	ieee80211_debugfs_key_remove(key);
 
-	list_del(&key->list);
-
 	kfree(key);
 }
 
@@ -253,6 +314,10 @@ void ieee80211_set_default_key(struct ie
 void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata)
 {
 	struct ieee80211_key *key, *tmp;
+	LIST_HEAD(tmp_list);
+
+	ASSERT_RTNL();
+	might_sleep();
 
 	list_for_each_entry_safe(key, tmp, &sdata->key_list, list)
 		ieee80211_key_free(key);
@@ -262,8 +327,10 @@ void ieee80211_enable_keys(struct ieee80
 {
 	struct ieee80211_key *key;
 
-	WARN_ON(!netif_running(sdata->dev));
-	if (!netif_running(sdata->dev))
+	ASSERT_RTNL();
+	might_sleep();
+
+	if (WARN_ON(!netif_running(sdata->dev)))
 		return;
 
 	list_for_each_entry(key, &sdata->key_list, list)
@@ -274,6 +341,9 @@ void ieee80211_disable_keys(struct ieee8
 {
 	struct ieee80211_key *key;
 
+	ASSERT_RTNL();
+	might_sleep();
+
 	list_for_each_entry(key, &sdata->key_list, list)
 		ieee80211_key_disable_hw_accel(key);
 }
--- everything.orig/net/mac80211/cfg.c	2008-02-23 15:13:17.000000000 +0100
+++ everything/net/mac80211/cfg.c	2008-02-24 10:27:18.000000000 +0100
@@ -143,6 +143,7 @@ static int ieee80211_add_key(struct wiph
 	struct sta_info *sta = NULL;
 	enum ieee80211_key_alg alg;
 	int ret;
+	struct ieee80211_key *key;
 
 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
@@ -161,16 +162,21 @@ static int ieee80211_add_key(struct wiph
 		return -EINVAL;
 	}
 
+	key = ieee80211_key_alloc(alg, key_idx, params->key_len, params->key);
+	if (!key)
+		return -ENOMEM;
+
 	if (mac_addr) {
 		sta = sta_info_get(sdata->local, mac_addr);
-		if (!sta)
+		if (!sta) {
+			ieee80211_key_free(key);
 			return -ENOENT;
+		}
 	}
 
+	ieee80211_key_link(key, sdata, sta);
+
 	ret = 0;
-	if (!ieee80211_key_alloc(sdata, sta, alg, key_idx,
-				 params->key_len, params->key))
-		ret = -ENOMEM;
 
 	if (sta)
 		sta_info_put(sta);
@@ -184,6 +190,7 @@ static int ieee80211_del_key(struct wiph
 	struct ieee80211_sub_if_data *sdata;
 	struct sta_info *sta;
 	int ret;
+	struct ieee80211_key *key;
 
 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
@@ -193,9 +200,11 @@ static int ieee80211_del_key(struct wiph
 			return -ENOENT;
 
 		ret = 0;
-		if (sta->key)
-			ieee80211_key_free(sta->key);
-		else
+		if (sta->key) {
+			key = sta->key;
+			ieee80211_key_free(key);
+			WARN_ON(sta->key);
+		} else
 			ret = -ENOENT;
 
 		sta_info_put(sta);
@@ -205,7 +214,9 @@ static int ieee80211_del_key(struct wiph
 	if (!sdata->keys[key_idx])
 		return -ENOENT;
 
-	ieee80211_key_free(sdata->keys[key_idx]);
+	key = sdata->keys[key_idx];
+	ieee80211_key_free(key);
+	WARN_ON(sdata->keys[key_idx]);
 
 	return 0;
 }
--- everything.orig/net/mac80211/sta_info.c	2008-02-23 14:59:28.000000000 +0100
+++ everything/net/mac80211/sta_info.c	2008-02-24 10:27:37.000000000 +0100
@@ -342,7 +342,7 @@ void sta_info_free(struct sta_info *sta)
 #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
 
 	ieee80211_key_free(sta->key);
-	sta->key = NULL;
+	WARN_ON(sta->key);
 
 	if (local->ops->sta_notify) {
 

-- 


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

* [PATCH 06/10] mac80211: RCU-ify STA info structure access
  2008-02-25 15:27 [PATCH 00/10] mac80211 locking/sta info work Johannes Berg
                   ` (4 preceding siblings ...)
  2008-02-25 15:27 ` [PATCH 05/10] mac80211: split ieee80211_key_alloc/free Johannes Berg
@ 2008-02-25 15:27 ` Johannes Berg
  2008-02-26  0:22   ` Luis Carlos Cobo
  2008-02-25 15:27 ` [PATCH 07/10] mac80211: split sta_info_add Johannes Berg
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 16+ messages in thread
From: Johannes Berg @ 2008-02-25 15:27 UTC (permalink / raw)
  To: John Linville; +Cc: linux-wireless, Luis Carlos Cobo

This makes access to the STA hash table/list use RCU to protect
against freeing of items. However, it's not a true RCU, the
copy step is missing: whenever somebody changes a STA item it
is simply updated. This is an existing race condition that is
now somewhat understandable.

This patch also fixes the race key freeing vs. STA destruction
by making sure that sta_info_destroy() is always called under
RTNL and frees the key.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
 drivers/net/wireless/iwlwifi/iwl-3945-rs.c |   31 +-
 drivers/net/wireless/iwlwifi/iwl-4965-rs.c |   27 +-
 net/mac80211/cfg.c                         |  110 ++++----
 net/mac80211/debugfs_sta.c                 |    4 
 net/mac80211/debugfs_sta.h                 |    2 
 net/mac80211/ieee80211.c                   |   78 +++--
 net/mac80211/ieee80211_i.h                 |   13 
 net/mac80211/ieee80211_iface.c             |   11 
 net/mac80211/ieee80211_ioctl.c             |   42 +--
 net/mac80211/ieee80211_rate.c              |    8 
 net/mac80211/ieee80211_rate.h              |    1 
 net/mac80211/ieee80211_sta.c               |  127 ++++++---
 net/mac80211/key.c                         |    8 
 net/mac80211/mesh.c                        |    5 
 net/mac80211/mesh.h                        |   16 -
 net/mac80211/mesh_hwmp.c                   |    6 
 net/mac80211/mesh_pathtbl.c                |   30 +-
 net/mac80211/mesh_plink.c                  |  101 +++----
 net/mac80211/rc80211_pid_algo.c            |   25 +
 net/mac80211/rc80211_simple.c              |   18 -
 net/mac80211/rx.c                          |   24 -
 net/mac80211/sta_info.c                    |  383 +++++++++++++++++------------
 net/mac80211/sta_info.h                    |   61 +++-
 net/mac80211/tx.c                          |   48 +--
 net/mac80211/wme.c                         |    8 
 25 files changed, 699 insertions(+), 488 deletions(-)

--- everything.orig/net/mac80211/ieee80211_i.h	2008-02-25 16:15:22.000000000 +0100
+++ everything/net/mac80211/ieee80211_i.h	2008-02-25 16:15:22.000000000 +0100
@@ -574,6 +574,7 @@ struct ieee80211_local {
 	unsigned int filter_flags; /* FIF_* */
 	struct iw_statistics wstats;
 	u8 wstats_flags;
+	bool tim_in_locked_section; /* see ieee80211_beacon_get() */
 	int tx_headroom; /* required headroom for hardware/radiotap */
 
 	enum {
@@ -591,9 +592,15 @@ struct ieee80211_local {
 	struct sk_buff_head skb_queue;
 	struct sk_buff_head skb_queue_unreliable;
 
-	/* Station data structures */
-	rwlock_t sta_lock; /* protects STA data structures */
-	int num_sta; /* number of stations in sta_list */
+	/* Station data */
+	/*
+	 * The lock only protects the list, hash, timer and counter
+	 * against manipulation, reads are done in RCU. Additionally,
+	 * the lock protects each BSS's TIM bitmap and a few items
+	 * in a STA info structure.
+	 */
+	spinlock_t sta_lock;
+	unsigned long num_sta;
 	struct list_head sta_list;
 	struct sta_info *sta_hash[STA_HASH_SIZE];
 	struct timer_list sta_cleanup;
--- everything.orig/net/mac80211/ieee80211_sta.c	2008-02-25 16:14:08.000000000 +0100
+++ everything/net/mac80211/ieee80211_sta.c	2008-02-25 16:15:22.000000000 +0100
@@ -24,6 +24,7 @@
 #include <linux/wireless.h>
 #include <linux/random.h>
 #include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
 #include <net/iw_handler.h>
 #include <asm/types.h>
 
@@ -845,6 +846,8 @@ static void ieee80211_associated(struct 
 
 	ifsta->state = IEEE80211_ASSOCIATED;
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, ifsta->bssid);
 	if (!sta) {
 		printk(KERN_DEBUG "%s: No STA entry for own AP %s\n",
@@ -860,7 +863,7 @@ static void ieee80211_associated(struct 
 				       "range\n",
 				       dev->name, print_mac(mac, ifsta->bssid));
 				disassoc = 1;
-				sta_info_free(sta);
+				sta_info_unlink(&sta);
 			} else
 				ieee80211_send_probe_req(dev, ifsta->bssid,
 							 local->scan_ssid,
@@ -876,8 +879,17 @@ static void ieee80211_associated(struct 
 							 ifsta->ssid_len);
 			}
 		}
-		sta_info_put(sta);
 	}
+
+	rcu_read_unlock();
+
+	if (disassoc && sta) {
+		synchronize_rcu();
+		rtnl_lock();
+		sta_info_destroy(sta);
+		rtnl_unlock();
+	}
+
 	if (disassoc) {
 		ifsta->state = IEEE80211_DISABLED;
 		ieee80211_set_associated(dev, ifsta, 0);
@@ -1103,9 +1115,13 @@ static void ieee80211_sta_process_addba_
 	int ret = -EOPNOTSUPP;
 	DECLARE_MAC_BUF(mac);
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, mgmt->sa);
-	if (!sta)
+	if (!sta) {
+		rcu_read_unlock();
 		return;
+	}
 
 	/* extract session parameters from addba request frame */
 	dialog_token = mgmt->u.action.u.addba_req.dialog_token;
@@ -1196,9 +1212,9 @@ end:
 	spin_unlock_bh(&sta->ampdu_mlme.ampdu_rx);
 
 end_no_lock:
-	ieee80211_send_addba_resp(sta->dev, sta->addr, tid, dialog_token,
-				status, 1, buf_size, timeout);
-	sta_info_put(sta);
+	ieee80211_send_addba_resp(sta->sdata->dev, sta->addr, tid,
+				  dialog_token, status, 1, buf_size, timeout);
+	rcu_read_unlock();
 }
 
 static void ieee80211_sta_process_addba_resp(struct net_device *dev,
@@ -1212,9 +1228,13 @@ static void ieee80211_sta_process_addba_
 	u16 tid;
 	u8 *state;
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, mgmt->sa);
-	if (!sta)
+	if (!sta) {
+		rcu_read_unlock();
 		return;
+	}
 
 	capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab);
 	tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
@@ -1229,7 +1249,7 @@ static void ieee80211_sta_process_addba_
 #ifdef CONFIG_MAC80211_HT_DEBUG
 		printk(KERN_DEBUG "wrong addBA response token, tid %d\n", tid);
 #endif /* CONFIG_MAC80211_HT_DEBUG */
-		sta_info_put(sta);
+		rcu_read_unlock();
 		return;
 	}
 
@@ -1243,7 +1263,7 @@ static void ieee80211_sta_process_addba_
 			spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
 			printk(KERN_DEBUG "state not HT_ADDBA_REQUESTED_MSK:"
 				"%d\n", *state);
-			sta_info_put(sta);
+			rcu_read_unlock();
 			return;
 		}
 
@@ -1270,7 +1290,7 @@ static void ieee80211_sta_process_addba_
 		ieee80211_stop_tx_ba_session(hw, sta->addr, tid,
 					     WLAN_BACK_INITIATOR);
 	}
-	sta_info_put(sta);
+	rcu_read_unlock();
 }
 
 void ieee80211_send_delba(struct net_device *dev, const u8 *da, u16 tid,
@@ -1325,16 +1345,20 @@ void ieee80211_sta_stop_rx_ba_session(st
 	struct sta_info *sta;
 	int ret, i;
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, ra);
-	if (!sta)
+	if (!sta) {
+		rcu_read_unlock();
 		return;
+	}
 
 	/* check if TID is in operational state */
 	spin_lock_bh(&sta->ampdu_mlme.ampdu_rx);
 	if (sta->ampdu_mlme.tid_rx[tid].state
 				!= HT_AGG_STATE_OPERATIONAL) {
 		spin_unlock_bh(&sta->ampdu_mlme.ampdu_rx);
-		sta_info_put(sta);
+		rcu_read_unlock();
 		return;
 	}
 	sta->ampdu_mlme.tid_rx[tid].state =
@@ -1373,7 +1397,7 @@ void ieee80211_sta_stop_rx_ba_session(st
 	kfree(sta->ampdu_mlme.tid_rx[tid].reorder_buf);
 
 	sta->ampdu_mlme.tid_rx[tid].state = HT_AGG_STATE_IDLE;
-	sta_info_put(sta);
+	rcu_read_unlock();
 }
 
 
@@ -1386,9 +1410,13 @@ static void ieee80211_sta_process_delba(
 	u16 initiator;
 	DECLARE_MAC_BUF(mac);
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, mgmt->sa);
-	if (!sta)
+	if (!sta) {
+		rcu_read_unlock();
 		return;
+	}
 
 	params = le16_to_cpu(mgmt->u.action.u.delba.params);
 	tid = (params & IEEE80211_DELBA_PARAM_TID_MASK) >> 12;
@@ -1413,7 +1441,7 @@ static void ieee80211_sta_process_delba(
 		ieee80211_stop_tx_ba_session(&local->hw, sta->addr, tid,
 					     WLAN_BACK_RECIPIENT);
 	}
-	sta_info_put(sta);
+	rcu_read_unlock();
 }
 
 /*
@@ -1436,9 +1464,13 @@ void sta_addba_resp_timer_expired(unsign
 	struct sta_info *sta;
 	u8 *state;
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, temp_sta->addr);
-	if (!sta)
+	if (!sta) {
+		rcu_read_unlock();
 		return;
+	}
 
 	state = &sta->ampdu_mlme.tid_tx[tid].state;
 	/* check if the TID waits for addBA response */
@@ -1460,7 +1492,7 @@ void sta_addba_resp_timer_expired(unsign
 				     WLAN_BACK_INITIATOR);
 
 timer_expired_exit:
-	sta_info_put(sta);
+	rcu_read_unlock();
 }
 
 /*
@@ -1480,8 +1512,8 @@ void sta_rx_agg_session_timer_expired(un
 					 timer_to_tid[0]);
 
 	printk(KERN_DEBUG "rx session timer expired on tid %d\n", (u16)*ptid);
-	ieee80211_sta_stop_rx_ba_session(sta->dev, sta->addr, (u16)*ptid,
-					 WLAN_BACK_TIMER,
+	ieee80211_sta_stop_rx_ba_session(sta->sdata->dev, sta->addr,
+					 (u16)*ptid, WLAN_BACK_TIMER,
 					 WLAN_REASON_QSTA_TIMEOUT);
 }
 
@@ -1790,14 +1822,18 @@ static void ieee80211_rx_mgmt_assoc_resp
 	if (ifsta->assocresp_ies)
 		memcpy(ifsta->assocresp_ies, pos, ifsta->assocresp_ies_len);
 
+	rcu_read_lock();
+
 	/* Add STA entry for the AP */
 	sta = sta_info_get(local, ifsta->bssid);
 	if (!sta) {
 		struct ieee80211_sta_bss *bss;
-		sta = sta_info_add(local, dev, ifsta->bssid, GFP_KERNEL);
+
+		sta = sta_info_add(sdata, ifsta->bssid);
 		if (IS_ERR(sta)) {
 			printk(KERN_DEBUG "%s: failed to add STA entry for the"
 			       " AP (error %ld)\n", dev->name, PTR_ERR(sta));
+			rcu_read_unlock();
 			return;
 		}
 		bss = ieee80211_rx_bss_get(dev, ifsta->bssid,
@@ -1811,7 +1847,6 @@ static void ieee80211_rx_mgmt_assoc_resp
 		}
 	}
 
-	sta->dev = dev;
 	sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_AP |
 		      WLAN_STA_AUTHORIZED;
 
@@ -1882,7 +1917,7 @@ static void ieee80211_rx_mgmt_assoc_resp
 	bss_conf->aid = aid;
 	ieee80211_set_associated(dev, ifsta, 1);
 
-	sta_info_put(sta);
+	rcu_read_unlock();
 
 	ieee80211_associated(dev, ifsta);
 }
@@ -2328,6 +2363,8 @@ static void ieee80211_rx_bss_info(struct
 				      mesh_peer_accepts_plinks(&elems, dev));
 	}
 
+	rcu_read_lock();
+
 	if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS && elems.supp_rates &&
 	    memcmp(mgmt->bssid, sdata->u.sta.bssid, ETH_ALEN) == 0 &&
 	    (sta = sta_info_get(local, mgmt->sa))) {
@@ -2353,9 +2390,10 @@ static void ieee80211_rx_bss_info(struct
 			       (unsigned long long) supp_rates,
 			       (unsigned long long) sta->supp_rates[rx_status->band]);
 		}
-		sta_info_put(sta);
 	}
 
+	rcu_read_unlock();
+
 	if (elems.ds_params && elems.ds_params_len == 1)
 		freq = ieee80211_channel_to_frequency(elems.ds_params[0]);
 	else
@@ -2549,8 +2587,10 @@ static void ieee80211_rx_bss_info(struct
 				       dev->name, print_mac(mac, mgmt->bssid));
 #endif /* CONFIG_MAC80211_IBSS_DEBUG */
 			ieee80211_sta_join_ibss(dev, &sdata->u.sta, bss);
+			rcu_read_lock();
 			ieee80211_ibss_add_sta(dev, NULL,
 					       mgmt->bssid, mgmt->sa);
+			rcu_read_unlock();
 		}
 	}
 
@@ -2892,17 +2932,20 @@ static int ieee80211_sta_active_ibss(str
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	int active = 0;
 	struct sta_info *sta;
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-	read_lock_bh(&local->sta_lock);
-	list_for_each_entry(sta, &local->sta_list, list) {
-		if (sta->dev == dev &&
+	rcu_read_lock();
+
+	list_for_each_entry_rcu(sta, &local->sta_list, list) {
+		if (sta->sdata == sdata &&
 		    time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL,
 			       jiffies)) {
 			active++;
 			break;
 		}
 	}
-	read_unlock_bh(&local->sta_lock);
+
+	rcu_read_unlock();
 
 	return active;
 }
@@ -2914,22 +2957,25 @@ static void ieee80211_sta_expire(struct 
 	struct sta_info *sta, *tmp;
 	LIST_HEAD(tmp_list);
 	DECLARE_MAC_BUF(mac);
+	unsigned long flags;
 
-	write_lock_bh(&local->sta_lock);
+	spin_lock_irqsave(&local->sta_lock, flags);
 	list_for_each_entry_safe(sta, tmp, &local->sta_list, list)
 		if (time_after(jiffies, sta->last_rx + exp_time)) {
 			printk(KERN_DEBUG "%s: expiring inactive STA %s\n",
 			       dev->name, print_mac(mac, sta->addr));
-			__sta_info_get(sta);
-			sta_info_remove(sta);
-			list_add(&sta->list, &tmp_list);
-		}
-	write_unlock_bh(&local->sta_lock);
-
-	list_for_each_entry_safe(sta, tmp, &tmp_list, list) {
-		sta_info_free(sta);
-		sta_info_put(sta);
-	}
+			sta_info_unlink(&sta);
+			if (sta)
+				list_add(&sta->list, &tmp_list);
+		}
+	spin_unlock_irqrestore(&local->sta_lock, flags);
+
+	synchronize_rcu();
+
+	rtnl_lock();
+	list_for_each_entry_safe(sta, tmp, &tmp_list, list)
+		sta_info_destroy(sta);
+	rtnl_unlock();
 }
 
 
@@ -3976,6 +4022,7 @@ int ieee80211_sta_set_extra_ie(struct ne
 }
 
 
+/* must be called under RCU read lock */
 struct sta_info * ieee80211_ibss_add_sta(struct net_device *dev,
 					 struct sk_buff *skb, u8 *bssid,
 					 u8 *addr)
@@ -3998,7 +4045,7 @@ struct sta_info * ieee80211_ibss_add_sta
 	printk(KERN_DEBUG "%s: Adding new IBSS station %s (dev=%s)\n",
 	       wiphy_name(local->hw.wiphy), print_mac(mac, addr), dev->name);
 
-	sta = sta_info_add(local, dev, addr, GFP_ATOMIC);
+	sta = sta_info_add(sdata, addr);
 	if (IS_ERR(sta))
 		return NULL;
 
@@ -4009,7 +4056,7 @@ struct sta_info * ieee80211_ibss_add_sta
 
 	rate_control_rate_init(sta, local);
 
-	return sta; /* caller will call sta_info_put() */
+	return sta;
 }
 
 
--- everything.orig/net/mac80211/sta_info.c	2008-02-25 16:15:22.000000000 +0100
+++ everything/net/mac80211/sta_info.c	2008-02-25 16:15:22.000000000 +0100
@@ -15,6 +15,7 @@
 #include <linux/skbuff.h>
 #include <linux/if_arp.h>
 #include <linux/timer.h>
+#include <linux/rtnetlink.h>
 
 #include <net/mac80211.h>
 #include "ieee80211_i.h"
@@ -23,14 +24,43 @@
 #include "debugfs_sta.h"
 #include "mesh.h"
 
-/* Caller must hold local->sta_lock */
-static void sta_info_hash_add(struct ieee80211_local *local,
-			      struct sta_info *sta)
-{
-	sta->hnext = local->sta_hash[STA_HASH(sta->addr)];
-	local->sta_hash[STA_HASH(sta->addr)] = sta;
-}
-
+/**
+ * DOC: STA information lifetime rules
+ *
+ * STA info structures (&struct sta_info) are managed in a hash table
+ * for faster lookup and a list for iteration. They are managed using
+ * RCU, i.e. access to the list and hash table is protected by RCU.
+ *
+ * STA info structures are always "alive" when they are added with
+ * @sta_info_add() [this may be changed in the future to allow allocating
+ * outside of a critical section!], they are then added to the hash
+ * table and list. Therefore, @sta_info_add() must also be RCU protected,
+ * also, the caller of @sta_info_add() cannot assume that it owns the
+ * structure.
+ *
+ * Because there are debugfs entries for each station, and adding those
+ * must be able to sleep, it is also possible to "pin" a station entry,
+ * that means it can be removed from the hash table but not be freed.
+ * See the comment in @__sta_info_unlink() for more information.
+ *
+ * In order to remove a STA info structure, the caller needs to first
+ * unlink it (@sta_info_unlink()) from the list and hash tables and
+ * then wait for an RCU synchronisation before it can be freed. Due to
+ * the pinning and the possibility of multiple callers trying to remove
+ * the same STA info at the same time, @sta_info_unlink() can clear the
+ * STA info pointer it is passed to indicate that the STA info is owned
+ * by somebody else now.
+ *
+ * If @sta_info_unlink() did not clear the pointer then the caller owns
+ * the STA info structure now and is responsible of destroying it with
+ * a call to @sta_info_destroy(), not before RCU synchronisation, of
+ * course. Note that sta_info_destroy() must be protected by the RTNL.
+ *
+ * In all other cases, there is no concept of ownership on a STA entry,
+ * each structure is owned by the global hash table/list until it is
+ * removed. All users of the structure need to be RCU protected so that
+ * the structure won't be freed before they are done using it.
+ */
 
 /* Caller must hold local->sta_lock */
 static int sta_info_hash_del(struct ieee80211_local *local,
@@ -42,46 +72,39 @@ static int sta_info_hash_del(struct ieee
 	if (!s)
 		return -ENOENT;
 	if (s == sta) {
-		local->sta_hash[STA_HASH(sta->addr)] = s->hnext;
+		rcu_assign_pointer(local->sta_hash[STA_HASH(sta->addr)],
+				   s->hnext);
 		return 0;
 	}
 
 	while (s->hnext && s->hnext != sta)
 		s = s->hnext;
 	if (s->hnext) {
-		s->hnext = sta->hnext;
+		rcu_assign_pointer(s->hnext, sta->hnext);
 		return 0;
 	}
 
 	return -ENOENT;
 }
 
-/* must hold local->sta_lock */
+/* protected by RCU */
 static struct sta_info *__sta_info_find(struct ieee80211_local *local,
 					u8 *addr)
 {
 	struct sta_info *sta;
 
-	sta = local->sta_hash[STA_HASH(addr)];
+	sta = rcu_dereference(local->sta_hash[STA_HASH(addr)]);
 	while (sta) {
 		if (compare_ether_addr(sta->addr, addr) == 0)
 			break;
-		sta = sta->hnext;
+		sta = rcu_dereference(sta->hnext);
 	}
 	return sta;
 }
 
 struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr)
 {
-	struct sta_info *sta;
-
-	read_lock_bh(&local->sta_lock);
-	sta = __sta_info_find(local, addr);
-	if (sta)
-		__sta_info_get(sta);
-	read_unlock_bh(&local->sta_lock);
-
-	return sta;
+	return __sta_info_find(local, addr);
 }
 EXPORT_SYMBOL(sta_info_get);
 
@@ -91,81 +114,97 @@ struct sta_info *sta_info_get_by_idx(str
 	struct sta_info *sta;
 	int i = 0;
 
-	read_lock_bh(&local->sta_lock);
-	list_for_each_entry(sta, &local->sta_list, list) {
+	list_for_each_entry_rcu(sta, &local->sta_list, list) {
 		if (i < idx) {
 			++i;
 			continue;
-		} else if (!dev || dev == sta->dev) {
-			__sta_info_get(sta);
-			read_unlock_bh(&local->sta_lock);
+		} else if (!dev || dev == sta->sdata->dev) {
 			return sta;
 		}
 	}
-	read_unlock_bh(&local->sta_lock);
 
 	return NULL;
 }
 
-static void sta_info_release(struct kref *kref)
+void sta_info_destroy(struct sta_info *sta)
 {
-	struct sta_info *sta = container_of(kref, struct sta_info, kref);
 	struct ieee80211_local *local = sta->local;
 	struct sk_buff *skb;
 	int i;
 
-	/* free sta structure; it has already been removed from
-	 * hash table etc. external structures. Make sure that all
-	 * buffered frames are release (one might have been added
-	 * after sta_info_free() was called). */
+	ASSERT_RTNL();
+	might_sleep();
+
+	rate_control_remove_sta_debugfs(sta);
+	ieee80211_sta_debugfs_remove(sta);
+
+	if (ieee80211_vif_is_mesh(&sta->sdata->vif))
+		mesh_plink_deactivate(sta);
+
+	/*
+	 * NOTE: This will call synchronize_rcu() internally to
+	 * make sure no key references can be in use. We rely on
+	 * that here for the mesh code!
+	 */
+	ieee80211_key_free(sta->key);
+	WARN_ON(sta->key);
+
+	if (ieee80211_vif_is_mesh(&sta->sdata->vif))
+		del_timer_sync(&sta->plink_timer);
+
 	while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
 		local->total_ps_buffered--;
 		dev_kfree_skb_any(skb);
 	}
-	while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) {
+
+	while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL)
 		dev_kfree_skb_any(skb);
-	}
+
 	for (i = 0; i <  STA_TID_NUM; i++) {
 		del_timer_sync(&sta->ampdu_mlme.tid_rx[i].session_timer);
 		del_timer_sync(&sta->ampdu_mlme.tid_tx[i].addba_resp_timer);
 	}
 	rate_control_free_sta(sta->rate_ctrl, sta->rate_ctrl_priv);
 	rate_control_put(sta->rate_ctrl);
+
 	kfree(sta);
 }
 
 
-void sta_info_put(struct sta_info *sta)
+/* Caller must hold local->sta_lock */
+static void sta_info_hash_add(struct ieee80211_local *local,
+			      struct sta_info *sta)
 {
-	kref_put(&sta->kref, sta_info_release);
+	sta->hnext = local->sta_hash[STA_HASH(sta->addr)];
+	rcu_assign_pointer(local->sta_hash[STA_HASH(sta->addr)], sta);
 }
-EXPORT_SYMBOL(sta_info_put);
 
-
-struct sta_info *sta_info_add(struct ieee80211_local *local,
-			      struct net_device *dev, u8 *addr, gfp_t gfp)
+struct sta_info *sta_info_add(struct ieee80211_sub_if_data *sdata,
+			      u8 *addr)
 {
+	struct ieee80211_local *local = sdata->local;
 	struct sta_info *sta;
 	int i;
 	DECLARE_MAC_BUF(mac);
+	unsigned long flags;
 
-	sta = kzalloc(sizeof(*sta), gfp);
+	sta = kzalloc(sizeof(*sta), GFP_ATOMIC);
 	if (!sta)
 		return ERR_PTR(-ENOMEM);
 
-	kref_init(&sta->kref);
+	memcpy(sta->addr, addr, ETH_ALEN);
+	sta->local = local;
+	sta->sdata = sdata;
 
 	sta->rate_ctrl = rate_control_get(local->rate_ctrl);
-	sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl, gfp);
+	sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl,
+						     GFP_ATOMIC);
 	if (!sta->rate_ctrl_priv) {
 		rate_control_put(sta->rate_ctrl);
 		kfree(sta);
 		return ERR_PTR(-ENOMEM);
 	}
 
-	memcpy(sta->addr, addr, ETH_ALEN);
-	sta->local = local;
-	sta->dev = dev;
 	spin_lock_init(&sta->ampdu_mlme.ampdu_rx);
 	spin_lock_init(&sta->ampdu_mlme.ampdu_tx);
 	for (i = 0; i < STA_TID_NUM; i++) {
@@ -190,29 +229,26 @@ struct sta_info *sta_info_add(struct iee
 	}
 	skb_queue_head_init(&sta->ps_tx_buf);
 	skb_queue_head_init(&sta->tx_filtered);
-	write_lock_bh(&local->sta_lock);
-	/* mark sta as used (by caller) */
-	__sta_info_get(sta);
+	spin_lock_irqsave(&local->sta_lock, flags);
 	/* check if STA exists already */
 	if (__sta_info_find(local, addr)) {
-		write_unlock_bh(&local->sta_lock);
-		sta_info_put(sta);
+		spin_unlock_irqrestore(&local->sta_lock, flags);
 		return ERR_PTR(-EEXIST);
 	}
 	list_add(&sta->list, &local->sta_list);
 	local->num_sta++;
 	sta_info_hash_add(local, sta);
-	if (local->ops->sta_notify) {
-		struct ieee80211_sub_if_data *sdata;
 
-		sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	/* notify driver */
+	if (local->ops->sta_notify) {
 		if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN)
 			sdata = sdata->u.vlan.ap;
 
 		local->ops->sta_notify(local_to_hw(local), &sdata->vif,
 				       STA_NOTIFY_ADD, addr);
 	}
-	write_unlock_bh(&local->sta_lock);
+
+	spin_unlock_irqrestore(&local->sta_lock, flags);
 
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
 	printk(KERN_DEBUG "%s: Added STA %s\n",
@@ -252,19 +288,20 @@ static void __sta_info_set_tim_bit(struc
 {
 	if (bss)
 		__bss_tim_set(bss, sta->aid);
-	if (sta->local->ops->set_tim)
+	if (sta->local->ops->set_tim) {
+		sta->local->tim_in_locked_section = true;
 		sta->local->ops->set_tim(local_to_hw(sta->local), sta->aid, 1);
+		sta->local->tim_in_locked_section = false;
+	}
 }
 
 void sta_info_set_tim_bit(struct sta_info *sta)
 {
-	struct ieee80211_sub_if_data *sdata;
-
-	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	unsigned long flags;
 
-	read_lock_bh(&sta->local->sta_lock);
-	__sta_info_set_tim_bit(sdata->bss, sta);
-	read_unlock_bh(&sta->local->sta_lock);
+	spin_lock_irqsave(&sta->local->sta_lock, flags);
+	__sta_info_set_tim_bit(sta->sdata->bss, sta);
+	spin_unlock_irqrestore(&sta->local->sta_lock, flags);
 }
 
 static void __sta_info_clear_tim_bit(struct ieee80211_if_ap *bss,
@@ -272,93 +309,135 @@ static void __sta_info_clear_tim_bit(str
 {
 	if (bss)
 		__bss_tim_clear(bss, sta->aid);
-	if (sta->local->ops->set_tim)
+	if (sta->local->ops->set_tim) {
+		sta->local->tim_in_locked_section = true;
 		sta->local->ops->set_tim(local_to_hw(sta->local), sta->aid, 0);
+		sta->local->tim_in_locked_section = false;
+	}
 }
 
 void sta_info_clear_tim_bit(struct sta_info *sta)
 {
-	struct ieee80211_sub_if_data *sdata;
+	unsigned long flags;
 
-	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	spin_lock_irqsave(&sta->local->sta_lock, flags);
+	__sta_info_clear_tim_bit(sta->sdata->bss, sta);
+	spin_unlock_irqrestore(&sta->local->sta_lock, flags);
+}
 
-	read_lock_bh(&sta->local->sta_lock);
-	__sta_info_clear_tim_bit(sdata->bss, sta);
-	read_unlock_bh(&sta->local->sta_lock);
+/*
+ * See comment in __sta_info_unlink,
+ * caller must hold local->sta_lock.
+ */
+static void __sta_info_pin(struct sta_info *sta)
+{
+	WARN_ON(sta->pin_status != STA_INFO_PIN_STAT_NORMAL);
+	sta->pin_status = STA_INFO_PIN_STAT_PINNED;
 }
 
-/* Caller must hold local->sta_lock */
-void sta_info_remove(struct sta_info *sta)
+/*
+ * See comment in __sta_info_unlink, returns sta if it
+ * needs to be destroyed.
+ */
+static struct sta_info *__sta_info_unpin(struct sta_info *sta)
 {
-	struct ieee80211_local *local = sta->local;
-	struct ieee80211_sub_if_data *sdata;
+	struct sta_info *ret = NULL;
+	unsigned long flags;
 
-	/* don't do anything if we've been removed already */
-	if (sta_info_hash_del(local, sta))
-		return;
+	spin_lock_irqsave(&sta->local->sta_lock, flags);
+	WARN_ON(sta->pin_status != STA_INFO_PIN_STAT_DESTROY &&
+		sta->pin_status != STA_INFO_PIN_STAT_PINNED);
+	if (sta->pin_status == STA_INFO_PIN_STAT_DESTROY)
+		ret = sta;
+	sta->pin_status = STA_INFO_PIN_STAT_NORMAL;
+	spin_unlock_irqrestore(&sta->local->sta_lock, flags);
 
-	list_del(&sta->list);
-	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
-	if (sta->flags & WLAN_STA_PS) {
-		sta->flags &= ~WLAN_STA_PS;
-		if (sdata->bss)
-			atomic_dec(&sdata->bss->num_sta_ps);
-		__sta_info_clear_tim_bit(sdata->bss, sta);
-	}
-	local->num_sta--;
-
-	if (ieee80211_vif_is_mesh(&sdata->vif))
-		mesh_accept_plinks_update(sdata->dev);
+	return ret;
 }
 
-void sta_info_free(struct sta_info *sta)
+static void __sta_info_unlink(struct sta_info **sta)
 {
-	struct sk_buff *skb;
-	struct ieee80211_local *local = sta->local;
-	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
-
-	DECLARE_MAC_BUF(mac);
-
-	might_sleep();
+	struct ieee80211_local *local = (*sta)->local;
+	struct ieee80211_sub_if_data *sdata = (*sta)->sdata;
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+	DECLARE_MAC_BUF(mbuf);
+#endif
+	/*
+	 * pull caller's reference if we're already gone.
+	 */
+	if (sta_info_hash_del(local, *sta)) {
+		*sta = NULL;
+		return;
+	}
 
-	write_lock_bh(&local->sta_lock);
-	sta_info_remove(sta);
-	write_unlock_bh(&local->sta_lock);
+	/*
+	 * Also pull caller's reference if the STA is pinned by the
+	 * task that is adding the debugfs entries. In that case, we
+	 * leave the STA "to be freed".
+	 *
+	 * The rules are not trivial, but not too complex either:
+	 *  (1) pin_status is only modified under the sta_lock
+	 *  (2) sta_info_debugfs_add_work() will set the status
+	 *	to PINNED when it found an item that needs a new
+	 *	debugfs directory created. In that case, that item
+	 *	must not be freed although all *RCU* users are done
+	 *	with it. Hence, we tell the caller of _unlink()
+	 *	that the item is already gone (as can happen when
+	 *	two tasks try to unlink/destroy at the same time)
+	 *  (3) We set the pin_status to DESTROY here when we
+	 *	find such an item.
+	 *  (4) sta_info_debugfs_add_work() will reset the pin_status
+	 *	from PINNED to NORMAL when it is done with the item,
+	 *	but will check for DESTROY before resetting it in
+	 *	which case it will free the item.
+	 */
+	if ((*sta)->pin_status == STA_INFO_PIN_STAT_PINNED) {
+		(*sta)->pin_status = STA_INFO_PIN_STAT_DESTROY;
+		*sta = NULL;
+		return;
+	}
 
-	if (ieee80211_vif_is_mesh(&sdata->vif))
-		mesh_plink_deactivate(sta);
+	list_del(&(*sta)->list);
 
-	while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
-		local->total_ps_buffered--;
-		dev_kfree_skb(skb);
-	}
-	while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) {
-		dev_kfree_skb(skb);
+	if ((*sta)->flags & WLAN_STA_PS) {
+		(*sta)->flags &= ~WLAN_STA_PS;
+		if (sdata->bss)
+			atomic_dec(&sdata->bss->num_sta_ps);
+		__sta_info_clear_tim_bit(sdata->bss, *sta);
 	}
 
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-	printk(KERN_DEBUG "%s: Removed STA %s\n",
-	       wiphy_name(local->hw.wiphy), print_mac(mac, sta->addr));
-#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
-
-	ieee80211_key_free(sta->key);
-	WARN_ON(sta->key);
+	local->num_sta--;
 
 	if (local->ops->sta_notify) {
-
 		if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN)
 			sdata = sdata->u.vlan.ap;
 
 		local->ops->sta_notify(local_to_hw(local), &sdata->vif,
-				       STA_NOTIFY_REMOVE, sta->addr);
+				       STA_NOTIFY_REMOVE, (*sta)->addr);
 	}
 
-	rate_control_remove_sta_debugfs(sta);
-	ieee80211_sta_debugfs_remove(sta);
+	if (ieee80211_vif_is_mesh(&sdata->vif)) {
+		mesh_accept_plinks_update(sdata);
+#ifdef CONFIG_MAC80211_MESH
+		del_timer(&(*sta)->plink_timer);
+#endif
+	}
 
-	sta_info_put(sta);
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+	printk(KERN_DEBUG "%s: Removed STA %s\n",
+	       wiphy_name(local->hw.wiphy), print_mac(mbuf, (*sta)->addr));
+#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
 }
 
+void sta_info_unlink(struct sta_info **sta)
+{
+	struct ieee80211_local *local = (*sta)->local;
+	unsigned long flags;
+
+	spin_lock_irqsave(&local->sta_lock, flags);
+	__sta_info_unlink(sta);
+	spin_unlock_irqrestore(&local->sta_lock, flags);
+}
 
 static inline int sta_info_buffer_expired(struct ieee80211_local *local,
 					  struct sta_info *sta,
@@ -404,7 +483,7 @@ static void sta_info_cleanup_expire_buff
 		if (!skb)
 			break;
 
-		sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+		sdata = sta->sdata;
 		local->total_ps_buffered--;
 		printk(KERN_DEBUG "Buffered frame expired (STA "
 		       "%s)\n", print_mac(mac, sta->addr));
@@ -421,13 +500,10 @@ static void sta_info_cleanup(unsigned lo
 	struct ieee80211_local *local = (struct ieee80211_local *) data;
 	struct sta_info *sta;
 
-	read_lock_bh(&local->sta_lock);
-	list_for_each_entry(sta, &local->sta_list, list) {
-		__sta_info_get(sta);
+	rcu_read_lock();
+	list_for_each_entry_rcu(sta, &local->sta_list, list)
 		sta_info_cleanup_expire_buffered(local, sta);
-		sta_info_put(sta);
-	}
-	read_unlock_bh(&local->sta_lock);
+	rcu_read_unlock();
 
 	local->sta_cleanup.expires =
 		round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL);
@@ -435,37 +511,45 @@ static void sta_info_cleanup(unsigned lo
 }
 
 #ifdef CONFIG_MAC80211_DEBUGFS
-static void sta_info_debugfs_add_task(struct work_struct *work)
+static void sta_info_debugfs_add_work(struct work_struct *work)
 {
 	struct ieee80211_local *local =
 		container_of(work, struct ieee80211_local, sta_debugfs_add);
 	struct sta_info *sta, *tmp;
+	unsigned long flags;
 
 	while (1) {
 		sta = NULL;
-		read_lock_bh(&local->sta_lock);
+
+		spin_lock_irqsave(&local->sta_lock, flags);
 		list_for_each_entry(tmp, &local->sta_list, list) {
 			if (!tmp->debugfs.dir) {
 				sta = tmp;
-				__sta_info_get(sta);
+				__sta_info_pin(sta);
 				break;
 			}
 		}
-		read_unlock_bh(&local->sta_lock);
+		spin_unlock_irqrestore(&local->sta_lock, flags);
 
 		if (!sta)
 			break;
 
 		ieee80211_sta_debugfs_add(sta);
 		rate_control_add_sta_debugfs(sta);
-		sta_info_put(sta);
+
+		sta = __sta_info_unpin(sta);
+
+		if (sta) {
+			synchronize_rcu();
+			sta_info_destroy(sta);
+		}
 	}
 }
 #endif
 
 void sta_info_init(struct ieee80211_local *local)
 {
-	rwlock_init(&local->sta_lock);
+	spin_lock_init(&local->sta_lock);
 	INIT_LIST_HEAD(&local->sta_list);
 
 	setup_timer(&local->sta_cleanup, sta_info_cleanup,
@@ -474,7 +558,7 @@ void sta_info_init(struct ieee80211_loca
 		round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL);
 
 #ifdef CONFIG_MAC80211_DEBUGFS
-	INIT_WORK(&local->sta_debugfs_add, sta_info_debugfs_add_task);
+	INIT_WORK(&local->sta_debugfs_add, sta_info_debugfs_add_work);
 #endif
 }
 
@@ -493,24 +577,29 @@ void sta_info_stop(struct ieee80211_loca
 /**
  * sta_info_flush - flush matching STA entries from the STA table
  * @local: local interface data
- * @dev: matching rule for the net device (sta->dev) or %NULL to match all STAs
+ * @sdata: matching rule for the net device (sta->dev) or %NULL to match all STAs
  */
-void sta_info_flush(struct ieee80211_local *local, struct net_device *dev)
+void sta_info_flush(struct ieee80211_local *local,
+		    struct ieee80211_sub_if_data *sdata)
 {
 	struct sta_info *sta, *tmp;
 	LIST_HEAD(tmp_list);
+	unsigned long flags;
 
-	write_lock_bh(&local->sta_lock);
-	list_for_each_entry_safe(sta, tmp, &local->sta_list, list)
-		if (!dev || dev == sta->dev) {
-			__sta_info_get(sta);
-			sta_info_remove(sta);
-			list_add_tail(&sta->list, &tmp_list);
-		}
-	write_unlock_bh(&local->sta_lock);
+	might_sleep();
 
-	list_for_each_entry_safe(sta, tmp, &tmp_list, list) {
-		sta_info_free(sta);
-		sta_info_put(sta);
+	spin_lock_irqsave(&local->sta_lock, flags);
+	list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
+		if (!sdata || sdata == sta->sdata) {
+			__sta_info_unlink(&sta);
+			if (sta)
+				list_add_tail(&sta->list, &tmp_list);
+		}
 	}
+	spin_unlock_irqrestore(&local->sta_lock, flags);
+
+	synchronize_rcu();
+
+	list_for_each_entry_safe(sta, tmp, &tmp_list, list)
+		sta_info_destroy(sta);
 }
--- everything.orig/net/mac80211/sta_info.h	2008-02-25 16:15:22.000000000 +0100
+++ everything/net/mac80211/sta_info.h	2008-02-25 16:19:33.000000000 +0100
@@ -12,7 +12,6 @@
 #include <linux/list.h>
 #include <linux/types.h>
 #include <linux/if_ether.h>
-#include <linux/kref.h>
 #include "ieee80211_key.h"
 
 /**
@@ -134,8 +133,14 @@ struct sta_ampdu_mlme {
 	u8 dialog_token_allocator;
 };
 
+
+/* see __sta_info_unlink */
+#define STA_INFO_PIN_STAT_NORMAL	0
+#define STA_INFO_PIN_STAT_PINNED	1
+#define STA_INFO_PIN_STAT_DESTROY	2
+
+
 struct sta_info {
-	struct kref kref;
 	struct list_head list;
 	struct sta_info *hnext; /* next entry in hash table list */
 
@@ -166,8 +171,8 @@ struct sta_info {
 	/* last rates used to send a frame to this STA */
 	int last_txrate_idx, last_nonerp_txrate_idx;
 
-	struct net_device *dev; /* which net device is this station associated
-				 * to */
+	/* sub_if_data this sta belongs to */
+	struct ieee80211_sub_if_data *sdata;
 
 	struct ieee80211_key *key;
 
@@ -199,6 +204,12 @@ struct sta_info {
 
 	u16 listen_interval;
 
+	/*
+	 * for use by the internal lifetime management,
+	 * see __sta_info_unlink
+	 */
+	u8 pin_status;
+
 	struct ieee80211_ht_info ht_info; /* 802.11n HT capabilities
 					     of this STA */
 	struct sta_ampdu_mlme ampdu_mlme;
@@ -262,25 +273,37 @@ static inline enum plink_state sta_plink
  */
 #define STA_INFO_CLEANUP_INTERVAL (10 * HZ)
 
-static inline void __sta_info_get(struct sta_info *sta)
-{
-	kref_get(&sta->kref);
-}
-
-struct sta_info * sta_info_get(struct ieee80211_local *local, u8 *addr);
+/*
+ * Get a STA info, must have be under RCU read lock.
+ */
+struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr);
+/*
+ * Get STA info by index, BROKEN!
+ */
 struct sta_info *sta_info_get_by_idx(struct ieee80211_local *local, int idx,
 				      struct net_device *dev);
-void sta_info_put(struct sta_info *sta);
-struct sta_info *sta_info_add(struct ieee80211_local *local,
-			      struct net_device *dev, u8 *addr, gfp_t gfp);
-void sta_info_remove(struct sta_info *sta);
-void sta_info_free(struct sta_info *sta);
-void sta_info_init(struct ieee80211_local *local);
-int sta_info_start(struct ieee80211_local *local);
-void sta_info_stop(struct ieee80211_local *local);
-void sta_info_flush(struct ieee80211_local *local, struct net_device *dev);
+/*
+ * Add a new STA info, must be under RCU read lock
+ * because otherwise the returned reference isn't
+ * necessarily valid long enough.
+ */
+struct sta_info *sta_info_add(struct ieee80211_sub_if_data *sdata,
+			      u8 *addr);
+/*
+ * Unlink a STA info from the hash table/list.
+ * This can NULL the STA pointer if somebody else
+ * has already unlinked it.
+ */
+void sta_info_unlink(struct sta_info **sta);
 
+void sta_info_destroy(struct sta_info *sta);
 void sta_info_set_tim_bit(struct sta_info *sta);
 void sta_info_clear_tim_bit(struct sta_info *sta);
 
+void sta_info_init(struct ieee80211_local *local);
+int sta_info_start(struct ieee80211_local *local);
+void sta_info_stop(struct ieee80211_local *local);
+void sta_info_flush(struct ieee80211_local *local,
+		    struct ieee80211_sub_if_data *sdata);
+
 #endif /* STA_INFO_H */
--- everything.orig/net/mac80211/tx.c	2008-02-25 16:15:22.000000000 +0100
+++ everything/net/mac80211/tx.c	2008-02-25 16:15:22.000000000 +0100
@@ -327,10 +327,8 @@ static void purge_old_ps_buffers(struct 
 		}
 		total += skb_queue_len(&ap->ps_bc_buf);
 	}
-	rcu_read_unlock();
 
-	read_lock_bh(&local->sta_lock);
-	list_for_each_entry(sta, &local->sta_list, list) {
+	list_for_each_entry_rcu(sta, &local->sta_list, list) {
 		skb = skb_dequeue(&sta->ps_tx_buf);
 		if (skb) {
 			purged++;
@@ -338,7 +336,8 @@ static void purge_old_ps_buffers(struct 
 		}
 		total += skb_queue_len(&sta->ps_tx_buf);
 	}
-	read_unlock_bh(&local->sta_lock);
+
+	rcu_read_unlock();
 
 	local->total_ps_buffered = total;
 	printk(KERN_DEBUG "%s: PS buffers full - purged %d frames\n",
@@ -1141,20 +1140,17 @@ static int ieee80211_tx(struct net_devic
 		return 0;
 	}
 
+	rcu_read_lock();
+
 	/* 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;
 	}
 
-	/*
-	 * key references are protected using RCU and this requires that
-	 * we are in a read-site RCU section during receive processing
-	 */
-	rcu_read_lock();
-
 	sta = tx.sta;
 	tx.channel = local->hw.conf.channel;
 
@@ -1167,9 +1163,6 @@ static int ieee80211_tx(struct net_devic
 
 	skb = tx.skb; /* handlers are allowed to change skb */
 
-	if (sta)
-		sta_info_put(sta);
-
 	if (unlikely(res == TX_DROP)) {
 		I802_DEBUG_INC(local->tx_handlers_drop);
 		goto drop;
@@ -1489,11 +1482,11 @@ int ieee80211_subif_start_xmit(struct sk
 	 * in AP mode)
 	 */
 	if (!is_multicast_ether_addr(hdr.addr1)) {
+		rcu_read_lock();
 		sta = sta_info_get(local, hdr.addr1);
-		if (sta) {
+		if (sta)
 			sta_flags = sta->flags;
-			sta_info_put(sta);
-		}
+		rcu_read_unlock();
 	}
 
 	/* receiver is QoS enabled, use a QoS type frame */
@@ -1722,7 +1715,6 @@ static void ieee80211_beacon_add_tim(str
 
 	/* Generate bitmap for TIM only if there are any STAs in power save
 	 * mode. */
-	read_lock_bh(&local->sta_lock);
 	if (atomic_read(&bss->num_sta_ps) > 0)
 		/* in the hope that this is faster than
 		 * checking byte-for-byte */
@@ -1773,7 +1765,6 @@ static void ieee80211_beacon_add_tim(str
 		*pos++ = aid0; /* Bitmap control */
 		*pos++ = 0; /* Part Virt Bitmap */
 	}
-	read_unlock_bh(&local->sta_lock);
 }
 
 struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
@@ -1821,7 +1812,22 @@ struct sk_buff *ieee80211_beacon_get(str
 			ieee80211_include_sequence(sdata,
 					(struct ieee80211_hdr *)skb->data);
 
-			ieee80211_beacon_add_tim(local, ap, skb, beacon);
+			/*
+			 * Not very nice, but we want to allow the driver to call
+			 * ieee80211_beacon_get() as a response to the set_tim()
+			 * callback. That, however, is already invoked under the
+			 * sta_lock to guarantee consistent and race-free update
+			 * of the tim bitmap in mac80211 and the driver.
+			 */
+			if (local->tim_in_locked_section) {
+				ieee80211_beacon_add_tim(local, ap, skb, beacon);
+			} else {
+				unsigned long flags;
+
+				spin_lock_irqsave(&local->sta_lock, flags);
+				ieee80211_beacon_add_tim(local, ap, skb, beacon);
+				spin_unlock_irqrestore(&local->sta_lock, flags);
+			}
 
 			if (beacon->tail)
 				memcpy(skb_put(skb, beacon->tail_len),
@@ -1965,7 +1971,6 @@ ieee80211_get_buffered_bc(struct ieee802
 		rcu_read_unlock();
 		return NULL;
 	}
-	rcu_read_unlock();
 
 	if (bss->dtim_count != 0)
 		return NULL; /* send buffered bc/mc only after DTIM beacon */
@@ -2010,8 +2015,7 @@ ieee80211_get_buffered_bc(struct ieee802
 		skb = NULL;
 	}
 
-	if (sta)
-		sta_info_put(sta);
+	rcu_read_unlock();
 
 	return skb;
 }
--- everything.orig/net/mac80211/cfg.c	2008-02-25 16:15:22.000000000 +0100
+++ everything/net/mac80211/cfg.c	2008-02-25 16:15:22.000000000 +0100
@@ -142,7 +142,6 @@ static int ieee80211_add_key(struct wiph
 	struct ieee80211_sub_if_data *sdata;
 	struct sta_info *sta = NULL;
 	enum ieee80211_key_alg alg;
-	int ret;
 	struct ieee80211_key *key;
 
 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -176,12 +175,7 @@ static int ieee80211_add_key(struct wiph
 
 	ieee80211_key_link(key, sdata, sta);
 
-	ret = 0;
-
-	if (sta)
-		sta_info_put(sta);
-
-	return ret;
+	return 0;
 }
 
 static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
@@ -190,7 +184,6 @@ static int ieee80211_del_key(struct wiph
 	struct ieee80211_sub_if_data *sdata;
 	struct sta_info *sta;
 	int ret;
-	struct ieee80211_key *key;
 
 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
@@ -201,21 +194,18 @@ static int ieee80211_del_key(struct wiph
 
 		ret = 0;
 		if (sta->key) {
-			key = sta->key;
-			ieee80211_key_free(key);
+			ieee80211_key_free(sta->key);
 			WARN_ON(sta->key);
 		} else
 			ret = -ENOENT;
 
-		sta_info_put(sta);
 		return ret;
 	}
 
 	if (!sdata->keys[key_idx])
 		return -ENOENT;
 
-	key = sdata->keys[key_idx];
-	ieee80211_key_free(key);
+	ieee80211_key_free(sdata->keys[key_idx]);
 	WARN_ON(sdata->keys[key_idx]);
 
 	return 0;
@@ -298,8 +288,6 @@ static int ieee80211_get_key(struct wiph
 	err = 0;
 
  out:
-	if (sta)
-		sta_info_put(sta);
 	return err;
 }
 
@@ -317,7 +305,7 @@ static int ieee80211_config_default_key(
 
 static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
 {
-	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	struct ieee80211_sub_if_data *sdata = sta->sdata;
 
 	sinfo->filled = STATION_INFO_INACTIVE_TIME |
 			STATION_INFO_RX_BYTES |
@@ -346,16 +334,20 @@ static int ieee80211_dump_station(struct
 {
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct sta_info *sta;
+	int ret = -ENOENT;
+
+	rcu_read_lock();
 
 	sta = sta_info_get_by_idx(local, idx, dev);
-	if (!sta)
-		return -ENOENT;
+	if (sta) {
+		ret = 0;
+		memcpy(mac, sta->addr, ETH_ALEN);
+		sta_set_sinfo(sta, sinfo);
+	}
 
-	memcpy(mac, sta->addr, ETH_ALEN);
-	sta_set_sinfo(sta, sinfo);
-	sta_info_put(sta);
+	rcu_read_unlock();
 
-	return 0;
+	return ret;
 }
 
 static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
@@ -363,16 +355,21 @@ static int ieee80211_get_station(struct 
 {
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct sta_info *sta;
+	int ret = -ENOENT;
 
-	sta = sta_info_get(local, mac);
-	if (!sta)
-		return -ENOENT;
+	rcu_read_lock();
 
 	/* XXX: verify sta->dev == dev */
-	sta_set_sinfo(sta, sinfo);
-	sta_info_put(sta);
 
-	return 0;
+	sta = sta_info_get(local, mac);
+	if (sta) {
+		ret = 0;
+		sta_set_sinfo(sta, sinfo);
+	}
+
+	rcu_read_unlock();
+
+	return ret;
 }
 
 /*
@@ -565,8 +562,8 @@ static void ieee80211_send_layer2_update
 	msg->xid_info[1] = 1;	/* LLC types/classes: Type 1 LLC */
 	msg->xid_info[2] = 0;	/* XID sender's receive window size (RW) */
 
-	skb->dev = sta->dev;
-	skb->protocol = eth_type_trans(skb, sta->dev);
+	skb->dev = sta->sdata->dev;
+	skb->protocol = eth_type_trans(skb, sta->sdata->dev);
 	memset(skb->cb, 0, sizeof(skb->cb));
 	netif_rx(skb);
 }
@@ -578,7 +575,7 @@ static void sta_apply_parameters(struct 
 	u32 rates;
 	int i, j;
 	struct ieee80211_supported_band *sband;
-	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	struct ieee80211_sub_if_data *sdata = sta->sdata;
 
 	if (params->station_flags & STATION_FLAG_CHANGED) {
 		sta->flags &= ~WLAN_STA_AUTHORIZED;
@@ -650,14 +647,13 @@ static int ieee80211_add_station(struct 
 		sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
 	if (ieee80211_vif_is_mesh(&sdata->vif))
-		sta = mesh_plink_add(mac, DEFAULT_RATES, dev);
+		sta = mesh_plink_add(mac, DEFAULT_RATES, sdata);
 	else
-		sta = sta_info_add(local, dev, mac, GFP_KERNEL);
+		sta = sta_info_add(sdata, mac);
 
 	if (IS_ERR(sta))
 		return PTR_ERR(sta);
 
-	sta->dev = sdata->dev;
 	if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN ||
 	    sdata->vif.type == IEEE80211_IF_TYPE_AP)
 		ieee80211_send_layer2_update(sta);
@@ -668,15 +664,14 @@ static int ieee80211_add_station(struct 
 
 	rate_control_rate_init(sta, local);
 
-	sta_info_put(sta);
-
 	return 0;
 }
 
 static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
 				 u8 *mac)
 {
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_local *local = sdata->local;
 	struct sta_info *sta;
 
 	if (mac) {
@@ -685,10 +680,14 @@ static int ieee80211_del_station(struct 
 		if (!sta)
 			return -ENOENT;
 
-		sta_info_free(sta);
-		sta_info_put(sta);
+		sta_info_unlink(&sta);
+
+		if (sta) {
+			synchronize_rcu();
+			sta_info_destroy(sta);
+		}
 	} else
-		sta_info_flush(local, dev);
+		sta_info_flush(local, sdata);
 
 	return 0;
 }
@@ -707,21 +706,19 @@ static int ieee80211_change_station(stru
 	if (!sta)
 		return -ENOENT;
 
-	if (params->vlan && params->vlan != sta->dev) {
+	if (params->vlan && params->vlan != sta->sdata->dev) {
 		vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
 
 		if (vlansdata->vif.type != IEEE80211_IF_TYPE_VLAN ||
 		    vlansdata->vif.type != IEEE80211_IF_TYPE_AP)
 			return -EINVAL;
 
-		sta->dev = params->vlan;
+		sta->sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
 		ieee80211_send_layer2_update(sta);
 	}
 
 	sta_apply_parameters(local, sta, params);
 
-	sta_info_put(sta);
-
 	return 0;
 }
 
@@ -741,23 +738,26 @@ static int ieee80211_add_mpath(struct wi
 	if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
 		return -ENOTSUPP;
 
+	rcu_read_lock();
 	sta = sta_info_get(local, next_hop);
-	if (!sta)
+	if (!sta) {
+		rcu_read_unlock();
 		return -ENOENT;
+	}
 
 	err = mesh_path_add(dst, dev);
-	if (err)
+	if (err) {
+		rcu_read_unlock();
 		return err;
+	}
 
-	rcu_read_lock();
 	mpath = mesh_path_lookup(dst, dev);
 	if (!mpath) {
 		rcu_read_unlock();
-		sta_info_put(sta);
 		return -ENXIO;
 	}
 	mesh_path_fix_nexthop(mpath, sta);
-	sta_info_put(sta);
+
 	rcu_read_unlock();
 	return 0;
 }
@@ -766,7 +766,7 @@ static int ieee80211_del_mpath(struct wi
 				 u8 *dst)
 {
 	if (dst)
-		return mesh_path_del(dst, dev);
+		return mesh_path_del(dst, dev, false);
 
 	mesh_path_flush(dev);
 	return 0;
@@ -787,20 +787,22 @@ static int ieee80211_change_mpath(struct
 	if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
 		return -ENOTSUPP;
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, next_hop);
-	if (!sta)
+	if (!sta) {
+		rcu_read_unlock();
 		return -ENOENT;
+	}
 
-	rcu_read_lock();
 	mpath = mesh_path_lookup(dst, dev);
 	if (!mpath) {
 		rcu_read_unlock();
-		sta_info_put(sta);
 		return -ENOENT;
 	}
 
 	mesh_path_fix_nexthop(mpath, sta);
-	sta_info_put(sta);
+
 	rcu_read_unlock();
 	return 0;
 }
--- everything.orig/net/mac80211/ieee80211.c	2008-02-25 16:14:09.000000000 +0100
+++ everything/net/mac80211/ieee80211.c	2008-02-25 16:15:22.000000000 +0100
@@ -375,15 +375,19 @@ static int ieee80211_stop(struct net_dev
 
 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-	list_for_each_entry(sta, &local->sta_list, list) {
-		if (sta->dev == dev)
+	rcu_read_lock();
+
+	list_for_each_entry_rcu(sta, &local->sta_list, list) {
+		if (sta->sdata == sdata)
 			for (i = 0; i <  STA_TID_NUM; i++)
-				ieee80211_sta_stop_rx_ba_session(sta->dev,
+				ieee80211_sta_stop_rx_ba_session(sdata->dev,
 						sta->addr, i,
 						WLAN_BACK_RECIPIENT,
 						WLAN_REASON_QSTA_LEAVE_QBSS);
 	}
 
+	rcu_read_unlock();
+
 	netif_stop_queue(dev);
 
 	/*
@@ -449,7 +453,7 @@ static int ieee80211_stop(struct net_dev
 		netif_tx_unlock_bh(local->mdev);
 		break;
 	case IEEE80211_IF_TYPE_MESH_POINT:
-		sta_info_flush(local, dev);
+		sta_info_flush(local, sdata);
 		/* fall through */
 	case IEEE80211_IF_TYPE_STA:
 	case IEEE80211_IF_TYPE_IBSS:
@@ -522,9 +526,12 @@ int ieee80211_start_tx_ba_session(struct
 				print_mac(mac, ra), tid);
 #endif /* CONFIG_MAC80211_HT_DEBUG */
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, ra);
 	if (!sta) {
 		printk(KERN_DEBUG "Could not find the station\n");
+		rcu_read_unlock();
 		return -ENOENT;
 	}
 
@@ -564,7 +571,7 @@ int ieee80211_start_tx_ba_session(struct
 		spin_unlock_bh(&local->mdev->queue_lock);
 		goto start_ba_exit;
 	}
-	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	sdata = sta->sdata;
 
 	/* Ok, the Addba frame hasn't been sent yet, but if the driver calls the
 	 * call back right away, it must see that the flow has begun */
@@ -601,7 +608,7 @@ int ieee80211_start_tx_ba_session(struct
 			sta->ampdu_mlme.dialog_token_allocator;
 	sta->ampdu_mlme.tid_tx[tid].ssn = start_seq_num;
 
-	ieee80211_send_addba_request(sta->dev, ra, tid,
+	ieee80211_send_addba_request(sta->sdata->dev, ra, tid,
 			 sta->ampdu_mlme.tid_tx[tid].dialog_token,
 			 sta->ampdu_mlme.tid_tx[tid].ssn,
 			 0x40, 5000);
@@ -614,7 +621,7 @@ int ieee80211_start_tx_ba_session(struct
 
 start_ba_exit:
 	spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
-	sta_info_put(sta);
+	rcu_read_unlock();
 	return ret;
 }
 EXPORT_SYMBOL(ieee80211_start_tx_ba_session);
@@ -637,9 +644,12 @@ int ieee80211_stop_tx_ba_session(struct 
 				print_mac(mac, ra), tid);
 #endif /* CONFIG_MAC80211_HT_DEBUG */
 
+	rcu_read_lock();
 	sta = sta_info_get(local, ra);
-	if (!sta)
+	if (!sta) {
+		rcu_read_unlock();
 		return -ENOENT;
+	}
 
 	/* check if the TID is in aggregation */
 	state = &sta->ampdu_mlme.tid_tx[tid].state;
@@ -673,7 +683,7 @@ int ieee80211_stop_tx_ba_session(struct 
 
 stop_BA_exit:
 	spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
-	sta_info_put(sta);
+	rcu_read_unlock();
 	return ret;
 }
 EXPORT_SYMBOL(ieee80211_stop_tx_ba_session);
@@ -691,8 +701,10 @@ void ieee80211_start_tx_ba_cb(struct iee
 		return;
 	}
 
+	rcu_read_lock();
 	sta = sta_info_get(local, ra);
 	if (!sta) {
+		rcu_read_unlock();
 		printk(KERN_DEBUG "Could not find station: %s\n",
 				print_mac(mac, ra));
 		return;
@@ -705,7 +717,7 @@ void ieee80211_start_tx_ba_cb(struct iee
 		printk(KERN_DEBUG "addBA was not requested yet, state is %d\n",
 				*state);
 		spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
-		sta_info_put(sta);
+		rcu_read_unlock();
 		return;
 	}
 
@@ -718,7 +730,7 @@ void ieee80211_start_tx_ba_cb(struct iee
 		ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
 	}
 	spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
-	sta_info_put(sta);
+	rcu_read_unlock();
 }
 EXPORT_SYMBOL(ieee80211_start_tx_ba_cb);
 
@@ -739,10 +751,12 @@ void ieee80211_stop_tx_ba_cb(struct ieee
 	printk(KERN_DEBUG "Stop a BA session requested on DA %s tid %d\n",
 				print_mac(mac, ra), tid);
 
+	rcu_read_lock();
 	sta = sta_info_get(local, ra);
 	if (!sta) {
 		printk(KERN_DEBUG "Could not find station: %s\n",
 				print_mac(mac, ra));
+		rcu_read_unlock();
 		return;
 	}
 	state = &sta->ampdu_mlme.tid_tx[tid].state;
@@ -750,13 +764,13 @@ void ieee80211_stop_tx_ba_cb(struct ieee
 	spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
 	if ((*state & HT_AGG_STATE_REQ_STOP_BA_MSK) == 0) {
 		printk(KERN_DEBUG "unexpected callback to A-MPDU stop\n");
-		sta_info_put(sta);
 		spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
+		rcu_read_unlock();
 		return;
 	}
 
 	if (*state & HT_AGG_STATE_INITIATOR_MSK)
-		ieee80211_send_delba(sta->dev, ra, tid,
+		ieee80211_send_delba(sta->sdata->dev, ra, tid,
 			WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
 
 	agg_queue = sta->tid_to_tx_q[tid];
@@ -777,7 +791,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee
 	sta->ampdu_mlme.tid_tx[tid].addba_req_num = 0;
 	spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
 
-	sta_info_put(sta);
+	rcu_read_unlock();
 }
 EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb);
 
@@ -887,32 +901,41 @@ int ieee80211_if_update_wds(struct net_d
 	struct sta_info *sta;
 	DECLARE_MAC_BUF(mac);
 
+	might_sleep();
+
 	if (compare_ether_addr(remote_addr, sdata->u.wds.remote_addr) == 0)
 		return 0;
 
+	rcu_read_lock();
+
 	/* Create STA entry for the new peer */
-	sta = sta_info_add(local, dev, remote_addr, GFP_KERNEL);
-	if (IS_ERR(sta))
+	sta = sta_info_add(sdata, remote_addr);
+	if (IS_ERR(sta)) {
+		rcu_read_unlock();
 		return PTR_ERR(sta);
+	}
 
 	sta->flags |= WLAN_STA_AUTHORIZED;
 
-	sta_info_put(sta);
-
 	/* Remove STA entry for the old peer */
 	sta = sta_info_get(local, sdata->u.wds.remote_addr);
-	if (sta) {
-		sta_info_free(sta);
-		sta_info_put(sta);
-	} else {
+	if (sta)
+		sta_info_unlink(&sta);
+	else
 		printk(KERN_DEBUG "%s: could not find STA entry for WDS link "
 		       "peer %s\n",
 		       dev->name, print_mac(mac, sdata->u.wds.remote_addr));
-	}
 
 	/* Update WDS link data */
 	memcpy(&sdata->u.wds.remote_addr, remote_addr, ETH_ALEN);
 
+	rcu_read_unlock();
+
+	if (sta) {
+		synchronize_rcu();
+		sta_info_destroy(sta);
+	}
+
 	return 0;
 }
 
@@ -1330,6 +1353,8 @@ void ieee80211_tx_status(struct ieee8021
 		return;
 	}
 
+	rcu_read_lock();
+
 	if (status->excessive_retries) {
 		struct sta_info *sta;
 		sta = sta_info_get(local, hdr->addr1);
@@ -1343,10 +1368,9 @@ void ieee80211_tx_status(struct ieee8021
 				status->flags |= IEEE80211_TX_STATUS_TX_FILTERED;
 				ieee80211_handle_filtered_frame(local, sta,
 								skb, status);
-				sta_info_put(sta);
+				rcu_read_unlock();
 				return;
 			}
-			sta_info_put(sta);
 		}
 	}
 
@@ -1356,12 +1380,14 @@ void ieee80211_tx_status(struct ieee8021
 		if (sta) {
 			ieee80211_handle_filtered_frame(local, sta, skb,
 							status);
-			sta_info_put(sta);
+			rcu_read_unlock();
 			return;
 		}
 	} else
 		rate_control_tx_status(local->mdev, skb, status);
 
+	rcu_read_unlock();
+
 	ieee80211_led_tx(local, 0);
 
 	/* SNMP counters
--- everything.orig/net/mac80211/rc80211_pid_algo.c	2008-02-25 16:15:22.000000000 +0100
+++ everything/net/mac80211/rc80211_pid_algo.c	2008-02-25 16:15:22.000000000 +0100
@@ -77,7 +77,7 @@ static void rate_control_pid_adjust_rate
 	int cur_sorted, new_sorted, probe, tmp, n_bitrates, band;
 	int cur = sta->txrate_idx;
 
-	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	sdata = sta->sdata;
 	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
 	band = sband->band;
 	n_bitrates = sband->n_bitrates;
@@ -149,7 +149,7 @@ static void rate_control_pid_sample(stru
 				    struct sta_info *sta)
 {
 #ifdef CONFIG_MAC80211_MESH
-	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	struct ieee80211_sub_if_data *sdata = sta->sdata;
 #endif
 	struct rc_pid_sta_info *spinfo = sta->rate_ctrl_priv;
 	struct rc_pid_rateinfo *rinfo = pinfo->rinfo;
@@ -249,23 +249,25 @@ static void rate_control_pid_tx_status(v
 	unsigned long period;
 	struct ieee80211_supported_band *sband;
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, hdr->addr1);
 	sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
 
 	if (!sta)
-		return;
+		goto unlock;
 
 	/* Don't update the state if we're not controlling the rate. */
-	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	sdata = sta->sdata;
 	if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) {
 		sta->txrate_idx = sdata->bss->max_ratectrl_rateidx;
-		return;
+		goto unlock;
 	}
 
 	/* 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])
-		goto ignore;
+		goto unlock;
 
 	spinfo = sta->rate_ctrl_priv;
 	spinfo->tx_num_xmit++;
@@ -303,8 +305,8 @@ static void rate_control_pid_tx_status(v
 	if (time_after(jiffies, spinfo->last_sample + period))
 		rate_control_pid_sample(pinfo, local, sta);
 
-ignore:
-	sta_info_put(sta);
+ unlock:
+	rcu_read_unlock();
 }
 
 static void rate_control_pid_get_rate(void *priv, struct net_device *dev,
@@ -319,6 +321,8 @@ static void rate_control_pid_get_rate(vo
 	int rateidx;
 	u16 fc;
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, hdr->addr1);
 
 	/* Send management frames and broadcast/multicast data using lowest
@@ -327,8 +331,7 @@ static void rate_control_pid_get_rate(vo
 	if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
 	    is_multicast_ether_addr(hdr->addr1) || !sta) {
 		sel->rate = rate_lowest(local, sband, sta);
-		if (sta)
-			sta_info_put(sta);
+		rcu_read_unlock();
 		return;
 	}
 
@@ -344,7 +347,7 @@ static void rate_control_pid_get_rate(vo
 
 	sta->last_txrate_idx = rateidx;
 
-	sta_info_put(sta);
+	rcu_read_unlock();
 
 	sel->rate = &sband->bitrates[rateidx];
 
--- everything.orig/net/mac80211/rc80211_simple.c	2008-02-25 16:15:22.000000000 +0100
+++ everything/net/mac80211/rc80211_simple.c	2008-02-25 16:15:22.000000000 +0100
@@ -40,7 +40,7 @@ static void rate_control_rate_inc(struct
 	int i = sta->txrate_idx;
 	int maxrate;
 
-	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	sdata = sta->sdata;
 	if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) {
 		/* forced unicast rate - do not change STA rate */
 		return;
@@ -70,7 +70,7 @@ static void rate_control_rate_dec(struct
 	struct ieee80211_supported_band *sband;
 	int i = sta->txrate_idx;
 
-	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	sdata = sta->sdata;
 	if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) {
 		/* forced unicast rate - do not change STA rate */
 		return;
@@ -118,10 +118,12 @@ static void rate_control_simple_tx_statu
 	struct sta_info *sta;
 	struct sta_rate_control *srctrl;
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, hdr->addr1);
 
 	if (!sta)
-	    return;
+		goto unlock;
 
 	srctrl = sta->rate_ctrl_priv;
 	srctrl->tx_num_xmit++;
@@ -191,7 +193,8 @@ static void rate_control_simple_tx_statu
 		}
 	}
 
-	sta_info_put(sta);
+ unlock:
+	rcu_read_unlock();
 }
 
 
@@ -208,6 +211,8 @@ rate_control_simple_get_rate(void *priv,
 	int rateidx;
 	u16 fc;
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, hdr->addr1);
 
 	/* Send management frames and broadcast/multicast data using lowest
@@ -216,8 +221,7 @@ rate_control_simple_get_rate(void *priv,
 	if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
 	    is_multicast_ether_addr(hdr->addr1) || !sta) {
 		sel->rate = rate_lowest(local, sband, sta);
-		if (sta)
-			sta_info_put(sta);
+		rcu_read_unlock();
 		return;
 	}
 
@@ -233,7 +237,7 @@ rate_control_simple_get_rate(void *priv,
 
 	sta->last_txrate_idx = rateidx;
 
-	sta_info_put(sta);
+	rcu_read_unlock();
 
 	sel->rate = &sband->bitrates[rateidx];
 }
--- everything.orig/net/mac80211/rx.c	2008-02-25 16:15:22.000000000 +0100
+++ everything/net/mac80211/rx.c	2008-02-25 16:15:22.000000000 +0100
@@ -631,7 +631,7 @@ static void ap_sta_ps_start(struct net_d
 	struct ieee80211_sub_if_data *sdata;
 	DECLARE_MAC_BUF(mac);
 
-	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	sdata = sta->sdata;
 
 	if (sdata->bss)
 		atomic_inc(&sdata->bss->num_sta_ps);
@@ -652,7 +652,7 @@ static int ap_sta_ps_end(struct net_devi
 	struct ieee80211_tx_packet_data *pkt_data;
 	DECLARE_MAC_BUF(mac);
 
-	sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	sdata = sta->sdata;
 
 	if (sdata->bss)
 		atomic_dec(&sdata->bss->num_sta_ps);
@@ -1287,7 +1287,7 @@ ieee80211_deliver_skb(struct ieee80211_r
 				       "multicast frame\n", dev->name);
 		} else {
 			dsta = sta_info_get(local, skb->data);
-			if (dsta && dsta->dev == dev) {
+			if (dsta && dsta->sdata->dev == dev) {
 				/*
 				 * The destination station is associated to
 				 * this AP (in this VLAN), so send the frame
@@ -1297,8 +1297,6 @@ ieee80211_deliver_skb(struct ieee80211_r
 				xmit_skb = skb;
 				skb = NULL;
 			}
-			if (dsta)
-				sta_info_put(dsta);
 		}
 	}
 
@@ -1905,13 +1903,13 @@ static void __ieee80211_rx_handle_packet
 
 	rx.sta = sta_info_get(local, hdr->addr2);
 	if (rx.sta) {
-		rx.dev = rx.sta->dev;
-		rx.sdata = IEEE80211_DEV_TO_SUB_IF(rx.dev);
+		rx.sdata = rx.sta->sdata;
+		rx.dev = rx.sta->sdata->dev;
 	}
 
 	if ((status->flag & RX_FLAG_MMIC_ERROR)) {
 		ieee80211_rx_michael_mic_report(local->mdev, hdr, &rx);
-		goto end;
+		return;
 	}
 
 	if (unlikely(local->sta_sw_scanning || local->sta_hw_scanning))
@@ -1970,10 +1968,6 @@ static void __ieee80211_rx_handle_packet
 		ieee80211_invoke_rx_handlers(prev, &rx, skb);
 	} else
 		dev_kfree_skb(skb);
-
- end:
-	if (rx.sta)
-		sta_info_put(rx.sta);
 }
 
 #define SEQ_MODULO 0x1000
@@ -2150,7 +2144,7 @@ static u8 ieee80211_rx_reorder_ampdu(str
 	/* if this mpdu is fragmented - terminate rx aggregation session */
 	sc = le16_to_cpu(hdr->seq_ctrl);
 	if (sc & IEEE80211_SCTL_FRAG) {
-		ieee80211_sta_stop_rx_ba_session(sta->dev, sta->addr,
+		ieee80211_sta_stop_rx_ba_session(sta->sdata->dev, sta->addr,
 			tid, 0, WLAN_REASON_QSTA_REQUIRE_SETUP);
 		ret = 1;
 		goto end_reorder;
@@ -2160,9 +2154,7 @@ static u8 ieee80211_rx_reorder_ampdu(str
 	mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4;
 	ret = ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb,
 						mpdu_seq_num, 0);
-end_reorder:
-	if (sta)
-		sta_info_put(sta);
+ end_reorder:
 	return ret;
 }
 
--- everything.orig/net/mac80211/ieee80211_ioctl.c	2008-02-25 16:15:22.000000000 +0100
+++ everything/net/mac80211/ieee80211_ioctl.c	2008-02-25 16:15:22.000000000 +0100
@@ -33,8 +33,7 @@ static int ieee80211_set_encryption(stru
 				    size_t key_len)
 {
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	int ret;
-	struct sta_info *sta = NULL;
+	struct sta_info *sta;
 	struct ieee80211_key *key;
 	struct ieee80211_sub_if_data *sdata;
 
@@ -51,24 +50,23 @@ static int ieee80211_set_encryption(stru
 			key = sdata->keys[idx];
 		} else {
 			sta = sta_info_get(local, sta_addr);
-			if (!sta) {
-				ret = -ENOENT;
-				key = NULL;
-				goto err_out;
-			}
-
+			if (!sta)
+				return -ENOENT;
 			key = sta->key;
 		}
 
 		if (!key)
-			ret = -ENOENT;
-		else
-			ret = 0;
+			return -ENOENT;
+
+		ieee80211_key_free(key);
+		return 0;
 	} else {
 		key = ieee80211_key_alloc(alg, idx, key_len, _key);
 		if (!key)
 			return -ENOMEM;
 
+		sta = NULL;
+
 		if (!is_broadcast_ether_addr(sta_addr)) {
 			set_tx_key = 0;
 			/*
@@ -78,14 +76,14 @@ static int ieee80211_set_encryption(stru
 			 * work around this.
 			 */
 			if (idx != 0 && alg != ALG_WEP) {
-				ret = -EINVAL;
-				goto err_out;
+				ieee80211_key_free(key);
+				return -EINVAL;
 			}
 
 			sta = sta_info_get(local, sta_addr);
 			if (!sta) {
-				ret = -ENOENT;
-				goto err_out;
+				ieee80211_key_free(key);
+				return -ENOENT;
 			}
 		}
 
@@ -93,18 +91,9 @@ static int ieee80211_set_encryption(stru
 
 		if (set_tx_key || (!sta && !sdata->default_key && key))
 			ieee80211_set_default_key(sdata, idx);
-
-		/* don't free key later */
-		key = NULL;
-
-		ret = 0;
 	}
 
- err_out:
-	if (sta)
-		sta_info_put(sta);
-	ieee80211_key_free(key);
-	return ret;
+	return 0;
 }
 
 static int ieee80211_ioctl_siwgenie(struct net_device *dev,
@@ -628,7 +617,7 @@ static int ieee80211_ioctl_giwrate(struc
 	else
 		rate->value = 0;
 	rate->value *= 100000;
-	sta_info_put(sta);
+
 	return 0;
 }
 
@@ -1003,7 +992,6 @@ static struct iw_statistics *ieee80211_g
 		wstats->qual.qual = sta->last_signal;
 		wstats->qual.noise = sta->last_noise;
 		wstats->qual.updated = local->wstats_flags;
-		sta_info_put(sta);
 	}
 	return wstats;
 }
--- everything.orig/net/mac80211/ieee80211_iface.c	2008-02-25 16:15:22.000000000 +0100
+++ everything/net/mac80211/ieee80211_iface.c	2008-02-25 16:15:22.000000000 +0100
@@ -240,16 +240,21 @@ void ieee80211_if_reinit(struct net_devi
 		break;
 	}
 	case IEEE80211_IF_TYPE_WDS:
+		rcu_read_lock();
 		sta = sta_info_get(local, sdata->u.wds.remote_addr);
 		if (sta) {
-			sta_info_free(sta);
-			sta_info_put(sta);
+			sta_info_unlink(&sta);
 		} else {
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
 			printk(KERN_DEBUG "%s: Someone had deleted my STA "
 			       "entry for the WDS link\n", dev->name);
 #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
 		}
+		rcu_read_unlock();
+		if (sta) {
+			synchronize_rcu();
+			sta_info_destroy(sta);
+		}
 		break;
 	case IEEE80211_IF_TYPE_MESH_POINT:
 	case IEEE80211_IF_TYPE_STA:
@@ -275,7 +280,7 @@ void ieee80211_if_reinit(struct net_devi
 	}
 
 	/* remove all STAs that are bound to this virtual interface */
-	sta_info_flush(local, dev);
+	sta_info_flush(local, sdata);
 
 	memset(&sdata->u, 0, sizeof(sdata->u));
 	ieee80211_if_sdata_init(sdata);
--- everything.orig/net/mac80211/ieee80211_rate.c	2008-02-25 16:14:08.000000000 +0100
+++ everything/net/mac80211/ieee80211_rate.c	2008-02-25 16:15:22.000000000 +0100
@@ -170,9 +170,12 @@ void rate_control_get_rate(struct net_de
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct rate_control_ref *ref = local->rate_ctrl;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
-	struct sta_info *sta = sta_info_get(local, hdr->addr1);
+	struct sta_info *sta;
 	int i;
 
+	rcu_read_lock();
+	sta = sta_info_get(local, hdr->addr1);
+
 	memset(sel, 0, sizeof(struct rate_selection));
 
 	ref->ops->get_rate(ref->priv, dev, sband, skb, sel);
@@ -190,8 +193,7 @@ void rate_control_get_rate(struct net_de
 		}
 	}
 
-	if (sta)
-		sta_info_put(sta);
+	rcu_read_unlock();
 }
 
 struct rate_control_ref *rate_control_get(struct rate_control_ref *ref)
--- everything.orig/net/mac80211/ieee80211_rate.h	2008-02-25 16:14:08.000000000 +0100
+++ everything/net/mac80211/ieee80211_rate.h	2008-02-25 16:15:22.000000000 +0100
@@ -14,6 +14,7 @@
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
 #include <linux/types.h>
+#include <linux/kref.h>
 #include <net/mac80211.h>
 #include "ieee80211_i.h"
 #include "sta_info.h"
--- everything.orig/net/mac80211/key.c	2008-02-25 16:15:22.000000000 +0100
+++ everything/net/mac80211/key.c	2008-02-25 16:15:22.000000000 +0100
@@ -233,14 +233,17 @@ void ieee80211_key_link(struct ieee80211
 		if (sdata->vif.type == IEEE80211_IF_TYPE_STA) {
 			struct sta_info *ap;
 
+			rcu_read_lock();
+
 			/* same here, the AP could be using QoS */
 			ap = sta_info_get(key->local, key->sdata->u.sta.bssid);
 			if (ap) {
 				if (ap->flags & WLAN_STA_WME)
 					key->conf.flags |=
 						IEEE80211_KEY_FLAG_WMM_STA;
-				sta_info_put(ap);
 			}
+
+			rcu_read_unlock();
 		}
 	}
 
@@ -277,6 +280,9 @@ void ieee80211_key_free(struct ieee80211
 		 */
 		__ieee80211_key_replace(key->sdata, key->sta, key, NULL);
 
+		/*
+		 * Do NOT remove this without looking at sta_info_destroy()
+		 */
 		synchronize_rcu();
 
 		/*
--- everything.orig/net/mac80211/debugfs_sta.c	2008-02-25 16:15:22.000000000 +0100
+++ everything/net/mac80211/debugfs_sta.c	2008-02-25 16:15:22.000000000 +0100
@@ -51,7 +51,7 @@ static const struct file_operations sta_
 		STA_OPS(name)
 
 STA_FILE(aid, aid, D);
-STA_FILE(dev, dev->name, S);
+STA_FILE(dev, sdata->dev->name, S);
 STA_FILE(rx_packets, rx_packets, LU);
 STA_FILE(tx_packets, tx_packets, LU);
 STA_FILE(rx_bytes, rx_bytes, LU);
@@ -200,7 +200,7 @@ static ssize_t sta_agg_status_write(stru
 		const char __user *user_buf, size_t count, loff_t *ppos)
 {
 	struct sta_info *sta = file->private_data;
-	struct net_device *dev = sta->dev;
+	struct net_device *dev = sta->sdata->dev;
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct ieee80211_hw *hw = &local->hw;
 	u8 *da = sta->addr;
--- everything.orig/net/mac80211/wme.c	2008-02-25 16:14:08.000000000 +0100
+++ everything/net/mac80211/wme.c	2008-02-25 16:15:22.000000000 +0100
@@ -153,6 +153,7 @@ static int wme_qdiscop_enqueue(struct sk
 
 	if (pkt_data->flags & IEEE80211_TXPD_REQUEUE) {
 		queue = pkt_data->queue;
+		rcu_read_lock();
 		sta = sta_info_get(local, hdr->addr1);
 		tid = skb->priority & QOS_CONTROL_TAG1D_MASK;
 		if (sta) {
@@ -164,8 +165,8 @@ static int wme_qdiscop_enqueue(struct sk
 			} else {
 				pkt_data->flags &= ~IEEE80211_TXPD_AMPDU;
 			}
-			sta_info_put(sta);
 		}
+		rcu_read_unlock();
 		skb_queue_tail(&q->requeued[queue], skb);
 		qd->q.qlen++;
 		return 0;
@@ -187,6 +188,8 @@ static int wme_qdiscop_enqueue(struct sk
 		p++;
 		*p = 0;
 
+		rcu_read_lock();
+
 		sta = sta_info_get(local, hdr->addr1);
 		if (sta) {
 			int ampdu_queue = sta->tid_to_tx_q[tid];
@@ -197,8 +200,9 @@ static int wme_qdiscop_enqueue(struct sk
 			} else {
 				pkt_data->flags &= ~IEEE80211_TXPD_AMPDU;
 			}
-			sta_info_put(sta);
 		}
+
+		rcu_read_unlock();
 	}
 
 	if (unlikely(queue >= local->hw.queues)) {
--- everything.orig/net/mac80211/debugfs_sta.h	2008-02-25 16:14:08.000000000 +0100
+++ everything/net/mac80211/debugfs_sta.h	2008-02-25 16:15:22.000000000 +0100
@@ -1,6 +1,8 @@
 #ifndef __MAC80211_DEBUGFS_STA_H
 #define __MAC80211_DEBUGFS_STA_H
 
+#include "sta_info.h"
+
 #ifdef CONFIG_MAC80211_DEBUGFS
 void ieee80211_sta_debugfs_add(struct sta_info *sta);
 void ieee80211_sta_debugfs_remove(struct sta_info *sta);
--- everything.orig/drivers/net/wireless/iwlwifi/iwl-3945-rs.c	2008-02-25 16:14:09.000000000 +0100
+++ everything/drivers/net/wireless/iwlwifi/iwl-3945-rs.c	2008-02-25 16:15:22.000000000 +0100
@@ -471,10 +471,11 @@ static void rs_tx_status(void *priv_rate
 		return;
 	}
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, hdr->addr1);
 	if (!sta || !sta->rate_ctrl_priv) {
-		if (sta)
-			sta_info_put(sta);
+		rcu_read_unlock();
 		IWL_DEBUG_RATE("leave: No STA priv data to update!\n");
 		return;
 	}
@@ -547,7 +548,7 @@ static void rs_tx_status(void *priv_rate
 
 	spin_unlock_irqrestore(&rs_sta->lock, flags);
 
-	sta_info_put(sta);
+	rcu_read_unlock();
 
 	IWL_DEBUG_RATE("leave\n");
 
@@ -658,6 +659,8 @@ static void rs_get_rate(void *priv_rate,
 
 	IWL_DEBUG_RATE("enter\n");
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, hdr->addr1);
 
 	/* Send management frames and broadcast/multicast data using lowest
@@ -668,8 +671,7 @@ static void rs_get_rate(void *priv_rate,
 	    !sta || !sta->rate_ctrl_priv) {
 		IWL_DEBUG_RATE("leave: No STA priv data to update!\n");
 		sel->rate = rate_lowest(local, band, sta);
-		if (sta)
-			sta_info_put(sta);
+		rcu_read_unlock();
 		return;
 	}
 
@@ -811,7 +813,7 @@ static void rs_get_rate(void *priv_rate,
 	else
 		sta->txrate_idx = sta->last_txrate_idx;
 
-	sta_info_put(sta);
+	rcu_read_unlock();
 
 	IWL_DEBUG_RATE("leave: %d\n", index);
 
@@ -843,13 +845,15 @@ int iwl3945_fill_rs_info(struct ieee8021
 	unsigned long now = jiffies;
 	u32 max_time = 0;
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr);
 	if (!sta || !sta->rate_ctrl_priv) {
-		if (sta) {
-			sta_info_put(sta);
+		if (sta)
 			IWL_DEBUG_RATE("leave - no private rate data!\n");
-		} else
+		else
 			IWL_DEBUG_RATE("leave - no station!\n");
+		rcu_read_unlock();
 		return sprintf(buf, "station %d not found\n", sta_id);
 	}
 
@@ -890,7 +894,7 @@ int iwl3945_fill_rs_info(struct ieee8021
 		i = j;
 	}
 	spin_unlock_irqrestore(&rs_sta->lock, flags);
-	sta_info_put(sta);
+	rcu_read_unlock();
 
 	/* Display the average rate of all samples taken.
 	 *
@@ -927,11 +931,12 @@ void iwl3945_rate_scale_init(struct ieee
 		return;
 	}
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr);
 	if (!sta || !sta->rate_ctrl_priv) {
-		if (sta)
-			sta_info_put(sta);
 		IWL_DEBUG_RATE("leave - no private rate data!\n");
+		rcu_read_unlock();
 		return;
 	}
 
@@ -958,7 +963,7 @@ void iwl3945_rate_scale_init(struct ieee
 		break;
 	}
 
-	sta_info_put(sta);
+	rcu_read_unlock();
 	spin_unlock_irqrestore(&rs_sta->lock, flags);
 
 	rssi = priv->last_rx_rssi;
--- everything.orig/drivers/net/wireless/iwlwifi/iwl-4965-rs.c	2008-02-25 16:14:09.000000000 +0100
+++ everything/drivers/net/wireless/iwlwifi/iwl-4965-rs.c	2008-02-25 16:15:22.000000000 +0100
@@ -847,12 +847,12 @@ static void rs_tx_status(void *priv_rate
 	if (retries > 15)
 		retries = 15;
 
+	rcu_read_lock();
 
 	sta = sta_info_get(local, hdr->addr1);
 
 	if (!sta || !sta->rate_ctrl_priv) {
-		if (sta)
-			sta_info_put(sta);
+		rcu_read_unlock();
 		return;
 	}
 
@@ -891,7 +891,7 @@ static void rs_tx_status(void *priv_rate
 	if ((rs_index < 0) || (rs_index >= IWL_RATE_COUNT)) {
 		IWL_DEBUG_RATE("bad rate index at: %d rate 0x%X\n",
 			     rs_index, tx_mcs.rate_n_flags);
-		sta_info_put(sta);
+		rcu_read_unlock();
 		return;
 	}
 
@@ -909,7 +909,7 @@ static void rs_tx_status(void *priv_rate
 		IWL_DEBUG_RATE("initial rate does not match 0x%x 0x%x\n",
 				tx_mcs.rate_n_flags,
 				le32_to_cpu(table->rs_table[0].rate_n_flags));
-		sta_info_put(sta);
+		rcu_read_unlock();
 		return;
 	}
 
@@ -1025,7 +1025,7 @@ static void rs_tx_status(void *priv_rate
 
 	/* See if there's a better rate or modulation mode to try. */
 	rs_rate_scale_perform(priv, dev, hdr, sta);
-	sta_info_put(sta);
+	rcu_read_unlock();
 	return;
 }
 
@@ -2219,6 +2219,8 @@ static void rs_get_rate(void *priv_rate,
 
 	IWL_DEBUG_RATE_LIMIT("rate scale calculate new rate for skb\n");
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, hdr->addr1);
 
 	/* Send management frames and broadcast/multicast data using lowest
@@ -2227,8 +2229,7 @@ static void rs_get_rate(void *priv_rate,
 	if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1) ||
 	    !sta || !sta->rate_ctrl_priv) {
 		sel->rate = rate_lowest(local, sband, sta);
-		if (sta)
-			sta_info_put(sta);
+		rcu_read_unlock();
 		return;
 	}
 
@@ -2261,7 +2262,7 @@ static void rs_get_rate(void *priv_rate,
 		sel->rate = rate_lowest(local, sband, sta);
 		return;
 	}
-	sta_info_put(sta);
+	rcu_read_unlock();
 
 	sel->rate = &priv->ieee_rates[i];
 }
@@ -2735,13 +2736,15 @@ int iwl4965_fill_rs_info(struct ieee8021
 	u32 max_time = 0;
 	u8 lq_type, antenna;
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr);
 	if (!sta || !sta->rate_ctrl_priv) {
-		if (sta) {
-			sta_info_put(sta);
+		if (sta)
 			IWL_DEBUG_RATE("leave - no private rate data!\n");
-		} else
+		else
 			IWL_DEBUG_RATE("leave - no station!\n");
+		rcu_read_unlock();
 		return sprintf(buf, "station %d not found\n", sta_id);
 	}
 
@@ -2808,7 +2811,7 @@ int iwl4965_fill_rs_info(struct ieee8021
 			 "active_search %d rate index %d\n", lq_type, antenna,
 			 lq_sta->search_better_tbl, sta->last_txrate_idx);
 
-	sta_info_put(sta);
+	rcu_read_unlock();
 	return cnt;
 }
 
--- everything.orig/net/mac80211/mesh.h	2008-02-25 16:14:08.000000000 +0100
+++ everything/net/mac80211/mesh.h	2008-02-25 16:15:22.000000000 +0100
@@ -65,9 +65,10 @@ enum mesh_path_flags {
  * @state_lock: mesh pat state lock
  *
  *
- * The combination of dst and dev is unique in the mesh path table. A reference
- * to the next_hop sta will be kept and in case this sta is removed, the
- * mesh_path structure must be also removed or substitued in a rcu safe way
+ * The combination of dst and dev is unique in the mesh path table. Since the
+ * next_hop STA is only protected by RCU as well, deleting the STA must also
+ * remove/substitute the mesh_path structure and wait until that is no longer
+ * reachable before destroying the STA completely.
  */
 struct mesh_path {
 	u8 dst[ETH_ALEN];
@@ -230,8 +231,9 @@ void mesh_neighbour_update(u8 *hw_addr, 
 		bool add);
 bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie,
 			      struct net_device *dev);
-void mesh_accept_plinks_update(struct net_device *dev);
-struct sta_info *mesh_plink_add(u8 *hw_addr, u64 rates, struct net_device *dev);
+void mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata);
+struct sta_info *mesh_plink_add(u8 *hw_addr, u64 rates,
+				struct ieee80211_sub_if_data *sdata);
 void mesh_plink_broken(struct sta_info *sta);
 void mesh_plink_deactivate(struct sta_info *sta);
 int mesh_plink_open(struct sta_info *sta);
@@ -254,7 +256,7 @@ void mesh_path_flush_pending(struct mesh
 void mesh_path_tx_pending(struct mesh_path *mpath);
 int mesh_pathtbl_init(void);
 void mesh_pathtbl_unregister(void);
-int mesh_path_del(u8 *addr, struct net_device *dev);
+int mesh_path_del(u8 *addr, struct net_device *dev, bool force);
 void mesh_path_timer(unsigned long data);
 void mesh_path_flush_by_nexthop(struct sta_info *sta);
 void mesh_path_discard_frame(struct sk_buff *skb, struct net_device *dev);
@@ -270,7 +272,7 @@ static inline int mesh_plink_free_count(
 
 static inline bool mesh_plink_availables(struct ieee80211_sub_if_data *sdata)
 {
-	return (min(mesh_plink_free_count(sdata),
+	return (min_t(long, mesh_plink_free_count(sdata),
 		   MESH_MAX_PLINKS - sdata->local->num_sta)) > 0;
 }
 
--- everything.orig/net/mac80211/mesh.c	2008-02-25 16:14:08.000000000 +0100
+++ everything/net/mac80211/mesh.c	2008-02-25 16:15:22.000000000 +0100
@@ -83,11 +83,10 @@ bool mesh_peer_accepts_plinks(struct iee
 /**
  * mesh_accept_plinks_update: update accepting_plink in local mesh beacons
  *
- * @dev: mesh interface in which mesh beacons are going to be updated
+ * @sdata: mesh interface in which mesh beacons are going to be updated
  */
-void mesh_accept_plinks_update(struct net_device *dev)
+void mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata)
 {
-	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	bool free_plinks;
 
 	/* In case mesh_plink_free_count > 0 and mesh_plinktbl_capacity == 0,
--- everything.orig/net/mac80211/mesh_pathtbl.c	2008-02-25 16:14:15.000000000 +0100
+++ everything/net/mac80211/mesh_pathtbl.c	2008-02-25 16:19:30.000000000 +0100
@@ -55,10 +55,7 @@ static DEFINE_RWLOCK(pathtbl_resize_lock
  */
 void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta)
 {
-	__sta_info_get(sta);
-	if (mpath->next_hop)
-		sta_info_put(mpath->next_hop);
-	mpath->next_hop = sta;
+	rcu_assign_pointer(mpath->next_hop, sta);
 }
 
 
@@ -236,7 +233,7 @@ void mesh_plink_broken(struct sta_info *
 	struct mesh_path *mpath;
 	struct mpath_node *node;
 	struct hlist_node *p;
-	struct net_device *dev = sta->dev;
+	struct net_device *dev = sta->sdata->dev;
 	int i;
 
 	rcu_read_lock();
@@ -266,9 +263,9 @@ EXPORT_SYMBOL(mesh_plink_broken);
  *
  * RCU notes: this function is called when a mesh plink transitions from ESTAB
  * to any other state, since ESTAB state is the only one that allows path
- * creation. This will happen before the sta can be freed (since we hold
- * a reference to it) so any reader in a rcu read block will be protected
- * against the plink dissapearing.
+ * creation. This will happen before the sta can be freed (because
+ * sta_info_destroy() calls this) so any reader in a rcu read block will be
+ * protected against the plink disappearing.
  */
 void mesh_path_flush_by_nexthop(struct sta_info *sta)
 {
@@ -280,7 +277,7 @@ void mesh_path_flush_by_nexthop(struct s
 	for_each_mesh_entry(mesh_paths, p, node, i) {
 		mpath = node->mpath;
 		if (mpath->next_hop == sta)
-			mesh_path_del(mpath->dst, mpath->dev);
+			mesh_path_del(mpath->dst, mpath->dev, true);
 	}
 }
 
@@ -294,7 +291,7 @@ void mesh_path_flush(struct net_device *
 	for_each_mesh_entry(mesh_paths, p, node, i) {
 		mpath = node->mpath;
 		if (mpath->dev == dev)
-			mesh_path_del(mpath->dst, mpath->dev);
+			mesh_path_del(mpath->dst, mpath->dev, false);
 	}
 }
 
@@ -303,8 +300,8 @@ static void mesh_path_node_reclaim(struc
 	struct mpath_node *node = container_of(rp, struct mpath_node, rcu);
 	struct ieee80211_sub_if_data *sdata =
 		IEEE80211_DEV_TO_SUB_IF(node->mpath->dev);
-	if (node->mpath->next_hop)
-		sta_info_put(node->mpath->next_hop);
+
+	rcu_assign_pointer(node->mpath->next_hop, NULL);
 	atomic_dec(&sdata->u.sta.mpaths);
 	kfree(node->mpath);
 	kfree(node);
@@ -319,9 +316,10 @@ static void mesh_path_node_reclaim(struc
  * Returns: 0 if succesful
  *
  * State: if the path is being resolved, the deletion will be postponed until
- * the path resolution completes or times out.
+ * the path resolution completes or times out, unless the force parameter
+ * is given.
  */
-int mesh_path_del(u8 *addr, struct net_device *dev)
+int mesh_path_del(u8 *addr, struct net_device *dev, bool force)
 {
 	struct mesh_path *mpath;
 	struct mpath_node *node;
@@ -340,7 +338,7 @@ int mesh_path_del(u8 *addr, struct net_d
 		if (mpath->dev == dev &&
 				memcmp(addr, mpath->dst, ETH_ALEN) == 0) {
 			spin_lock_bh(&mpath->state_lock);
-			if (mpath->flags & MESH_PATH_RESOLVING) {
+			if (!force && mpath->flags & MESH_PATH_RESOLVING) {
 				mpath->flags |= MESH_PATH_DELETE;
 			} else {
 				mpath->flags |= MESH_PATH_RESOLVING;
@@ -510,7 +508,7 @@ void mesh_path_expire(struct net_device 
 			time_after(jiffies,
 			 mpath->exp_time + MESH_PATH_EXPIRE)) {
 			spin_unlock_bh(&mpath->state_lock);
-			mesh_path_del(mpath->dst, mpath->dev);
+			mesh_path_del(mpath->dst, mpath->dev, false);
 		} else
 			spin_unlock_bh(&mpath->state_lock);
 	}
--- everything.orig/net/mac80211/mesh_plink.c	2008-02-25 16:14:08.000000000 +0100
+++ everything/net/mac80211/mesh_plink.c	2008-02-25 16:15:22.000000000 +0100
@@ -65,14 +65,14 @@ static inline
 void mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata)
 {
 	atomic_inc(&sdata->u.sta.mshstats.estab_plinks);
-	mesh_accept_plinks_update(sdata->dev);
+	mesh_accept_plinks_update(sdata);
 }
 
 static inline
 void mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata)
 {
 	atomic_dec(&sdata->u.sta.mshstats.estab_plinks);
-	mesh_accept_plinks_update(sdata->dev);
+	mesh_accept_plinks_update(sdata);
 }
 
 /**
@@ -99,12 +99,13 @@ static inline void mesh_plink_fsm_restar
  *
  * Returns: non-NULL on success, ERR_PTR() on error.
  */
-struct sta_info *mesh_plink_add(u8 *hw_addr, u64 rates, struct net_device *dev)
+struct sta_info *mesh_plink_add(u8 *hw_addr, u64 rates,
+				struct ieee80211_sub_if_data *sdata)
 {
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_local *local = sdata->local;
 	struct sta_info *sta;
 
-	if (memcmp(hw_addr, dev->dev_addr, ETH_ALEN) == 0)
+	if (compare_ether_addr(hw_addr, sdata->dev->dev_addr) == 0)
 		/* never add ourselves as neighbours */
 		return ERR_PTR(-EINVAL);
 
@@ -114,7 +115,7 @@ struct sta_info *mesh_plink_add(u8 *hw_a
 	if (local->num_sta >= MESH_MAX_PLINKS)
 		return ERR_PTR(-ENOSPC);
 
-	sta = sta_info_add(local, dev, hw_addr, GFP_KERNEL);
+	sta = sta_info_add(sdata, hw_addr);
 	if (IS_ERR(sta))
 		return sta;
 
@@ -125,7 +126,7 @@ struct sta_info *mesh_plink_add(u8 *hw_a
 	sta->supp_rates[local->hw.conf.channel->band] = rates;
 	rate_control_rate_init(sta, local);
 
-	mesh_accept_plinks_update(dev);
+	mesh_accept_plinks_update(sdata);
 
 	return sta;
 }
@@ -141,7 +142,8 @@ struct sta_info *mesh_plink_add(u8 *hw_a
  */
 static void __mesh_plink_deactivate(struct sta_info *sta)
 {
-	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	struct ieee80211_sub_if_data *sdata = sta->sdata;
+
 	if (sta->plink_state == ESTAB)
 		mesh_plink_dec_estab_count(sdata);
 	sta->plink_state = BLOCKED;
@@ -246,11 +248,15 @@ void mesh_neighbour_update(u8 *hw_addr, 
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct sta_info *sta;
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, hw_addr);
 	if (!sta) {
-		sta = mesh_plink_add(hw_addr, rates, dev);
-		if (IS_ERR(sta))
+		sta = mesh_plink_add(hw_addr, rates, sdata);
+		if (IS_ERR(sta)) {
+			rcu_read_unlock();
 			return;
+		}
 	}
 
 	sta->last_rx = jiffies;
@@ -260,7 +266,7 @@ void mesh_neighbour_update(u8 *hw_addr, 
 			sdata->u.sta.mshcfg.auto_open_plinks)
 		mesh_plink_open(sta);
 
-	sta_info_put(sta);
+	rcu_read_unlock();
 }
 
 static void mesh_plink_timer(unsigned long data)
@@ -273,6 +279,11 @@ static void mesh_plink_timer(unsigned lo
 	DECLARE_MAC_BUF(mac);
 #endif
 
+	/*
+	 * This STA is valid because sta_info_destroy() will
+	 * del_timer_sync() this timer after having made sure
+	 * it cannot be readded (by deleting the plink.)
+	 */
 	sta = (struct sta_info *) data;
 
 	spin_lock_bh(&sta->plink_lock);
@@ -286,8 +297,8 @@ static void mesh_plink_timer(unsigned lo
 	reason = 0;
 	llid = sta->llid;
 	plid = sta->plid;
-	dev = sta->dev;
-	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	sdata = sta->sdata;
+	dev = sdata->dev;
 
 	switch (sta->plink_state) {
 	case OPN_RCVD:
@@ -302,8 +313,7 @@ static void mesh_plink_timer(unsigned lo
 			sta->plink_timeout = sta->plink_timeout +
 					     rand % sta->plink_timeout;
 			++sta->plink_retries;
-			if (!mod_plink_timer(sta, sta->plink_timeout))
-				__sta_info_get(sta);
+			mod_plink_timer(sta, sta->plink_timeout);
 			spin_unlock_bh(&sta->plink_lock);
 			mesh_plink_frame_tx(dev, PLINK_OPEN, sta->addr, llid,
 					    0, 0);
@@ -316,16 +326,14 @@ static void mesh_plink_timer(unsigned lo
 		if (!reason)
 			reason = cpu_to_le16(MESH_CONFIRM_TIMEOUT);
 		sta->plink_state = HOLDING;
-		if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)))
-			__sta_info_get(sta);
+		mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
 		spin_unlock_bh(&sta->plink_lock);
 		mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid, plid,
 				    reason);
 		break;
 	case HOLDING:
 		/* holding timer */
-		if (del_timer(&sta->plink_timer))
-			sta_info_put(sta);
+		del_timer(&sta->plink_timer);
 		mesh_plink_fsm_restart(sta);
 		spin_unlock_bh(&sta->plink_lock);
 		break;
@@ -333,8 +341,6 @@ static void mesh_plink_timer(unsigned lo
 		spin_unlock_bh(&sta->plink_lock);
 		break;
 	}
-
-	sta_info_put(sta);
 }
 
 static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)
@@ -343,14 +349,13 @@ static inline void mesh_plink_timer_set(
 	sta->plink_timer.data = (unsigned long) sta;
 	sta->plink_timer.function = mesh_plink_timer;
 	sta->plink_timeout = timeout;
-	__sta_info_get(sta);
 	add_timer(&sta->plink_timer);
 }
 
 int mesh_plink_open(struct sta_info *sta)
 {
 	__le16 llid;
-	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	struct ieee80211_sub_if_data *sdata = sta->sdata;
 #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
 	DECLARE_MAC_BUF(mac);
 #endif
@@ -360,7 +365,6 @@ int mesh_plink_open(struct sta_info *sta
 	sta->llid = llid;
 	if (sta->plink_state != LISTEN) {
 		spin_unlock_bh(&sta->plink_lock);
-		sta_info_put(sta);
 		return -EBUSY;
 	}
 	sta->plink_state = OPN_SNT;
@@ -369,7 +373,8 @@ int mesh_plink_open(struct sta_info *sta
 	mpl_dbg("Mesh plink: starting establishment with %s\n",
 		print_mac(mac, sta->addr));
 
-	return mesh_plink_frame_tx(sta->dev, PLINK_OPEN, sta->addr, llid, 0, 0);
+	return mesh_plink_frame_tx(sdata->dev, PLINK_OPEN,
+				   sta->addr, llid, 0, 0);
 }
 
 void mesh_plink_block(struct sta_info *sta)
@@ -386,7 +391,7 @@ void mesh_plink_block(struct sta_info *s
 
 int mesh_plink_close(struct sta_info *sta)
 {
-	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+	struct ieee80211_sub_if_data *sdata = sta->sdata;
 	int llid, plid, reason;
 #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
 	DECLARE_MAC_BUF(mac);
@@ -401,13 +406,11 @@ int mesh_plink_close(struct sta_info *st
 	if (sta->plink_state == LISTEN || sta->plink_state == BLOCKED) {
 		mesh_plink_fsm_restart(sta);
 		spin_unlock_bh(&sta->plink_lock);
-		sta_info_put(sta);
 		return 0;
 	} else if (sta->plink_state == ESTAB) {
 		__mesh_plink_deactivate(sta);
 		/* The timer should not be running */
-		if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)))
-			__sta_info_get(sta);
+		mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
 	} else if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)))
 		sta->ignore_plink_timer = true;
 
@@ -415,15 +418,16 @@ int mesh_plink_close(struct sta_info *st
 	llid = sta->llid;
 	plid = sta->plid;
 	spin_unlock_bh(&sta->plink_lock);
-	mesh_plink_frame_tx(sta->dev, PLINK_CLOSE, sta->addr, llid, plid,
-			    reason);
+	mesh_plink_frame_tx(sta->sdata->dev, PLINK_CLOSE, sta->addr, llid,
+			    plid, reason);
 	return 0;
 }
 
 void mesh_rx_plink_frame(struct net_device *dev, struct ieee80211_mgmt *mgmt,
 			 size_t len, struct ieee80211_rx_status *rx_status)
 {
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_local *local = sdata->local;
 	struct ieee802_11_elems elems;
 	struct sta_info *sta;
 	enum plink_event event;
@@ -435,7 +439,6 @@ void mesh_rx_plink_frame(struct net_devi
 #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
 	DECLARE_MAC_BUF(mac);
 #endif
-	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
 	if (is_multicast_ether_addr(mgmt->da)) {
 		mpl_dbg("Mesh plink: ignore frame from multicast address");
@@ -474,14 +477,17 @@ void mesh_rx_plink_frame(struct net_devi
 	if (ftype == PLINK_CONFIRM || (ftype == PLINK_CLOSE && ie_len == 7))
 		memcpy(&llid, PLINK_GET_PLID(elems.peer_link), 2);
 
+	rcu_read_lock();
+
 	sta = sta_info_get(local, mgmt->sa);
 	if (!sta && ftype != PLINK_OPEN) {
 		mpl_dbg("Mesh plink: cls or cnf from unknown peer\n");
+		rcu_read_unlock();
 		return;
 	}
 
 	if (sta && sta->plink_state == BLOCKED) {
-		sta_info_put(sta);
+		rcu_read_unlock();
 		return;
 	}
 
@@ -505,13 +511,15 @@ void mesh_rx_plink_frame(struct net_devi
 		u64 rates;
 		if (!mesh_plink_free_count(sdata)) {
 			mpl_dbg("Mesh plink error: no more free plinks\n");
+			rcu_read_unlock();
 			return;
 		}
 
 		rates = ieee80211_sta_get_rates(local, &elems, rx_status->band);
-		sta = mesh_plink_add(mgmt->sa, rates, dev);
+		sta = mesh_plink_add(mgmt->sa, rates, sdata);
 		if (IS_ERR(sta)) {
 			mpl_dbg("Mesh plink error: plink table full\n");
+			rcu_read_unlock();
 			return;
 		}
 		event = OPN_ACPT;
@@ -521,14 +529,14 @@ void mesh_rx_plink_frame(struct net_devi
 		switch (ftype) {
 		case PLINK_OPEN:
 			if (!mesh_plink_free_count(sdata) ||
-					(sta->plid && sta->plid != plid))
+			    (sta->plid && sta->plid != plid))
 				event = OPN_IGNR;
 			else
 				event = OPN_ACPT;
 			break;
 		case PLINK_CONFIRM:
 			if (!mesh_plink_free_count(sdata) ||
-				(sta->llid != llid || sta->plid != plid))
+			    (sta->llid != llid || sta->plid != plid))
 				event = CNF_IGNR;
 			else
 				event = CNF_ACPT;
@@ -555,7 +563,7 @@ void mesh_rx_plink_frame(struct net_devi
 		default:
 			mpl_dbg("Mesh plink: unknown frame subtype\n");
 			spin_unlock_bh(&sta->plink_lock);
-			sta_info_put(sta);
+			rcu_read_unlock();
 			return;
 		}
 	}
@@ -659,8 +667,7 @@ void mesh_rx_plink_frame(struct net_devi
 					    plid, 0);
 			break;
 		case CNF_ACPT:
-			if (del_timer(&sta->plink_timer))
-				sta_info_put(sta);
+			del_timer(&sta->plink_timer);
 			sta->plink_state = ESTAB;
 			mesh_plink_inc_estab_count(sdata);
 			spin_unlock_bh(&sta->plink_lock);
@@ -693,8 +700,7 @@ void mesh_rx_plink_frame(struct net_devi
 					    plid, reason);
 			break;
 		case OPN_ACPT:
-			if (del_timer(&sta->plink_timer))
-				sta_info_put(sta);
+			del_timer(&sta->plink_timer);
 			sta->plink_state = ESTAB;
 			mesh_plink_inc_estab_count(sdata);
 			spin_unlock_bh(&sta->plink_lock);
@@ -717,9 +723,7 @@ void mesh_rx_plink_frame(struct net_devi
 			__mesh_plink_deactivate(sta);
 			sta->plink_state = HOLDING;
 			llid = sta->llid;
-			if (!mod_plink_timer(sta,
-					dot11MeshHoldingTimeout(sdata)))
-				__sta_info_get(sta);
+			mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
 			spin_unlock_bh(&sta->plink_lock);
 			mesh_plink_frame_tx(dev, PLINK_CLOSE, sta->addr, llid,
 					    plid, reason);
@@ -738,10 +742,8 @@ void mesh_rx_plink_frame(struct net_devi
 	case HOLDING:
 		switch (event) {
 		case CLS_ACPT:
-			if (del_timer(&sta->plink_timer)) {
+			if (del_timer(&sta->plink_timer))
 				sta->ignore_plink_timer = 1;
-				sta_info_put(sta);
-			}
 			mesh_plink_fsm_restart(sta);
 			spin_unlock_bh(&sta->plink_lock);
 			break;
@@ -766,5 +768,6 @@ void mesh_rx_plink_frame(struct net_devi
 		spin_unlock_bh(&sta->plink_lock);
 		break;
 	}
-	sta_info_put(sta);
+
+	rcu_read_unlock();
 }
--- everything.orig/net/mac80211/mesh_hwmp.c	2008-02-25 16:14:08.000000000 +0100
+++ everything/net/mac80211/mesh_hwmp.c	2008-02-25 16:15:22.000000000 +0100
@@ -294,7 +294,6 @@ static u32 hwmp_route_info_get(struct ne
 		orig_metric = PREP_IE_METRIC(hwmp_ie);
 		break;
 	default:
-		sta_info_put(sta);
 		rcu_read_unlock();
 		return 0;
 	}
@@ -330,7 +329,6 @@ static u32 hwmp_route_info_get(struct ne
 			mpath = mesh_path_lookup(orig_addr, dev);
 			if (!mpath) {
 				rcu_read_unlock();
-				sta_info_put(sta);
 				return 0;
 			}
 			spin_lock_bh(&mpath->state_lock);
@@ -372,7 +370,6 @@ static u32 hwmp_route_info_get(struct ne
 			mpath = mesh_path_lookup(ta, dev);
 			if (!mpath) {
 				rcu_read_unlock();
-				sta_info_put(sta);
 				return 0;
 			}
 			spin_lock_bh(&mpath->state_lock);
@@ -391,7 +388,6 @@ static u32 hwmp_route_info_get(struct ne
 			spin_unlock_bh(&mpath->state_lock);
 	}
 
-	sta_info_put(sta);
 	rcu_read_unlock();
 
 	return process ? new_metric : 0;
@@ -861,5 +857,5 @@ void mesh_path_timer(unsigned long data)
 endmpathtimer:
 	rcu_read_unlock();
 	if (delete)
-		mesh_path_del(mpath->dst, mpath->dev);
+		mesh_path_del(mpath->dst, mpath->dev, false);
 }

-- 


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

* [PATCH 07/10] mac80211: split sta_info_add
  2008-02-25 15:27 [PATCH 00/10] mac80211 locking/sta info work Johannes Berg
                   ` (5 preceding siblings ...)
  2008-02-25 15:27 ` [PATCH 06/10] mac80211: RCU-ify STA info structure access Johannes Berg
@ 2008-02-25 15:27 ` Johannes Berg
  2008-02-25 15:27 ` [PATCH 08/10] mac80211: clean up sta_info and document locking Johannes Berg
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 16+ messages in thread
From: Johannes Berg @ 2008-02-25 15:27 UTC (permalink / raw)
  To: John Linville; +Cc: linux-wireless, Luis Carlos Cobo

sta_info_add() has two functions: allocating a station info
structure and inserting it into the hash table/list. Splitting
these two functions allows allocating with GFP_KERNEL in many
places instead of GFP_ATOMIC which is now required by the RCU
protection. Additionally, in many places RCU protection is now
no longer needed at all because between sta_info_alloc() and
sta_info_insert() the caller owns the structure.

This fixes a few race conditions with setting initial flags
and similar, but not all (see comments in ieee80211_sta.c and
cfg.c). More documentation on the existing races will be in
a follow-up patch.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
 net/mac80211/cfg.c           |   42 +++++++++++++++++++------
 net/mac80211/ieee80211.c     |   18 ++++++----
 net/mac80211/ieee80211_sta.c |   44 +++++++++++++++++++-------
 net/mac80211/mesh.h          |    4 +-
 net/mac80211/mesh_plink.c    |   43 ++++++++++++++-----------
 net/mac80211/sta_info.c      |   72 +++++++++++++++++++++++++++++--------------
 net/mac80211/sta_info.h      |   17 +++++++---
 7 files changed, 166 insertions(+), 74 deletions(-)

--- everything.orig/net/mac80211/sta_info.c	2008-02-25 16:25:11.000000000 +0100
+++ everything/net/mac80211/sta_info.c	2008-02-25 16:25:12.000000000 +0100
@@ -31,12 +31,13 @@
  * for faster lookup and a list for iteration. They are managed using
  * RCU, i.e. access to the list and hash table is protected by RCU.
  *
- * STA info structures are always "alive" when they are added with
- * @sta_info_add() [this may be changed in the future to allow allocating
- * outside of a critical section!], they are then added to the hash
- * table and list. Therefore, @sta_info_add() must also be RCU protected,
- * also, the caller of @sta_info_add() cannot assume that it owns the
- * structure.
+ * Upon allocating a STA info structure with @sta_info_alloc() or
+ * mesh_plink_alloc(), the caller owns that structure. It must then either
+ * destroy it using @sta_info_destroy() (which is pretty useless) or insert
+ * it into the hash table using @sta_info_insert() which demotes the reference
+ * from ownership to a regular RCU-protected reference; if the function
+ * is called without protection by an RCU critical section the reference
+ * is instantly invalidated.
  *
  * Because there are debugfs entries for each station, and adding those
  * must be able to sleep, it is also possible to "pin" a station entry,
@@ -131,6 +132,10 @@ void sta_info_destroy(struct sta_info *s
 	struct ieee80211_local *local = sta->local;
 	struct sk_buff *skb;
 	int i;
+	DECLARE_MAC_BUF(mbuf);
+
+	if (!sta)
+		return;
 
 	ASSERT_RTNL();
 	might_sleep();
@@ -167,6 +172,11 @@ void sta_info_destroy(struct sta_info *s
 	rate_control_free_sta(sta->rate_ctrl, sta->rate_ctrl_priv);
 	rate_control_put(sta->rate_ctrl);
 
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+	printk(KERN_DEBUG "%s: Destroyed STA %s\n",
+	       wiphy_name(local->hw.wiphy), print_mac(mbuf, sta->addr));
+#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
+
 	kfree(sta);
 }
 
@@ -179,18 +189,17 @@ static void sta_info_hash_add(struct iee
 	rcu_assign_pointer(local->sta_hash[STA_HASH(sta->addr)], sta);
 }
 
-struct sta_info *sta_info_add(struct ieee80211_sub_if_data *sdata,
-			      u8 *addr)
+struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
+				u8 *addr, gfp_t gfp)
 {
 	struct ieee80211_local *local = sdata->local;
 	struct sta_info *sta;
 	int i;
-	DECLARE_MAC_BUF(mac);
-	unsigned long flags;
+	DECLARE_MAC_BUF(mbuf);
 
-	sta = kzalloc(sizeof(*sta), GFP_ATOMIC);
+	sta = kzalloc(sizeof(*sta), gfp);
 	if (!sta)
-		return ERR_PTR(-ENOMEM);
+		return NULL;
 
 	memcpy(sta->addr, addr, ETH_ALEN);
 	sta->local = local;
@@ -198,11 +207,11 @@ struct sta_info *sta_info_add(struct iee
 
 	sta->rate_ctrl = rate_control_get(local->rate_ctrl);
 	sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl,
-						     GFP_ATOMIC);
+						     gfp);
 	if (!sta->rate_ctrl_priv) {
 		rate_control_put(sta->rate_ctrl);
 		kfree(sta);
-		return ERR_PTR(-ENOMEM);
+		return NULL;
 	}
 
 	spin_lock_init(&sta->ampdu_mlme.ampdu_rx);
@@ -229,11 +238,27 @@ struct sta_info *sta_info_add(struct iee
 	}
 	skb_queue_head_init(&sta->ps_tx_buf);
 	skb_queue_head_init(&sta->tx_filtered);
+
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+	printk(KERN_DEBUG "%s: Allocated STA %s\n",
+	       wiphy_name(local->hw.wiphy), print_mac(mbuf, sta->addr));
+#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
+
+	return sta;
+}
+
+int sta_info_insert(struct sta_info *sta)
+{
+	struct ieee80211_local *local = sta->local;
+	struct ieee80211_sub_if_data *sdata = sta->sdata;
+	unsigned long flags;
+	DECLARE_MAC_BUF(mac);
+
 	spin_lock_irqsave(&local->sta_lock, flags);
 	/* check if STA exists already */
-	if (__sta_info_find(local, addr)) {
+	if (__sta_info_find(local, sta->addr)) {
 		spin_unlock_irqrestore(&local->sta_lock, flags);
-		return ERR_PTR(-EEXIST);
+		return -EEXIST;
 	}
 	list_add(&sta->list, &local->sta_list);
 	local->num_sta++;
@@ -245,16 +270,16 @@ struct sta_info *sta_info_add(struct iee
 			sdata = sdata->u.vlan.ap;
 
 		local->ops->sta_notify(local_to_hw(local), &sdata->vif,
-				       STA_NOTIFY_ADD, addr);
+				       STA_NOTIFY_ADD, sta->addr);
 	}
 
-	spin_unlock_irqrestore(&local->sta_lock, flags);
-
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-	printk(KERN_DEBUG "%s: Added STA %s\n",
-	       wiphy_name(local->hw.wiphy), print_mac(mac, addr));
+	printk(KERN_DEBUG "%s: Inserted STA %s\n",
+	       wiphy_name(local->hw.wiphy), print_mac(mac, sta->addr));
 #endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
 
+	spin_unlock_irqrestore(&local->sta_lock, flags);
+
 #ifdef CONFIG_MAC80211_DEBUGFS
 	/* debugfs entry adding might sleep, so schedule process
 	 * context task for adding entry for STAs that do not yet
@@ -262,7 +287,10 @@ struct sta_info *sta_info_add(struct iee
 	queue_work(local->hw.workqueue, &local->sta_debugfs_add);
 #endif
 
-	return sta;
+	if (ieee80211_vif_is_mesh(&sdata->vif))
+		mesh_accept_plinks_update(sdata);
+
+	return 0;
 }
 
 static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid)
--- everything.orig/net/mac80211/sta_info.h	2008-02-25 16:25:11.000000000 +0100
+++ everything/net/mac80211/sta_info.h	2008-02-25 16:25:12.000000000 +0100
@@ -283,12 +283,19 @@ struct sta_info *sta_info_get(struct iee
 struct sta_info *sta_info_get_by_idx(struct ieee80211_local *local, int idx,
 				      struct net_device *dev);
 /*
- * Add a new STA info, must be under RCU read lock
- * because otherwise the returned reference isn't
- * necessarily valid long enough.
+ * Create a new STA info, caller owns returned structure
+ * until sta_info_insert().
  */
-struct sta_info *sta_info_add(struct ieee80211_sub_if_data *sdata,
-			      u8 *addr);
+struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
+				u8 *addr, gfp_t gfp);
+/*
+ * Insert STA info into hash table/list, returns zero or a
+ * -EEXIST if (if the same MAC address is already present).
+ *
+ * Calling this without RCU protection makes the caller
+ * relinquish its reference to @sta.
+ */
+int sta_info_insert(struct sta_info *sta);
 /*
  * Unlink a STA info from the hash table/list.
  * This can NULL the STA pointer if somebody else
--- everything.orig/net/mac80211/cfg.c	2008-02-25 16:25:11.000000000 +0100
+++ everything/net/mac80211/cfg.c	2008-02-25 16:25:12.000000000 +0100
@@ -577,6 +577,12 @@ static void sta_apply_parameters(struct 
 	struct ieee80211_supported_band *sband;
 	struct ieee80211_sub_if_data *sdata = sta->sdata;
 
+	/*
+	 * FIXME: updating the flags is racy when this function is
+	 *	  called from ieee80211_change_station(), this will
+	 *	  be resolved in a future patch.
+	 */
+
 	if (params->station_flags & STATION_FLAG_CHANGED) {
 		sta->flags &= ~WLAN_STA_AUTHORIZED;
 		if (params->station_flags & STATION_FLAG_AUTHORIZED)
@@ -591,6 +597,13 @@ static void sta_apply_parameters(struct 
 			sta->flags |= WLAN_STA_WME;
 	}
 
+	/*
+	 * FIXME: updating the following information is racy when this
+	 *	  function is called from ieee80211_change_station().
+	 *	  However, all this information should be static so
+	 *	  maybe we should just reject attemps to change it.
+	 */
+
 	if (params->aid) {
 		sta->aid = params->aid;
 		if (sta->aid > IEEE80211_MAX_AID)
@@ -632,6 +645,7 @@ static int ieee80211_add_station(struct 
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct sta_info *sta;
 	struct ieee80211_sub_if_data *sdata;
+	int err;
 
 	/* Prevent a race with changing the rate control algorithm */
 	if (!netif_running(dev))
@@ -647,16 +661,11 @@ static int ieee80211_add_station(struct 
 		sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
 	if (ieee80211_vif_is_mesh(&sdata->vif))
-		sta = mesh_plink_add(mac, DEFAULT_RATES, sdata);
+		sta = mesh_plink_alloc(sdata, mac, DEFAULT_RATES, GFP_KERNEL);
 	else
-		sta = sta_info_add(sdata, mac);
-
-	if (IS_ERR(sta))
-		return PTR_ERR(sta);
-
-	if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN ||
-	    sdata->vif.type == IEEE80211_IF_TYPE_AP)
-		ieee80211_send_layer2_update(sta);
+		sta = sta_info_alloc(sdata, mac, GFP_KERNEL);
+	if (!sta)
+		return -ENOMEM;
 
 	sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC;
 
@@ -664,6 +673,21 @@ static int ieee80211_add_station(struct 
 
 	rate_control_rate_init(sta, local);
 
+	rcu_read_lock();
+
+	err = sta_info_insert(sta);
+	if (err) {
+		sta_info_destroy(sta);
+		rcu_read_unlock();
+		return err;
+	}
+
+	if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN ||
+	    sdata->vif.type == IEEE80211_IF_TYPE_AP)
+		ieee80211_send_layer2_update(sta);
+
+	rcu_read_unlock();
+
 	return 0;
 }
 
--- everything.orig/net/mac80211/ieee80211.c	2008-02-25 16:25:11.000000000 +0100
+++ everything/net/mac80211/ieee80211.c	2008-02-25 16:25:12.000000000 +0100
@@ -899,6 +899,7 @@ int ieee80211_if_update_wds(struct net_d
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	struct sta_info *sta;
+	int err;
 	DECLARE_MAC_BUF(mac);
 
 	might_sleep();
@@ -906,16 +907,19 @@ int ieee80211_if_update_wds(struct net_d
 	if (compare_ether_addr(remote_addr, sdata->u.wds.remote_addr) == 0)
 		return 0;
 
-	rcu_read_lock();
-
 	/* Create STA entry for the new peer */
-	sta = sta_info_add(sdata, remote_addr);
-	if (IS_ERR(sta)) {
-		rcu_read_unlock();
-		return PTR_ERR(sta);
-	}
+	sta = sta_info_alloc(sdata, remote_addr, GFP_KERNEL);
+	if (!sta)
+		return -ENOMEM;
 
 	sta->flags |= WLAN_STA_AUTHORIZED;
+	err = sta_info_insert(sta);
+	if (err) {
+		sta_info_destroy(sta);
+		return err;
+	}
+
+	rcu_read_lock();
 
 	/* Remove STA entry for the old peer */
 	sta = sta_info_get(local, sdata->u.wds.remote_addr);
--- everything.orig/net/mac80211/ieee80211_sta.c	2008-02-25 16:25:11.000000000 +0100
+++ everything/net/mac80211/ieee80211_sta.c	2008-02-25 16:25:12.000000000 +0100
@@ -1453,7 +1453,7 @@ void sta_addba_resp_timer_expired(unsign
 {
 	/* not an elegant detour, but there is no choice as the timer passes
 	 * only one argument, and both sta_info and TID are needed, so init
-	 * flow in sta_info_add gives the TID as data, while the timer_to_id
+	 * flow in sta_info_create gives the TID as data, while the timer_to_id
 	 * array gives the sta through container_of */
 	u16 tid = *(int *)data;
 	struct sta_info *temp_sta = container_of((void *)data,
@@ -1504,7 +1504,7 @@ void sta_rx_agg_session_timer_expired(un
 {
 	/* not an elegant detour, but there is no choice as the timer passes
 	 * only one argument, and verious sta_info are needed here, so init
-	 * flow in sta_info_add gives the TID as data, while the timer_to_id
+	 * flow in sta_info_create gives the TID as data, while the timer_to_id
 	 * array gives the sta through container_of */
 	u8 *ptid = (u8 *)data;
 	u8 *timer_to_id = ptid - *ptid;
@@ -1828,11 +1828,12 @@ static void ieee80211_rx_mgmt_assoc_resp
 	sta = sta_info_get(local, ifsta->bssid);
 	if (!sta) {
 		struct ieee80211_sta_bss *bss;
+		int err;
 
-		sta = sta_info_add(sdata, ifsta->bssid);
-		if (IS_ERR(sta)) {
-			printk(KERN_DEBUG "%s: failed to add STA entry for the"
-			       " AP (error %ld)\n", dev->name, PTR_ERR(sta));
+		sta = sta_info_alloc(sdata, ifsta->bssid, GFP_ATOMIC);
+		if (!sta) {
+			printk(KERN_DEBUG "%s: failed to alloc STA entry for"
+			       " the AP\n", dev->name);
 			rcu_read_unlock();
 			return;
 		}
@@ -1845,8 +1846,27 @@ static void ieee80211_rx_mgmt_assoc_resp
 			sta->last_noise = bss->noise;
 			ieee80211_rx_bss_put(dev, bss);
 		}
+
+		err = sta_info_insert(sta);
+		if (err) {
+			printk(KERN_DEBUG "%s: failed to insert STA entry for"
+			       " the AP (error %d)\n", dev->name, err);
+			sta_info_destroy(sta);
+			rcu_read_unlock();
+			return;
+		}
 	}
 
+	/*
+	 * FIXME: Do we really need to update the sta_info's information here?
+	 *	  We already know about the AP (we found it in our list) so it
+	 *	  should already be filled with the right info, no?
+	 *	  As is stands, all this is racy because typically we assume
+	 *	  the information that is filled in here (except flags) doesn't
+	 *	  change while a STA structure is alive. As such, it should move
+	 *	  to between the sta_info_alloc() and sta_info_insert() above.
+	 */
+
 	sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_AP |
 		      WLAN_STA_AUTHORIZED;
 
@@ -2587,10 +2607,8 @@ static void ieee80211_rx_bss_info(struct
 				       dev->name, print_mac(mac, mgmt->bssid));
 #endif /* CONFIG_MAC80211_IBSS_DEBUG */
 			ieee80211_sta_join_ibss(dev, &sdata->u.sta, bss);
-			rcu_read_lock();
 			ieee80211_ibss_add_sta(dev, NULL,
 					       mgmt->bssid, mgmt->sa);
-			rcu_read_unlock();
 		}
 	}
 
@@ -4022,7 +4040,6 @@ int ieee80211_sta_set_extra_ie(struct ne
 }
 
 
-/* must be called under RCU read lock */
 struct sta_info * ieee80211_ibss_add_sta(struct net_device *dev,
 					 struct sk_buff *skb, u8 *bssid,
 					 u8 *addr)
@@ -4045,8 +4062,8 @@ struct sta_info * ieee80211_ibss_add_sta
 	printk(KERN_DEBUG "%s: Adding new IBSS station %s (dev=%s)\n",
 	       wiphy_name(local->hw.wiphy), print_mac(mac, addr), dev->name);
 
-	sta = sta_info_add(sdata, addr);
-	if (IS_ERR(sta))
+	sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
+	if (!sta)
 		return NULL;
 
 	sta->flags |= WLAN_STA_AUTHORIZED;
@@ -4056,6 +4073,11 @@ struct sta_info * ieee80211_ibss_add_sta
 
 	rate_control_rate_init(sta, local);
 
+	if (sta_info_insert(sta)) {
+		sta_info_destroy(sta);
+		return NULL;
+	}
+
 	return sta;
 }
 
--- everything.orig/net/mac80211/mesh.h	2008-02-25 16:25:11.000000000 +0100
+++ everything/net/mac80211/mesh.h	2008-02-25 16:25:12.000000000 +0100
@@ -232,8 +232,8 @@ void mesh_neighbour_update(u8 *hw_addr, 
 bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie,
 			      struct net_device *dev);
 void mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata);
-struct sta_info *mesh_plink_add(u8 *hw_addr, u64 rates,
-				struct ieee80211_sub_if_data *sdata);
+struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
+				  u8 *hw_addr, u64 rates, gfp_t gfp);
 void mesh_plink_broken(struct sta_info *sta);
 void mesh_plink_deactivate(struct sta_info *sta);
 int mesh_plink_open(struct sta_info *sta);
--- everything.orig/net/mac80211/mesh_plink.c	2008-02-25 16:25:11.000000000 +0100
+++ everything/net/mac80211/mesh_plink.c	2008-02-25 16:25:12.000000000 +0100
@@ -89,44 +89,41 @@ static inline void mesh_plink_fsm_restar
 }
 
 /**
- * mesh_plink_add - allocate and add a new mesh peer link
+ * mesh_plink_alloc - allocate a new mesh peer link
  *
+ * @sdata: local mesh interface
  * @hw_addr: hardware address (ETH_ALEN length)
  * @rates: rates the mesh peer supports
- * @dev: local mesh interface
  *
  * The initial state of the new plink is set to LISTEN
  *
- * Returns: non-NULL on success, ERR_PTR() on error.
+ * Returns: NULL on error.
  */
-struct sta_info *mesh_plink_add(u8 *hw_addr, u64 rates,
-				struct ieee80211_sub_if_data *sdata)
+struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
+				  u8 *hw_addr, u64 rates, gfp_t gfp)
 {
 	struct ieee80211_local *local = sdata->local;
 	struct sta_info *sta;
 
 	if (compare_ether_addr(hw_addr, sdata->dev->dev_addr) == 0)
 		/* never add ourselves as neighbours */
-		return ERR_PTR(-EINVAL);
+		return NULL;
 
 	if (is_multicast_ether_addr(hw_addr))
-		return ERR_PTR(-EINVAL);
+		return NULL;
 
 	if (local->num_sta >= MESH_MAX_PLINKS)
-		return ERR_PTR(-ENOSPC);
+		return NULL;
 
-	sta = sta_info_add(sdata, hw_addr);
-	if (IS_ERR(sta))
-		return sta;
+	sta = sta_info_alloc(sdata, hw_addr, gfp);
+	if (!sta)
+		return NULL;
 
 	sta->plink_state = LISTEN;
 	spin_lock_init(&sta->plink_lock);
 	init_timer(&sta->plink_timer);
 	sta->flags |= WLAN_STA_AUTHORIZED;
 	sta->supp_rates[local->hw.conf.channel->band] = rates;
-	rate_control_rate_init(sta, local);
-
-	mesh_accept_plinks_update(sdata);
 
 	return sta;
 }
@@ -252,8 +249,13 @@ void mesh_neighbour_update(u8 *hw_addr, 
 
 	sta = sta_info_get(local, hw_addr);
 	if (!sta) {
-		sta = mesh_plink_add(hw_addr, rates, sdata);
-		if (IS_ERR(sta)) {
+		sta = mesh_plink_alloc(sdata, hw_addr, rates, GFP_ATOMIC);
+		if (!sta) {
+			rcu_read_unlock();
+			return;
+		}
+		if (sta_info_insert(sta)) {
+			sta_info_destroy(sta);
 			rcu_read_unlock();
 			return;
 		}
@@ -516,12 +518,17 @@ void mesh_rx_plink_frame(struct net_devi
 		}
 
 		rates = ieee80211_sta_get_rates(local, &elems, rx_status->band);
-		sta = mesh_plink_add(mgmt->sa, rates, sdata);
-		if (IS_ERR(sta)) {
+		sta = mesh_plink_alloc(sdata, mgmt->sa, rates, GFP_ATOMIC);
+		if (!sta) {
 			mpl_dbg("Mesh plink error: plink table full\n");
 			rcu_read_unlock();
 			return;
 		}
+		if (sta_info_insert(sta)) {
+			sta_info_destroy(sta);
+			rcu_read_unlock();
+			return;
+		}
 		event = OPN_ACPT;
 		spin_lock_bh(&sta->plink_lock);
 	} else {

-- 


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

* [PATCH 08/10] mac80211: clean up sta_info and document locking
  2008-02-25 15:27 [PATCH 00/10] mac80211 locking/sta info work Johannes Berg
                   ` (6 preceding siblings ...)
  2008-02-25 15:27 ` [PATCH 07/10] mac80211: split sta_info_add Johannes Berg
@ 2008-02-25 15:27 ` Johannes Berg
  2008-02-25 15:27 ` [PATCH 09/10] mac80211: remove STA entries when taking down interface Johannes Berg
  2008-02-25 15:27 ` [PATCH 10/10] b43: verify sta_notify mac80211 callback Johannes Berg
  9 siblings, 0 replies; 16+ messages in thread
From: Johannes Berg @ 2008-02-25 15:27 UTC (permalink / raw)
  To: John Linville; +Cc: linux-wireless, Luis Carlos Cobo

This patch cleans up the sta_info struct and documents how
each set of variables is locked. Notably, flags locking is
completely missing. It also adds kernel-doc for some (but
not all yet) members of the struct.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
 net/mac80211/debugfs_sta.c |    2 
 net/mac80211/sta_info.h    |  135 +++++++++++++++++++++++++++------------------
 2 files changed, 83 insertions(+), 54 deletions(-)

--- everything.orig/net/mac80211/sta_info.h	2008-02-24 12:29:22.000000000 +0100
+++ everything/net/mac80211/sta_info.h	2008-02-24 12:42:49.000000000 +0100
@@ -139,84 +139,113 @@ struct sta_ampdu_mlme {
 #define STA_INFO_PIN_STAT_PINNED	1
 #define STA_INFO_PIN_STAT_DESTROY	2
 
-
+/**
+ * struct sta_info - STA information
+ *
+ * This structure collects information about a station that
+ * mac80211 is communicating with.
+ *
+ * @list: global linked list entry
+ * @hnext: hash table linked list pointer
+ * @local: pointer to the global information
+ * @addr: MAC address of this STA
+ * @aid: STA's unique AID (1..2007, 0 = not assigned yet),
+ *	only used in AP (and IBSS?) mode
+ * @flags: STA flags, see &enum ieee80211_sta_info_flags
+ * @ps_tx_buf: buffer of frames to transmit to this station
+ *	when it leaves power saving state
+ * @tx_filtered: buffer of frames we already tried to transmit
+ *	but were filtered by hardware due to STA having entered
+ *	power saving state
+ * @rx_packets: Number of MSDUs received from this STA
+ * @rx_bytes: Number of bytes received from this STA
+ * @supp_rates: Bitmap of supported rates (per band)
+ * @ht_info: HT capabilities of this STA
+ */
 struct sta_info {
+	/* General information, mostly static */
 	struct list_head list;
-	struct sta_info *hnext; /* next entry in hash table list */
-
+	struct sta_info *hnext;
 	struct ieee80211_local *local;
-
-	u8 addr[ETH_ALEN];
-	u16 aid; /* STA's unique AID (1..2007), 0 = not yet assigned */
-	u32 flags; /* WLAN_STA_ */
-
-	struct sk_buff_head ps_tx_buf; /* buffer of TX frames for station in
-					* power saving state */
-	struct sk_buff_head tx_filtered; /* buffer of TX frames that were
-					  * already given to low-level driver,
-					  * but were filtered */
-	unsigned long rx_packets, tx_packets; /* number of RX/TX MSDUs */
-	unsigned long rx_bytes, tx_bytes;
-	unsigned long tx_retry_failed, tx_retry_count;
-	unsigned long tx_filtered_count;
-	/* moving percentage of failed MSDUs */
-	unsigned int fail_avg;
-
-	unsigned int wep_weak_iv_count; /* number of RX frames with weak IV */
-
-	unsigned long last_rx;
-	/* bitmap of supported rates per band */
-	u64 supp_rates[IEEE80211_NUM_BANDS];
-	int txrate_idx;
-	/* last rates used to send a frame to this STA */
-	int last_txrate_idx, last_nonerp_txrate_idx;
-
-	/* sub_if_data this sta belongs to */
 	struct ieee80211_sub_if_data *sdata;
-
 	struct ieee80211_key *key;
-
-	u32 tx_num_consecutive_failures;
-	u32 tx_num_mpdu_ok;
-	u32 tx_num_mpdu_fail;
-
 	struct rate_control_ref *rate_ctrl;
 	void *rate_ctrl_priv;
+	struct ieee80211_ht_info ht_info;
+	u64 supp_rates[IEEE80211_NUM_BANDS];
+	u8 addr[ETH_ALEN];
+	u16 aid;
+	u16 listen_interval;
 
-	/* last received seq/frag number from this STA (per RX queue) */
-	__le16 last_seq_ctrl[NUM_RX_DATA_QUEUES];
+	/*
+	 * for use by the internal lifetime management,
+	 * see __sta_info_unlink
+	 */
+	u8 pin_status;
+
+	/* frequently updated information, needs locking? */
+	u32 flags;
+
+	/*
+	 * STA powersave frame queues, no more than the internal
+	 * locking required.
+	 */
+	struct sk_buff_head ps_tx_buf;
+	struct sk_buff_head tx_filtered;
+
+	/* Updated from RX path only, no locking requirements */
+	unsigned long rx_packets, rx_bytes;
+	unsigned long wep_weak_iv_count;
+	unsigned long last_rx;
 	unsigned long num_duplicates; /* number of duplicate frames received
 				       * from this STA */
-	unsigned long tx_fragments; /* number of transmitted MPDUs */
 	unsigned long rx_fragments; /* number of received MPDUs */
 	unsigned long rx_dropped; /* number of dropped MPDUs from this STA */
-
 	int last_rssi; /* RSSI of last received frame from this STA */
 	int last_signal; /* signal of last received frame from this STA */
 	int last_noise; /* noise of last received frame from this STA */
-	int channel_use;
-	int channel_use_raw;
-
+	/* last received seq/frag number from this STA (per RX queue) */
+	__le16 last_seq_ctrl[NUM_RX_DATA_QUEUES];
 #ifdef CONFIG_MAC80211_DEBUG_COUNTERS
 	unsigned int wme_rx_queue[NUM_RX_DATA_QUEUES];
+#endif
+
+	/* Updated from TX status path only, no locking requirements */
+	unsigned long tx_filtered_count;
+	unsigned long tx_retry_failed, tx_retry_count;
+	/* TODO: update in generic code not rate control? */
+	u32 tx_num_consecutive_failures;
+	u32 tx_num_mpdu_ok;
+	u32 tx_num_mpdu_fail;
+	/* moving percentage of failed MSDUs */
+	unsigned int fail_avg;
+
+	/* Updated from TX path only, no locking requirements */
+	unsigned long tx_packets; /* number of RX/TX MSDUs */
+	unsigned long tx_bytes;
+	unsigned long tx_fragments; /* number of transmitted MPDUs */
+	int txrate_idx;
+	int last_txrate_idx;
+#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
 	unsigned int wme_tx_queue[NUM_RX_DATA_QUEUES];
-#endif /* CONFIG_MAC80211_DEBUG_COUNTERS */
+#endif
 
-	u16 listen_interval;
+	/* Debug counters, no locking doesn't matter */
+	int channel_use;
+	int channel_use_raw;
 
 	/*
-	 * for use by the internal lifetime management,
-	 * see __sta_info_unlink
+	 * Aggregation information, comes with own locking.
 	 */
-	u8 pin_status;
-
-	struct ieee80211_ht_info ht_info; /* 802.11n HT capabilities
-					     of this STA */
 	struct sta_ampdu_mlme ampdu_mlme;
-	u8 timer_to_tid[STA_TID_NUM];	/* convert timer id to tid */
+	u8 timer_to_tid[STA_TID_NUM];	/* identity mapping to ID timers */
 	u8 tid_to_tx_q[STA_TID_NUM];	/* map tid to tx queue */
+
 #ifdef CONFIG_MAC80211_MESH
-	/* mesh peer link attributes */
+	/*
+	 * Mesh peer link attributes
+	 * TODO: move to a sub-structure that is referenced with pointer?
+	 */
 	__le16 llid;		/* Local link ID */
 	__le16 plid;		/* Peer link ID */
 	__le16 reason;		/* Buffer for cancel reason on HOLDING state */
--- everything.orig/net/mac80211/debugfs_sta.c	2008-02-24 12:22:26.000000000 +0100
+++ everything/net/mac80211/debugfs_sta.c	2008-02-24 12:41:13.000000000 +0100
@@ -67,7 +67,7 @@ STA_FILE(last_rssi, last_rssi, D);
 STA_FILE(last_signal, last_signal, D);
 STA_FILE(last_noise, last_noise, D);
 STA_FILE(channel_use, channel_use, D);
-STA_FILE(wep_weak_iv_count, wep_weak_iv_count, D);
+STA_FILE(wep_weak_iv_count, wep_weak_iv_count, LU);
 
 static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
 			      size_t count, loff_t *ppos)

-- 


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

* [PATCH 09/10] mac80211: remove STA entries when taking down interface
  2008-02-25 15:27 [PATCH 00/10] mac80211 locking/sta info work Johannes Berg
                   ` (7 preceding siblings ...)
  2008-02-25 15:27 ` [PATCH 08/10] mac80211: clean up sta_info and document locking Johannes Berg
@ 2008-02-25 15:27 ` Johannes Berg
  2008-02-25 15:27 ` [PATCH 10/10] b43: verify sta_notify mac80211 callback Johannes Berg
  9 siblings, 0 replies; 16+ messages in thread
From: Johannes Berg @ 2008-02-25 15:27 UTC (permalink / raw)
  To: John Linville; +Cc: linux-wireless, Luis Carlos Cobo

When we take down an interface, we need to remove the STA info
items that belong to it because otherwise we might invoke a
sta_notify() callback in the driver when we later delete the
STA entries, but in that case the driver will already have
removed its knowledge of the interface they belonged to leading
to confusion. Also, we could invoke the set_tim() callback after
the driver removed its knowledge of the interface, which can
lead to a crash if it requests a beacon with a then-invalid vif
pointer!

A side effect of this patch is that, because it was easier, it
disallows changing the WDS peer while an interface is up. Should
that actually be necessary, it can be added back, but the WDS
peer STA entry may not be added while the interface is UP so for
now I've simplified the WDS peer's STA entry lifetime management.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
Fixes a crash in ieee80211_beacon_get() when I rmmod b43 or
ip link set wlan0 down while hostapd is running on that iface
and stations are associated.

 net/mac80211/ieee80211.c       |   99 +++++++++++++++++------------------------
 net/mac80211/ieee80211_i.h     |    1 
 net/mac80211/ieee80211_iface.c |   22 +--------
 net/mac80211/ieee80211_ioctl.c |   18 +++++--
 net/mac80211/sta_info.c        |   14 ++++-
 net/mac80211/sta_info.h        |    2 
 6 files changed, 73 insertions(+), 83 deletions(-)

--- everything.orig/net/mac80211/ieee80211.c	2008-02-25 16:25:12.000000000 +0100
+++ everything/net/mac80211/ieee80211.c	2008-02-25 16:25:17.000000000 +0100
@@ -183,6 +183,7 @@ static int ieee80211_open(struct net_dev
 	struct ieee80211_if_init_conf conf;
 	int res;
 	bool need_hw_reconfig = 0;
+	struct sta_info *sta;
 
 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
@@ -256,6 +257,20 @@ static int ieee80211_open(struct net_dev
 	case IEEE80211_IF_TYPE_WDS:
 		if (is_zero_ether_addr(sdata->u.wds.remote_addr))
 			return -ENOLINK;
+
+		/* Create STA entry for the WDS peer */
+		sta = sta_info_alloc(sdata, sdata->u.wds.remote_addr,
+				     GFP_KERNEL);
+		if (!sta)
+			return -ENOMEM;
+
+		sta->flags |= WLAN_STA_AUTHORIZED;
+
+		res = sta_info_insert(sta);
+		if (res) {
+			sta_info_destroy(sta);
+			return res;
+		}
 		break;
 	case IEEE80211_IF_TYPE_VLAN:
 		if (!sdata->u.vlan.ap)
@@ -367,14 +382,20 @@ static int ieee80211_open(struct net_dev
 
 static int ieee80211_stop(struct net_device *dev)
 {
-	struct ieee80211_sub_if_data *sdata;
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ieee80211_local *local = sdata->local;
 	struct ieee80211_if_init_conf conf;
 	struct sta_info *sta;
 	int i;
 
-	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	/*
+	 * Stop TX on this interface first.
+	 */
+	netif_stop_queue(dev);
 
+	/*
+	 * Now delete all active aggregation sessions.
+	 */
 	rcu_read_lock();
 
 	list_for_each_entry_rcu(sta, &local->sta_list, list) {
@@ -388,7 +409,24 @@ static int ieee80211_stop(struct net_dev
 
 	rcu_read_unlock();
 
-	netif_stop_queue(dev);
+	/*
+	 * Remove all stations associated with this interface.
+	 *
+	 * This must be done before calling ops->remove_interface()
+	 * because otherwise we can later invoke ops->sta_notify()
+	 * whenever the STAs are removed, and that invalidates driver
+	 * assumptions about always getting a vif pointer that is valid
+	 * (because if we remove a STA after ops->remove_interface()
+	 * the driver will have removed the vif info already!)
+	 *
+	 * We could relax this and only unlink the stations from the
+	 * hash table and list but keep them on a per-sdata list that
+	 * will be inserted back again when the interface is brought
+	 * up again, but I don't currently see a use case for that,
+	 * except with WDS which gets a STA entry created when it is
+	 * brought up.
+	 */
+	sta_info_flush(local, sdata);
 
 	/*
 	 * Don't count this interface for promisc/allmulti while it
@@ -453,8 +491,6 @@ static int ieee80211_stop(struct net_dev
 		netif_tx_unlock_bh(local->mdev);
 		break;
 	case IEEE80211_IF_TYPE_MESH_POINT:
-		sta_info_flush(local, sdata);
-		/* fall through */
 	case IEEE80211_IF_TYPE_STA:
 	case IEEE80211_IF_TYPE_IBSS:
 		sdata->u.sta.state = IEEE80211_DISABLED;
@@ -892,57 +928,6 @@ void ieee80211_if_setup(struct net_devic
 	dev->destructor = ieee80211_if_free;
 }
 
-/* WDS specialties */
-
-int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr)
-{
-	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-	struct sta_info *sta;
-	int err;
-	DECLARE_MAC_BUF(mac);
-
-	might_sleep();
-
-	if (compare_ether_addr(remote_addr, sdata->u.wds.remote_addr) == 0)
-		return 0;
-
-	/* Create STA entry for the new peer */
-	sta = sta_info_alloc(sdata, remote_addr, GFP_KERNEL);
-	if (!sta)
-		return -ENOMEM;
-
-	sta->flags |= WLAN_STA_AUTHORIZED;
-	err = sta_info_insert(sta);
-	if (err) {
-		sta_info_destroy(sta);
-		return err;
-	}
-
-	rcu_read_lock();
-
-	/* Remove STA entry for the old peer */
-	sta = sta_info_get(local, sdata->u.wds.remote_addr);
-	if (sta)
-		sta_info_unlink(&sta);
-	else
-		printk(KERN_DEBUG "%s: could not find STA entry for WDS link "
-		       "peer %s\n",
-		       dev->name, print_mac(mac, sdata->u.wds.remote_addr));
-
-	/* Update WDS link data */
-	memcpy(&sdata->u.wds.remote_addr, remote_addr, ETH_ALEN);
-
-	rcu_read_unlock();
-
-	if (sta) {
-		synchronize_rcu();
-		sta_info_destroy(sta);
-	}
-
-	return 0;
-}
-
 /* everything else */
 
 static int __ieee80211_if_config(struct net_device *dev,
--- everything.orig/net/mac80211/sta_info.c	2008-02-25 16:25:12.000000000 +0100
+++ everything/net/mac80211/sta_info.c	2008-02-25 16:25:17.000000000 +0100
@@ -254,6 +254,8 @@ int sta_info_insert(struct sta_info *sta
 	unsigned long flags;
 	DECLARE_MAC_BUF(mac);
 
+	WARN_ON(!netif_running(sdata->dev));
+
 	spin_lock_irqsave(&local->sta_lock, flags);
 	/* check if STA exists already */
 	if (__sta_info_find(local, sta->addr)) {
@@ -604,14 +606,18 @@ void sta_info_stop(struct ieee80211_loca
 
 /**
  * sta_info_flush - flush matching STA entries from the STA table
+ *
+ * Returns the number of removed STA entries.
+ *
  * @local: local interface data
  * @sdata: matching rule for the net device (sta->dev) or %NULL to match all STAs
  */
-void sta_info_flush(struct ieee80211_local *local,
+int sta_info_flush(struct ieee80211_local *local,
 		    struct ieee80211_sub_if_data *sdata)
 {
 	struct sta_info *sta, *tmp;
 	LIST_HEAD(tmp_list);
+	int ret = 0;
 	unsigned long flags;
 
 	might_sleep();
@@ -620,8 +626,10 @@ void sta_info_flush(struct ieee80211_loc
 	list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
 		if (!sdata || sdata == sta->sdata) {
 			__sta_info_unlink(&sta);
-			if (sta)
+			if (sta) {
 				list_add_tail(&sta->list, &tmp_list);
+				ret++;
+			}
 		}
 	}
 	spin_unlock_irqrestore(&local->sta_lock, flags);
@@ -630,4 +638,6 @@ void sta_info_flush(struct ieee80211_loc
 
 	list_for_each_entry_safe(sta, tmp, &tmp_list, list)
 		sta_info_destroy(sta);
+
+	return ret;
 }
--- everything.orig/net/mac80211/ieee80211_iface.c	2008-02-25 16:25:11.000000000 +0100
+++ everything/net/mac80211/ieee80211_iface.c	2008-02-25 16:25:17.000000000 +0100
@@ -187,8 +187,8 @@ void ieee80211_if_reinit(struct net_devi
 {
 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-	struct sta_info *sta;
 	struct sk_buff *skb;
+	int flushed;
 
 	ASSERT_RTNL();
 
@@ -240,21 +240,7 @@ void ieee80211_if_reinit(struct net_devi
 		break;
 	}
 	case IEEE80211_IF_TYPE_WDS:
-		rcu_read_lock();
-		sta = sta_info_get(local, sdata->u.wds.remote_addr);
-		if (sta) {
-			sta_info_unlink(&sta);
-		} else {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
-			printk(KERN_DEBUG "%s: Someone had deleted my STA "
-			       "entry for the WDS link\n", dev->name);
-#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
-		}
-		rcu_read_unlock();
-		if (sta) {
-			synchronize_rcu();
-			sta_info_destroy(sta);
-		}
+		/* nothing to do */
 		break;
 	case IEEE80211_IF_TYPE_MESH_POINT:
 	case IEEE80211_IF_TYPE_STA:
@@ -279,8 +265,8 @@ void ieee80211_if_reinit(struct net_devi
 		break;
 	}
 
-	/* remove all STAs that are bound to this virtual interface */
-	sta_info_flush(local, sdata);
+	flushed = sta_info_flush(local, sdata);
+	WARN_ON(flushed);
 
 	memset(&sdata->u, 0, sizeof(sdata->u));
 	ieee80211_if_sdata_init(sdata);
--- everything.orig/net/mac80211/ieee80211_i.h	2008-02-25 16:25:11.000000000 +0100
+++ everything/net/mac80211/ieee80211_i.h	2008-02-25 16:25:17.000000000 +0100
@@ -858,7 +858,6 @@ int ieee80211_hw_config(struct ieee80211
 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);
-int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr);
 void ieee80211_if_setup(struct net_device *dev);
 int ieee80211_hw_config_ht(struct ieee80211_local *local, int enable_ht,
 			   struct ieee80211_ht_info *req_ht_cap,
--- everything.orig/net/mac80211/sta_info.h	2008-02-25 16:25:16.000000000 +0100
+++ everything/net/mac80211/sta_info.h	2008-02-25 16:25:17.000000000 +0100
@@ -339,7 +339,7 @@ void sta_info_clear_tim_bit(struct sta_i
 void sta_info_init(struct ieee80211_local *local);
 int sta_info_start(struct ieee80211_local *local);
 void sta_info_stop(struct ieee80211_local *local);
-void sta_info_flush(struct ieee80211_local *local,
+int sta_info_flush(struct ieee80211_local *local,
 		    struct ieee80211_sub_if_data *sdata);
 
 #endif /* STA_INFO_H */
--- everything.orig/net/mac80211/ieee80211_ioctl.c	2008-02-25 16:25:11.000000000 +0100
+++ everything/net/mac80211/ieee80211_ioctl.c	2008-02-25 16:25:17.000000000 +0100
@@ -471,10 +471,20 @@ static int ieee80211_ioctl_siwap(struct 
 		ieee80211_sta_req_auth(dev, &sdata->u.sta);
 		return 0;
 	} else if (sdata->vif.type == IEEE80211_IF_TYPE_WDS) {
-		if (memcmp(sdata->u.wds.remote_addr, (u8 *) &ap_addr->sa_data,
-			   ETH_ALEN) == 0)
-			return 0;
-		return ieee80211_if_update_wds(dev, (u8 *) &ap_addr->sa_data);
+		/*
+		 * If it is necessary to update the WDS peer address
+		 * while the interface is running, then we need to do
+		 * more work here, namely if it is running we need to
+		 * add a new and remove the old STA entry, this is
+		 * normally handled by _open() and _stop().
+		 */
+		if (netif_running(dev))
+			return -EBUSY;
+
+		memcpy(&sdata->u.wds.remote_addr, (u8 *) &ap_addr->sa_data,
+		       ETH_ALEN);
+
+		return 0;
 	}
 
 	return -EOPNOTSUPP;

-- 


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

* [PATCH 10/10] b43: verify sta_notify mac80211 callback
  2008-02-25 15:27 [PATCH 00/10] mac80211 locking/sta info work Johannes Berg
                   ` (8 preceding siblings ...)
  2008-02-25 15:27 ` [PATCH 09/10] mac80211: remove STA entries when taking down interface Johannes Berg
@ 2008-02-25 15:27 ` Johannes Berg
  9 siblings, 0 replies; 16+ messages in thread
From: Johannes Berg @ 2008-02-25 15:27 UTC (permalink / raw)
  To: John Linville; +Cc: linux-wireless, Luis Carlos Cobo, Michael Buesch

This helps verify that nothing bad is going on in mac80211,
it is unfortunately not possible to implement this generically
in mac80211 easily because there we can't assume that we only
have a single vif which b43 currently can assume.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Michael Buesch <mb@bu3sch.de>
---
 drivers/net/wireless/b43/main.c |   11 +++++++++++
 1 file changed, 11 insertions(+)

--- everything.orig/drivers/net/wireless/b43/main.c	2008-02-25 08:46:14.000000000 +0100
+++ everything/drivers/net/wireless/b43/main.c	2008-02-25 08:49:42.000000000 +0100
@@ -3835,6 +3835,16 @@ static int b43_op_ibss_beacon_update(str
 	return 0;
 }
 
+static void b43_op_sta_notify(struct ieee80211_hw *hw,
+			      struct ieee80211_vif *vif,
+			      enum sta_notify_cmd notify_cmd,
+			      const u8 *addr)
+{
+	struct b43_wl *wl = hw_to_b43_wl(hw);
+
+	B43_WARN_ON(!vif || wl->vif != vif);
+}
+
 static const struct ieee80211_ops b43_hw_ops = {
 	.tx			= b43_op_tx,
 	.conf_tx		= b43_op_conf_tx,
@@ -3851,6 +3861,7 @@ static const struct ieee80211_ops b43_hw
 	.set_retry_limit	= b43_op_set_retry_limit,
 	.set_tim		= b43_op_beacon_set_tim,
 	.beacon_update		= b43_op_ibss_beacon_update,
+	.sta_notify		= b43_op_sta_notify,
 };
 
 /* Hard-reset the chip. Do not call this directly.

-- 


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

* Re: [PATCH 06/10] mac80211: RCU-ify STA info structure access
  2008-02-25 15:27 ` [PATCH 06/10] mac80211: RCU-ify STA info structure access Johannes Berg
@ 2008-02-26  0:22   ` Luis Carlos Cobo
  2008-02-26  8:23     ` Johannes Berg
  0 siblings, 1 reply; 16+ messages in thread
From: Luis Carlos Cobo @ 2008-02-26  0:22 UTC (permalink / raw)
  To: Johannes Berg; +Cc: John Linville, linux-wireless

On Mon, 2008-02-25 at 16:27 +0100, Johannes Berg wrote:
> @@ -303,8 +300,8 @@ static void mesh_path_node_reclaim(struc
>  	struct mpath_node *node = container_of(rp, struct mpath_node, rcu);
>  	struct ieee80211_sub_if_data *sdata =
>  		IEEE80211_DEV_TO_SUB_IF(node->mpath->dev);
> -	if (node->mpath->next_hop)
> -		sta_info_put(node->mpath->next_hop);
> +
> +	rcu_assign_pointer(node->mpath->next_hop, NULL);

Is that necessary? If we are here nobody else should have access to this
path and we should just free it, right?

Also since now we can force deletion, the mesh path timer might be
running and we need a del_timer_sync() somewhere. After adding that, we
may as well always force the path deletion.


-- 
Luis Carlos Cobo Rus       GnuPG ID: 44019B60
cozybit Inc.



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

* Re: [PATCH 06/10] mac80211: RCU-ify STA info structure access
  2008-02-26  0:22   ` Luis Carlos Cobo
@ 2008-02-26  8:23     ` Johannes Berg
  2008-02-26 19:26       ` Luis Carlos Cobo
  0 siblings, 1 reply; 16+ messages in thread
From: Johannes Berg @ 2008-02-26  8:23 UTC (permalink / raw)
  To: Luis Carlos Cobo; +Cc: John Linville, linux-wireless

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


> > @@ -303,8 +300,8 @@ static void mesh_path_node_reclaim(struc
> >  	struct mpath_node *node = container_of(rp, struct mpath_node, rcu);
> >  	struct ieee80211_sub_if_data *sdata =
> >  		IEEE80211_DEV_TO_SUB_IF(node->mpath->dev);
> > -	if (node->mpath->next_hop)
> > -		sta_info_put(node->mpath->next_hop);
> > +
> > +	rcu_assign_pointer(node->mpath->next_hop, NULL);
> 
> Is that necessary? If we are here nobody else should have access to this
> path and we should just free it, right?

Yeah I think you're right and we can just free it since access is gone
already.

> Also since now we can force deletion, the mesh path timer might be
> running and we need a del_timer_sync() somewhere. After adding that, we
> may as well always force the path deletion.

I put a del_timer() into sta_info_unlink() and a del_timer_sync() into
sta_info_destroy(). That should be enough afaict.

johannes

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

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

* Re: [PATCH 06/10] mac80211: RCU-ify STA info structure access
  2008-02-26  8:23     ` Johannes Berg
@ 2008-02-26 19:26       ` Luis Carlos Cobo
  2008-02-27 11:23         ` Johannes Berg
  0 siblings, 1 reply; 16+ messages in thread
From: Luis Carlos Cobo @ 2008-02-26 19:26 UTC (permalink / raw)
  To: Johannes Berg; +Cc: John Linville, linux-wireless

On Tue, 2008-02-26 at 09:23 +0100, Johannes Berg wrote:
> I put a del_timer() into sta_info_unlink() and a del_timer_sync() into
> sta_info_destroy(). That should be enough afaict.

That is for the plink_timer in struct sta_info, if I understand
correctly. I am talking about the per mesh-path timer (the one in struct
mesh_path). 

-- 
Luis Carlos Cobo Rus       GnuPG ID: 44019B60
cozybit Inc.



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

* Re: [PATCH 06/10] mac80211: RCU-ify STA info structure access
  2008-02-26 19:26       ` Luis Carlos Cobo
@ 2008-02-27 11:23         ` Johannes Berg
  2008-02-27 18:34           ` Luis Carlos Cobo
  0 siblings, 1 reply; 16+ messages in thread
From: Johannes Berg @ 2008-02-27 11:23 UTC (permalink / raw)
  To: Luis Carlos Cobo; +Cc: John Linville, linux-wireless

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


On Tue, 2008-02-26 at 11:26 -0800, Luis Carlos Cobo wrote:
> On Tue, 2008-02-26 at 09:23 +0100, Johannes Berg wrote:
> > I put a del_timer() into sta_info_unlink() and a del_timer_sync() into
> > sta_info_destroy(). That should be enough afaict.
> 
> That is for the plink_timer in struct sta_info, if I understand
> correctly. I am talking about the per mesh-path timer (the one in struct
> mesh_path). 

Oh. Good point, I missed that one. I suppose that
mesh_path_node_reclaim() always runs in a context suitable for doing
del_timer_sync() so it should be fine to just do that there, right? I
can look into it or leave it to you, just let me know so we don't both
do it.

johannes

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

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

* Re: [PATCH 06/10] mac80211: RCU-ify STA info structure access
  2008-02-27 11:23         ` Johannes Berg
@ 2008-02-27 18:34           ` Luis Carlos Cobo
  0 siblings, 0 replies; 16+ messages in thread
From: Luis Carlos Cobo @ 2008-02-27 18:34 UTC (permalink / raw)
  To: Johannes Berg; +Cc: John Linville, linux-wireless

On Wed, 2008-02-27 at 12:23 +0100, Johannes Berg wrote:
> Oh. Good point, I missed that one. I suppose that
> mesh_path_node_reclaim() always runs in a context suitable for doing
> del_timer_sync() so it should be fine to just do that there, right? I
> can look into it or leave it to you, just let me know so we don't both
> do it.

I will take care of it.

-- 
Luis Carlos Cobo Rus       GnuPG ID: 44019B60
cozybit Inc.



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

end of thread, other threads:[~2008-02-27 18:33 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-02-25 15:27 [PATCH 00/10] mac80211 locking/sta info work Johannes Berg
2008-02-25 15:27 ` [PATCH 01/10] mac80211: clarify use of TX status/RX callbacks Johannes Berg
2008-02-25 15:27 ` [PATCH 02/10] mac80211: safely free beacon in ieee80211_if_reinit Johannes Berg
2008-02-25 15:27 ` [PATCH 03/10] mac80211: split ieee80211_txrx_data Johannes Berg
2008-02-25 15:27 ` [PATCH 04/10] mac80211: remove STA infos last_ack stuff Johannes Berg
2008-02-25 15:27 ` [PATCH 05/10] mac80211: split ieee80211_key_alloc/free Johannes Berg
2008-02-25 15:27 ` [PATCH 06/10] mac80211: RCU-ify STA info structure access Johannes Berg
2008-02-26  0:22   ` Luis Carlos Cobo
2008-02-26  8:23     ` Johannes Berg
2008-02-26 19:26       ` Luis Carlos Cobo
2008-02-27 11:23         ` Johannes Berg
2008-02-27 18:34           ` Luis Carlos Cobo
2008-02-25 15:27 ` [PATCH 07/10] mac80211: split sta_info_add Johannes Berg
2008-02-25 15:27 ` [PATCH 08/10] mac80211: clean up sta_info and document locking Johannes Berg
2008-02-25 15:27 ` [PATCH 09/10] mac80211: remove STA entries when taking down interface Johannes Berg
2008-02-25 15:27 ` [PATCH 10/10] b43: verify sta_notify mac80211 callback 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).