Re: [PATCH v1 2/2] rust: time: add Delta::to_jiffies_timeout() for timeout conversion
"Gary Guo" <[email protected]>
| Newsgroups | org.kernel.vger.rust-for-linux |
|---|---|
| Message-ID | <[email protected]> |
On Tue Aug 11, 2026 at 4:01 PM BST, FUJITA Tomonori wrote: > From: FUJITA Tomonori <[email protected]> > > Add Delta<Nsec>::to_jiffies_timeout() conversion. Unless the result > saturates, the value is rounded up, so the resulting timeout is never > shorter than the requested span. > > The result saturates at zero jiffies for a negative span, i.e. an > immediate timeout, and at the kernel's MAX_JIFFY_OFFSET "wait forever" > value for a span that is too large. > > Reviewed-by: Gary Guo <[email protected]> You should drop old review tags given this has been changed non trivially. > Reviewed-by: Andreas Hindborg <[email protected]> > Signed-off-by: FUJITA Tomonori <[email protected]> > --- > rust/kernel/time.rs | 66 +++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 66 insertions(+) > > diff --git a/rust/kernel/time.rs b/rust/kernel/time.rs > index b0b43ad3aa47..4ec1748c2602 100644 > --- a/rust/kernel/time.rs > +++ b/rust/kernel/time.rs > @@ -39,6 +39,10 @@ > /// The number of nanoseconds per second. > pub const NSEC_PER_SEC: i64 = bindings::NSEC_PER_SEC as i64; > > +/// The C side `MAX_JIFFY_OFFSET`, i.e. `((LONG_MAX >> 1) - 1)`, which the kernel > +/// treats as an infinite timeout. > +const MAX_JIFFY_OFFSET: isize = (isize::MAX >> 1) - 1; > + > /// The time unit of Linux kernel. One jiffy equals (1/HZ) second. > pub type Jiffies = crate::ffi::c_ulong; > > @@ -569,6 +573,51 @@ pub fn as_millis_ceil(self) -> i64 { > } > } > > + /// Convert this span to a [`Delta<Jiffy>`] suitable for use as a timeout. > + /// > + /// Unless the result saturates, the value is rounded up to the next whole > + /// jiffy, so the resulting timeout is never shorter than `self` (as > + /// [`msecs_to_jiffies()`] does). > + /// > + /// The result saturates at zero jiffies for a negative span, i.e. an > + /// immediate timeout, and at the kernel's [`MAX_JIFFY_OFFSET`] for a span > + /// that is too large, which the C side treats as an infinite timeout. > + /// > + /// # Examples > + /// > + /// ``` > + /// use kernel::time::Delta; > + /// > + /// // A negative span is an immediate timeout. > + /// assert_eq!(Delta::from_millis(-1).to_jiffies_timeout().as_jiffies(), 0); > + /// > + /// // A span shorter than a jiffy still waits, i.e. the timeout is never > + /// // shorter than the span. > + /// assert!(Delta::from_nanos(1).to_jiffies_timeout().as_jiffies() >= 1); > + /// ``` > + /// > + /// [`msecs_to_jiffies()`]: srctree/include/linux/jiffies.h > + /// [`MAX_JIFFY_OFFSET`]: srctree/include/linux/jiffies.h > + #[inline] > + pub fn to_jiffies_timeout(self) -> Delta<Jiffy> { > + let msecs = self.as_millis_ceil(); > + > + // CAST: `msecs` is clamped to `0..=c_uint::MAX`, so it is non-negative and > + // fits in `c_uint`. > + let msecs = msecs.clamp(0, i64::from(crate::ffi::c_uint::MAX)) as crate::ffi::c_uint; > + > + // SAFETY: `__msecs_to_jiffies()` is always safe to call. > + let jiffies = unsafe { bindings::__msecs_to_jiffies(msecs) }; As I mentioned in previous 2 versions, I don't think __msecs_to_jiffies should be used for this. You're doing two rounding and saturation operations here. If you do nsecs_to_jiffies64 and then clamp, you wouldn't run into any of these boundary conditions. Best, Gary > + > + // `__msecs_to_jiffies()` only saturates when its argument is negative as an > + // `int`: with `HZ=1000` it returns `msecs` as-is, which exceeds > + // `MAX_JIFFY_OFFSET` on 32 bit. > + let jiffies = jiffies.min(MAX_JIFFY_OFFSET as crate::ffi::c_ulong); > + > + // CAST: `jiffies` is clamped to `MAX_JIFFY_OFFSET`, which is `<= isize::MAX`. > + Delta::<Jiffy>::from_jiffies(jiffies as isize) > + } > + > /// Return `self % dividend` where `dividend` is in nanoseconds. > /// > /// The kernel doesn't have any emulation for `s64 % s64` on 32 bit platforms, so this is > @@ -621,4 +670,21 @@ fn as_millis_ceil_extremes() { > MIN_MILLIS_CEIL > ); > } > + > + #[test] > + fn to_jiffies_timeout_saturates() { > + // `__msecs_to_jiffies()` does not clamp its result for every `HZ` configuration, > + // e.g. with `HZ=1000` it returns the millisecond value as-is, which exceeds > + // `MAX_JIFFY_OFFSET` on 32 bit. > + let max = Delta::from_millis(i64::from(i32::MAX)).to_jiffies_timeout(); > + assert!(max.as_jiffies() <= MAX_JIFFY_OFFSET); > + > + // An overlong span is an infinite timeout. > + let overlong = Delta::from_nanos(i64::MAX).to_jiffies_timeout(); > + assert_eq!(overlong.as_jiffies(), MAX_JIFFY_OFFSET); > + > + // A negative span is an immediate timeout, however long it is. > + let negative = Delta::from_nanos(i64::MIN).to_jiffies_timeout(); > + assert_eq!(negative.as_jiffies(), 0); > + } > }