diff mbox series

[net-next,v3,4/8] rust: time: Implement addition of Ktime and Delta

Message ID 20241016035214.2229-5-fujita.tomonori@gmail.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series rust: Add IO polling | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 5 this patch: 5
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers warning 1 maintainers not CCed: boqun.feng@gmail.com
netdev/build_clang success Errors and warnings before: 3 this patch: 3
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 No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 4 this patch: 4
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 14 lines checked
netdev/build_clang_rust fail Link
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

FUJITA Tomonori Oct. 16, 2024, 3:52 a.m. UTC
Implement Add<Delta> for Ktime to support the operation:

Ktime = Ktime + Delta

This is typically used to calculate the future time when the timeout
will occur.

timeout Ktime = current Ktime (via ktime_get()) + Delta;
// do something
if (ktime_get() > timeout Ktime) {
    // timed out
}

Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
---
 rust/kernel/time.rs | 11 +++++++++++
 1 file changed, 11 insertions(+)

Comments

Alice Ryhl Oct. 16, 2024, 8:25 a.m. UTC | #1
On Wed, Oct 16, 2024 at 5:53 AM FUJITA Tomonori
<fujita.tomonori@gmail.com> wrote:
>
> Implement Add<Delta> for Ktime to support the operation:
>
> Ktime = Ktime + Delta
>
> This is typically used to calculate the future time when the timeout
> will occur.
>
> timeout Ktime = current Ktime (via ktime_get()) + Delta;
> // do something
> if (ktime_get() > timeout Ktime) {
>     // timed out
> }
>
> Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>

Reviewed-by: Alice Ryhl <aliceryhl@google.com>

>  rust/kernel/time.rs | 11 +++++++++++
>  1 file changed, 11 insertions(+)
>
> diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs
> index 8c00854db58c..9b0537b63cf7 100644
> --- a/rust/kernel/time.rs
> +++ b/rust/kernel/time.rs
> @@ -155,3 +155,14 @@ pub fn as_secs(self) -> i64 {
>          self.nanos / NSEC_PER_SEC
>      }
>  }
> +
> +impl core::ops::Add<Delta> for Ktime {
> +    type Output = Ktime;
> +
> +    #[inline]
> +    fn add(self, delta: Delta) -> Ktime {
> +        Ktime {
> +            inner: self.inner + delta.as_nanos(),

You don't want to do `delta.inner` here?

Alice
Boqun Feng Oct. 16, 2024, 7:54 p.m. UTC | #2
On Wed, Oct 16, 2024 at 12:52:09PM +0900, FUJITA Tomonori wrote:
> Implement Add<Delta> for Ktime to support the operation:
> 
> Ktime = Ktime + Delta
> 
> This is typically used to calculate the future time when the timeout
> will occur.
> 
> timeout Ktime = current Ktime (via ktime_get()) + Delta;
> // do something
> if (ktime_get() > timeout Ktime) {
>     // timed out
> }
> 
> Signed-off-by: FUJITA Tomonori <fujita.tomonori@gmail.com>
> ---
>  rust/kernel/time.rs | 11 +++++++++++
>  1 file changed, 11 insertions(+)
> 
> diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs
> index 8c00854db58c..9b0537b63cf7 100644
> --- a/rust/kernel/time.rs
> +++ b/rust/kernel/time.rs
> @@ -155,3 +155,14 @@ pub fn as_secs(self) -> i64 {
>          self.nanos / NSEC_PER_SEC
>      }
>  }
> +
> +impl core::ops::Add<Delta> for Ktime {
> +    type Output = Ktime;
> +
> +    #[inline]
> +    fn add(self, delta: Delta) -> Ktime {
> +        Ktime {
> +            inner: self.inner + delta.as_nanos(),

What if overflow happens in this addition? Is the expectation that user
should avoid overflows? I asked because we have ktime_add_safe() which
saturate at KTIME_SEC_MAX.

Regards,
Boqun

> +        }
> +    }
> +}
> -- 
> 2.43.0
> 
>
FUJITA Tomonori Oct. 17, 2024, 9:31 a.m. UTC | #3
On Wed, 16 Oct 2024 12:54:07 -0700
Boqun Feng <boqun.feng@gmail.com> wrote:

>> diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs
>> index 8c00854db58c..9b0537b63cf7 100644
>> --- a/rust/kernel/time.rs
>> +++ b/rust/kernel/time.rs
>> @@ -155,3 +155,14 @@ pub fn as_secs(self) -> i64 {
>>          self.nanos / NSEC_PER_SEC
>>      }
>>  }
>> +
>> +impl core::ops::Add<Delta> for Ktime {
>> +    type Output = Ktime;
>> +
>> +    #[inline]
>> +    fn add(self, delta: Delta) -> Ktime {
>> +        Ktime {
>> +            inner: self.inner + delta.as_nanos(),
> 
> What if overflow happens in this addition? Is the expectation that user
> should avoid overflows?

Yes, I'll add a comment.

> I asked because we have ktime_add_safe() which saturate at
> KTIME_SEC_MAX.

We could add the Rust version of add_safe method. But looks like
ktime_add_safe() is used by only some core systems so we don't need to
add it now?
Alice Ryhl Oct. 17, 2024, 9:33 a.m. UTC | #4
On Thu, Oct 17, 2024 at 11:31 AM FUJITA Tomonori
<fujita.tomonori@gmail.com> wrote:
>
> On Wed, 16 Oct 2024 12:54:07 -0700
> Boqun Feng <boqun.feng@gmail.com> wrote:
>
> >> diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs
> >> index 8c00854db58c..9b0537b63cf7 100644
> >> --- a/rust/kernel/time.rs
> >> +++ b/rust/kernel/time.rs
> >> @@ -155,3 +155,14 @@ pub fn as_secs(self) -> i64 {
> >>          self.nanos / NSEC_PER_SEC
> >>      }
> >>  }
> >> +
> >> +impl core::ops::Add<Delta> for Ktime {
> >> +    type Output = Ktime;
> >> +
> >> +    #[inline]
> >> +    fn add(self, delta: Delta) -> Ktime {
> >> +        Ktime {
> >> +            inner: self.inner + delta.as_nanos(),
> >
> > What if overflow happens in this addition? Is the expectation that user
> > should avoid overflows?
>
> Yes, I'll add a comment.
>
> > I asked because we have ktime_add_safe() which saturate at
> > KTIME_SEC_MAX.
>
> We could add the Rust version of add_safe method. But looks like
> ktime_add_safe() is used by only some core systems so we don't need to
> add it now?

I think it makes sense to follow the standard Rust addition
conventions here. Rust normally treats + as addition that BUGs on
overflow (with the appropriate configs set), and then there's a
saturating_add function for when you want it to saturate.

Alice
Miguel Ojeda Oct. 17, 2024, 4:33 p.m. UTC | #5
On Thu, Oct 17, 2024 at 11:31 AM FUJITA Tomonori
<fujita.tomonori@gmail.com> wrote:
>
> We could add the Rust version of add_safe method. But looks like
> ktime_add_safe() is used by only some core systems so we don't need to
> add it now?

There was some discussion in the past about this -- I wrote there a
summary of the `add` variants:

    https://lore.kernel.org/rust-for-linux/CANiq72ka4UvJzb4dN12fpA1WirgDHXcvPurvc7B9t+iPUfWnew@mail.gmail.com/

I think this is a case where following the naming of the C side would
be worse, i.e. where it is worth not applying our usual guideline.
Calling something `_safe`/`_unsafe` like the C macros would be quite
confusing for Rust.

Personally, I would prefer that we stay consistent, which will help
when dealing with more code. That is (from the message above):

  - No suffix: not supposed to wrap. So, in Rust, map it to operators.
  - `_unsafe()`: wraps. So, in Rust, map it to `wrapping` methods.
  - `_safe()`: saturates. So, in Rust, map it to `saturating` methods.

(assuming I read the C code correctly back then.)

And if there are any others that are Rust-unsafe, then map it to
`unchecked` methods, of course.

Cheers,
Miguel
Boqun Feng Oct. 17, 2024, 5:03 p.m. UTC | #6
On Thu, Oct 17, 2024 at 06:33:23PM +0200, Miguel Ojeda wrote:
> On Thu, Oct 17, 2024 at 11:31 AM FUJITA Tomonori
> <fujita.tomonori@gmail.com> wrote:
> >
> > We could add the Rust version of add_safe method. But looks like
> > ktime_add_safe() is used by only some core systems so we don't need to
> > add it now?
> 
> There was some discussion in the past about this -- I wrote there a
> summary of the `add` variants:
> 
>     https://lore.kernel.org/rust-for-linux/CANiq72ka4UvJzb4dN12fpA1WirgDHXcvPurvc7B9t+iPUfWnew@mail.gmail.com/
> 
> I think this is a case where following the naming of the C side would
> be worse, i.e. where it is worth not applying our usual guideline.
> Calling something `_safe`/`_unsafe` like the C macros would be quite
> confusing for Rust.
> 
> Personally, I would prefer that we stay consistent, which will help
> when dealing with more code. That is (from the message above):
> 
>   - No suffix: not supposed to wrap. So, in Rust, map it to operators.
>   - `_unsafe()`: wraps. So, in Rust, map it to `wrapping` methods.
>   - `_safe()`: saturates. So, in Rust, map it to `saturating` methods.
> 
> (assuming I read the C code correctly back then.)
> 
> And if there are any others that are Rust-unsafe, then map it to
> `unchecked` methods, of course.
> 

The point I tried to make is that `+` operator of Ktime can cause
overflow because of *user inputs*, unlike the `-` operator of Ktime,
which cannot cause overflow as long as Ktime is implemented correctly
(as a timestamp). Because the overflow possiblity is exposed to users,
then we need to 1) document it and 2) provide saturating_add() (maybe
also checked_add() and overflowing_add()) so that users won't need to do
the saturating themselves:

	let mut kt = Ktime::ktime_get();
	let d: Delta = <maybe a userspace input>;

	// kt + d may overflow, so checking
	if let Some(_) = kt.as_ns().checked_add(d.as_nanos()) {
	    // not overflow, can add
	    kt = kt + d;
	} else {
	    // set kt to KTIME_SEC_MAX
	}

instead, they can do:

	let kt = Ktime::ktime_get();
	let d: Delta = <maybe a userspace input>;

	kt = kt.saturating_add(d);

but one thing I'm not sure is since it looks like saturating to
KTIME_SEC_MAX is the current C choice, if we want to do the same, should
we use the name `add_safe()` instead of `saturating_add()`? FWIW, it
seems harmless to saturate at KTIME_MAX to me. So personally, I like
what Alice suggested.

Hope these make sense.

Regards,
Boqun

> Cheers,
> Miguel
Miguel Ojeda Oct. 17, 2024, 6:10 p.m. UTC | #7
On Thu, Oct 17, 2024 at 7:03 PM Boqun Feng <boqun.feng@gmail.com> wrote:
>
> The point I tried to make is that `+` operator of Ktime can cause
> overflow because of *user inputs*, unlike the `-` operator of Ktime,
> which cannot cause overflow as long as Ktime is implemented correctly
> (as a timestamp). Because the overflow possiblity is exposed to users,
> then we need to 1) document it and 2) provide saturating_add() (maybe
> also checked_add() and overflowing_add()) so that users won't need to do
> the saturating themselves:

Generally, operators are expected to possibly wrap/panic, so that
would be fine, no?

It may be surprising to `ktime_t` users, and that is bad. It is one
more reason to avoid using the same name for the type, too.

My only concern is that people may expect this sort of thing (timing
related) to "usually/just work" and not expect panics. That is bad,
but if we remain consistent, it may be best to pay the cost now. In
other words, when someone sees an operator, it is saying it is never
supposed to wrap, and that is easy to remember. Perhaps some types
could avoid providing the operators if it is too dangerous to use
them.

The other option is be inconsistent in our use of operators, and
instead give operators the "best" semantics for each type. That can
definitely provide better ergonomics in some cases, but there is a
high risk of forgetting what each operator does for each type --
operator overloading can be quite risky.

So that is why I think it may be best/easier to generally say "we
don't do operator overloading in general unless the operator really
behaves like a core one", especially early on.

>         kt = kt.saturating_add(d);

Yeah, that is what we want (I may be missing something in your argument though).

> but one thing I'm not sure is since it looks like saturating to
> KTIME_SEC_MAX is the current C choice, if we want to do the same, should
> we use the name `add_safe()` instead of `saturating_add()`? FWIW, it
> seems harmless to saturate at KTIME_MAX to me. So personally, I like
> what Alice suggested.

Do you mean it would be confusing to not saturate to the highest the
lower/inner level type can hold?

I mean, nothing says we need to saturate to the highest -- we could
have a type invariant. (One more reason to avoid the same name).

Worst case, we can have two saturating methods, or two types, if really needed.

Thomas can probably tell us what are the most important use cases
needed, and whether it is a good opportunity to clean/redesign this in
Rust differently.

Cheers,
Miguel
Miguel Ojeda Oct. 17, 2024, 6:58 p.m. UTC | #8
On Thu, Oct 17, 2024 at 7:03 PM Boqun Feng <boqun.feng@gmail.com> wrote:
>
> but one thing I'm not sure is since it looks like saturating to
> KTIME_SEC_MAX is the current C choice, if we want to do the same, should
> we use the name `add_safe()` instead of `saturating_add()`? FWIW, it
> seems harmless to saturate at KTIME_MAX to me. So personally, I like

Wait -- `ktime_add_safe()` calls `ktime_set(KTIME_SEC_MAX, 0)` which
goes into the conditional that returns `KTIME_MAX`, not `KTIME_SEC_MAX
* NSEC_PER_SEC` (which is what I guess you were saying).

So I am confused -- it doesn't saturate to `KTIME_SEC_MAX` (scaled)
anyway. Which is confusing in itself.

In fact, it means that `ktime_add_safe()` allows you to get any value
whatsoever as long as you don't overflow, but `ktime_set` does not
allow you to -- unless you use enough nanoseconds to get you there
(i.e. over a second in nanoseconds).

Cheers,
Miguel
Boqun Feng Oct. 17, 2024, 7:02 p.m. UTC | #9
On Thu, Oct 17, 2024 at 08:10:17PM +0200, Miguel Ojeda wrote:
> On Thu, Oct 17, 2024 at 7:03 PM Boqun Feng <boqun.feng@gmail.com> wrote:
> >
> > The point I tried to make is that `+` operator of Ktime can cause
> > overflow because of *user inputs*, unlike the `-` operator of Ktime,
> > which cannot cause overflow as long as Ktime is implemented correctly
> > (as a timestamp). Because the overflow possiblity is exposed to users,
> > then we need to 1) document it and 2) provide saturating_add() (maybe
> > also checked_add() and overflowing_add()) so that users won't need to do
> > the saturating themselves:
> 
> Generally, operators are expected to possibly wrap/panic, so that
> would be fine, no?
> 

Yes, but I guess I need to make it clear: the current `+` operator
implemention is fine for me. What I'm trying to say is: since we have a
`+` that expects users to not provide inputs that causes overflow, then
it makes sense to provide a saturating_add() at the same time for the
API completeness.

> It may be surprising to `ktime_t` users, and that is bad. It is one
> more reason to avoid using the same name for the type, too.
> 
> My only concern is that people may expect this sort of thing (timing
> related) to "usually/just work" and not expect panics. That is bad,
> but if we remain consistent, it may be best to pay the cost now. In
> other words, when someone sees an operator, it is saying it is never
> supposed to wrap, and that is easy to remember. Perhaps some types
> could avoid providing the operators if it is too dangerous to use
> them.
> 

Sounds reasonable to me.

> The other option is be inconsistent in our use of operators, and
> instead give operators the "best" semantics for each type. That can
> definitely provide better ergonomics in some cases, but there is a
> high risk of forgetting what each operator does for each type --
> operator overloading can be quite risky.
> 

Agreed.

> So that is why I think it may be best/easier to generally say "we
> don't do operator overloading in general unless the operator really
> behaves like a core one", especially early on.
> 
> >         kt = kt.saturating_add(d);
> 
> Yeah, that is what we want (I may be missing something in your argument though).
> 
> > but one thing I'm not sure is since it looks like saturating to
> > KTIME_SEC_MAX is the current C choice, if we want to do the same, should
> > we use the name `add_safe()` instead of `saturating_add()`? FWIW, it
> > seems harmless to saturate at KTIME_MAX to me. So personally, I like
> > what Alice suggested.
> 
> Do you mean it would be confusing to not saturate to the highest the
> lower/inner level type can hold?
> 

Yes.

> I mean, nothing says we need to saturate to the highest -- we could
> have a type invariant. (One more reason to avoid the same name).
> 
> Worst case, we can have two saturating methods, or two types, if really needed.
> 
> Thomas can probably tell us what are the most important use cases
> needed, and whether it is a good opportunity to clean/redesign this in
> Rust differently.
> 

Works for me!

Regards,
Boqun

> Cheers,
> Miguel
FUJITA Tomonori Oct. 23, 2024, 6:51 a.m. UTC | #10
On Thu, 17 Oct 2024 18:33:23 +0200
Miguel Ojeda <miguel.ojeda.sandonis@gmail.com> wrote:

> On Thu, Oct 17, 2024 at 11:31 AM FUJITA Tomonori
> <fujita.tomonori@gmail.com> wrote:
>>
>> We could add the Rust version of add_safe method. But looks like
>> ktime_add_safe() is used by only some core systems so we don't need to
>> add it now?
> 
> There was some discussion in the past about this -- I wrote there a
> summary of the `add` variants:
> 
>     https://lore.kernel.org/rust-for-linux/CANiq72ka4UvJzb4dN12fpA1WirgDHXcvPurvc7B9t+iPUfWnew@mail.gmail.com/
> 
> I think this is a case where following the naming of the C side would
> be worse, i.e. where it is worth not applying our usual guideline.
> Calling something `_safe`/`_unsafe` like the C macros would be quite
> confusing for Rust.
> 
> Personally, I would prefer that we stay consistent, which will help
> when dealing with more code. That is (from the message above):
> 
>   - No suffix: not supposed to wrap. So, in Rust, map it to operators.
>   - `_unsafe()`: wraps. So, in Rust, map it to `wrapping` methods.
>   - `_safe()`: saturates. So, in Rust, map it to `saturating` methods.

Can we add the above to Documentation/rust/coding-guidelines.rst?

I think that it's better than adding the similar comment to every
function that performs arithmetic.
Miguel Ojeda Oct. 23, 2024, 10:59 a.m. UTC | #11
On Wed, Oct 23, 2024 at 8:51 AM FUJITA Tomonori
<fujita.tomonori@gmail.com> wrote:
>
> Can we add the above to Documentation/rust/coding-guidelines.rst?

Sounds good to me -- I will send a patch.

Just to confirm, do you mean the whole operators overloading guideline
that I mentioned elsewhere and what semantics the arithmetic operators
should have (i.e.to avoid having to repeatedly document why operator
do "not supposed to wrap" and why we relegate saturating/wrapping/...
to methods), or something else?

Thanks!

Cheers,
Miguel
FUJITA Tomonori Oct. 23, 2024, 11:24 a.m. UTC | #12
On Wed, 23 Oct 2024 12:59:03 +0200
Miguel Ojeda <miguel.ojeda.sandonis@gmail.com> wrote:

> On Wed, Oct 23, 2024 at 8:51 AM FUJITA Tomonori
> <fujita.tomonori@gmail.com> wrote:
>>
>> Can we add the above to Documentation/rust/coding-guidelines.rst?
> 
> Sounds good to me -- I will send a patch.

Great, thanks!

> Just to confirm, do you mean the whole operators overloading guideline
> that I mentioned elsewhere and what semantics the arithmetic operators
> should have (i.e.to avoid having to repeatedly document why operator
> do "not supposed to wrap" and why we relegate saturating/wrapping/...
> to methods), or something else?

I was only thinking about the guideline for naming (at least as a
starter); your mail in this thread:

https://lore.kernel.org/netdev/CANiq72mbWVVCA_EjV_7DtMYHH_RF9P9Br=sRdyLtPFkythST1w@mail.gmail.com/
Boqun Feng Oct. 25, 2024, 8:47 p.m. UTC | #13
On Thu, Oct 17, 2024 at 08:58:48PM +0200, Miguel Ojeda wrote:
> On Thu, Oct 17, 2024 at 7:03 PM Boqun Feng <boqun.feng@gmail.com> wrote:
> >
> > but one thing I'm not sure is since it looks like saturating to
> > KTIME_SEC_MAX is the current C choice, if we want to do the same, should
> > we use the name `add_safe()` instead of `saturating_add()`? FWIW, it
> > seems harmless to saturate at KTIME_MAX to me. So personally, I like
> 
> Wait -- `ktime_add_safe()` calls `ktime_set(KTIME_SEC_MAX, 0)` which
> goes into the conditional that returns `KTIME_MAX`, not `KTIME_SEC_MAX
> * NSEC_PER_SEC` (which is what I guess you were saying).
> 

Yeah.. this is very interesting ;-) I missed that.

> So I am confused -- it doesn't saturate to `KTIME_SEC_MAX` (scaled)
> anyway. Which is confusing in itself.
> 

Then I think it suffices to say ktime_add_safe() is just a
saturating_add() for i64? ;-)

> In fact, it means that `ktime_add_safe()` allows you to get any value
> whatsoever as long as you don't overflow, but `ktime_set` does not
> allow you to -- unless you use enough nanoseconds to get you there
> (i.e. over a second in nanoseconds).
> 

That seems to the be case.

Regards,
Boqun

> Cheers,
> Miguel
>
diff mbox series

Patch

diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs
index 8c00854db58c..9b0537b63cf7 100644
--- a/rust/kernel/time.rs
+++ b/rust/kernel/time.rs
@@ -155,3 +155,14 @@  pub fn as_secs(self) -> i64 {
         self.nanos / NSEC_PER_SEC
     }
 }
+
+impl core::ops::Add<Delta> for Ktime {
+    type Output = Ktime;
+
+    #[inline]
+    fn add(self, delta: Delta) -> Ktime {
+        Ktime {
+            inner: self.inner + delta.as_nanos(),
+        }
+    }
+}