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 /drivers/net/ethernet | |
| 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>
Diffstat (limited to 'drivers/net/ethernet')
| -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 | ||
