diff mbox series

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

Message ID 20180320154014.7346-2-ddstreet@canonical.com
State New
Headers show
Series [Xenial,1/2] i40e/i40evf: Allow up to 12K bytes of data per Tx descriptor instead of 8K | expand

Commit Message

Dan Streetman March 20, 2018, 3:40 p.m. UTC
From: Alexander Duyck <alexander.h.duyck@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@intel.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
(cherry-picked from 248de22e638f10bd5bfc7624a357f940f66ba137)
Signed-off-by: Dan Streetman <ddstreet@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(-)

Comments

Dan Streetman March 21, 2018, 12:36 p.m. UTC | #1
sorry for the duplicates, i thought it was a mail server issue on my
end at first but looks like they all finally made it thru.

On Tue, Mar 20, 2018 at 11:40 AM, Dan Streetman <ddstreet@canonical.com> wrote:
> From: Alexander Duyck <alexander.h.duyck@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@intel.com>
> Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
> (cherry-picked from 248de22e638f10bd5bfc7624a357f940f66ba137)
> Signed-off-by: Dan Streetman <ddstreet@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;
> --
> 2.15.1
>
>
> --
> kernel-team mailing list
> kernel-team@lists.ubuntu.com
> https://lists.ubuntu.com/mailman/listinfo/kernel-team
Kleber Sacilotto de Souza April 3, 2018, 11:21 a.m. UTC | #2
On 03/20/18 16:40, Dan Streetman wrote:
> From: Alexander Duyck <alexander.h.duyck@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@intel.com>
> Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
> (cherry-picked from 248de22e638f10bd5bfc7624a357f940f66ba137)
> Signed-off-by: Dan Streetman <ddstreet@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
diff mbox series

Patch

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;