diff mbox series

[net,5/6] igc: Fix launchtime before start of cycle

Message ID 20230710163503.2821068-6-anthony.l.nguyen@intel.com (mailing list archive)
State Accepted
Commit c1bca9ac0bcb355be11354c2e68bc7bf31f5ac5a
Delegated to: Netdev Maintainers
Headers show
Series igc: Fix corner cases for TSN offload | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net
netdev/fixes_present success Fixes tag present in non-next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 1341 this patch: 1341
netdev/cc_maintainers success CCed 5 of 5 maintainers
netdev/build_clang success Errors and warnings before: 1364 this patch: 1364
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success Fixes tag looks correct
netdev/build_allmodconfig_warn success Errors and warnings before: 1364 this patch: 1364
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 8 lines checked
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Tony Nguyen July 10, 2023, 4:35 p.m. UTC
From: Florian Kauer <florian.kauer@linutronix.de>

It is possible (verified on a running system) that frames are processed
by igc_tx_launchtime with a txtime before the start of the cycle
(baset_est).

However, the result of txtime - baset_est is written into a u32,
leading to a wrap around to a positive number. The following
launchtime > 0 check will only branch to executing launchtime = 0
if launchtime is already 0.

Fix it by using a s32 before checking launchtime > 0.

Fixes: db0b124f02ba ("igc: Enhance Qbv scheduling by using first flag bit")
Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
Reviewed-by: Kurt Kanzenbach <kurt@linutronix.de>
Tested-by: Naama Meir <naamax.meir@linux.intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
---
 drivers/net/ethernet/intel/igc/igc_main.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Comments

Leon Romanovsky July 11, 2023, 7:09 a.m. UTC | #1
On Mon, Jul 10, 2023 at 09:35:02AM -0700, Tony Nguyen wrote:
> From: Florian Kauer <florian.kauer@linutronix.de>
> 
> It is possible (verified on a running system) that frames are processed
> by igc_tx_launchtime with a txtime before the start of the cycle
> (baset_est).
> 
> However, the result of txtime - baset_est is written into a u32,
> leading to a wrap around to a positive number. The following
> launchtime > 0 check will only branch to executing launchtime = 0
> if launchtime is already 0.
> 
> Fix it by using a s32 before checking launchtime > 0.
> 
> Fixes: db0b124f02ba ("igc: Enhance Qbv scheduling by using first flag bit")
> Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
> Reviewed-by: Kurt Kanzenbach <kurt@linutronix.de>
> Tested-by: Naama Meir <naamax.meir@linux.intel.com>
> Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
> ---
>  drivers/net/ethernet/intel/igc/igc_main.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c
> index 5d24930fed8f..4855caa3bae4 100644
> --- a/drivers/net/ethernet/intel/igc/igc_main.c
> +++ b/drivers/net/ethernet/intel/igc/igc_main.c
> @@ -1016,7 +1016,7 @@ static __le32 igc_tx_launchtime(struct igc_ring *ring, ktime_t txtime,
>  	ktime_t base_time = adapter->base_time;
>  	ktime_t now = ktime_get_clocktai();
>  	ktime_t baset_est, end_of_cycle;
> -	u32 launchtime;
> +	s32 launchtime;

The rest of igc_tx_launchtime() function is very questionable,
as ktime_sub_ns() returns ktime_t which is s64.

  1049         launchtime = ktime_sub_ns(txtime, baset_est);
  1050         if (launchtime > 0)
  1051                 div_s64_rem(launchtime, cycle_time, &launchtime);
  1052         else
  1053                 launchtime = 0;
  1054
  1055         return cpu_to_le32(launchtime);


>  	s64 n;
>  
>  	n = div64_s64(ktime_sub_ns(now, base_time), cycle_time);
> -- 
> 2.38.1
> 
>
Florian Kauer July 11, 2023, 8:37 a.m. UTC | #2
On 11.07.23 09:09, Leon Romanovsky wrote:
> On Mon, Jul 10, 2023 at 09:35:02AM -0700, Tony Nguyen wrote:
>> From: Florian Kauer <florian.kauer@linutronix.de>
>>
>> It is possible (verified on a running system) that frames are processed
>> by igc_tx_launchtime with a txtime before the start of the cycle
>> (baset_est).
>>
>> However, the result of txtime - baset_est is written into a u32,
>> leading to a wrap around to a positive number. The following
>> launchtime > 0 check will only branch to executing launchtime = 0
>> if launchtime is already 0.
>>
>> Fix it by using a s32 before checking launchtime > 0.
>>
>> Fixes: db0b124f02ba ("igc: Enhance Qbv scheduling by using first flag bit")
>> Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
>> Reviewed-by: Kurt Kanzenbach <kurt@linutronix.de>
>> Tested-by: Naama Meir <naamax.meir@linux.intel.com>
>> Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
>> ---
>>  drivers/net/ethernet/intel/igc/igc_main.c | 2 +-
>>  1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c
>> index 5d24930fed8f..4855caa3bae4 100644
>> --- a/drivers/net/ethernet/intel/igc/igc_main.c
>> +++ b/drivers/net/ethernet/intel/igc/igc_main.c
>> @@ -1016,7 +1016,7 @@ static __le32 igc_tx_launchtime(struct igc_ring *ring, ktime_t txtime,
>>  	ktime_t base_time = adapter->base_time;
>>  	ktime_t now = ktime_get_clocktai();
>>  	ktime_t baset_est, end_of_cycle;
>> -	u32 launchtime;
>> +	s32 launchtime;
> 
> The rest of igc_tx_launchtime() function is very questionable,
> as ktime_sub_ns() returns ktime_t which is s64.
> 
>   1049         launchtime = ktime_sub_ns(txtime, baset_est);
>   1050         if (launchtime > 0)
>   1051                 div_s64_rem(launchtime, cycle_time, &launchtime);
>   1052         else
>   1053                 launchtime = 0;
>   1054
>   1055         return cpu_to_le32(launchtime);
> 

If I understand correctly, ktime_sub_ns(txtime, baset_est) will only return
something larger than s32 max if cycle_time is larger than s32 max and if that
is the case everything will be broken anyway since the corresponding hardware
register only holds 30 bits.

However, I do not see on first inspection where that case is properly handled
(probably just by rejecting the TAPRIO schedule).

Can someone with more experience in that area please jump in?

Thanks,
Florian

> 
>>  	s64 n;
>>  
>>  	n = div64_s64(ktime_sub_ns(now, base_time), cycle_time);
>> -- 
>> 2.38.1
>>
>>
Leon Romanovsky July 11, 2023, 10:12 a.m. UTC | #3
On Tue, Jul 11, 2023 at 10:37:48AM +0200, Florian Kauer wrote:
> On 11.07.23 09:09, Leon Romanovsky wrote:
> > On Mon, Jul 10, 2023 at 09:35:02AM -0700, Tony Nguyen wrote:
> >> From: Florian Kauer <florian.kauer@linutronix.de>
> >>
> >> It is possible (verified on a running system) that frames are processed
> >> by igc_tx_launchtime with a txtime before the start of the cycle
> >> (baset_est).
> >>
> >> However, the result of txtime - baset_est is written into a u32,
> >> leading to a wrap around to a positive number. The following
> >> launchtime > 0 check will only branch to executing launchtime = 0
> >> if launchtime is already 0.
> >>
> >> Fix it by using a s32 before checking launchtime > 0.
> >>
> >> Fixes: db0b124f02ba ("igc: Enhance Qbv scheduling by using first flag bit")
> >> Signed-off-by: Florian Kauer <florian.kauer@linutronix.de>
> >> Reviewed-by: Kurt Kanzenbach <kurt@linutronix.de>
> >> Tested-by: Naama Meir <naamax.meir@linux.intel.com>
> >> Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
> >> ---
> >>  drivers/net/ethernet/intel/igc/igc_main.c | 2 +-
> >>  1 file changed, 1 insertion(+), 1 deletion(-)
> >>
> >> diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c
> >> index 5d24930fed8f..4855caa3bae4 100644
> >> --- a/drivers/net/ethernet/intel/igc/igc_main.c
> >> +++ b/drivers/net/ethernet/intel/igc/igc_main.c
> >> @@ -1016,7 +1016,7 @@ static __le32 igc_tx_launchtime(struct igc_ring *ring, ktime_t txtime,
> >>  	ktime_t base_time = adapter->base_time;
> >>  	ktime_t now = ktime_get_clocktai();
> >>  	ktime_t baset_est, end_of_cycle;
> >> -	u32 launchtime;
> >> +	s32 launchtime;
> > 
> > The rest of igc_tx_launchtime() function is very questionable,
> > as ktime_sub_ns() returns ktime_t which is s64.
> > 
> >   1049         launchtime = ktime_sub_ns(txtime, baset_est);
> >   1050         if (launchtime > 0)
> >   1051                 div_s64_rem(launchtime, cycle_time, &launchtime);
> >   1052         else
> >   1053                 launchtime = 0;
> >   1054
> >   1055         return cpu_to_le32(launchtime);
> > 
> 
> If I understand correctly, ktime_sub_ns(txtime, baset_est) will only return
> something larger than s32 max if cycle_time is larger than s32 max and if that
> is the case everything will be broken anyway since the corresponding hardware
> register only holds 30 bits.

I suggest you to use proper variable types, what about the following
snippet?

ktime_t launchtime;

launchtime = ktime_sub_ns(txtime, baset_est);
WARN_ON(upper_32_bits(launchtime));
div_s64_rem(launchtime, cycle_time, &launchtime);

return cpu_to_le32(lower_32_bits(launchtime));

> 
> However, I do not see on first inspection where that case is properly handled
> (probably just by rejecting the TAPRIO schedule).
> 
> Can someone with more experience in that area please jump in?
> 
> Thanks,
> Florian
> 
> > 
> >>  	s64 n;
> >>  
> >>  	n = div64_s64(ktime_sub_ns(now, base_time), cycle_time);
> >> -- 
> >> 2.38.1
> >>
> >>
>
Jakub Kicinski July 12, 2023, 12:54 a.m. UTC | #4
On Tue, 11 Jul 2023 13:12:33 +0300 Leon Romanovsky wrote:
> > If I understand correctly, ktime_sub_ns(txtime, baset_est) will only return
> > something larger than s32 max if cycle_time is larger than s32 max and if that
> > is the case everything will be broken anyway since the corresponding hardware
> > register only holds 30 bits.  
> 
> I suggest you to use proper variable types, what about the following
> snippet?
> 
> ktime_t launchtime;
> 
> launchtime = ktime_sub_ns(txtime, baset_est);
> WARN_ON(upper_32_bits(launchtime));
> div_s64_rem(launchtime, cycle_time, &launchtime);
> 
> return cpu_to_le32(lower_32_bits(launchtime));

That needs to be coupled with some control path checks on cycle_time?
Seems like a separate fix TBH.
Leon Romanovsky July 12, 2023, 6:11 a.m. UTC | #5
On Tue, Jul 11, 2023 at 05:54:54PM -0700, Jakub Kicinski wrote:
> On Tue, 11 Jul 2023 13:12:33 +0300 Leon Romanovsky wrote:
> > > If I understand correctly, ktime_sub_ns(txtime, baset_est) will only return
> > > something larger than s32 max if cycle_time is larger than s32 max and if that
> > > is the case everything will be broken anyway since the corresponding hardware
> > > register only holds 30 bits.  
> > 
> > I suggest you to use proper variable types, what about the following
> > snippet?
> > 
> > ktime_t launchtime;
> > 
> > launchtime = ktime_sub_ns(txtime, baset_est);
> > WARN_ON(upper_32_bits(launchtime));
> > div_s64_rem(launchtime, cycle_time, &launchtime);
> > 
> > return cpu_to_le32(lower_32_bits(launchtime));
> 
> That needs to be coupled with some control path checks on cycle_time?

I think so, as I didn't find any checks which protect from overflow.

Thanks
diff mbox series

Patch

diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c
index 5d24930fed8f..4855caa3bae4 100644
--- a/drivers/net/ethernet/intel/igc/igc_main.c
+++ b/drivers/net/ethernet/intel/igc/igc_main.c
@@ -1016,7 +1016,7 @@  static __le32 igc_tx_launchtime(struct igc_ring *ring, ktime_t txtime,
 	ktime_t base_time = adapter->base_time;
 	ktime_t now = ktime_get_clocktai();
 	ktime_t baset_est, end_of_cycle;
-	u32 launchtime;
+	s32 launchtime;
 	s64 n;
 
 	n = div64_s64(ktime_sub_ns(now, base_time), cycle_time);