mirror of
https://github.com/torvalds/linux
synced 2024-11-05 18:23:50 +00:00
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>
This commit is contained in:
parent
6e54030932
commit
30520831f0
2 changed files with 20 additions and 30 deletions
|
@ -2410,14 +2410,12 @@ static int i40e_maybe_stop_tx(struct i40e_ring *tx_ring, int size)
|
||||||
* i40e_chk_linearize - Check if there are more than 8 fragments per packet
|
* i40e_chk_linearize - Check if there are more than 8 fragments per packet
|
||||||
* @skb: send buffer
|
* @skb: send buffer
|
||||||
* @tx_flags: collected send information
|
* @tx_flags: collected send information
|
||||||
* @hdr_len: size of the packet header
|
|
||||||
*
|
*
|
||||||
* Note: Our HW can't scatter-gather more than 8 fragments to build
|
* Note: Our HW can't scatter-gather more than 8 fragments to build
|
||||||
* a packet on the wire and so we need to figure out the cases where we
|
* a packet on the wire and so we need to figure out the cases where we
|
||||||
* need to linearize the skb.
|
* need to linearize the skb.
|
||||||
**/
|
**/
|
||||||
static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags,
|
static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags)
|
||||||
const u8 hdr_len)
|
|
||||||
{
|
{
|
||||||
struct skb_frag_struct *frag;
|
struct skb_frag_struct *frag;
|
||||||
bool linearize = false;
|
bool linearize = false;
|
||||||
|
@ -2429,7 +2427,7 @@ static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags,
|
||||||
gso_segs = skb_shinfo(skb)->gso_segs;
|
gso_segs = skb_shinfo(skb)->gso_segs;
|
||||||
|
|
||||||
if (tx_flags & (I40E_TX_FLAGS_TSO | I40E_TX_FLAGS_FSO)) {
|
if (tx_flags & (I40E_TX_FLAGS_TSO | I40E_TX_FLAGS_FSO)) {
|
||||||
u16 j = 1;
|
u16 j = 0;
|
||||||
|
|
||||||
if (num_frags < (I40E_MAX_BUFFER_TXD))
|
if (num_frags < (I40E_MAX_BUFFER_TXD))
|
||||||
goto linearize_chk_done;
|
goto linearize_chk_done;
|
||||||
|
@ -2440,21 +2438,18 @@ static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags,
|
||||||
goto linearize_chk_done;
|
goto linearize_chk_done;
|
||||||
}
|
}
|
||||||
frag = &skb_shinfo(skb)->frags[0];
|
frag = &skb_shinfo(skb)->frags[0];
|
||||||
size = hdr_len;
|
|
||||||
/* we might still have more fragments per segment */
|
/* we might still have more fragments per segment */
|
||||||
do {
|
do {
|
||||||
size += skb_frag_size(frag);
|
size += skb_frag_size(frag);
|
||||||
frag++; j++;
|
frag++; j++;
|
||||||
|
if ((size >= skb_shinfo(skb)->gso_size) &&
|
||||||
|
(j < I40E_MAX_BUFFER_TXD)) {
|
||||||
|
size = (size % skb_shinfo(skb)->gso_size);
|
||||||
|
j = (size) ? 1 : 0;
|
||||||
|
}
|
||||||
if (j == I40E_MAX_BUFFER_TXD) {
|
if (j == I40E_MAX_BUFFER_TXD) {
|
||||||
if (size < skb_shinfo(skb)->gso_size) {
|
linearize = true;
|
||||||
linearize = true;
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
j = 1;
|
|
||||||
size -= skb_shinfo(skb)->gso_size;
|
|
||||||
if (size)
|
|
||||||
j++;
|
|
||||||
size += hdr_len;
|
|
||||||
}
|
}
|
||||||
num_frags--;
|
num_frags--;
|
||||||
} while (num_frags);
|
} while (num_frags);
|
||||||
|
@ -2724,7 +2719,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
|
||||||
if (tsyn)
|
if (tsyn)
|
||||||
tx_flags |= I40E_TX_FLAGS_TSYN;
|
tx_flags |= I40E_TX_FLAGS_TSYN;
|
||||||
|
|
||||||
if (i40e_chk_linearize(skb, tx_flags, hdr_len))
|
if (i40e_chk_linearize(skb, tx_flags))
|
||||||
if (skb_linearize(skb))
|
if (skb_linearize(skb))
|
||||||
goto out_drop;
|
goto out_drop;
|
||||||
|
|
||||||
|
|
|
@ -1619,14 +1619,12 @@ static void i40e_create_tx_ctx(struct i40e_ring *tx_ring,
|
||||||
* i40e_chk_linearize - Check if there are more than 8 fragments per packet
|
* i40e_chk_linearize - Check if there are more than 8 fragments per packet
|
||||||
* @skb: send buffer
|
* @skb: send buffer
|
||||||
* @tx_flags: collected send information
|
* @tx_flags: collected send information
|
||||||
* @hdr_len: size of the packet header
|
|
||||||
*
|
*
|
||||||
* Note: Our HW can't scatter-gather more than 8 fragments to build
|
* Note: Our HW can't scatter-gather more than 8 fragments to build
|
||||||
* a packet on the wire and so we need to figure out the cases where we
|
* a packet on the wire and so we need to figure out the cases where we
|
||||||
* need to linearize the skb.
|
* need to linearize the skb.
|
||||||
**/
|
**/
|
||||||
static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags,
|
static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags)
|
||||||
const u8 hdr_len)
|
|
||||||
{
|
{
|
||||||
struct skb_frag_struct *frag;
|
struct skb_frag_struct *frag;
|
||||||
bool linearize = false;
|
bool linearize = false;
|
||||||
|
@ -1638,7 +1636,7 @@ static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags,
|
||||||
gso_segs = skb_shinfo(skb)->gso_segs;
|
gso_segs = skb_shinfo(skb)->gso_segs;
|
||||||
|
|
||||||
if (tx_flags & (I40E_TX_FLAGS_TSO | I40E_TX_FLAGS_FSO)) {
|
if (tx_flags & (I40E_TX_FLAGS_TSO | I40E_TX_FLAGS_FSO)) {
|
||||||
u16 j = 1;
|
u16 j = 0;
|
||||||
|
|
||||||
if (num_frags < (I40E_MAX_BUFFER_TXD))
|
if (num_frags < (I40E_MAX_BUFFER_TXD))
|
||||||
goto linearize_chk_done;
|
goto linearize_chk_done;
|
||||||
|
@ -1649,21 +1647,18 @@ static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags,
|
||||||
goto linearize_chk_done;
|
goto linearize_chk_done;
|
||||||
}
|
}
|
||||||
frag = &skb_shinfo(skb)->frags[0];
|
frag = &skb_shinfo(skb)->frags[0];
|
||||||
size = hdr_len;
|
|
||||||
/* we might still have more fragments per segment */
|
/* we might still have more fragments per segment */
|
||||||
do {
|
do {
|
||||||
size += skb_frag_size(frag);
|
size += skb_frag_size(frag);
|
||||||
frag++; j++;
|
frag++; j++;
|
||||||
|
if ((size >= skb_shinfo(skb)->gso_size) &&
|
||||||
|
(j < I40E_MAX_BUFFER_TXD)) {
|
||||||
|
size = (size % skb_shinfo(skb)->gso_size);
|
||||||
|
j = (size) ? 1 : 0;
|
||||||
|
}
|
||||||
if (j == I40E_MAX_BUFFER_TXD) {
|
if (j == I40E_MAX_BUFFER_TXD) {
|
||||||
if (size < skb_shinfo(skb)->gso_size) {
|
linearize = true;
|
||||||
linearize = true;
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
j = 1;
|
|
||||||
size -= skb_shinfo(skb)->gso_size;
|
|
||||||
if (size)
|
|
||||||
j++;
|
|
||||||
size += hdr_len;
|
|
||||||
}
|
}
|
||||||
num_frags--;
|
num_frags--;
|
||||||
} while (num_frags);
|
} while (num_frags);
|
||||||
|
@ -1950,7 +1945,7 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb,
|
||||||
else if (tso)
|
else if (tso)
|
||||||
tx_flags |= I40E_TX_FLAGS_TSO;
|
tx_flags |= I40E_TX_FLAGS_TSO;
|
||||||
|
|
||||||
if (i40e_chk_linearize(skb, tx_flags, hdr_len))
|
if (i40e_chk_linearize(skb, tx_flags))
|
||||||
if (skb_linearize(skb))
|
if (skb_linearize(skb))
|
||||||
goto out_drop;
|
goto out_drop;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue