Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

[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
Loading
Loading
Loading
Loading
+39 −55
Original line number Diff line number Diff line
@@ -3442,30 +3442,46 @@ static void tg3_tx_timeout(struct net_device *dev)
	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 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 mss)
				       u32 last_plus_one, u32 *start,
				       u32 base_flags, u32 mss)
{
	struct sk_buff *new_skb = skb_copy(skb, GFP_ATOMIC);
	dma_addr_t new_addr;
	dma_addr_t new_addr = 0;
	u32 entry = *start;
	int i;
	int i, ret = 0;

	if (!new_skb) {
		dev_kfree_skb(skb);
		return -1;
	}

		ret = -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,
		    (skb->ip_summed == CHECKSUM_HW) ?
		    TXD_FLAG_TCPUDP_CSUM : 0, 1 | (mss << 1));
				    base_flags, 1 | (mss << 1));
			*start = NEXT_TX(entry);
		}
	}

	/* Now clean up the sw ring entries. */
	i = 0;
@@ -3491,7 +3507,7 @@ static int tigon3_4gb_hwbug_workaround(struct tg3 *tp, struct sk_buff *skb,

	dev_kfree_skb(skb);

	return 0;
	return ret;
}

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;
}

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)
{
	struct tg3 *tp = netdev_priv(dev);
	dma_addr_t mapping;
	unsigned int i;
	u32 len, entry, base_flags, mss;
	int would_hit_hwbug;

@@ -3624,7 +3631,7 @@ static int tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
	would_hit_hwbug = 0;

	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,
		    (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)
			tp->tx_buffers[entry].skb = NULL;
			pci_unmap_addr_set(&tp->tx_buffers[entry], mapping, mapping);

			if (tg3_4g_overflow_test(mapping, len)) {
				/* Only one should match. */
				if (would_hit_hwbug)
					BUG();
				would_hit_hwbug = entry + 1;
			}
			if (tg3_4g_overflow_test(mapping, len))
				would_hit_hwbug = 1;

			if (tp->tg3_flags2 & TG3_FLG2_HW_TSO)
				tg3_set_txd(tp, entry, mapping, len,
@@ -3669,34 +3672,15 @@ static int tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
	if (would_hit_hwbug) {
		u32 last_plus_one = entry;
		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++;
			entry = NEXT_TX(entry);

		}
		start = entry - 1 - skb_shinfo(skb)->nr_frags;
		start &= (TG3_TX_RING_SIZE - 1);

		/* If the workaround fails due to memory/mapping
		 * failure, silently drop this packet.
		 */
		if (tigon3_4gb_hwbug_workaround(tp, skb,
						entry, len,
						last_plus_one,
						&start, mss))
		if (tigon3_4gb_hwbug_workaround(tp, skb, last_plus_one,
						&start, base_flags, mss))
			goto out_unlock;

		entry = start;