Kernel time APIs for Rust [LWN.net]

0
3


Thank you for reading this post, don't forget to subscribe!

LWN.net needs you!

Without subscribers, LWN would simply not exist. Please consider
signing up for a subscription and helping
to keep LWN publishing

By Jonathan Corbet
March 2, 2023

While the 6.3 kernel has gained more support for the Rust language, it
still remains true that there is little that can be done in Rust beyond the
creation of a “hello world” module. That functionality was already
available in C, of course, with a level of safety similar to what Rust can
provide. Interest is growing, though, in merging actually useful modules
written in Rust; that will require some more capable infrastructure than is
currently present. A recent discussion on the handling of time values in
Rust demonstrates the challenges — and opportunities — inherent in this
effort.

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.



(Log in to post comments)



Source link

LEAVE A REPLY

Please enter your comment!
Please enter your name here