Kernel time APIs for Rust
Asahi Lina, who is implementing a graphics driver for Apple hardware in Rust, has posted a number of pieces of Rust infrastructure, including a module for timekeeping functions. The timekeeping module itself is just a beginning, weighing in at all of 25 lines; it looks like this:
// SPDX-License-Identifier: GPL-2.0
//! Timekeeping functions.
//!
//! C header: [`include/linux/ktime.h`](../../../../include/linux/ktime.h)
//! C header: [`include/linux/timekeeping.h`](../../../../include/linux/timekeeping.h)
use crate::bindings;
use core::time::Duration;
/// Returns the kernel time elapsed since boot, excluding time spent
/// sleeping, as a [`Duration`].
pub fn ktime_get() -> Duration {
// SAFETY: Function has no side effects and no inputs.
Duration::from_nanos(unsafe { bindings::ktime_get() }.try_into().unwrap())
}
/// Returns the kernel time elapsed since boot, including time spent
/// sleeping, as a [`Duration`].
pub fn ktime_get_boottime() -> Duration {
Duration::from_nanos(
// SAFETY: Function has no side effects and no variable inputs.
unsafe { bindings::ktime_get_with_offset(bindings::tk_offsets_TK_OFFS_BOOT) }
.try_into()
.unwrap(),
)
}
This module expresses two kernel functions — ktime_get() and ktime_get_boottime() — as Rust equivalents that return values as the Rust Duration type. In C, these functions both return a ktime_t, which is a signed, 64-bit value reflecting a time in nanoseconds. The origin of that time — what real-world date and time is represented by a ktime_t of zero — varies depending on which clock is being queried. In the case of ktime_get_boottime(), for example, the returned value represents the time that has passed since the system booted.
Kernel times are thus, at their core, a delta value; they are a count of nanoseconds since some beginning-of-the-universe event. The proposed Rust implementation followed that lead in its use of the Duration type. But Thomas Gleixner, who is responsible for much of the kernel's timekeeping code, questioned this approach. Since both of the functions are meant, each in its own way, to represent an absolute point in time, he suggested that the Rust functions should return an absolute-time type; he suggested either Instant or SystemTime. Both represent absolute time values; Instant is monotonic (it will never go backward) while SystemTime is not.
That approach will not work well in the kernel, though, for a couple of reasons. The first, as pointed out by Boqun Feng, is that those two types are defined in the Rust standard library ("std"), which, just like the C standard library, is not available in the kernel. So, at best, those two types would have to be reimplemented for kernel use. But the other problem is that the kernel supports a multitude of clocks; the list can be found in the clock_gettime() man page. Each clock has a reason for existing, and each behaves a little differently. A type like Instant is defined to use exactly one clock, but kernel code will need access to several of them.
Gleixner was not really concerned about the exact types used, but he did call for different types to be used for absolute times (timestamps) and delta times (or intervals). The kernel does not currently have such a distinction in its types for times, but libraries for many languages do make that distinction. Given that Rust is being brought into the kernel in the hope of making it easy to write safer code, it makes sense to use Rust's type system to prevent developers from, for example, trying to add two absolute-time values together.
Indeed, as Lina pointed out, type safety in the Rust interface should even go one step further. Subtracting one absolute time from another will yield a delta time — but that delta time will only make sense if the two absolute times came from the same clock. So the type system should prevent the incorrect mixing of times from different clocks.
What about delta times? Gleixner initially suggested that time deltas could be independent of any clock; a time delta obtained by subtracting one CLOCK_BOOTTIME value from another would be the same type as a delta calculated as a difference of CLOCK_TAI values. Heghedus Razvan agreed with this idea and posted a sample implementation; Gary Guo then polished that idea into a "more Rusty" implementation. Miguel Ojeda, though, suggested that delta times, too, could be tied to a specific clock. Gleixner was not entirely convinced that this distinction was needed, but agreed that there might be value in it, especially when dealing with timers. Kernel timers, too, can be based on specific clocks, so it might make sense to ensure that any time deltas used with those timers are generated with the same clock, he said.
Feng suggested proceeding with an implementation using Duration for all time delta values and something resembling Instant, but with clock-specific typing, for absolute times. Lina agreed, and seems ready to proceed in this direction, starting with the example posted by Razvan. A new patch will, presumably, be forthcoming.
It seems likely that we will see this sort of conversation happening
repeatedly as more Rust infrastructure heads toward the mainline. It is
certainly possible to reproduce something like the existing kernel APIs in
Rust, and doing so would make the resulting Rust code look more familiar to
current kernel developers. But that approach also risks losing much of
the hoped-for benefit that is driving the Rust experiment in the first
place. Doing this experiment properly will require showing how Rust can
lead to the creation of better, safer APIs than what the kernel has now.
So a lot of APIs are going to have to be redesigned from the beginning;
they can benefit from years of experience in the kernel community, but will
have to leave many of that community's conventions behind. It seems like a
project that will keep people busy for some time.
| Index entries for this article | |
|---|---|
| Kernel | Development tools/Rust |
| Kernel | Timekeeping |
Posted Mar 2, 2023 16:51 UTC (Thu)
by rillian (subscriber, #11344)
[Link] (18 responses)
In case anyone else was wondering, the It seems like the module could call the higher-level
Posted Mar 2, 2023 20:46 UTC (Thu)
by rrolls (subscriber, #151126)
[Link] (5 responses)
Perhaps it's just because I'm used to how Python does things (here, with timedelta), but if I have two instants, A and B, then I'd expect to be able to do A - B and get a duration that could be positive, zero or negative - positive if A was in the future of B, zero if A and B are the same instant, and negative if A was in the past of B. Similarly, adding a positive duration to some instant C would result in an instant in C's future, and adding a negative duration to C would result in an instant in C's past.
Does Rust have a different type intended to be used for this purpose? If so, then what is core::time::Duration for? Or if not, would I be expected to carry around a future/past flag when it's needed, like an "absolute value plus sign" representation?
Posted Mar 2, 2023 21:20 UTC (Thu)
by mb (subscriber, #50428)
[Link]
Posted Mar 2, 2023 21:53 UTC (Thu)
by mbunkus (subscriber, #87248)
[Link]
There are several popular crates out there with signed duration types, allowing for real differences. "chrono" and "time" are popular ones, and both state explicitly that they allow arithmetic and negative durations.
Posted Mar 2, 2023 21:56 UTC (Thu)
by mbunkus (subscriber, #87248)
[Link] (2 responses)
Posted Mar 3, 2023 15:34 UTC (Fri)
by magi (subscriber, #4051)
[Link] (1 responses)
Posted Mar 7, 2023 5:20 UTC (Tue)
by ssmith32 (subscriber, #72404)
[Link]
https://www.merriam-webster.com/dictionary/duration
For the non-negative speaker: as sometimes happens, you're understanding of the language is more correct than, perhaps, some native English speakers ;)
Posted Mar 2, 2023 23:42 UTC (Thu)
by RogerOdle (subscriber, #60791)
[Link] (11 responses)
Make time a signed 64 bit integer everywhere. Do not use Duration, use Time. Do not use Instant, use Time. Just pick one type please. If you create more than one, may kitten will die,
Posted Mar 3, 2023 0:48 UTC (Fri)
by mathstuf (subscriber, #69389)
[Link]
This is a C problem. Rust doesn't allow such comparisons. It has explicit casts, but they look "weird" and usually indicate that some API is missing.
I wouldn't mind seeing `Duration<Monotonic>` and `Duration<Wall>` that are *not* comparable. Everything being the same type makes it too easy to mix them up and make nonsense differences.
Posted Mar 3, 2023 1:33 UTC (Fri)
by NYKevin (subscriber, #129325)
[Link] (8 responses)
Signed (or unsigned, for that matter) 64-bit integers do not allow for nanosecond precision to an acceptable range. They go to a reasonably large range, but I would argue that it's not really good enough:
Sure, 2262 is a long time in the future. But it's not so absurdly far into the future that you won't eventually have compatibility problems (unlike the Python max datetime, which is thousands of years from now, or the 64-bit *second* rollover, which is multiple times the age of the universe). Realistically, if you want precision finer than 1 second increments, you should probably use some kind of struct. So now you have two time types, like it or not.
Aside from that, the whole point of using multiple types is so that the type checker can catch invalid comparisons. Rust is not C, it does not let you just write nonsense and have it automagically coerce to the Wrong Thing.
Posted Mar 3, 2023 7:48 UTC (Fri)
by Cyberax (✭ supporter ✭, #52523)
[Link] (1 responses)
This is actually a real problem already! My friends are working on software that processes Excel spreadsheets, and they had to abandon the standard Go timekeeping infrastructure, because it can't represent date differences that are used in actual spreadsheets that work with historical dates.
Posted Mar 5, 2023 16:57 UTC (Sun)
by Wol (subscriber, #4433)
[Link]
I've heard of it before, but it makes things SOOO much simpler if the 24hr clock doesn't force you to use times between 00:00 and 23:59. I'll be able to start shifts at the correct time of -5:00! Or we have shifts that are supposed to finish at 24:00, and I'll be able to correctly record drivers coming back to base and clocking off at 25:30, which I did on one occasion ...
Cheers,
Posted Mar 3, 2023 16:26 UTC (Fri)
by rillian (subscriber, #11344)
[Link] (5 responses)
Following this logic, the Rust std library This aligns with the general language philosophy of being correct by default. The downside is it's slower. Of course if that's an issue, one is free to make one's own types with smaller representations.
Posted Mar 3, 2023 23:00 UTC (Fri)
by NYKevin (subscriber, #129325)
[Link] (4 responses)
a == b * (a.div_duration_u64(b)) + (a % b)
Where a and b are arbitrary durations, b is nonzero, and overflow does not occur (there might also need to be a checked variant). div_duration_u64 returns a u64. Implementing those correctly is not trivial, and library consumers ought not have to do it themselves. They are needed for answering questions such as "How many days, hours, minutes, and seconds is this duration?" without having to extract seconds into u64 and do all the math in untyped integers.
Posted Mar 4, 2023 0:31 UTC (Sat)
by rillian (subscriber, #11344)
[Link] (3 responses)
Are there other use-cases? I agree converting a The
Posted Mar 4, 2023 1:24 UTC (Sat)
by NYKevin (subscriber, #129325)
[Link] (2 responses)
Posted Mar 4, 2023 1:25 UTC (Sat)
by NYKevin (subscriber, #129325)
[Link]
Posted Mar 4, 2023 2:23 UTC (Sat)
by rillian (subscriber, #11344)
[Link]
I see what you mean. Thanks. I suppose there's
Posted Mar 3, 2023 5:46 UTC (Fri)
by josh (subscriber, #17465)
[Link]
Absolute time and relative time are not the same thing. It's a good thing that:
Posted Mar 2, 2023 20:27 UTC (Thu)
by iabervon (subscriber, #722)
[Link] (4 responses)
I think returning Durations might be a good cue that the caller should use these functions in order to have the difference between two return values best reflect the time that elapsed between those calls, and look elsewhere for the official current time. (But developers looking for time information should probably be reminded of the existence of what they probably really want.)
Posted Mar 2, 2023 23:42 UTC (Thu)
by NYKevin (subscriber, #129325)
[Link]
Posted Mar 3, 2023 0:12 UTC (Fri)
by tglx (subscriber, #31301)
[Link]
So in theory the return value for these functions could be unsigned. But C is patently bad about unsigned vs. signed math unless you are very carefully wrapping things with the required type casts. So we ended up with a signed data type.
But that does not mean that ktime_get*() will ever return a negative value. That would be clearly a bug.
Now we are talking about interfaces for a proper type safe language and we really have to be very careful about these things and not just willy-nilly declare that the kernel does not care and timestamps are considered relative.
There is a clear distinction between reading time, which is by definition absolute and the delta between two timestamps which is by definition relative. That's nothing which is defined by the kernel or by rust, it's a well understood fact.
Making the sloppy abstraction of everything is a Duration is just thwarting the justification for Rust in the kernel.
Posted Mar 3, 2023 6:07 UTC (Fri)
by matthias (subscriber, #94967)
[Link] (1 responses)
This somewhat misses the point. Of course this should be absolute times, in the sense that they refer to some point in time and not a duration, i.e. the difference between two points in time. However get_ktime() uses a different calendar, i.e., one that does not start with the birth of Christ as some other calendars. But the distinction between point in time (returned by get_ktime()) and duration (time that passed between two points in time) is still meaningful and important.
Of course, if you want to store some point in time, you always have to store a difference to some arbitrary point in time that you fix as the 0 in your calendar. Be it birth of Christ, Unix epoch, or time of system boot. But there is always the clear distinction between a point in time (stored relative to the 0) and the duration between two points in time. And comparing one to another is most likely a big mistake that should be prevented by the type system.
> I think returning Durations might be a good cue that the caller should use these functions in order to have the difference between two return values best reflect the time that elapsed between those calls, and look elsewhere for the official current time. (But developers looking for time information should probably be reminded of the existence of what they probably really want.)
No, as also discussed in the article, you can use different kinds of Instances instead. So you have Instant<KTIME>, Instant<TAI>, Instant<WALL>, etc. It would be an error to calculate a Duration from two Instances that come from different clocks. Which type of clock you want would be encoded in the concrete type. What is not that clear to me is whether Durations calculated from different clocks are different types. But in kernel space it probably makes sense, because the Duration between the same two points in time can depend on the clock used to record these points in time. Some clocks are monotonic while others are not.
Posted Mar 3, 2023 9:51 UTC (Fri)
by Wol (subscriber, #4433)
[Link]
That - I think - gives you everything you need to convert between them, but the rule would be any attempt to convert/compare either requires the information to do a valid operation or, if the information is not there, you can force the operation on bare values. What you cannot do is the operation where you have the clock definition but no conversion information.
Cheers,
Posted Mar 2, 2023 22:37 UTC (Thu)
by amarao (guest, #87073)
[Link] (1 responses)
I find myself occasionally 'thinking in Rust' even if I write in soft-typed language.
Posted Mar 3, 2023 0:14 UTC (Fri)
by tglx (subscriber, #31301)
[Link]
Posted Mar 4, 2023 0:26 UTC (Sat)
by edeloget (subscriber, #88392)
[Link] (1 responses)
Or maybe there is something I don't see?
Posted Mar 5, 2023 16:01 UTC (Sun)
by plugwash (subscriber, #29694)
[Link]
Posted Apr 22, 2023 11:13 UTC (Sat)
by charmitro (guest, #164741)
[Link] (1 responses)
AFAIK there are only samples, but it's been a long time since last time I checked.
Posted Apr 22, 2023 11:29 UTC (Sat)
by rahulsundaram (subscriber, #21946)
[Link]
> When are we expected to see modules implemented in Rust on the mainline?
I haven't seen a specific timeline but here is what I have read
https://www.heise.de/hintergrund/Three-Questions-and-Answ...
"The first drivers (and the abstractions supporting them) that will start to be upstreamed are likely to be the Asahi Linux's GPU driver, Android's Binder and the NVMe driver. These are all non-trivial and will set the example for future Rust kernel abstractions and drivers."
Kernel time APIs for Rust
bindings::ktime_get().try_into().unwrap() is because ktime_t is a signed 64-bit integer, but Rust's core::time::Duration wraps an unsigned type, since durations are non-negative intervals. The Rust code will panic if ktime_get() returns a negative clock value. Part of the absolute vs. offset complications!ktime_get_ns() and ktime_get_boottime() instead to be more isolated from implementation changes. Maybe explicit is better?Kernel time APIs for Rust
Kernel time APIs for Rust
It also has a checked_duration_since() function, which returns None, if the result would be negative (by your definition).
Kernel time APIs for Rust
Kernel time APIs for Rust
Kernel time APIs for Rust
Kernel time APIs for Rust
Kernel time APIs for Rust
Kernel time APIs for Rust
Kernel time APIs for Rust
Python 3.10.9 (main, Dec 7 2022, 13:47:07) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import datetime as dt
>>> epoch = dt.datetime(1970, 1, 1, 0, 0, 0, tzinfo=dt.timezone.utc)
>>> print(epoch)
1970-01-01 00:00:00+00:00
>>> print(epoch + dt.timedelta(microseconds=(2**63 - 1) // 1000))
2262-04-11 23:47:16.854775+00:00
>>> print(epoch - dt.timedelta(microseconds=(2**63) // 1000))
1677-09-21 00:12:43.145225+00:00
>>> # For comparison:
>>> print(epoch + dt.timedelta(seconds=(2**31 - 1)))
2038-01-19 03:14:07+00:00
>>> print(epoch + dt.timedelta(seconds=(2**32 - 1)))
2106-02-07 06:28:15+00:00
>>> print(epoch + dt.timedelta(seconds=(2**63 - 1)))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: Python int too large to convert to C int
>>> print(dt.datetime.max)
9999-12-31 23:59:59.999999
Kernel time APIs for Rust
Kernel time APIs for Rust
Wol
Kernel time APIs for Rust
Duration type is actually a 96-bit struct, so it has both nanosecond precision and the range of a 64-bit second counter.Kernel time APIs for Rust
a % b < b
Kernel time APIs for Rust
Duration to various calendar intervals isn't trivial, but does that support belong in the std library, rather than the library implementing the conversions? Wouldn't such a method further constrain the (currently private) representation?time and chrono libraries in fact both provide their own Duration types with methods for computing how many minutes, hours, weeks, etc. a given Duration represents. Amusingly for this discussion they both use signed integers internally.Kernel time APIs for Rust
Kernel time APIs for Rust
Kernel time APIs for Rust
pub const fn as_nanos(&self) -> u128...Kernel time APIs for Rust
Stop using multiple types for the same thing
Kernel time APIs for Rust
Kernel time APIs for Rust
Kernel time APIs for Rust
Kernel time APIs for Rust
Kernel time APIs for Rust
Wol
Kernel time APIs for Rust
Kernel time APIs for Rust
Kernel time APIs for Rust
Kernel time APIs for Rust
Kernel time APIs for Rust
Kernel time APIs for Rust
