Skip to content

drivers: video: video_common: refactor video_closest_frmival_stepwise() #85697

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

josuah
Copy link
Collaborator

@josuah josuah commented Feb 13, 2025

Avoid an integer overflow by reducing the precision until the result is checked to fit in 32-bit. This is possible by simplifying fractions by 2, which should not affect the result significantly as this is only done when the values are extremely large (too large to fit 32-bit int).

Also adding a test case that would overflow without proper handling.

Fixes:


/* Loose precision until landing within 32-bit range */
while (numerator_min > UINT32_MAX || numerator_step > UINT32_MAX ||
denominator >UINT32_MAX) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^^ missing space, see the compliance check

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right... Should be fixed now!

@josuah josuah force-pushed the pr-fix-video-common branch from 7f723af to eaee9f1 Compare February 13, 2025 12:49
Copy link

This pull request has been marked as stale because it has been open (more than) 60 days with no activity. Remove the stale label or add a comment saying that you would like to have the label removed otherwise this pull request will automatically be closed in 14 days. Note, that you can always re-open a closed pull request at any time.

@github-actions github-actions bot added the Stale label Apr 15, 2025
Comment on lines 108 to 133
uint64_t numerator_min = stepwise->min.numerator;
uint64_t numerator_step = stepwise->step.numerator;
uint64_t numerator_goal = desired->numerator;
uint64_t denominator;
uint64_t numerator;
Copy link
Collaborator

@ngphibang ngphibang Apr 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be uint32_t. There is a mistake in the mipi_csi2rx driver that I pushed a fix.

https://elixir.bootlin.com/linux/v6.11.1/source/include/uapi/linux/videodev2.h#L441

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The local variables were made 64-bit to allow storing 32-bit by 32-bit multiplication result, but this might be solved through your other comment.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, sorry, we indeed need uint64_t for storing the intermediate result.

Comment on lines 130 to 156
/* Set a common denominator to these 2 values */
numerator_min *= stepwise->step.denominator;
numerator_step *= stepwise->min.denominator;
denominator = stepwise->min.denominator * stepwise->step.denominator;

/* Loose precision until landing within 32-bit range */
while (numerator_min > UINT32_MAX || numerator_step > UINT32_MAX ||
denominator > UINT32_MAX) {
numerator_min /= 2;
numerator_step /= 2;
denominator /= 2;
}

/* Set a common denominator to these 3 values */
numerator_min *= desired->denominator;
numerator_step *= desired->denominator;
numerator_goal *= denominator;
denominator *= desired->denominator;

/* Loose precision until landing within 32-bit range */
while (numerator_min > UINT32_MAX || numerator_step > UINT32_MAX ||
numerator_goal > UINT32_MAX || denominator > UINT32_MAX) {
numerator_min /= 2;
numerator_step /= 2;
numerator_goal /= 2;
denominator /= 2;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there should be a method to avoid exceeding the range ? For example, x * y / z could be done as x / z * y

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not find one the last time except converting everything to frame intervals in microseconds and compare the values like this... Which would simplify everything. Is it good enough?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I see now, this is not a simple problem ...

  • Would it be possible and simpler if we activate CONFIG_FPU to do the computation ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe converting back and forth with integers and floats would give good precision, but not give exact fractional results, i.e. 1000/9999 vs 1/10. Maybe that works everywhere though!

Should frame intervals only be stored as nanosecond instead of fractions to avoid these kind of problems?

I just pushed a compromise performing the comparison as nanoseconds (using video_frmival_nsec(), could lead to different results in extreme "tie-breaker" situations), but storing the result as a fraction (like currently).

@github-actions github-actions bot removed the Stale label Apr 16, 2025
ngphibang
ngphibang previously approved these changes Apr 16, 2025
Comment on lines 108 to 133
uint64_t numerator_min = stepwise->min.numerator;
uint64_t numerator_step = stepwise->step.numerator;
uint64_t numerator_goal = desired->numerator;
uint64_t denominator;
uint64_t numerator;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, sorry, we indeed need uint64_t for storing the intermediate result.

Comment on lines 130 to 156
/* Set a common denominator to these 2 values */
numerator_min *= stepwise->step.denominator;
numerator_step *= stepwise->min.denominator;
denominator = stepwise->min.denominator * stepwise->step.denominator;

/* Loose precision until landing within 32-bit range */
while (numerator_min > UINT32_MAX || numerator_step > UINT32_MAX ||
denominator > UINT32_MAX) {
numerator_min /= 2;
numerator_step /= 2;
denominator /= 2;
}

/* Set a common denominator to these 3 values */
numerator_min *= desired->denominator;
numerator_step *= desired->denominator;
numerator_goal *= denominator;
denominator *= desired->denominator;

/* Loose precision until landing within 32-bit range */
while (numerator_min > UINT32_MAX || numerator_step > UINT32_MAX ||
numerator_goal > UINT32_MAX || denominator > UINT32_MAX) {
numerator_min /= 2;
numerator_step /= 2;
numerator_goal /= 2;
denominator /= 2;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I see now, this is not a simple problem ...

  • Would it be possible and simpler if we activate CONFIG_FPU to do the computation ?

@josuah josuah force-pushed the pr-fix-video-common branch 4 times, most recently from 6667c1c to 0d7bacf Compare April 16, 2025 20:46
@josuah
Copy link
Collaborator Author

josuah commented Apr 16, 2025

force-push:

  • simplify: comparison done as nanoseconds (simpler), result stored as exact fraction (as accurate)
  • rebase on top of latest main

@josuah josuah force-pushed the pr-fix-video-common branch from 0d7bacf to 1644eb2 Compare June 2, 2025 12:15
@github-actions github-actions bot requested a review from avolmat-st June 2, 2025 12:16
@josuah
Copy link
Collaborator Author

josuah commented Jun 2, 2025

Force-push:

  • Rebase on top of main after more commits were added to video_common.c

@josuah
Copy link
Collaborator Author

josuah commented Jun 2, 2025

@ngphibang Would it be possible and simpler if we activate CONFIG_FPU to do the computation?

That sounds more than enough! I will try that now...

@josuah josuah force-pushed the pr-fix-video-common branch from 1644eb2 to 7561345 Compare June 2, 2025 12:41
@josuah
Copy link
Collaborator Author

josuah commented Jun 2, 2025

Force-push:

  • Convert to uint64_t instead of fractions

It seems like double would not have added more precision in this particular case.
If this considered good enough precision for video system, would it make sense to convert everything to use frame intervals? For instance, USB Video uses 100 nsec units instead of fractions.

Otherwise, feel free to validate this PR only and this will be scoped to only video_closest_frmival_stepwise() implementation.

@josuah josuah requested a review from ngphibang June 2, 2025 12:43
@josuah josuah marked this pull request as draft June 2, 2025 18:35
@josuah
Copy link
Collaborator Author

josuah commented Jun 2, 2025

I'll go fix the CI now, please wait a little more...

@josuah josuah force-pushed the pr-fix-video-common branch from 7561345 to a9b9c54 Compare June 7, 2025 20:23
@josuah
Copy link
Collaborator Author

josuah commented Jun 7, 2025

Force-push:

  • Rebase on latest main

@josuah
Copy link
Collaborator Author

josuah commented Jun 7, 2025

By making all the computation in nanoseconds, we start to loose a bit of precision when the numerator and denominator are both huge:

>>> (29994500/20000000)
1.499725
>>> (1499999985/1000000000)
1.499999985

This test was passing without imprecision.

@josuah josuah force-pushed the pr-fix-video-common branch from a9b9c54 to 9cc9fe1 Compare June 7, 2025 21:06
Avoid an integer overflow by dividing the numerator and denominator
by two (dropping one bit of precision), which preserve the ratio.
Result still accurate, as only done on values larger than UINT32_MAX.
fixes CID-444385, CID-444388 and CID-444389

Signed-off-by: Josuah Demangeon <[email protected]>
@josuah josuah force-pushed the pr-fix-video-common branch from 9cc9fe1 to 34d38bd Compare June 7, 2025 21:09
Copy link

sonarqubecloud bot commented Jun 7, 2025

@josuah
Copy link
Collaborator Author

josuah commented Jun 7, 2025

Now CI passes, but I had to introduce between 10 and 1000 nanoseconds of imprecision.
I will try with doubles and see if that improves it.

@josuah
Copy link
Collaborator Author

josuah commented Jun 19, 2025

Double is not usable as the computation relies on integer division to select the correct step, with DIV_ROUND_CLOSEST() doing the actual selection operation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants