RFC-0260: Kernel Boot Time Support | |
---|---|
Status | Accepted |
Areas |
|
Description | Enumerates the Zircon API changes needed to support the boot timeline. |
Issues | |
Gerrit change | |
Authors | |
Reviewers | |
Date submitted (year-month-day) | 2024-06-14 |
Date reviewed (year-month-day) | 2024-09-25 |
Problem Statement
The Monotonic Clock Suspension and the Boot Timeline RFC stated that Fuchsia must provide a boot timeline that reports the amount of time elapsed since boot, including any time spent in suspend-to-idle. That RFC mentioned that there would need to be kernel changes but did not specify exactly what those were.
Summary
This RFC describes the syscalls and API changes needed to support the boot timeline in the Zircon kernel.
Stakeholders
Facilitator: jamesr@google.com
Reviewers:
- adamperry@google.com
- andresoportus@google.com
- cja@google.com
- fmil@google.com
- gkalsi@google.com
- harshanv@google.com
- johngro@google.com
- maniscalco@google.com
- mcgrathr@google.com
- miguelfrde@google.com
- surajmalhotra@google.com
- tgales@google.com
- tmandry@google.com
Socialization:
This RFC was socialized as a set of several documents that each discussed various portions of the design. These documents were sent to and iterated on by the kernel team and by various people across the org.
Requirements
The kernel MUST support:
- Reading the current value of the boot timeline.
- Creating custom clocks using the boot timeline as the reference timeline.
- Creating timers on the boot timeline, though timers that can wake the system are explicitly left for a future RFC.
- Reporting the timestamp of log messages and crashlogs in the boot timeline.
Note that we do not plan to support other time related syscalls, such as object or port waits, on the boot timeline, as we have not identified a motivating use case to do so. We will revisit this decision in future RFCs if a use case arises.
Design
Introducing New Time Type Aliases in the Zircon API
Zircon has several types for representing time today:
zx_time_t
: Represents a point in time in nanoseconds. Generally this point is sampled from the monotonic timeline, but not always;zx_clock_read
, for example, also returns azx_time_t
but is not on the monotonic timeline.zx_duration_t
: Represents either an amount of time or a distance between two points in time. This quantity has no concrete association with a timeline, but is always in units of nanoseconds.zx_ticks_t
: Represents either a point or a duration of time in platform specific ticks.
These are all aliases of int64_t
, which means that the compiler provides no
type checking for any of these entities. They exist solely to improve
the readability of code. Reusing these types for the boot timeline would
significantly degrade the readability of code working with timestamps, as
developers would not be able to easily differentiate between timestamps on
different timelines.
Therefore, new type aliases will be introduced with the following structure:
zx_<kind>_<timeline>_[units_]t
where:
kind
is eitherinstant
orduration
.timeline
is eithermono
orboot
.units
is eitherticks
or omitted, in which case the unit is understood to be nanoseconds. The vast majority of code uses time in nanoseconds, so this omission makes the common case a bit easier to type.
Most existing uses of zx_time_t
will be migrated to use zx_instant_mono_t
,
and most existing uses of zx_duration_t
will be migrated to use
zx_duration_mono_t
. Uses of zx_ticks_t
will be evaluated and migrated on
a case-by-case basis.
The existing aliases will not be removed, and will be used when an ambiguous
type is required (for example, zx_clock_read
will still return zx_time_t
).
It is the aim of the kernel team to remove these ambiguous types in the long
term, but the exact nature of how that happens is out of scope and left to
future RFCs.
Userspace Types for Time
While the new type aliases for time described in the preceding section will improve code clarity and readability in the Zircon APIs, they do not provide compiler-enforced type safety. This type safety will instead be provided by the userspace syscall wrapper libraries found in the Rust bindings for Zircon and the C++ libzx library, both of which will need to be extended with the syscalls introduced in and modified by this RFC. Userspace code should prefer to use these libraries whenever possible.
Appropriate APIs for strong typing need careful design work in each language to be idiomatic and easy to use in practice. This won't be rushed and its completion for each language won't be required for the completion of the type migration in the C API detailed in the preceding section.
Reading the Current Boot Time
Two syscalls will be introduced to allow usermode to read the current value of the boot timeline:
zx_instant_boot_t zx_clock_get_boot(void);
zx_instant_boot_ticks_t zx_ticks_get_boot(void);
The first will report the time in nanoseconds, and the second will report the time in platform ticks. This mirrors the corresponding syscalls on the monotonic timeline.
Note that the ratio of ticks to seconds will be the same for the monotonic and
the boot timelines, meaning that the result of zx_ticks_per_second
can be
used to convert the results of both zx_ticks_get
and zx_ticks_get_boot
to
seconds.
Creating Custom Clocks
Usermode clocks are created using the zx_clock_create
syscall. The syscall
already accepts an options
parameter, meaning we do not need to change the
function signature.
Instead, a new options flag called ZX_CLOCK_OPT_BOOT
will be introduced that
instructs the newly created clock to use the boot timeline as its reference
timeline.
The timeline property of clocks (monotonic and boot) will be immutable, meaning that a clock cannot switch timelines once created.
Renaming Fields in zx_clock_details
The zx_clock_details_v1_t
struct contains two fields that store the
transformation from the monotonic timeline to the clock's synthetic timeline:
typedef struct zx_clock_details_v1 {
// other fields
zx_clock_transformation_t ticks_to_synthetic;
zx_clock_transformation_t mono_to_synthetic;
// other fields
} zx_clock_details_v1_t;
These fields will be renamed to highlight the fact that they will now store the transformation from the reference timeline to the clock's synthetic timeline:
typedef struct zx_clock_details_v1 {
// other fields
zx_clock_transformation_t reference_ticks_to_synthetic;
zx_clock_transformation_t reference_to_synthetic;
// other fields
} zx_clock_details_v1_t;
A quick scan of the code base shows very few users of these fields, and none of those users are outside of fuchsia.git. Therefore, an in-place rename should be relatively straightforward.
Creating Boot Timers
Usermode timers are created using the zx_timer_create
syscall. This syscall
conveniently accepts a clock_id
parameter that dictates the timeline the timer
should operate on.
Thus, a new clock_id
value called ZX_CLOCK_BOOT
will be introduced that
instructs the newly created timer to operate on the boot timeline.
Similar to clocks, the timeline property of timers will be immutable, meaning that a timer cannot switch timelines once created.
Note that zx_timer_set
will be reused to set boot timers. Because this
syscall takes as input both monotonic and boot deadlines, its parameter type
will remain zx_time_t
.
Adding the clock_id
to zx_info_timer_t
Users can call zx_object_get_info
with the ZX_INFO_TIMER
topic to get
information about a timer handle. The resulting zx_info_timer_t
struct
will be modified to include the clock_id
the timer was created with.
Normally, this would require some struct evolution, but in this case the struct
already has 4 bytes of padding, which is just enough space to hold our
clock_id
, which is stored as a 4 byte zx_clock_t
.
Updating timestamps in Zircon structures
There are several timestamps in Zircon structures that report monotonic time today, but should switch to using boot time once it is available.
Note that timestamps that continue to report monotonic time will not be changed
functionally but will have their types updated to zx_instant_mono_t
as
described in the type alias section above.
zx_log_record_t
Timestamps
zx_log_record_t
is a struct exposed by Zircon that describes the structure
of a message in the debuglog. This struct contains a timestamp
field that is
a zx_time_t
and is a point on ZX_CLOCK_MONOTONIC
. This will be updated to be
a zx_instant_boot_t
and therefore will be a point on ZX_CLOCK_BOOT
.
Crashlog Uptime Timestamp
The crashlog currently reports an uptime
field that utilizes monotonic time.
Seeing as monotonic time does not include periods of suspension, this should be
modified to use boot time and its type should be updated to zx_instant_boot_t
.
Interrupt API Changes
Interrupt Syscalls
The Zircon Interrupt API contains two syscalls that operate on monotonic timestamps:
// Current function signatures.
// Allows users to wait on an interrupt, and returns the timestamp at which the
// interrupt was triggered.
zx_status_t zx_interrupt_wait(zx_handle_t handle,
zx_time_t* out_timestamp);
// Triggers a virtual interrupt at the given timestamp.
zx_status_t zx_interrupt_trigger(zx_handle_t handle,
uint32_t options,
zx_time_t timestamp);
This presents a problem because interrupts can fire during periods of suspension, in which the monotonic clock will be paused. Therefore, these timestamps must switch to using the boot timeline. Concretely, the syscall signatures will be changed to:
// Proposed function signatures.
zx_status_t zx_interrupt_wait(zx_handle_t handle,
zx_instant_boot_t* out_timestamp);
zx_status_t zx_interrupt_trigger(zx_handle_t handle,
uint32_t options,
zx_instant_boot_t timestamp);
Since both zx_instant_boot_t
and zx_time_t
are aliases of int64_t
, this
change can be accomplished with a simple search and replace.
libzx
Wrappers
The libzx
library provides C++ wrappers around the zx_interrupt_wait
and
zx_interrupt_trigger
syscalls. These wrappers will need to be updated to use
the new boot timestamps. Because these wrappers use strong types for timestamps
on different timelines, this will require migrating all callsites to use boot
time.
Port Packet Changes
There are a couple port packets that contain timestamps that will need to be modified.
zx_packet_interrupt_t
The zx_interrupt_bind
syscall allows users to bind or unbind an interrupt
object to a port. When a bound interrupt is triggered, a packet of type
ZX_PKT_TYPE_INTERRUPT
is queued on the given port. That packet has the
following structure:
typedef struct zx_packet_interrupt {
// Timestamp at which the interrupt was triggered.
zx_time_t timestamp;
// ..some reserved fields...
} zx_packet_interrupt_t;
The timestamp
field must switch to using boot time (and therefore become a
zx_instant_boot_t
) for the same reason the timestamp returned by
zx_interrupt_wait
must be on the boot timeline.
zx_packet_signal_t
This packet is queued to a port that is bound to an object using the
zx_object_wait_async
syscall, which has the following signature:
zx_status_t zx_object_wait_async(zx_handle_t handle,
zx_handle_t port,
uint64_t key,
zx_signals_t signals,
uint32_t options);
When a signal in the set of signals
is asserted on handle
, and if the
caller passed ZX_WAIT_ASYNC_TIMESTAMP
in the options
field, the resulting
packet is enqueued to the port:
typedef struct zx_packet_signal {
// ... other fields ...
uint64_t timestamp;
// ... reserved fields ...
} zx_packet_signal_t;
The timestamp
field records when the signal was asserted. Depending on the
type of object we're waiting on, it would make sense for the timestamp to be on
different timelines. For example, we'd want a boot timestamp when waiting on a
boot timer.
So, we propose the following:
- Add a new flag to the
zx_object_wait_async
syscall calledZX_WAIT_ASYNC_BOOT_TIMESTAMP
, which signals that thetimestamp
field should utilize the boot timeline. - Switch the type of
timestamp
to the polymorphic aliaszx_time_t
.
We will audit all existing usages of the ZX_WAIT_ASYNC_TIMESTAMP
flag and
ensure that any caller that needs boot timestamps is migrated over before we
pause the monotonic clock.
Implementation
The changes to the kernel API will be implemented first, with each category of change (reading time, clocks, timers, etc.) separated into different CLs.
These changes (which should be relatively small) will be followed by slightly larger changes to add boot time support to our Rust and C++ syscall wrapper libraries. These changes will proceed in two phases:
- Phase 1 will add support for the syscalls added or modified by this RFC. This will require some modification of the types used, but this phase will focus mainly on functional changes.
- Phase 2 will add more robust type enforcement for time types. This will be done after Phase 1 as it may take longer to iron out the precise type structure needed in each language.
Performance
The syscall modifications needed to support boot time are purely additive, and therefore should not change any existing performance characteristics of the system.
Similarly, updating the timestamp field of various structures in Zircon to use the boot timeline should have no impact on performance, as computing the boot time is no more expensive than computing the monotonic time.
Ergonomics
Users of the time syscalls and types may have to be aware of the fact that there are now multiple timelines supported by the kernel. However, most programs should be able to just use monotonic time.
Backwards Compatibility
Except for the semantic change to the timeline of the log record timestamp, these changes are source and binary backwards compatible.
However, many pieces of existing code may need to switch from using the monotonic timeline to the boot timeline to preserve correctness. Please refer to the Monotonic Clock Suspension and the Boot Timeline RFC for more information on how we plan to handle this.
Security considerations
We do not anticipate any security ramifications from this proposal. For more information on why this is the case, please refer to the security section of the Monotonic Clock Suspension and the Boot Timeline RFC.
Privacy considerations
This proposal does not impact system privacy, since adding boot time support to the kernel does not involve any sort of data collection.
Testing
The core tests will be modified to exercise both the new syscalls and the modifications to the existing syscalls.
Documentation
All of the Zircon API modifications described above will need to be documented. Concretely, this means:
- Documenting all of the new type aliases and updating the docs for the existing type aliases.
- Documenting the newly added syscalls.
- Documenting the new flags added to existing syscalls.
- Documenting the timeline of the various timestamps that were updated.
In addition, we should update our clock documentation on fuchsia.dev to
highlight how zx_time_t
is not tethered to a particular timeline.