Intel-Wired-Lan Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Jacob Keller <jacob.e.keller@intel.com>
To: Yajun Deng <yajun.deng@linux.dev>, <jesse.brandeburg@intel.com>,
	<anthony.l.nguyen@intel.com>, <davem@davemloft.net>,
	<edumazet@google.com>, <kuba@kernel.org>, <pabeni@redhat.com>,
	<richardcochran@gmail.com>
Cc: netdev@vger.kernel.org, intel-wired-lan@lists.osuosl.org,
	linux-kernel@vger.kernel.org, stable@vger.kernel.org
Subject: Re: [Intel-wired-lan] [PATCH] i40e: fix the wrong PTP frequency calculation
Date: Tue, 27 Jun 2023 13:20:44 -0700	[thread overview]
Message-ID: <10269e86-ed8a-0b09-a39a-a5239a1ba744@intel.com> (raw)
In-Reply-To: <20230627022658.1876747-1-yajun.deng@linux.dev>



On 6/26/2023 7:26 PM, Yajun Deng wrote:
> The new adjustment should be based on the base frequency, not the
> I40E_PTP_40GB_INCVAL in i40e_ptp_adjfine().
> 
> This issue was introduced in commit 3626a690b717 ("i40e: use
> mul_u64_u64_div_u64 for PTP frequency calculation"), and was fixed in
> commit 1060707e3809 ("ptp: introduce helpers to adjust by scaled
> parts per million"). However the latter is a new feature and hasn't been
> backported to the stable releases.
> 
> This issue affects both v6.0 and v6.1 versions, and the v6.1 version is
> an LTS version.
> 
> Fixes: 3626a690b717 ("i40e: use mul_u64_u64_div_u64 for PTP frequency calculation")
> Cc: <stable@vger.kernel.org> # 6.1
> Signed-off-by: Yajun Deng <yajun.deng@linux.dev>
> ---
>  drivers/net/ethernet/intel/i40e/i40e_ptp.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
> index ffea0c9c82f1..97a9efe7b713 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
> @@ -361,9 +361,9 @@ static int i40e_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
>  				   1000000ULL << 16);
>  
>  	if (neg_adj)
> -		adj = I40E_PTP_40GB_INCVAL - diff;
> +		adj = freq - diff;
>  	else
> -		adj = I40E_PTP_40GB_INCVAL + diff;
> +		adj = freq + diff;
>  
>  	wr32(hw, I40E_PRTTSYN_INC_L, adj & 0xFFFFFFFF);
>  	wr32(hw, I40E_PRTTSYN_INC_H, adj >> 32);

This straight forward fix makes sense. However, it wasn't obvious to me
without context why the 3626a690b717 ("i40e: use mul_u64_u64_div_u64 for
PTP frequency calculation") was where the fault got introduced. Thus,
here is that context for anyone else who failed to spot it just looking
at shrunk patch diffs.

--> code before that commit <---
> /**
>  * i40e_ptp_adjfreq - Adjust the PHC frequency
>  * @ptp: The PTP clock structure
>  * @ppb: Parts per billion adjustment from the base
>  *
>  * Adjust the frequency of the PHC by the indicated parts per billion from the
>  * base frequency.
>  **/
> static int i40e_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
> {
>         struct i40e_pf *pf = container_of(ptp, struct i40e_pf, ptp_caps);
>         struct i40e_hw *hw = &pf->hw;
>         u64 adj, freq, diff;
>         int neg_adj = 0;
> 
>         if (ppb < 0) {
>                 neg_adj = 1;
>                 ppb = -ppb;
>         }
> 
>         freq = I40E_PTP_40GB_INCVAL;

frequency is left just as base I40E_PTP_40GB_INCVAL.

>         freq *= ppb;
>         diff = div_u64(freq, 1000000000ULL);
> 
>         if (neg_adj)
>                 adj = I40E_PTP_40GB_INCVAL - diff;
>         else
>                 adj = I40E_PTP_40GB_INCVAL + diff;
> 

So the base here can't be freq since we modify freq above using *=, but
using I40E_PTP_40GB_INCVAL is consistent.

>         /* At some link speeds, the base incval is so large that directly
>          * multiplying by ppb would result in arithmetic overflow even when
>          * using a u64. Avoid this by instead calculating the new incval
>          * always in terms of the 40GbE clock rate and then multiplying by the
>          * link speed factor afterwards. This does result in slightly lower
>          * precision at lower link speeds, but it is fairly minor.
>          */
>         smp_mb(); /* Force any pending update before accessing. */
>         adj *= READ_ONCE(pf->ptp_adj_mult);
> 

Finally, the multiply is applied last. This affects the combined base +
difference, and is done in order to avoid overflowing the *= used in the
original implementation.

>         wr32(hw, I40E_PRTTSYN_INC_L, adj & 0xFFFFFFFF);
>         wr32(hw, I40E_PRTTSYN_INC_H, adj >> 32);
> 
>         return 0;
> }


---> code after that commit <---
> /**
>  * i40e_ptp_adjfreq - Adjust the PHC frequency
>  * @ptp: The PTP clock structure
>  * @ppb: Parts per billion adjustment from the base
>  *
>  * Adjust the frequency of the PHC by the indicated parts per billion from the
>  * base frequency.
>  **/
> static int i40e_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
> {
>         struct i40e_pf *pf = container_of(ptp, struct i40e_pf, ptp_caps);
>         struct i40e_hw *hw = &pf->hw;
>         u64 adj, freq, diff;
>         int neg_adj = 0;
> 
>         if (ppb < 0) {
>                 neg_adj = 1;
>                 ppb = -ppb;
>         }
> 
>         smp_mb(); /* Force any pending update before accessing. */
>         freq = I40E_PTP_40GB_INCVAL * READ_ONCE(pf->ptp_adj_mult);
>         diff = mul_u64_u64_div_u64(freq, (u64)ppb,
>                                    1000000000ULL);
> 

Here, we assign freq to be the I40E_PTP_40GB_INCVAL times the
ptp_adj_mult value, and then we don't modify it, instead using
mul_u64_u64_div_u64.

>         if (neg_adj)
>                 adj = I40E_PTP_40GB_INCVAL - diff;
>         else
>                 adj = I40E_PTP_40GB_INCVAL + diff;
> 

But then the diff is applied on the wrong value, and no multiplication
is done afterwards.

>         wr32(hw, I40E_PRTTSYN_INC_L, adj & 0xFFFFFFFF);
>         wr32(hw, I40E_PRTTSYN_INC_H, adj >> 32);
> 
>         return 0;
> }

---> current version with adjust_by_scaled_ppm <---
> /**
>  * i40e_ptp_adjfine - Adjust the PHC frequency
>  * @ptp: The PTP clock structure
>  * @scaled_ppm: Scaled parts per million adjustment from base
>  *
>  * Adjust the frequency of the PHC by the indicated delta from the base
>  * frequency.
>  *
>  * Scaled parts per million is ppm with a 16 bit binary fractional field.
>  **/
> static int i40e_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
> {
>         struct i40e_pf *pf = container_of(ptp, struct i40e_pf, ptp_caps);
>         struct i40e_hw *hw = &pf->hw;
>         u64 adj, base_adj;
> 
>         smp_mb(); /* Force any pending update before accessing. */
>         base_adj = I40E_PTP_40GB_INCVAL * READ_ONCE(pf->ptp_adj_mult);
> 
>         adj = adjust_by_scaled_ppm(base_adj, scaled_ppm);
> 

Using adjust_by_scaled_ppm correctly performs the calculation and uses
the base adjustment, so there's no error here.

>         wr32(hw, I40E_PRTTSYN_INC_L, adj & 0xFFFFFFFF);
>         wr32(hw, I40E_PRTTSYN_INC_H, adj >> 32);
> 
>         return 0;
> }


Thanks for finding and fixing this mistake. I think its the simplest fix
to get into the stable kernel that are broken, since taking the
adjust_by_scaled_ppm version would require additional patches.

Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>

_______________________________________________
Intel-wired-lan mailing list
Intel-wired-lan@osuosl.org
https://lists.osuosl.org/mailman/listinfo/intel-wired-lan

  reply	other threads:[~2023-06-27 20:20 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-06-27  2:26 [Intel-wired-lan] [PATCH] i40e: fix the wrong PTP frequency calculation Yajun Deng
2023-06-27 20:20 ` Jacob Keller [this message]
2023-09-25  7:55   ` Yajun Deng
2023-09-25 23:59     ` Tony Nguyen
2023-09-26  1:54       ` Yajun Deng
2023-09-26  4:47         ` Greg KH

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=10269e86-ed8a-0b09-a39a-a5239a1ba744@intel.com \
    --to=jacob.e.keller@intel.com \
    --cc=anthony.l.nguyen@intel.com \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=intel-wired-lan@lists.osuosl.org \
    --cc=jesse.brandeburg@intel.com \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=richardcochran@gmail.com \
    --cc=stable@vger.kernel.org \
    --cc=yajun.deng@linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox