Skip to content

Commit 248de22

Browse files
Alexander DuyckJeff Kirsher
authored andcommitted
i40e/i40evf: Account for frags split over multiple descriptors in check linearize
The original code for __i40e_chk_linearize didn't take into account the fact that if a fragment is 16K in size or larger it has to be split over 2 descriptors and the smaller of those 2 descriptors will be on the trailing edge of the transmit. As a result we can get into situations where we didn't catch requests that could result in a Tx hang. This patch takes care of that by subtracting the length of all but the trailing edge of the stale fragment before we test for sum. By doing this we can guarantee that we have all cases covered, including the case of a fragment that spans multiple descriptors. We don't need to worry about checking the inner portions of this since 12K is the maximum aligned DMA size and that is larger than any MSS will ever be since the MTU limit for jumbos is something on the order of 9K. Signed-off-by: Alexander Duyck <[email protected]> Tested-by: Andrew Bowers <[email protected]> Signed-off-by: Jeff Kirsher <[email protected]>
1 parent 64e711c commit 248de22

File tree

2 files changed

+46
-6
lines changed

2 files changed

+46
-6
lines changed

drivers/net/ethernet/intel/i40e/i40e_txrx.c

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3047,18 +3047,38 @@ bool __i40e_chk_linearize(struct sk_buff *skb)
30473047
/* Walk through fragments adding latest fragment, testing it, and
30483048
* then removing stale fragments from the sum.
30493049
*/
3050-
stale = &skb_shinfo(skb)->frags[0];
3051-
for (;;) {
3050+
for (stale = &skb_shinfo(skb)->frags[0];; stale++) {
3051+
int stale_size = skb_frag_size(stale);
3052+
30523053
sum += skb_frag_size(frag++);
30533054

3055+
/* The stale fragment may present us with a smaller
3056+
* descriptor than the actual fragment size. To account
3057+
* for that we need to remove all the data on the front and
3058+
* figure out what the remainder would be in the last
3059+
* descriptor associated with the fragment.
3060+
*/
3061+
if (stale_size > I40E_MAX_DATA_PER_TXD) {
3062+
int align_pad = -(stale->page_offset) &
3063+
(I40E_MAX_READ_REQ_SIZE - 1);
3064+
3065+
sum -= align_pad;
3066+
stale_size -= align_pad;
3067+
3068+
do {
3069+
sum -= I40E_MAX_DATA_PER_TXD_ALIGNED;
3070+
stale_size -= I40E_MAX_DATA_PER_TXD_ALIGNED;
3071+
} while (stale_size > I40E_MAX_DATA_PER_TXD);
3072+
}
3073+
30543074
/* if sum is negative we failed to make sufficient progress */
30553075
if (sum < 0)
30563076
return true;
30573077

30583078
if (!nr_frags--)
30593079
break;
30603080

3061-
sum -= skb_frag_size(stale++);
3081+
sum -= stale_size;
30623082
}
30633083

30643084
return false;

drivers/net/ethernet/intel/i40evf/i40e_txrx.c

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2012,18 +2012,38 @@ bool __i40evf_chk_linearize(struct sk_buff *skb)
20122012
/* Walk through fragments adding latest fragment, testing it, and
20132013
* then removing stale fragments from the sum.
20142014
*/
2015-
stale = &skb_shinfo(skb)->frags[0];
2016-
for (;;) {
2015+
for (stale = &skb_shinfo(skb)->frags[0];; stale++) {
2016+
int stale_size = skb_frag_size(stale);
2017+
20172018
sum += skb_frag_size(frag++);
20182019

2020+
/* The stale fragment may present us with a smaller
2021+
* descriptor than the actual fragment size. To account
2022+
* for that we need to remove all the data on the front and
2023+
* figure out what the remainder would be in the last
2024+
* descriptor associated with the fragment.
2025+
*/
2026+
if (stale_size > I40E_MAX_DATA_PER_TXD) {
2027+
int align_pad = -(stale->page_offset) &
2028+
(I40E_MAX_READ_REQ_SIZE - 1);
2029+
2030+
sum -= align_pad;
2031+
stale_size -= align_pad;
2032+
2033+
do {
2034+
sum -= I40E_MAX_DATA_PER_TXD_ALIGNED;
2035+
stale_size -= I40E_MAX_DATA_PER_TXD_ALIGNED;
2036+
} while (stale_size > I40E_MAX_DATA_PER_TXD);
2037+
}
2038+
20192039
/* if sum is negative we failed to make sufficient progress */
20202040
if (sum < 0)
20212041
return true;
20222042

20232043
if (!nr_frags--)
20242044
break;
20252045

2026-
sum -= skb_frag_size(stale++);
2046+
sum -= stale_size;
20272047
}
20282048

20292049
return false;

0 commit comments

Comments
 (0)