Skip to contentSkip to conversation

mac80211: Revise,pending queue depth in ieee80211_local data structure

PATCH In review [email protected] 5 participants 15 messages Mar 18, 2010 – Mar 31, 2010 mac80211

lore
Share & tools
lore
#0 lore raw

PATCH v2 1/1 mac80211: Revise,pending queue depth in ieee80211_local data structure

Hi all,

I resend the patch in order to fix style violations that Larry suggested me.

I noticed a possible issue in the pending queue management of the ieee80211_local data structure. In particular, there is no control of the queue depth and this could cause a memory overflow. In the tests I carried out I obtain a memory overflow when I use a low priority queue (e.g. Backgreound queue) and I transmit a data stream that exceeds the channel capacity (e.g. 50Mbps@MCS 3, 800ns GI and 20MHz channel width). I tested the patch below on the last compat-wireless (2010-03-03) on an AR9280 chipset (Ubiquiti Rocket M with the latest version of OpenWrt trunk).

---

net/mac80211/ieee80211_i.h +2 -0
@@ -703,6 +703,8 @@
 	struct work_struct sta_finish_work;
 	int sta_generation;
 
+/* Pending buffer dimension */
+#define PENDING_BUF	512
 	struct sk_buff_head pending[IEEE80211_MAX_QUEUES];
 	struct tasklet_struct tx_pending_tasklet;
 
net/mac80211/tx.c +18 -5
@@ -1403,10 +1403,17 @@
 		if (local->queue_stop_reasons[queue] ||
 		    !skb_queue_empty(&local->pending[queue])) {
 			/*
-			 * if queue is stopped, queue up frames for later
-			 * transmission from the tasklet
+			 * if queue is stopped and there is enough space
+			 * in the queue, queue up frames for later transmission
+			 * from the tasklet
 			 */
-			do {
+			if (skb_queue_len(&local->pending[queue])
+					  >= PENDING_BUF) {
+				spin_unlock_irqrestore(
+						&local->queue_stop_reason_lock,
+						flags);
+				goto drop;
+			} do {
 				next = skb->next;
 				skb->next = NULL;
 				if (unlikely(txpending))
@@ -2028,8 +2035,14 @@
 						flags);
 
 			txok = ieee80211_tx_pending_skb(local, skb);
-			if (!txok)
-				__skb_queue_head(&local->pending[i], skb);
+			if (!txok) {
+				if (skb_queue_len(&local->pending[i])
+						  < PENDING_BUF)
+					__skb_queue_head(&local->pending[i],
+							 skb);
+				else
+					kfree_skb(skb);
+			}
 			spin_lock_irqsave(&local->queue_stop_reason_lock,
 					  flags);
 			if (!txok)
net/mac80211/util.c +9 -3
@@ -383,7 +383,10 @@
 
 	spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 	__ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
-	__skb_queue_tail(&local->pending[queue], skb);
+	if (skb_queue_len(&local->pending[queue]) < PENDING_BUF)
+		__skb_queue_tail(&local->pending[queue], skb);
+	else
+		kfree_skb(skb);
 	__ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
 	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 }
@@ -409,9 +412,12 @@
 			continue;
 		}
 
-		ret++;
 		queue = skb_get_queue_mapping(skb);
-		__skb_queue_tail(&local->pending[queue], skb);
+		if (skb_queue_len(&local->pending[queue]) < PENDING_BUF) {
+			ret++;
+			__skb_queue_tail(&local->pending[queue], skb);
+		} else
+			kfree_skb(skb);
 	}
 
 	for (i = 0; i < hw->queues; i++)

--

Regards

Lorenzo

maintainer
#1 lore raw
Quoted 4 lines · On Thu, 2010-03-18 at 16:43 +0100, [email protected] wrote:

Hi all,

I resend the patch in order to fix style violations that Larry suggested me.

Quoted 8 lines

I noticed a possible issue in the pending queue management of the ieee80211_local data structure. In particular, there is no control of the queue depth and this could cause a memory overflow. In the tests I carried out I obtain a memory overflow when I use a low priority queue (e.g. Backgreound queue) and I transmit a data stream that exceeds the channel capacity (e.g. 50Mbps@MCS 3, 800ns GI and 20MHz channel width). I tested the patch below on the last compat-wireless (2010-03-03) on an AR9280 chipset (Ubiquiti Rocket M with the latest version of OpenWrt trunk).

What kernel are you testing this on? I would have thought cf0277e714a0db302a8f80e1b85fd61c32cf00b3 fixed this.

johannes

maintainer
#3 lore raw
In reply to [email protected]

Please try this.

johannes

---
 net/mac80211/tx.c   |    6 ++++++
 net/mac80211/util.c |    6 ------
 2 files changed, 6 insertions(+), 6 deletions(-)
net/mac80211/tx.c +6 -0
@@ -1991,6 +1991,7 @@ static bool ieee80211_tx_pending_skb(str
 void ieee80211_tx_pending(unsigned long data)
 {
 	struct ieee80211_local *local = (struct ieee80211_local *)data;
+	struct ieee80211_sub_if_data *sdata;
 	unsigned long flags;
 	int i;
 	bool txok;
@@ -2029,6 +2030,11 @@ void ieee80211_tx_pending(unsigned long
 			if (!txok)
 				break;
 		}
+
+		if (skb_queue_empty(&local->pending[i]))
+			list_for_each_entry_rcu(sdata, &local->interfaces, list)
+				netif_tx_wake_queue(
+					netdev_get_tx_queue(sdata->dev, i));
 	}
 	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 
net/mac80211/util.c +0 -6
@@ -268,7 +268,6 @@ static void __ieee80211_wake_queue(struc
 				   enum queue_stop_reason reason)
 {
 	struct ieee80211_local *local = hw_to_local(hw);
-	struct ieee80211_sub_if_data *sdata;
 
 	if (WARN_ON(queue >= hw->queues))
 		return;
@@ -281,11 +280,6 @@ static void __ieee80211_wake_queue(struc
 
 	if (!skb_queue_empty(&local->pending[queue]))
 		tasklet_schedule(&local->tx_pending_tasklet);
-
-	rcu_read_lock();
-	list_for_each_entry_rcu(sdata, &local->interfaces, list)
-		netif_tx_wake_queue(netdev_get_tx_queue(sdata->dev, queue));
-	rcu_read_unlock();
 }
 
 void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
#4 lore raw
Quoted 45 lines

Please try this.

johannes

---
 net/mac80211/tx.c   |    6 ++++++
 net/mac80211/util.c |    6 ------
 2 files changed, 6 insertions(+), 6 deletions(-)
net/mac80211/tx.c +6 -0
@@ -1991,6 +1991,7 @@ static bool ieee80211_tx_pending_skb(str
 void ieee80211_tx_pending(unsigned long data)
 {
 	struct ieee80211_local *local = (struct ieee80211_local *)data;
+	struct ieee80211_sub_if_data *sdata;
 	unsigned long flags;
 	int i;
 	bool txok;
@@ -2029,6 +2030,11 @@ void ieee80211_tx_pending(unsigned long
 			if (!txok)
 				break;
 		}
+
+		if (skb_queue_empty(&local->pending[i]))
+			list_for_each_entry_rcu(sdata, &local->interfaces, list)
+				netif_tx_wake_queue(
+					netdev_get_tx_queue(sdata->dev, i));
 	}
 	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 
net/mac80211/util.c +0 -1
@@ -268,7 +268,6 @@ static void __ieee80211_wake_queue(struc
 				   enum queue_stop_reason reason)
 {
 	struct ieee80211_local *local = hw_to_local(hw);
-	struct ieee80211_sub_if_data *sdata;
 
 	if (WARN_ON(queue >= hw->queues))
 		return;
@@ -281,11 +280,6 @@ static void __ieee80211_wake_queue(struc
 

net/mac80211/util.c static void __ieee80211_wake_queue(struc · line 285

Quoted 10 lines
net/mac80211/util.c +0 -5
 	if (!skb_queue_empty(&local->pending[queue]))
 		tasklet_schedule(&local->tx_pending_tasklet);
-
-	rcu_read_lock();
-	list_for_each_entry_rcu(sdata, &local->interfaces, list)
-		netif_tx_wake_queue(netdev_get_tx_queue(sdata->dev, queue));
-	rcu_read_unlock();
 }
 
 void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,

Hi Johannes,

I tested the patch on kernel 2.6.32.7 with compat-wireless-2010-03-03 but it seems that the problem is not solved. If I set the lowest priority queue (Backgreound), the system will crash for an out of memory panic. During the tests I carried out, I transmit 50Mbps UDP traffic.

Regards

Lorenzo

maintainer
#5 lore raw

On Fri, 19 Mar 2010 10:33:47 +0100, "[email protected]"

Quoted 7 lines

net/mac80211/util.c static void __ieee80211_wake_queue(struc · line 288

Quoted 7 lines
net/mac80211/util.c +0 -5
 	if (!skb_queue_empty(&local->pending[queue]))
 		tasklet_schedule(&local->tx_pending_tasklet);
-
-	rcu_read_lock();
-	list_for_each_entry_rcu(sdata, &local->interfaces, list)
-		netif_tx_wake_queue(netdev_get_tx_queue(sdata->dev, queue));
-	rcu_read_unlock();

That's obviously wrong, need to move the code to the else branch of the above if instead.

Quoted 1 line

I tested the patch on kernel 2.6.32.7 with compat-wireless-2010-03-03

but

Quoted 1 line

it seems that the problem is not solved. If I set the lowest priority

queue

Quoted 1 line

(Backgreound), the system will crash for an out of memory panic. During

the

Quoted 1 line

tests I carried out, I transmit 50Mbps UDP traffic.

How are you generating traffic? I just dumped like 2Gbps traffic at it and everything works just fine. I verified queues are stopped and started properly.

johannes

#6 lore raw
Quoted 25 lines · On Saturday 20 March 2010 03:49:27 Johannes Berg wrote:

On Fri, 19 Mar 2010 10:33:47 +0100, "[email protected]"

Quoted 9 lines

net/mac80211/util.c static void __ieee80211_wake_queue(struc · line 288

Quoted 9 lines
net/mac80211/util.c +0 -5
 	if (!skb_queue_empty(&local->pending[queue]))
 
 		tasklet_schedule(&local->tx_pending_tasklet);
 
-
-	rcu_read_lock();
-	list_for_each_entry_rcu(sdata, &local->interfaces, list)
-		netif_tx_wake_queue(netdev_get_tx_queue(sdata->dev, queue));
-	rcu_read_unlock();

That's obviously wrong, need to move the code to the else branch of the above if instead.

Quoted 1 line

I tested the patch on kernel 2.6.32.7 with compat-wireless-2010-03-03

but

Quoted 1 line

it seems that the problem is not solved. If I set the lowest priority

queue

Quoted 1 line

(Backgreound), the system will crash for an out of memory panic. During

Quoted 7 lines

the

Quoted 1 line

tests I carried out, I transmit 50Mbps UDP traffic.

How are you generating traffic? I just dumped like 2Gbps traffic at it and everything works just fine. I verified queues are stopped and started properly.

i think i'm hitting the same bug, but not sure (i will try to check next week). this is easy to reproduce, but you need 4 (at least 3) machines:

setup 2 wireless cards, talking to each other (i use IBSS) on different machines. then setup routing like this:

[1] - [WL1] - [WL2] - [2]

now send more UDP traffic than the wireless link can handle (iperf -u -b 50M for example) between [1] and [2]. watch the free memory get less and less at [WL1] until it chrashes.

bruno

#7 lore raw
Quoted 11 lines · On Saturday 20 March 2010 11:44:24 Bruno Randolf wrote:

i think i'm hitting the same bug, but not sure (i will try to check next week). this is easy to reproduce, but you need 4 (at least 3) machines:

setup 2 wireless cards, talking to each other (i use IBSS) on different machines. then setup routing like this:

[1] - [WL1] - [WL2] - [2]

now send more UDP traffic than the wireless link can handle (iperf -u -b 50M for example) between [1] and [2]. watch the free memory get less and less at [WL1] until it chrashes.

forgot to add: [1] and [2] are connected by ethernet to [WL1/2]... (obviously?)

bruno

#8 lore raw
Quoted 9 lines · On Saturday 20 March 2010 11:44:24 Bruno Randolf wrote:
Quoted 8 lines

i think i'm hitting the same bug, but not sure (i will try to check next week). this is easy to reproduce, but you need 4 (at least 3) machines:

setup 2 wireless cards, talking to each other (i use IBSS) on different machines. then setup routing like this:

[1] - [WL1] - [WL2] - [2]

Quoted 9 lines
Quoted 3 lines

now send more UDP traffic than the wireless link can handle (iperf -u -b 50M for example) between [1] and [2]. watch the free memory get less and less at [WL1] until it chrashes.

forgot to add: [1] and [2] are connected by ethernet to [WL1/2]... (obviously?)

bruno

Hi,

I tested the same network configuration used by bruno:

PC1<--ethernet cable-->Device1<--wireless link-->Device2<--ethernet cable-->PC2

but I injected packets using a VAP in monitor mode on both wireless devices. I used UDP iperf traffic (50Mbps) from PC1 to PC2 as data stream. I agree with bruno,
I think the main aspect of the test is to transmit a traffic steam that the wireless link is not
able to handle. In order to trigger the crash, if the device is EDCA capable, I suggest to use the
Best Effort hardware queue (skb->priority=3, AC_queue=2 in the ieee80211_select_queue()) for all
data packets.

Regards

Lorenzo

maintainer
#9 lore raw
Quoted 5 lines · Lorenzo Bianconi
Quoted 2 lines
Quoted 2 lines

setup 2 wireless cards, talking to each other (i use IBSS) on different machines. then setup routing like this:

PC1<--ethernet cable-->Device1<--wireless link-->Device2<--ethernet cable-->PC2

I'm not really sure how this scenario differs from injecting traffic locally, but I'll try to reproduce.

johannes

#10 lore raw
Quoted 10 lines · On Sunday 21 March 2010 05:40:05 Johannes Berg wrote:
Quoted 6 lines · Lorenzo Bianconi
Quoted 4 lines
Quoted 4 lines

setup 2 wireless cards, talking to each other (i use IBSS) on different

machines. then setup routing like this:

PC1<--ethernet cable-->Device1<--wireless link-->Device2<--ethernet cable-->PC2

I'm not really sure how this scenario differs from injecting traffic locally, but I'll try to reproduce.

if you generate traffic locally it's enough to stop the queues, this will cause userspace to stop generating traffic. in the forwarding case (which we are talking about) stopping the queues will not stop the sender to send traffic, thus we have to start dropping packets at some point.

bruno

maintainer
#11 lore raw
Quoted 9 lines · Bruno Randolf
Quoted 8 lines · On Sunday 21 March 2010 05:40:05 Johannes Berg wrote:
Quoted 6 lines · Lorenzo Bianconi
Quoted 4 lines
Quoted 4 lines

setup 2 wireless cards, talking to each other (i use IBSS) on different

machines. then setup routing like this:

PC1<--ethernet cable-->Device1<--wireless link-->Device2<--ethernet cable-->PC2

Quoted 7 lines
Quoted 2 lines

I'm not really sure how this scenario differs from injecting traffic locally, but I'll try to reproduce.

if you generate traffic locally it's enough to stop the queues, this will cause userspace to stop generating traffic. in the forwarding case (which we are talking about) stopping the queues will not stop the sender to send traffic, thus we have to start dropping packets at some point.

Yeah, but since we stop the netdev queues it should already be happening there.

johannes

maintainer
#12 lore raw
In reply to Bruno Randolf
Quoted 9 lines · Bruno Randolf
Quoted 8 lines · On Sunday 21 March 2010 05:40:05 Johannes Berg wrote:
Quoted 6 lines · Lorenzo Bianconi
Quoted 4 lines
Quoted 4 lines

setup 2 wireless cards, talking to each other (i use IBSS) on different

machines. then setup routing like this:

PC1<--ethernet cable-->Device1<--wireless link-->Device2<--ethernet cable-->PC2

Quoted 7 lines
Quoted 2 lines

I'm not really sure how this scenario differs from injecting traffic locally, but I'll try to reproduce.

if you generate traffic locally it's enough to stop the queues, this will cause userspace to stop generating traffic. in the forwarding case (which we are talking about) stopping the queues will not stop the sender to send traffic, thus we have to start dropping packets at some point.

So I tried this scenario, and I see the queues stopping, and traffic being dropped quite as you'd want it. Even the race condition that I noticed didn't ever trigger.

Please make sure you're using a kernel recent enough to include the fixes, maybe try compat-wireless.

johannes

#13 lore raw
Quoted 18 lines · On Tuesday 23 March 2010 03:12:44 you wrote:
Quoted 9 lines · Bruno Randolf
Quoted 8 lines · On Sunday 21 March 2010 05:40:05 Johannes Berg wrote:
Quoted 6 lines · Lorenzo Bianconi
Quoted 4 lines
Quoted 4 lines

setup 2 wireless cards, talking to each other (i use IBSS) on different

machines. then setup routing like this:

PC1<--ethernet cable-->Device1<--wireless link-->Device2<--ethernet cable-->PC2

Quoted 7 lines
Quoted 2 lines

I'm not really sure how this scenario differs from injecting traffic locally, but I'll try to reproduce.

if you generate traffic locally it's enough to stop the queues, this will cause userspace to stop generating traffic. in the forwarding case (which we are talking about) stopping the queues will not stop the sender to send traffic, thus we have to start dropping packets at some point.

Quoted 6 lines

So I tried this scenario, and I see the queues stopping, and traffic being dropped quite as you'd want it. Even the race condition that I noticed didn't ever trigger.

Please make sure you're using a kernel recent enough to include the fixes, maybe try compat-wireless.

so it must be an ath5k bug then... lorenzo's description just resembled my case so much, that i thought it could be the same bug. sorry to have bothered you with this...

bruno

maintainer
#14 lore raw
Quoted 23 lines · Bruno Randolf
Quoted 22 lines · On Tuesday 23 March 2010 03:12:44 you wrote:
Quoted 9 lines · Bruno Randolf
Quoted 8 lines · On Sunday 21 March 2010 05:40:05 Johannes Berg wrote:
Quoted 6 lines · Lorenzo Bianconi
Quoted 4 lines
Quoted 4 lines

setup 2 wireless cards, talking to each other (i use IBSS) on different

machines. then setup routing like this:

PC1<--ethernet cable-->Device1<--wireless link-->Device2<--ethernet cable-->PC2

Quoted 7 lines
Quoted 2 lines

I'm not really sure how this scenario differs from injecting traffic locally, but I'll try to reproduce.

if you generate traffic locally it's enough to stop the queues, this will cause userspace to stop generating traffic. in the forwarding case (which we are talking about) stopping the queues will not stop the sender to send traffic, thus we have to start dropping packets at some point.

So I tried this scenario, and I see the queues stopping, and traffic being dropped quite as you'd want it. Even the race condition that I noticed didn't ever trigger.

Quoted 6 lines
Quoted 2 lines

Please make sure you're using a kernel recent enough to include the fixes, maybe try compat-wireless.

so it must be an ath5k bug then... lorenzo's description just resembled my case so much, that i thought it could be the same bug. sorry to have bothered you with this...

I don't think there's any way this can be a driver bug, assuming it stops the queues properly, which it has to unless it comes up with its own queueing scheme...

johannes