aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/rt2x00
diff options
context:
space:
mode:
authorGertjan van Wingerde <gwingerde@gmail.com>2011-07-06 16:56:24 -0400
committerJohn W. Linville <linville@tuxdriver.com>2011-07-07 13:20:57 -0400
commit77a861c405da75d81e9e6e32c50eb7f9777777e8 (patch)
tree90003fd46901d70ae7fabfe7c31a8ad758fcfd1d /drivers/net/wireless/rt2x00
parent9352f69c9194f1dcb3e096377e5c4804ab1bb5fb (diff)
rt2x00: Serialize TX operations on a queue.
The rt2x00 driver gets frequent occurrences of the following error message when operating under load: phy0 -> rt2x00queue_write_tx_frame: Error - Arrived at non-free entry in the non-full queue 2. This is caused by simultaneous attempts from mac80211 to send a frame via rt2x00, which are not properly serialized inside rt2x00queue_write_tx_frame, causing the second frame to fail sending with the above mentioned error message. Fix this by introducing a per-queue spinlock to serialize the TX operations on that queue. Reported-by: Andreas Hartmann <andihartmann@01019freenet.de> Signed-off-by: Gertjan van Wingerde <gwingerde@gmail.com> Acked-by: Helmut Schaa <helmut.schaa@googlemail.com> Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/rt2x00')
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00queue.c21
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00queue.h2
2 files changed, 18 insertions, 5 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index c7fc9def6bcf..551cee1209ca 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -555,15 +555,21 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
555 bool local) 555 bool local)
556{ 556{
557 struct ieee80211_tx_info *tx_info; 557 struct ieee80211_tx_info *tx_info;
558 struct queue_entry *entry = rt2x00queue_get_entry(queue, Q_INDEX); 558 struct queue_entry *entry;
559 struct txentry_desc txdesc; 559 struct txentry_desc txdesc;
560 struct skb_frame_desc *skbdesc; 560 struct skb_frame_desc *skbdesc;
561 u8 rate_idx, rate_flags; 561 u8 rate_idx, rate_flags;
562 int ret = 0;
563
564 spin_lock(&queue->tx_lock);
565
566 entry = rt2x00queue_get_entry(queue, Q_INDEX);
562 567
563 if (unlikely(rt2x00queue_full(queue))) { 568 if (unlikely(rt2x00queue_full(queue))) {
564 ERROR(queue->rt2x00dev, 569 ERROR(queue->rt2x00dev,
565 "Dropping frame due to full tx queue %d.\n", queue->qid); 570 "Dropping frame due to full tx queue %d.\n", queue->qid);
566 return -ENOBUFS; 571 ret = -ENOBUFS;
572 goto out;
567 } 573 }
568 574
569 if (unlikely(test_and_set_bit(ENTRY_OWNER_DEVICE_DATA, 575 if (unlikely(test_and_set_bit(ENTRY_OWNER_DEVICE_DATA,
@@ -572,7 +578,8 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
572 "Arrived at non-free entry in the non-full queue %d.\n" 578 "Arrived at non-free entry in the non-full queue %d.\n"
573 "Please file bug report to %s.\n", 579 "Please file bug report to %s.\n",
574 queue->qid, DRV_PROJECT); 580 queue->qid, DRV_PROJECT);
575 return -EINVAL; 581 ret = -EINVAL;
582 goto out;
576 } 583 }
577 584
578 /* 585 /*
@@ -634,7 +641,8 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
634 if (unlikely(rt2x00queue_write_tx_data(entry, &txdesc))) { 641 if (unlikely(rt2x00queue_write_tx_data(entry, &txdesc))) {
635 clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags); 642 clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags);
636 entry->skb = NULL; 643 entry->skb = NULL;
637 return -EIO; 644 ret = -EIO;
645 goto out;
638 } 646 }
639 647
640 set_bit(ENTRY_DATA_PENDING, &entry->flags); 648 set_bit(ENTRY_DATA_PENDING, &entry->flags);
@@ -643,7 +651,9 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
643 rt2x00queue_write_tx_descriptor(entry, &txdesc); 651 rt2x00queue_write_tx_descriptor(entry, &txdesc);
644 rt2x00queue_kick_tx_queue(queue, &txdesc); 652 rt2x00queue_kick_tx_queue(queue, &txdesc);
645 653
646 return 0; 654out:
655 spin_unlock(&queue->tx_lock);
656 return ret;
647} 657}
648 658
649int rt2x00queue_clear_beacon(struct rt2x00_dev *rt2x00dev, 659int rt2x00queue_clear_beacon(struct rt2x00_dev *rt2x00dev,
@@ -1184,6 +1194,7 @@ static void rt2x00queue_init(struct rt2x00_dev *rt2x00dev,
1184 struct data_queue *queue, enum data_queue_qid qid) 1194 struct data_queue *queue, enum data_queue_qid qid)
1185{ 1195{
1186 mutex_init(&queue->status_lock); 1196 mutex_init(&queue->status_lock);
1197 spin_lock_init(&queue->tx_lock);
1187 spin_lock_init(&queue->index_lock); 1198 spin_lock_init(&queue->index_lock);
1188 1199
1189 queue->rt2x00dev = rt2x00dev; 1200 queue->rt2x00dev = rt2x00dev;
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h
index 590047499e3c..f2100f4ddcff 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.h
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.h
@@ -432,6 +432,7 @@ enum data_queue_flags {
432 * @flags: Entry flags, see &enum queue_entry_flags. 432 * @flags: Entry flags, see &enum queue_entry_flags.
433 * @status_lock: The mutex for protecting the start/stop/flush 433 * @status_lock: The mutex for protecting the start/stop/flush
434 * handling on this queue. 434 * handling on this queue.
435 * @tx_lock: Spinlock to serialize tx operations on this queue.
435 * @index_lock: Spinlock to protect index handling. Whenever @index, @index_done or 436 * @index_lock: Spinlock to protect index handling. Whenever @index, @index_done or
436 * @index_crypt needs to be changed this lock should be grabbed to prevent 437 * @index_crypt needs to be changed this lock should be grabbed to prevent
437 * index corruption due to concurrency. 438 * index corruption due to concurrency.
@@ -458,6 +459,7 @@ struct data_queue {
458 unsigned long flags; 459 unsigned long flags;
459 460
460 struct mutex status_lock; 461 struct mutex status_lock;
462 spinlock_t tx_lock;
461 spinlock_t index_lock; 463 spinlock_t index_lock;
462 464
463 unsigned int count; 465 unsigned int count;