aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLarry Finger <Larry.Finger@lwfinger.net>2011-12-21 19:47:59 -0500
committerJohn W. Linville <linville@tuxdriver.com>2012-01-04 14:30:43 -0500
commit5d07a3d62f63f3a9ce769c37108f8411c014903e (patch)
tree8545015a8ee333adad1a1f98dc1fbd0c05025539
parent9bd2857188d920f358cfb740fc6f88e1a17a837e (diff)
b43legacy: Avoid packet losses in the dma worker code
This patch addresses a bug in the dma worker code that keeps draining packets even when the hardware queues are full. In such cases packets can not be passed down to the device and are erroneusly dropped by the code. It is based on commit bad6919469662b7c92bc6353642aaaa777b36bac, which fixes the same problem in b43. Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--drivers/net/wireless/b43legacy/b43legacy.h20
-rw-r--r--drivers/net/wireless/b43legacy/dma.c65
-rw-r--r--drivers/net/wireless/b43legacy/dma.h5
-rw-r--r--drivers/net/wireless/b43legacy/main.c86
4 files changed, 126 insertions, 50 deletions
diff --git a/drivers/net/wireless/b43legacy/b43legacy.h b/drivers/net/wireless/b43legacy/b43legacy.h
index 1d4fc9db7f5e..98e3d44400c6 100644
--- a/drivers/net/wireless/b43legacy/b43legacy.h
+++ b/drivers/net/wireless/b43legacy/b43legacy.h
@@ -560,8 +560,16 @@ struct b43legacy_key {
560 u8 algorithm; 560 u8 algorithm;
561}; 561};
562 562
563#define B43legacy_QOS_QUEUE_NUM 4
564
563struct b43legacy_wldev; 565struct b43legacy_wldev;
564 566
567/* QOS parameters for a queue. */
568struct b43legacy_qos_params {
569 /* The QOS parameters */
570 struct ieee80211_tx_queue_params p;
571};
572
565/* Data structure for the WLAN parts (802.11 cores) of the b43legacy chip. */ 573/* Data structure for the WLAN parts (802.11 cores) of the b43legacy chip. */
566struct b43legacy_wl { 574struct b43legacy_wl {
567 /* Pointer to the active wireless device on this chip */ 575 /* Pointer to the active wireless device on this chip */
@@ -611,6 +619,18 @@ struct b43legacy_wl {
611 bool beacon1_uploaded; 619 bool beacon1_uploaded;
612 bool beacon_templates_virgin; /* Never wrote the templates? */ 620 bool beacon_templates_virgin; /* Never wrote the templates? */
613 struct work_struct beacon_update_trigger; 621 struct work_struct beacon_update_trigger;
622 /* The current QOS parameters for the 4 queues. */
623 struct b43legacy_qos_params qos_params[B43legacy_QOS_QUEUE_NUM];
624
625 /* Packet transmit work */
626 struct work_struct tx_work;
627
628 /* Queue of packets to be transmitted. */
629 struct sk_buff_head tx_queue[B43legacy_QOS_QUEUE_NUM];
630
631 /* Flag that implement the queues stopping. */
632 bool tx_queue_stopped[B43legacy_QOS_QUEUE_NUM];
633
614}; 634};
615 635
616/* Pointers to the firmware data and meta information about it. */ 636/* Pointers to the firmware data and meta information about it. */
diff --git a/drivers/net/wireless/b43legacy/dma.c b/drivers/net/wireless/b43legacy/dma.c
index c5535adf6991..aebef75a2c62 100644
--- a/drivers/net/wireless/b43legacy/dma.c
+++ b/drivers/net/wireless/b43legacy/dma.c
@@ -727,7 +727,6 @@ struct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev,
727 } else 727 } else
728 B43legacy_WARN_ON(1); 728 B43legacy_WARN_ON(1);
729 } 729 }
730 spin_lock_init(&ring->lock);
731#ifdef CONFIG_B43LEGACY_DEBUG 730#ifdef CONFIG_B43LEGACY_DEBUG
732 ring->last_injected_overflow = jiffies; 731 ring->last_injected_overflow = jiffies;
733#endif 732#endif
@@ -1144,10 +1143,8 @@ int b43legacy_dma_tx(struct b43legacy_wldev *dev,
1144{ 1143{
1145 struct b43legacy_dmaring *ring; 1144 struct b43legacy_dmaring *ring;
1146 int err = 0; 1145 int err = 0;
1147 unsigned long flags;
1148 1146
1149 ring = priority_to_txring(dev, skb_get_queue_mapping(skb)); 1147 ring = priority_to_txring(dev, skb_get_queue_mapping(skb));
1150 spin_lock_irqsave(&ring->lock, flags);
1151 B43legacy_WARN_ON(!ring->tx); 1148 B43legacy_WARN_ON(!ring->tx);
1152 1149
1153 if (unlikely(ring->stopped)) { 1150 if (unlikely(ring->stopped)) {
@@ -1157,16 +1154,14 @@ int b43legacy_dma_tx(struct b43legacy_wldev *dev,
1157 * For now, just refuse the transmit. */ 1154 * For now, just refuse the transmit. */
1158 if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) 1155 if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE))
1159 b43legacyerr(dev->wl, "Packet after queue stopped\n"); 1156 b43legacyerr(dev->wl, "Packet after queue stopped\n");
1160 err = -ENOSPC; 1157 return -ENOSPC;
1161 goto out_unlock;
1162 } 1158 }
1163 1159
1164 if (unlikely(WARN_ON(free_slots(ring) < SLOTS_PER_PACKET))) { 1160 if (unlikely(WARN_ON(free_slots(ring) < SLOTS_PER_PACKET))) {
1165 /* If we get here, we have a real error with the queue 1161 /* If we get here, we have a real error with the queue
1166 * full, but queues not stopped. */ 1162 * full, but queues not stopped. */
1167 b43legacyerr(dev->wl, "DMA queue overflow\n"); 1163 b43legacyerr(dev->wl, "DMA queue overflow\n");
1168 err = -ENOSPC; 1164 return -ENOSPC;
1169 goto out_unlock;
1170 } 1165 }
1171 1166
1172 /* dma_tx_fragment might reallocate the skb, so invalidate pointers pointing 1167 /* dma_tx_fragment might reallocate the skb, so invalidate pointers pointing
@@ -1176,25 +1171,23 @@ int b43legacy_dma_tx(struct b43legacy_wldev *dev,
1176 /* Drop this packet, as we don't have the encryption key 1171 /* Drop this packet, as we don't have the encryption key
1177 * anymore and must not transmit it unencrypted. */ 1172 * anymore and must not transmit it unencrypted. */
1178 dev_kfree_skb_any(skb); 1173 dev_kfree_skb_any(skb);
1179 err = 0; 1174 return 0;
1180 goto out_unlock;
1181 } 1175 }
1182 if (unlikely(err)) { 1176 if (unlikely(err)) {
1183 b43legacyerr(dev->wl, "DMA tx mapping failure\n"); 1177 b43legacyerr(dev->wl, "DMA tx mapping failure\n");
1184 goto out_unlock; 1178 return err;
1185 } 1179 }
1186 if ((free_slots(ring) < SLOTS_PER_PACKET) || 1180 if ((free_slots(ring) < SLOTS_PER_PACKET) ||
1187 should_inject_overflow(ring)) { 1181 should_inject_overflow(ring)) {
1188 /* This TX ring is full. */ 1182 /* This TX ring is full. */
1189 ieee80211_stop_queue(dev->wl->hw, txring_to_priority(ring)); 1183 unsigned int skb_mapping = skb_get_queue_mapping(skb);
1184 ieee80211_stop_queue(dev->wl->hw, skb_mapping);
1185 dev->wl->tx_queue_stopped[skb_mapping] = 1;
1190 ring->stopped = 1; 1186 ring->stopped = 1;
1191 if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) 1187 if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE))
1192 b43legacydbg(dev->wl, "Stopped TX ring %d\n", 1188 b43legacydbg(dev->wl, "Stopped TX ring %d\n",
1193 ring->index); 1189 ring->index);
1194 } 1190 }
1195out_unlock:
1196 spin_unlock_irqrestore(&ring->lock, flags);
1197
1198 return err; 1191 return err;
1199} 1192}
1200 1193
@@ -1205,14 +1198,29 @@ void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev,
1205 struct b43legacy_dmadesc_meta *meta; 1198 struct b43legacy_dmadesc_meta *meta;
1206 int retry_limit; 1199 int retry_limit;
1207 int slot; 1200 int slot;
1201 int firstused;
1208 1202
1209 ring = parse_cookie(dev, status->cookie, &slot); 1203 ring = parse_cookie(dev, status->cookie, &slot);
1210 if (unlikely(!ring)) 1204 if (unlikely(!ring))
1211 return; 1205 return;
1212 B43legacy_WARN_ON(!irqs_disabled());
1213 spin_lock(&ring->lock);
1214
1215 B43legacy_WARN_ON(!ring->tx); 1206 B43legacy_WARN_ON(!ring->tx);
1207
1208 /* Sanity check: TX packets are processed in-order on one ring.
1209 * Check if the slot deduced from the cookie really is the first
1210 * used slot. */
1211 firstused = ring->current_slot - ring->used_slots + 1;
1212 if (firstused < 0)
1213 firstused = ring->nr_slots + firstused;
1214 if (unlikely(slot != firstused)) {
1215 /* This possibly is a firmware bug and will result in
1216 * malfunction, memory leaks and/or stall of DMA functionality.
1217 */
1218 b43legacydbg(dev->wl, "Out of order TX status report on DMA "
1219 "ring %d. Expected %d, but got %d\n",
1220 ring->index, firstused, slot);
1221 return;
1222 }
1223
1216 while (1) { 1224 while (1) {
1217 B43legacy_WARN_ON(!(slot >= 0 && slot < ring->nr_slots)); 1225 B43legacy_WARN_ON(!(slot >= 0 && slot < ring->nr_slots));
1218 op32_idx2desc(ring, slot, &meta); 1226 op32_idx2desc(ring, slot, &meta);
@@ -1285,14 +1293,21 @@ void b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev,
1285 dev->stats.last_tx = jiffies; 1293 dev->stats.last_tx = jiffies;
1286 if (ring->stopped) { 1294 if (ring->stopped) {
1287 B43legacy_WARN_ON(free_slots(ring) < SLOTS_PER_PACKET); 1295 B43legacy_WARN_ON(free_slots(ring) < SLOTS_PER_PACKET);
1288 ieee80211_wake_queue(dev->wl->hw, txring_to_priority(ring));
1289 ring->stopped = 0; 1296 ring->stopped = 0;
1297 }
1298
1299 if (dev->wl->tx_queue_stopped[ring->queue_prio]) {
1300 dev->wl->tx_queue_stopped[ring->queue_prio] = 0;
1301 } else {
1302 /* If the driver queue is running wake the corresponding
1303 * mac80211 queue. */
1304 ieee80211_wake_queue(dev->wl->hw, ring->queue_prio);
1290 if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) 1305 if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE))
1291 b43legacydbg(dev->wl, "Woke up TX ring %d\n", 1306 b43legacydbg(dev->wl, "Woke up TX ring %d\n",
1292 ring->index); 1307 ring->index);
1293 } 1308 }
1294 1309 /* Add work to the queue. */
1295 spin_unlock(&ring->lock); 1310 ieee80211_queue_work(dev->wl->hw, &dev->wl->tx_work);
1296} 1311}
1297 1312
1298static void dma_rx(struct b43legacy_dmaring *ring, 1313static void dma_rx(struct b43legacy_dmaring *ring,
@@ -1415,22 +1430,14 @@ void b43legacy_dma_rx(struct b43legacy_dmaring *ring)
1415 1430
1416static void b43legacy_dma_tx_suspend_ring(struct b43legacy_dmaring *ring) 1431static void b43legacy_dma_tx_suspend_ring(struct b43legacy_dmaring *ring)
1417{ 1432{
1418 unsigned long flags;
1419
1420 spin_lock_irqsave(&ring->lock, flags);
1421 B43legacy_WARN_ON(!ring->tx); 1433 B43legacy_WARN_ON(!ring->tx);
1422 op32_tx_suspend(ring); 1434 op32_tx_suspend(ring);
1423 spin_unlock_irqrestore(&ring->lock, flags);
1424} 1435}
1425 1436
1426static void b43legacy_dma_tx_resume_ring(struct b43legacy_dmaring *ring) 1437static void b43legacy_dma_tx_resume_ring(struct b43legacy_dmaring *ring)
1427{ 1438{
1428 unsigned long flags;
1429
1430 spin_lock_irqsave(&ring->lock, flags);
1431 B43legacy_WARN_ON(!ring->tx); 1439 B43legacy_WARN_ON(!ring->tx);
1432 op32_tx_resume(ring); 1440 op32_tx_resume(ring);
1433 spin_unlock_irqrestore(&ring->lock, flags);
1434} 1441}
1435 1442
1436void b43legacy_dma_tx_suspend(struct b43legacy_wldev *dev) 1443void b43legacy_dma_tx_suspend(struct b43legacy_wldev *dev)
diff --git a/drivers/net/wireless/b43legacy/dma.h b/drivers/net/wireless/b43legacy/dma.h
index 504a58767e95..c3282f906bc7 100644
--- a/drivers/net/wireless/b43legacy/dma.h
+++ b/drivers/net/wireless/b43legacy/dma.h
@@ -150,8 +150,9 @@ struct b43legacy_dmaring {
150 enum b43legacy_dmatype type; 150 enum b43legacy_dmatype type;
151 /* Boolean. Is this ring stopped at ieee80211 level? */ 151 /* Boolean. Is this ring stopped at ieee80211 level? */
152 bool stopped; 152 bool stopped;
153 /* Lock, only used for TX. */ 153 /* The QOS priority assigned to this ring. Only used for TX rings.
154 spinlock_t lock; 154 * This is the mac80211 "queue" value. */
155 u8 queue_prio;
155 struct b43legacy_wldev *dev; 156 struct b43legacy_wldev *dev;
156#ifdef CONFIG_B43LEGACY_DEBUG 157#ifdef CONFIG_B43LEGACY_DEBUG
157 /* Maximum number of used slots. */ 158 /* Maximum number of used slots. */
diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c
index 20f02437af8c..d9185633e82f 100644
--- a/drivers/net/wireless/b43legacy/main.c
+++ b/drivers/net/wireless/b43legacy/main.c
@@ -2440,30 +2440,64 @@ static int b43legacy_rng_init(struct b43legacy_wl *wl)
2440 return err; 2440 return err;
2441} 2441}
2442 2442
2443static void b43legacy_tx_work(struct work_struct *work)
2444{
2445 struct b43legacy_wl *wl = container_of(work, struct b43legacy_wl,
2446 tx_work);
2447 struct b43legacy_wldev *dev;
2448 struct sk_buff *skb;
2449 int queue_num;
2450 int err = 0;
2451
2452 mutex_lock(&wl->mutex);
2453 dev = wl->current_dev;
2454 if (unlikely(!dev || b43legacy_status(dev) < B43legacy_STAT_STARTED)) {
2455 mutex_unlock(&wl->mutex);
2456 return;
2457 }
2458
2459 for (queue_num = 0; queue_num < B43legacy_QOS_QUEUE_NUM; queue_num++) {
2460 while (skb_queue_len(&wl->tx_queue[queue_num])) {
2461 skb = skb_dequeue(&wl->tx_queue[queue_num]);
2462 if (b43legacy_using_pio(dev))
2463 err = b43legacy_pio_tx(dev, skb);
2464 else
2465 err = b43legacy_dma_tx(dev, skb);
2466 if (err == -ENOSPC) {
2467 wl->tx_queue_stopped[queue_num] = 1;
2468 ieee80211_stop_queue(wl->hw, queue_num);
2469 skb_queue_head(&wl->tx_queue[queue_num], skb);
2470 break;
2471 }
2472 if (unlikely(err))
2473 dev_kfree_skb(skb); /* Drop it */
2474 err = 0;
2475 }
2476
2477 if (!err)
2478 wl->tx_queue_stopped[queue_num] = 0;
2479 }
2480
2481 mutex_unlock(&wl->mutex);
2482}
2483
2443static void b43legacy_op_tx(struct ieee80211_hw *hw, 2484static void b43legacy_op_tx(struct ieee80211_hw *hw,
2444 struct sk_buff *skb) 2485 struct sk_buff *skb)
2445{ 2486{
2446 struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); 2487 struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
2447 struct b43legacy_wldev *dev = wl->current_dev;
2448 int err = -ENODEV;
2449 unsigned long flags;
2450 2488
2451 if (unlikely(!dev)) 2489 if (unlikely(skb->len < 2 + 2 + 6)) {
2452 goto out; 2490 /* Too short, this can't be a valid frame. */
2453 if (unlikely(b43legacy_status(dev) < B43legacy_STAT_STARTED))
2454 goto out;
2455 /* DMA-TX is done without a global lock. */
2456 if (b43legacy_using_pio(dev)) {
2457 spin_lock_irqsave(&wl->irq_lock, flags);
2458 err = b43legacy_pio_tx(dev, skb);
2459 spin_unlock_irqrestore(&wl->irq_lock, flags);
2460 } else
2461 err = b43legacy_dma_tx(dev, skb);
2462out:
2463 if (unlikely(err)) {
2464 /* Drop the packet. */
2465 dev_kfree_skb_any(skb); 2491 dev_kfree_skb_any(skb);
2492 return;
2466 } 2493 }
2494 B43legacy_WARN_ON(skb_shinfo(skb)->nr_frags);
2495
2496 skb_queue_tail(&wl->tx_queue[skb->queue_mapping], skb);
2497 if (!wl->tx_queue_stopped[skb->queue_mapping])
2498 ieee80211_queue_work(wl->hw, &wl->tx_work);
2499 else
2500 ieee80211_stop_queue(wl->hw, skb->queue_mapping);
2467} 2501}
2468 2502
2469static int b43legacy_op_conf_tx(struct ieee80211_hw *hw, 2503static int b43legacy_op_conf_tx(struct ieee80211_hw *hw,
@@ -2879,6 +2913,7 @@ static void b43legacy_wireless_core_stop(struct b43legacy_wldev *dev)
2879{ 2913{
2880 struct b43legacy_wl *wl = dev->wl; 2914 struct b43legacy_wl *wl = dev->wl;
2881 unsigned long flags; 2915 unsigned long flags;
2916 int queue_num;
2882 2917
2883 if (b43legacy_status(dev) < B43legacy_STAT_STARTED) 2918 if (b43legacy_status(dev) < B43legacy_STAT_STARTED)
2884 return; 2919 return;
@@ -2898,11 +2933,16 @@ static void b43legacy_wireless_core_stop(struct b43legacy_wldev *dev)
2898 /* Must unlock as it would otherwise deadlock. No races here. 2933 /* Must unlock as it would otherwise deadlock. No races here.
2899 * Cancel the possibly running self-rearming periodic work. */ 2934 * Cancel the possibly running self-rearming periodic work. */
2900 cancel_delayed_work_sync(&dev->periodic_work); 2935 cancel_delayed_work_sync(&dev->periodic_work);
2936 cancel_work_sync(&wl->tx_work);
2901 mutex_lock(&wl->mutex); 2937 mutex_lock(&wl->mutex);
2902 2938
2903 ieee80211_stop_queues(wl->hw); /* FIXME this could cause a deadlock */ 2939 /* Drain all TX queues. */
2940 for (queue_num = 0; queue_num < B43legacy_QOS_QUEUE_NUM; queue_num++) {
2941 while (skb_queue_len(&wl->tx_queue[queue_num]))
2942 dev_kfree_skb(skb_dequeue(&wl->tx_queue[queue_num]));
2943 }
2904 2944
2905 b43legacy_mac_suspend(dev); 2945b43legacy_mac_suspend(dev);
2906 free_irq(dev->dev->irq, dev); 2946 free_irq(dev->dev->irq, dev);
2907 b43legacydbg(wl, "Wireless interface stopped\n"); 2947 b43legacydbg(wl, "Wireless interface stopped\n");
2908} 2948}
@@ -3748,6 +3788,7 @@ static int b43legacy_wireless_init(struct ssb_device *dev)
3748 struct ieee80211_hw *hw; 3788 struct ieee80211_hw *hw;
3749 struct b43legacy_wl *wl; 3789 struct b43legacy_wl *wl;
3750 int err = -ENOMEM; 3790 int err = -ENOMEM;
3791 int queue_num;
3751 3792
3752 b43legacy_sprom_fixup(dev->bus); 3793 b43legacy_sprom_fixup(dev->bus);
3753 3794
@@ -3782,6 +3823,13 @@ static int b43legacy_wireless_init(struct ssb_device *dev)
3782 mutex_init(&wl->mutex); 3823 mutex_init(&wl->mutex);
3783 INIT_LIST_HEAD(&wl->devlist); 3824 INIT_LIST_HEAD(&wl->devlist);
3784 INIT_WORK(&wl->beacon_update_trigger, b43legacy_beacon_update_trigger_work); 3825 INIT_WORK(&wl->beacon_update_trigger, b43legacy_beacon_update_trigger_work);
3826 INIT_WORK(&wl->tx_work, b43legacy_tx_work);
3827
3828 /* Initialize queues and flags. */
3829 for (queue_num = 0; queue_num < B43legacy_QOS_QUEUE_NUM; queue_num++) {
3830 skb_queue_head_init(&wl->tx_queue[queue_num]);
3831 wl->tx_queue_stopped[queue_num] = 0;
3832 }
3785 3833
3786 ssb_set_devtypedata(dev, wl); 3834 ssb_set_devtypedata(dev, wl);
3787 b43legacyinfo(wl, "Broadcom %04X WLAN found (core revision %u)\n", 3835 b43legacyinfo(wl, "Broadcom %04X WLAN found (core revision %u)\n",