APPLIED[Xenial/Artful]: [Xenial][Artful][PATCH 2/2] i40e/i40evf: Account for frags split over multiple descriptors in check linearize

Kleber Souza kleber.souza at canonical.com
Tue Apr 3 11:21:56 UTC 2018


On 03/20/18 16:40, Dan Streetman wrote:
> From: Alexander Duyck <alexander.h.duyck at intel.com>
> 
> BugLink: http://bugs.launchpad.net/bugs/1723127
> 
> 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 <alexander.h.duyck at intel.com>
> Tested-by: Andrew Bowers <andrewx.bowers at intel.com>
> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher at intel.com>
> (cherry-picked from 248de22e638f10bd5bfc7624a357f940f66ba137)
> Signed-off-by: Dan Streetman <ddstreet at canonical.com>
> 
> ---
> Note that this patch applies to Xenial and Artful, but the previous patch
> in the series is only required in Xenial.
> 
>  drivers/net/ethernet/intel/i40e/i40e_txrx.c   | 26 +++++++++++++++++++++++---
>  drivers/net/ethernet/intel/i40evf/i40e_txrx.c | 26 +++++++++++++++++++++++---
>  2 files changed, 46 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
> index a18c91e33dea..2c83a6ce1b47 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
> @@ -2640,10 +2640,30 @@ bool __i40e_chk_linearize(struct sk_buff *skb)
>  	/* Walk through fragments adding latest fragment, testing it, and
>  	 * then removing stale fragments from the sum.
>  	 */
> -	stale = &skb_shinfo(skb)->frags[0];
> -	for (;;) {
> +	for (stale = &skb_shinfo(skb)->frags[0];; stale++) {
> +		int stale_size = skb_frag_size(stale);
> +
>  		sum += skb_frag_size(frag++);
>  
> +		/* The stale fragment may present us with a smaller
> +		 * descriptor than the actual fragment size. To account
> +		 * for that we need to remove all the data on the front and
> +		 * figure out what the remainder would be in the last
> +		 * descriptor associated with the fragment.
> +		 */
> +		if (stale_size > I40E_MAX_DATA_PER_TXD) {
> +			int align_pad = -(stale->page_offset) &
> +					(I40E_MAX_READ_REQ_SIZE - 1);
> +
> +			sum -= align_pad;
> +			stale_size -= align_pad;
> +
> +			do {
> +				sum -= I40E_MAX_DATA_PER_TXD_ALIGNED;
> +				stale_size -= I40E_MAX_DATA_PER_TXD_ALIGNED;
> +			} while (stale_size > I40E_MAX_DATA_PER_TXD);
> +		}
> +
>  		/* if sum is negative we failed to make sufficient progress */
>  		if (sum < 0)
>  			return true;
> @@ -2651,7 +2671,7 @@ bool __i40e_chk_linearize(struct sk_buff *skb)
>  		if (!nr_frags--)
>  			break;
>  
> -		sum -= skb_frag_size(stale++);
> +		sum -= stale_size;
>  	}
>  
>  	return false;
> diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
> index f2163735528d..1f48bcdf1ea5 100644
> --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
> +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c
> @@ -1843,10 +1843,30 @@ bool __i40evf_chk_linearize(struct sk_buff *skb)
>  	/* Walk through fragments adding latest fragment, testing it, and
>  	 * then removing stale fragments from the sum.
>  	 */
> -	stale = &skb_shinfo(skb)->frags[0];
> -	for (;;) {
> +	for (stale = &skb_shinfo(skb)->frags[0];; stale++) {
> +		int stale_size = skb_frag_size(stale);
> +
>  		sum += skb_frag_size(frag++);
>  
> +		/* The stale fragment may present us with a smaller
> +		 * descriptor than the actual fragment size. To account
> +		 * for that we need to remove all the data on the front and
> +		 * figure out what the remainder would be in the last
> +		 * descriptor associated with the fragment.
> +		 */
> +		if (stale_size > I40E_MAX_DATA_PER_TXD) {
> +			int align_pad = -(stale->page_offset) &
> +					(I40E_MAX_READ_REQ_SIZE - 1);
> +
> +			sum -= align_pad;
> +			stale_size -= align_pad;
> +
> +			do {
> +				sum -= I40E_MAX_DATA_PER_TXD_ALIGNED;
> +				stale_size -= I40E_MAX_DATA_PER_TXD_ALIGNED;
> +			} while (stale_size > I40E_MAX_DATA_PER_TXD);
> +		}
> +
>  		/* if sum is negative we failed to make sufficient progress */
>  		if (sum < 0)
>  			return true;
> @@ -1854,7 +1874,7 @@ bool __i40evf_chk_linearize(struct sk_buff *skb)
>  		if (!nr_frags--)
>  			break;
>  
> -		sum -= skb_frag_size(stale++);
> +		sum -= stale_size;
>  	}
>  
>  	return false;
> 

Applied to xenial/master-next and artful/master-next branches.

Thanks,
Kleber




More information about the kernel-team mailing list