diff options
author | Anjali Singhai Jain <anjali.singhai@intel.com> | 2015-05-08 18:35:52 -0400 |
---|---|---|
committer | Jeff Kirsher <jeffrey.t.kirsher@intel.com> | 2015-06-04 23:06:06 -0400 |
commit | 30520831f058cd9d75c0f6b360bc5c5ae49b5f27 (patch) | |
tree | a7bc0fbfcd4c0f6713aba379c9b0b257c18a3f00 | |
parent | 6e540309326188f769e03bb4c6dd8ff6752930c2 (diff) |
i40e/i40evf: Fix mixed size frags and linearization
This patch fixes a bug where the i40e Tx queue will hang if this
skb is passed to the driver.
With mixed size fragments while using TSO there was a corner case
where we needed to linearize but we were not. This was seen with
iSCSI traffic and could be reproduced with a frag list that looks
like this:
num_frags = 17, gso_segs = 17, hdr_len = 66,
skb_shinfo(skb)->gso_size = 1448
size = 3002, j = 1, frag_size = 2936, num_frags = 17
size = 4268, j = 1, frag_size = 4096, num_frags = 16
size = 5534, j = 1, frag_size = 4096, num_frags = 15
size = 5352, j = 1, frag_size = 4096, num_frags = 14
size = 5170, j = 1, frag_size = 4096, num_frags = 13
size = 3468, j = 1, frag_size = 2576, num_frags = 12
size = 750, j = 1, frag_size = 112, num_frags = 11
size = 862, j = 2, frag_size = 112, num_frags = 10
size = 974, j = 3, frag_size = 112, num_frags = 9
size = 1126, j = 4, frag_size = 152, num_frags = 8
size = 1330, j = 5, frag_size = 204, num_frags = 7
size = 1534, j = 6, frag_size = 204, num_frags = 6
size = 356, j = 1, frag_size = 204, num_frags = 5
size = 560, j = 2, frag_size = 204, num_frags = 4
size = 764, j = 3, frag_size = 204, num_frags = 3
size = 968, j = 4, frag_size = 204, num_frags = 2
size = 1140, j = 5, frag_size = 172, num_frags = 1
result: linearize = 0, j = 6
Change-ID: I79bb1aeab0af255fe2ce28e93672a85d85bf47e8
Signed-off-by: Anjali Singhai Jain <anjali.singhai@intel.com>
Signed-off-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
-rw-r--r-- | drivers/net/ethernet/intel/i40e/i40e_txrx.c | 25 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 25 |
2 files changed, 20 insertions, 30 deletions
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 4bd3a80aba82..9d95042d5a0f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c | |||
@@ -2410,14 +2410,12 @@ static int i40e_maybe_stop_tx(struct i40e_ring *tx_ring, int size) | |||
2410 | * i40e_chk_linearize - Check if there are more than 8 fragments per packet | 2410 | * i40e_chk_linearize - Check if there are more than 8 fragments per packet |
2411 | * @skb: send buffer | 2411 | * @skb: send buffer |
2412 | * @tx_flags: collected send information | 2412 | * @tx_flags: collected send information |
2413 | * @hdr_len: size of the packet header | ||
2414 | * | 2413 | * |
2415 | * Note: Our HW can't scatter-gather more than 8 fragments to build | 2414 | * Note: Our HW can't scatter-gather more than 8 fragments to build |
2416 | * a packet on the wire and so we need to figure out the cases where we | 2415 | * a packet on the wire and so we need to figure out the cases where we |
2417 | * need to linearize the skb. | 2416 | * need to linearize the skb. |
2418 | **/ | 2417 | **/ |
2419 | static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags, | 2418 | static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags) |
2420 | const u8 hdr_len) | ||
2421 | { | 2419 | { |
2422 | struct skb_frag_struct *frag; | 2420 | struct skb_frag_struct *frag; |
2423 | bool linearize = false; | 2421 | bool linearize = false; |
@@ -2429,7 +2427,7 @@ static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags, | |||
2429 | gso_segs = skb_shinfo(skb)->gso_segs; | 2427 | gso_segs = skb_shinfo(skb)->gso_segs; |
2430 | 2428 | ||
2431 | if (tx_flags & (I40E_TX_FLAGS_TSO | I40E_TX_FLAGS_FSO)) { | 2429 | if (tx_flags & (I40E_TX_FLAGS_TSO | I40E_TX_FLAGS_FSO)) { |
2432 | u16 j = 1; | 2430 | u16 j = 0; |
2433 | 2431 | ||
2434 | if (num_frags < (I40E_MAX_BUFFER_TXD)) | 2432 | if (num_frags < (I40E_MAX_BUFFER_TXD)) |
2435 | goto linearize_chk_done; | 2433 | goto linearize_chk_done; |
@@ -2440,21 +2438,18 @@ static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags, | |||
2440 | goto linearize_chk_done; | 2438 | goto linearize_chk_done; |
2441 | } | 2439 | } |
2442 | frag = &skb_shinfo(skb)->frags[0]; | 2440 | frag = &skb_shinfo(skb)->frags[0]; |
2443 | size = hdr_len; | ||
2444 | /* we might still have more fragments per segment */ | 2441 | /* we might still have more fragments per segment */ |
2445 | do { | 2442 | do { |
2446 | size += skb_frag_size(frag); | 2443 | size += skb_frag_size(frag); |
2447 | frag++; j++; | 2444 | frag++; j++; |
2445 | if ((size >= skb_shinfo(skb)->gso_size) && | ||
2446 | (j < I40E_MAX_BUFFER_TXD)) { | ||
2447 | size = (size % skb_shinfo(skb)->gso_size); | ||
2448 | j = (size) ? 1 : 0; | ||
2449 | } | ||
2448 | if (j == I40E_MAX_BUFFER_TXD) { | 2450 | if (j == I40E_MAX_BUFFER_TXD) { |
2449 | if (size < skb_shinfo(skb)->gso_size) { | 2451 | linearize = true; |
2450 | linearize = true; | 2452 | break; |
2451 | break; | ||
2452 | } | ||
2453 | j = 1; | ||
2454 | size -= skb_shinfo(skb)->gso_size; | ||
2455 | if (size) | ||
2456 | j++; | ||
2457 | size += hdr_len; | ||
2458 | } | 2453 | } |
2459 | num_frags--; | 2454 | num_frags--; |
2460 | } while (num_frags); | 2455 | } while (num_frags); |
@@ -2724,7 +2719,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, | |||
2724 | if (tsyn) | 2719 | if (tsyn) |
2725 | tx_flags |= I40E_TX_FLAGS_TSYN; | 2720 | tx_flags |= I40E_TX_FLAGS_TSYN; |
2726 | 2721 | ||
2727 | if (i40e_chk_linearize(skb, tx_flags, hdr_len)) | 2722 | if (i40e_chk_linearize(skb, tx_flags)) |
2728 | if (skb_linearize(skb)) | 2723 | if (skb_linearize(skb)) |
2729 | goto out_drop; | 2724 | goto out_drop; |
2730 | 2725 | ||
diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index b077e02a0cc7..458fbb421090 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c | |||
@@ -1619,14 +1619,12 @@ static void i40e_create_tx_ctx(struct i40e_ring *tx_ring, | |||
1619 | * i40e_chk_linearize - Check if there are more than 8 fragments per packet | 1619 | * i40e_chk_linearize - Check if there are more than 8 fragments per packet |
1620 | * @skb: send buffer | 1620 | * @skb: send buffer |
1621 | * @tx_flags: collected send information | 1621 | * @tx_flags: collected send information |
1622 | * @hdr_len: size of the packet header | ||
1623 | * | 1622 | * |
1624 | * Note: Our HW can't scatter-gather more than 8 fragments to build | 1623 | * Note: Our HW can't scatter-gather more than 8 fragments to build |
1625 | * a packet on the wire and so we need to figure out the cases where we | 1624 | * a packet on the wire and so we need to figure out the cases where we |
1626 | * need to linearize the skb. | 1625 | * need to linearize the skb. |
1627 | **/ | 1626 | **/ |
1628 | static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags, | 1627 | static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags) |
1629 | const u8 hdr_len) | ||
1630 | { | 1628 | { |
1631 | struct skb_frag_struct *frag; | 1629 | struct skb_frag_struct *frag; |
1632 | bool linearize = false; | 1630 | bool linearize = false; |
@@ -1638,7 +1636,7 @@ static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags, | |||
1638 | gso_segs = skb_shinfo(skb)->gso_segs; | 1636 | gso_segs = skb_shinfo(skb)->gso_segs; |
1639 | 1637 | ||
1640 | if (tx_flags & (I40E_TX_FLAGS_TSO | I40E_TX_FLAGS_FSO)) { | 1638 | if (tx_flags & (I40E_TX_FLAGS_TSO | I40E_TX_FLAGS_FSO)) { |
1641 | u16 j = 1; | 1639 | u16 j = 0; |
1642 | 1640 | ||
1643 | if (num_frags < (I40E_MAX_BUFFER_TXD)) | 1641 | if (num_frags < (I40E_MAX_BUFFER_TXD)) |
1644 | goto linearize_chk_done; | 1642 | goto linearize_chk_done; |
@@ -1649,21 +1647,18 @@ static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags, | |||
1649 | goto linearize_chk_done; | 1647 | goto linearize_chk_done; |
1650 | } | 1648 | } |
1651 | frag = &skb_shinfo(skb)->frags[0]; | 1649 | frag = &skb_shinfo(skb)->frags[0]; |
1652 | size = hdr_len; | ||
1653 | /* we might still have more fragments per segment */ | 1650 | /* we might still have more fragments per segment */ |
1654 | do { | 1651 | do { |
1655 | size += skb_frag_size(frag); | 1652 | size += skb_frag_size(frag); |
1656 | frag++; j++; | 1653 | frag++; j++; |
1654 | if ((size >= skb_shinfo(skb)->gso_size) && | ||
1655 | (j < I40E_MAX_BUFFER_TXD)) { | ||
1656 | size = (size % skb_shinfo(skb)->gso_size); | ||
1657 | j = (size) ? 1 : 0; | ||
1658 | } | ||
1657 | if (j == I40E_MAX_BUFFER_TXD) { | 1659 | if (j == I40E_MAX_BUFFER_TXD) { |
1658 | if (size < skb_shinfo(skb)->gso_size) { | 1660 | linearize = true; |
1659 | linearize = true; | 1661 | break; |
1660 | break; | ||
1661 | } | ||
1662 | j = 1; | ||
1663 | size -= skb_shinfo(skb)->gso_size; | ||
1664 | if (size) | ||
1665 | j++; | ||
1666 | size += hdr_len; | ||
1667 | } | 1662 | } |
1668 | num_frags--; | 1663 | num_frags--; |
1669 | } while (num_frags); | 1664 | } while (num_frags); |
@@ -1950,7 +1945,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, | |||
1950 | else if (tso) | 1945 | else if (tso) |
1951 | tx_flags |= I40E_TX_FLAGS_TSO; | 1946 | tx_flags |= I40E_TX_FLAGS_TSO; |
1952 | 1947 | ||
1953 | if (i40e_chk_linearize(skb, tx_flags, hdr_len)) | 1948 | if (i40e_chk_linearize(skb, tx_flags)) |
1954 | if (skb_linearize(skb)) | 1949 | if (skb_linearize(skb)) |
1955 | goto out_drop; | 1950 | goto out_drop; |
1956 | 1951 | ||