Commit c58ec932 authored by Michael Chan's avatar Michael Chan Committed by David S. Miller

[TG3]: Fix 4GB boundary tx handling

Fix and simplify the workaround code for the 4GB boundary tx buffer
hardware bug.

1. Need to unmap the original SKB's dma addresses if a new SKB cannot
   be allocated.

2. Need to pass the base flag to tigon3_4gb_hwbug_workaround() or TSO
   won't work properly.

3. The guilty entry and length parameters for
   tigon3_4gb_hwbug_workaround() are removed as they are not necessary.

4. Remove assumption that only one fragment can hit the 4GB boundary.
   Another fragment can hit 8GB for example.
Signed-off-by: default avatarMichael Chan <mchan@broadcom.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent eb8edb08
...@@ -3442,31 +3442,47 @@ static void tg3_tx_timeout(struct net_device *dev) ...@@ -3442,31 +3442,47 @@ static void tg3_tx_timeout(struct net_device *dev)
schedule_work(&tp->reset_task); schedule_work(&tp->reset_task);
} }
/* Test for DMA buffers crossing any 4GB boundaries: 4G, 8G, etc */
static inline int tg3_4g_overflow_test(dma_addr_t mapping, int len)
{
u32 base = (u32) mapping & 0xffffffff;
return ((base > 0xffffdcc0) &&
(base + len + 8 < base));
}
static void tg3_set_txd(struct tg3 *, int, dma_addr_t, int, u32, u32); static void tg3_set_txd(struct tg3 *, int, dma_addr_t, int, u32, u32);
static int tigon3_4gb_hwbug_workaround(struct tg3 *tp, struct sk_buff *skb, static int tigon3_4gb_hwbug_workaround(struct tg3 *tp, struct sk_buff *skb,
u32 guilty_entry, int guilty_len, u32 last_plus_one, u32 *start,
u32 last_plus_one, u32 *start, u32 mss) u32 base_flags, u32 mss)
{ {
struct sk_buff *new_skb = skb_copy(skb, GFP_ATOMIC); struct sk_buff *new_skb = skb_copy(skb, GFP_ATOMIC);
dma_addr_t new_addr; dma_addr_t new_addr = 0;
u32 entry = *start; u32 entry = *start;
int i; int i, ret = 0;
if (!new_skb) { if (!new_skb) {
dev_kfree_skb(skb); ret = -1;
return -1; } else {
/* New SKB is guaranteed to be linear. */
entry = *start;
new_addr = pci_map_single(tp->pdev, new_skb->data, new_skb->len,
PCI_DMA_TODEVICE);
/* Make sure new skb does not cross any 4G boundaries.
* Drop the packet if it does.
*/
if (tg3_4g_overflow_test(new_addr, new_skb->len)) {
ret = -1;
dev_kfree_skb(new_skb);
new_skb = NULL;
} else {
tg3_set_txd(tp, entry, new_addr, new_skb->len,
base_flags, 1 | (mss << 1));
*start = NEXT_TX(entry);
}
} }
/* New SKB is guaranteed to be linear. */
entry = *start;
new_addr = pci_map_single(tp->pdev, new_skb->data, new_skb->len,
PCI_DMA_TODEVICE);
tg3_set_txd(tp, entry, new_addr, new_skb->len,
(skb->ip_summed == CHECKSUM_HW) ?
TXD_FLAG_TCPUDP_CSUM : 0, 1 | (mss << 1));
*start = NEXT_TX(entry);
/* Now clean up the sw ring entries. */ /* Now clean up the sw ring entries. */
i = 0; i = 0;
while (entry != last_plus_one) { while (entry != last_plus_one) {
...@@ -3491,7 +3507,7 @@ static int tigon3_4gb_hwbug_workaround(struct tg3 *tp, struct sk_buff *skb, ...@@ -3491,7 +3507,7 @@ static int tigon3_4gb_hwbug_workaround(struct tg3 *tp, struct sk_buff *skb,
dev_kfree_skb(skb); dev_kfree_skb(skb);
return 0; return ret;
} }
static void tg3_set_txd(struct tg3 *tp, int entry, static void tg3_set_txd(struct tg3 *tp, int entry,
...@@ -3517,19 +3533,10 @@ static void tg3_set_txd(struct tg3 *tp, int entry, ...@@ -3517,19 +3533,10 @@ static void tg3_set_txd(struct tg3 *tp, int entry,
txd->vlan_tag = vlan_tag << TXD_VLAN_TAG_SHIFT; txd->vlan_tag = vlan_tag << TXD_VLAN_TAG_SHIFT;
} }
static inline int tg3_4g_overflow_test(dma_addr_t mapping, int len)
{
u32 base = (u32) mapping & 0xffffffff;
return ((base > 0xffffdcc0) &&
(base + len + 8 < base));
}
static int tg3_start_xmit(struct sk_buff *skb, struct net_device *dev) static int tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
{ {
struct tg3 *tp = netdev_priv(dev); struct tg3 *tp = netdev_priv(dev);
dma_addr_t mapping; dma_addr_t mapping;
unsigned int i;
u32 len, entry, base_flags, mss; u32 len, entry, base_flags, mss;
int would_hit_hwbug; int would_hit_hwbug;
...@@ -3624,7 +3631,7 @@ static int tg3_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -3624,7 +3631,7 @@ static int tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
would_hit_hwbug = 0; would_hit_hwbug = 0;
if (tg3_4g_overflow_test(mapping, len)) if (tg3_4g_overflow_test(mapping, len))
would_hit_hwbug = entry + 1; would_hit_hwbug = 1;
tg3_set_txd(tp, entry, mapping, len, base_flags, tg3_set_txd(tp, entry, mapping, len, base_flags,
(skb_shinfo(skb)->nr_frags == 0) | (mss << 1)); (skb_shinfo(skb)->nr_frags == 0) | (mss << 1));
...@@ -3648,12 +3655,8 @@ static int tg3_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -3648,12 +3655,8 @@ static int tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
tp->tx_buffers[entry].skb = NULL; tp->tx_buffers[entry].skb = NULL;
pci_unmap_addr_set(&tp->tx_buffers[entry], mapping, mapping); pci_unmap_addr_set(&tp->tx_buffers[entry], mapping, mapping);
if (tg3_4g_overflow_test(mapping, len)) { if (tg3_4g_overflow_test(mapping, len))
/* Only one should match. */ would_hit_hwbug = 1;
if (would_hit_hwbug)
BUG();
would_hit_hwbug = entry + 1;
}
if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) if (tp->tg3_flags2 & TG3_FLG2_HW_TSO)
tg3_set_txd(tp, entry, mapping, len, tg3_set_txd(tp, entry, mapping, len,
...@@ -3669,34 +3672,15 @@ static int tg3_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -3669,34 +3672,15 @@ static int tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (would_hit_hwbug) { if (would_hit_hwbug) {
u32 last_plus_one = entry; u32 last_plus_one = entry;
u32 start; u32 start;
unsigned int len = 0;
would_hit_hwbug -= 1;
entry = entry - 1 - skb_shinfo(skb)->nr_frags;
entry &= (TG3_TX_RING_SIZE - 1);
start = entry;
i = 0;
while (entry != last_plus_one) {
if (i == 0)
len = skb_headlen(skb);
else
len = skb_shinfo(skb)->frags[i-1].size;
if (entry == would_hit_hwbug)
break;
i++; start = entry - 1 - skb_shinfo(skb)->nr_frags;
entry = NEXT_TX(entry); start &= (TG3_TX_RING_SIZE - 1);
}
/* If the workaround fails due to memory/mapping /* If the workaround fails due to memory/mapping
* failure, silently drop this packet. * failure, silently drop this packet.
*/ */
if (tigon3_4gb_hwbug_workaround(tp, skb, if (tigon3_4gb_hwbug_workaround(tp, skb, last_plus_one,
entry, len, &start, base_flags, mss))
last_plus_one,
&start, mss))
goto out_unlock; goto out_unlock;
entry = start; entry = start;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment