Skip to content

Domain schedules - #445

Merged
midnightveil merged 9 commits into
seL4:mainfrom
au-ts:run_time_domains
Jul 21, 2026
Merged

Domain schedules#445
midnightveil merged 9 commits into
seL4:mainfrom
au-ts:run_time_domains

Conversation

@Kswin01

@Kswin01 Kswin01 commented Mar 24, 2026

Copy link
Copy Markdown
Contributor

This adds support for domain schedules to the Microkit. It defines syntax that looks like:

    <!-- This element is used to declare the list of domains, to give
         each domain a human-readable name. -->
    <domains>
        <!-- The first domain contains the Microkit Monitor PD, but can contain others. -->
        <domain name="monitor" id="0" />
        <!-- You give each domain a name and its domain id. -->
        <domain name="emitter" id="1" />
        <!-- The domain ID is optional and can be auto-assigned if desired.
             It will be assigned to the smallest unused ID, and must be less
             than KernelNumDomains. -->
        <domain name="collector" />

        <!-- This is a domain schedule. Note that the same domain can appear twice
             in the schedule.
             Also supports optional attributes "start_index" and "index_shift".
         -->
        <domain_schedule>
            <schedule_entry domain="monitor" duration="6000 us" />
            <schedule_entry domain="emitter" duration="8000 us" />
            <schedule_entry domain="collector" duration="2000 us" />
            <!-- This element is optional, and is implicitly there anyway. -->
            <schedule_end_marker />
        </domain_schedule>
    </domains>

    <protection_domain name="emitter" domain="emitter">
        <program_image path="emitter.elf" />
    </protection_domain>

The standard, non-SMP config is changed to always have the domain schedule configured. This is only a minor memory cost to users who do not want to use domains.

Depends on seL4/rust-sel4#360 being merged.

Original description

This PR adds support for defining domain schedules in Microkit, and relies on the run-time domain scheduler changes due to be merged into seL4.

It also depends on the following branch of rust-sel4: https://github.com/au-ts/rust-sel4/tree/domain_set?branch=domain_set

Changes for users

In build_sdk.py we define two new configurations, debug_domains and release_domains. These configurations build the kernel with a max of 256 domains, and 256 entries into the domain schedule. The user can change this to values as they see fit. I have separated these from the regular configs as the users may not wish to have the extra memory overhead (although mostly negligible). I can merge these configs into one if its more desirable.

A domain schedule is defined in the sdf as:

  <domain_schedule>
      <domain name="domain_1" length="1000" />
      <domain name="domain_2" length="1000" />
      <domain name="domain_3" length="2000" />
  </domain_schedule>

The length is defined in milliseconds, and reads the TIMER_FREQUENCY from the kernel config to covert to timer ticks that the kernel requires. This is the case for aarch64 and riscv64. However, on x86, we don't have a static definition of the timer frequency, and is instead set at runtime by the kernel. I'm not sure of the best way to do it here, whether we should do this conversion in the capDL initialiser instead.

The above will insert the schedule using the new DomainSet invocations, beginning at index 0 by default. We also provide capDL a start index of 0 by default.

The users can optionally set these values like so:

    <domain_schedule>
        <domain name="domain_1" length="1000" />
        <domain name="domain_2" length="1000" />
        <domain name="domain_3" length="2000" />
        <domain_start index="2" />
        <domain_idx_shift shift="10" />
    </domain_schedule>

The start index is relative to the schedule list that the user provides not an absolute index. It must be in bounds of the length of schedule that the user defines. We also can have a domain shift, which means that will start inserting the schedule at that shifted index (in the above case 10). The shift + length of the schedule must be less than the kernel configured max number of domain schedules.

We also now build one monitor per domain. The monitor is responsible for handling faults, and also making threads passive if requested. The decision to create a monitor per domain was that these faults/requests can be serviced within that domains timeslice, and not have to wait for a singular domain to be scheduled again.

@Ivan-Velickovic

Copy link
Copy Markdown
Contributor

In build_sdk.py we define two new configurations, debug_domains and release_domains. These configurations build the kernel with a max of 256 domains, and 256 entries into the domain schedule. The user can change this to values as they see fit. I have separated these from the regular configs as the users may not wish to have the extra memory overhead (although mostly negligible). I can merge these configs into one if its more desirable.

Having the extra configs doesn't scale well, it already is a bit out of hand with the SMP ones, I think the domain functionality should be part of the existing configurations.

@lsf37

lsf37 commented Mar 24, 2026

Copy link
Copy Markdown
Member

Having the extra configs doesn't scale well, it already is a bit out of hand with the SMP ones, I think the domain functionality should be part of the existing configurations.

If you're always running with NUM_DOMAINS > 1 and don't set up a schedule, you will get the default schedule that wraps around once 2^56-1 ticks have passed. On a 1 GHz timer tick that's after about 833 days. Impact is negligible, just wanted to point out that if you are using this for a very long-term deployment with super precise time requirements you would see a blip every few years.

@Kswin01
Kswin01 force-pushed the run_time_domains branch 2 times, most recently from 8c458dd to a3b6e88 Compare March 24, 2026 05:32
@Indanz

Indanz commented Mar 24, 2026

Copy link
Copy Markdown

Some comments:

  • Default of 256 domains is way too many. 4 domains would be much more sensible and reduce the performance and memory overhead enough that can you enable it always.
  • 256 entries is over the top too, but that only costs a little bit of extra memory, not performance.
  • Milliseconds for domain durations is too coarse, I think you want microseconds.
  • Domains have no SMP support, so it's either one or the other, but never both.
  • Using domain_start index="1" as an example is misleading because it gives the impression that index starts from 1 instead of 0.

When we move to a clock based MCS API instead of time based, we'll run into the same issues as for domains.

For x86, the timing info is passed on via bootinfo. The problem of that is that it's hard to use by stand-alone or library code, as it depends on the system how to get that information. That's why I proposed a new syscall to get the same info (it could take either a domain cap, or an SchedContext/SchedControl cap). Even on Arm and RISC-V it depends on the system used whether those kernel configs are passed on or not.

@lsf37

lsf37 commented Mar 25, 2026

Copy link
Copy Markdown
Member
  • Default of 256 domains is way too many. 4 domains would be much more sensible and reduce the performance and memory overhead enough that can you enable it always.

The max number of domains has no performance impact at all as long as it fits 8 bits. You can have NUM_DOMAINS=256 and only use 4 of them. I do agree that you don't want to actually use 256 domains, that would indeed have a performance impact, but there is no reason to pick a particular low number for the maximum.

  • 256 entries is over the top too, but that only costs a little bit of extra memory, not performance.

That is the only one that actually does cost a little. The kernel default is 100, but I don't think 256 is a problem.

  • Milliseconds for domain durations is too coarse, I think you want microseconds.

I agree, people have been asking for a shorter minimum duration for non-MCS as well.

@midnightveil

midnightveil commented Mar 25, 2026

Copy link
Copy Markdown
Collaborator

The max number of domains has no performance impact at all as long as it fits 8 bits. You can have NUM_DOMAINS=256 and only use 4 of them. I do agree that you don't want to actually use 256 domains, that would indeed have a performance impact, but there is no reason to pick a particular low number for the maximum.

That's not true, all the scheduling queues are duplicated per-domain, and they're often some of the largest data in the kernel image aside from kernel page table structures. Though it's probably not a performance impact but more just a memory usage one. (But yes, I broadly agree with you).

@lsf37

lsf37 commented Mar 25, 2026

Copy link
Copy Markdown
Member

The max number of domains has no performance impact at all as long as it fits 8 bits. You can have NUM_DOMAINS=256 and only use 4 of them. I do agree that you don't want to actually use 256 domains, that would indeed have a performance impact, but there is no reason to pick a particular low number for the maximum.

That's not true, all the scheduling queues are duplicated per-domain, and they're often some of the largest data in the kernel image aside from kernel page table structures. Though it's probably not a performance impact but more just a memory usage one. (But yes, I broadly agree with you).

You're right, there is indeed a memory impact.

@Kswin01

Kswin01 commented Mar 25, 2026

Copy link
Copy Markdown
Contributor Author

Using domain_start index="1" as an example is misleading because it gives the impression that index starts from 1 instead of 0.

I do explicitly mention just below the xml snippet:
"The above will insert the schedule using the new DomainSet invocations, beginning at index 0 by default"

And I also mention something similar in the manual.md changes in this PR. Hopefully this is enough to indicate that index starts from 0, but I'll change the example here to be clearer.

@Kswin01

Kswin01 commented Mar 25, 2026

Copy link
Copy Markdown
Contributor Author

I've switched the default maximums to 64 domains, and 128 domain schedule entries. Would this be sufficient?

@Kswin01
Kswin01 force-pushed the run_time_domains branch 3 times, most recently from a791ef9 to ed650e1 Compare March 25, 2026 01:17
@Kswin01
Kswin01 force-pushed the run_time_domains branch 2 times, most recently from 1085b8d to 887c554 Compare March 25, 2026 03:16
@Indanz

Indanz commented Mar 25, 2026

Copy link
Copy Markdown

I've switched the default maximums to 64 domains, and 128 domain schedule entries. Would this be sufficient?

64 is still way too many, especially considering Microkit practically has a limit of about 64 PDs and already uses MCS, which reduces the need for domains.

I also strongly recommend reducing the number of priorities to something sane. E.g.

#define NUM_READY_QUEUES (CONFIG_NUM_DOMAINS * CONFIG_NUM_PRIORITIES)

and the same is true for ksReadyQueuesL2Bitmap.

If you reduce the number of priorities to 64 and have a default number of domains of 4, then you don't use much more memory than now without domains enabled.

@Kswin01

Kswin01 commented Mar 26, 2026

Copy link
Copy Markdown
Contributor Author

and already uses MCS, which reduces the need for domains.

I'm not so sure that MCS has much to do with reducing the need for domains. The main use case we have now is supporting ARINC like systems where domains represent independent subsystems that we want to temporally isolate from each other. We want to define a fixed schedule for these subsystems, MCS doesn't provide us that.

If you reduce the number of priorities to 64 and have a default number of domains of 4, then you don't use much more memory than now without domains enabled.

I won't comment on the reduction of priorities here as it's out of scope for this PR, but as said before the main current use case is using domains to represent independent subsystems, 4 is too limiting for that. KSU's simple examples already use ~11 domains.

I'll reduce the defaults to 32 domains and 64 domain schedule entries

@Kswin01
Kswin01 force-pushed the run_time_domains branch 2 times, most recently from 054ec74 to ba3f824 Compare March 30, 2026 04:30

@Indanz Indanz left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it make more sense to make idx_shift and domain_start attributes of domain_schedule instead? Adding it as an attribute to an entry makes no sense.

And I would also make it standard integers, not some custom weird hexadecimal format (assuming that's supported) which can't easily defined with XSD and is less user friendly anyway. If you don't support that already, then perhaps don't use characters that would be valid within hexadecimal, it's ambiguous now.

@Kswin01
Kswin01 force-pushed the run_time_domains branch 2 times, most recently from 3b992bb to d0ff46d Compare March 31, 2026 02:12
Comment thread docs/manual.md Outdated
Comment thread docs/manual.md Outdated
Comment thread docs/manual.md Outdated
Comment thread docs/manual.md Outdated
Comment thread docs/manual.md Outdated
Comment thread tool/microkit/src/capdl/builder.rs Outdated
Comment thread tool/microkit/src/main.rs Outdated

// The monitor is just a special PD
system_elfs.push(monitor_elf);
// Create a copy of the monitor elf for every domain in the system

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems a bit silly, but it works I suppose.

Why do you have a monitor per domain again? That's a bit unclear to me.

If monitors are merely fault handlers that handle unexpected faults, then having one in domain 0 would be nice, as you can separate it from the real-time code, so one task faulting doesn't affect other execution in that domain. They seem to do a lot of slow UART printing, and they're not passive, but at a higher priority it seems.

(They also convert a task to passive, but that's not needed with lazy SC unbind.)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should document this.

Comment thread tool/microkit/src/sdf.rs Outdated
Comment thread tool/microkit/src/sdf.rs Outdated
Comment thread tool/microkit/src/sel4.rs Outdated
@midnightveil

Copy link
Copy Markdown
Collaborator

I'm inclined to leave the number of domains at the verification default of 16.

(The verification is generic over the number of domains, right, though?)

@lsf37

lsf37 commented Jun 11, 2026

Copy link
Copy Markdown
Member

I'm inclined to leave the number of domains at the verification default of 16.

(The verification is generic over the number of domains, right, though?)

Yes, it can be configured to anything <=255.

@midnightveil midnightveil self-assigned this Jun 12, 2026
midnightveil added a commit to seL4/capdl that referenced this pull request Jul 14, 2026
In the work on [RFC-20] it was proposed that it would make sense
to remove the (micro)seconds-to-ticks conversions from seL4
and migrate all of (MCS) userspace APIs to be in ticks, to make dealing
with these calculations and their trickiness to instead be done
at user-level - and the tradeoffs one has to make to be no longer
kernel policy.

This PR demonstrates implements this for the capDL initialiser.
For other cases supported by the capDL specification, such
(MCS) budgets and periods currently specified in microseconds,
the capDL initialiser should be able to paper over the change
in kernel APIs without the users needing to deal with this
distinction.

These changes are motivated by the [Domain schedules] PR to the
seL4 Microkit, which creates a capDL specification for the
(Rust version) of the capDL initialiser to use. At the moment,
this PR is unable to be consistent about the values exposed
to the user for the units of the domain schedule: whilst we can
statically determine the us->ticks conversion for ARM/RISC-V
platforms, on x86 this is a runtime-known value only. Since
the spec is packed at system-build-time, we cannot therefore
specify x86 domain schedules in a consistent set of units.

The concrete specification changes from this PR is the addition
of a 'unit' suffix inside the domain schedule syntax. The
initialiser can then support either ticks (the default without
any units) or microseconds, and it performs a runtime conversion
of this value to ticks for the domain scheduler API.

Canonically, the end marker is left without units as (0, 0), but
we permit inputs of (0, 0 ticks) or (0, 0 us) for consistency.

Note: the meaning of a kernel tick is different between MCS and
non-MCS configurations.

One consequence of taking input in 'us' is that there are cases
where the input is unrepresentable in 'ticks'; and so this
introduces the possiblity of failure in the initialiser that
is unchecked at build-time.

For non-MCS, as kernel ticks are an integer multiple of 1 ms,
the conversion from us to ticks will fail if the conversion is
not exact. This is easy to avoid, one has to specify a 'us' value
that is a multiple of (CONFIG_TIMER_TICK_MS * 1000), something
which does not differ per platform.

For MCS, the kernel tick reflects the underlying timer frequency
(for the domain scheduler). Thus, there exists many frequencies
for which it is impossible to be exact in the conversion from
us to ticks. (those frequencies above 1 MHz, which is most
of them on modern 64-bit platforms). We guarantee a conversion
that is accurate to the nearest tick value, or failure otherwise.
This can be ±1 timer period. If you want to specify durations of
length close to the period between timer ticks, please use ticks
exactly. However, depending on kernel WCET domains of this
duration may not be schedulable.

[RFC-20]: seL4/rfcs#33
[Domain schedules]: seL4/microkit#445

Signed-off-by: Julia Vassiliki <julia.vassiliki@unsw.edu.au>
midnightveil added a commit to seL4/capdl that referenced this pull request Jul 15, 2026
In the work on [RFC-20] it was proposed that it would make sense
to remove the (micro)seconds-to-ticks conversions from seL4
and migrate all of (MCS) userspace APIs to be in ticks, to make dealing
with these calculations and their trickiness to instead be done
at user-level - and the tradeoffs one has to make to be no longer
kernel policy.

This PR demonstrates implements this for the capDL initialiser.
For other cases supported by the capDL specification, such
(MCS) budgets and periods currently specified in microseconds,
the capDL initialiser should be able to paper over the change
in kernel APIs without the users needing to deal with this
distinction.

These changes are motivated by the [Domain schedules] PR to the
seL4 Microkit, which creates a capDL specification for the
(Rust version) of the capDL initialiser to use. At the moment,
this PR is unable to be consistent about the values exposed
to the user for the units of the domain schedule: whilst we can
statically determine the us->ticks conversion for ARM/RISC-V
platforms, on x86 this is a runtime-known value only. Since
the spec is packed at system-build-time, we cannot therefore
specify x86 domain schedules in a consistent set of units.

The concrete specification changes from this PR is the addition
of a 'unit' suffix inside the domain schedule syntax. The
initialiser can then support either ticks (the default without
any units) or microseconds, and it performs a runtime conversion
of this value to ticks for the domain scheduler API.

Canonically, the end marker is left without units as (0, 0), but
we permit inputs of (0, 0 ticks) or (0, 0 us) for consistency.

Note: the meaning of a kernel tick is different between MCS and
non-MCS configurations.

One consequence of taking input in 'us' is that there are cases
where the input is unrepresentable in 'ticks'; and so this
introduces the possiblity of failure in the initialiser that
is unchecked at build-time.

For non-MCS, as kernel ticks are an integer multiple of 1 ms,
the conversion from us to ticks will fail if the conversion is
not exact. This is easy to avoid, one has to specify a 'us' value
that is a multiple of (CONFIG_TIMER_TICK_MS * 1000), something
which does not differ per platform.

For MCS, the kernel tick reflects the underlying timer frequency
(for the domain scheduler). Thus, there exists many frequencies
for which it is impossible to be exact in the conversion from
us to ticks. (those frequencies above 1 MHz, which is most
of them on modern 64-bit platforms). We guarantee a conversion
that is accurate to the nearest tick value, or failure otherwise.
This can be ±1 timer period. If you want to specify durations of
length close to the period between timer ticks, please use ticks
exactly. However, depending on kernel WCET domains of this
duration may not be schedulable.

[RFC-20]: seL4/rfcs#33
[Domain schedules]: seL4/microkit#445

Signed-off-by: Julia Vassiliki <julia.vassiliki@unsw.edu.au>
@midnightveil

Copy link
Copy Markdown
Collaborator

The original branch is here: https://github.com/au-ts/microkit/tree/archive/krishan-runtime-domains

I'm going to restart this branch and reorder a lot of the commits here, because upstream has changed enough it's annoying to rebase now.

@midnightveil
midnightveil marked this pull request as draft July 16, 2026 02:53
This is confusing with seL4 domains.

Signed-off-by: Julia Vassiliki <julia.vassiliki@unsw.edu.au>
@midnightveil
midnightveil marked this pull request as ready for review July 17, 2026 03:13
@midnightveil
midnightveil force-pushed the run_time_domains branch 2 times, most recently from 03185c4 to 54c9476 Compare July 17, 2026 03:24
Comment thread docs/manual.md
Comment on lines +1197 to +1199
* `duration`: The duration for which the domain should be active.
This contains a value and a unit, i.e. `1000 us`.
The unit can either be 'us' or 'ticks'.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a duration_unit or duration_type attribute in the domain_schedule element would be better than making this duration a value and a unit. It would make parsing easier and I can't imagine wanting a schedule that mixes units.

@midnightveil midnightveil Jul 18, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fair. My primary reason for doing it like this is that I like the way it reads better, that is, duration="1000" is confusing but duration="1000 us" reads like how you would write a value in English.

Parsing wise it's not that hard, not any more than I would have to do to add another attribute.

Not sure what's "best".

I can definitely see a few situations where for the most care you are OK with the us conversions but where some domains are shorter or you want exact control.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to leave it as is.

Comment thread docs/manual.md
Comment on lines +1164 to +1166
number of schedule entries, determined by the kernel configurations `KernelNumDomains`
and `KernelNumDomainSchedules`. In the default SDK build, we support 16 domains and
100 schedule entries.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably makes more sense in the seL4 manual, but it would be nice to know the actual memory trade-off of increasing these numbers.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Memory usage of various data structures scales by the number of priorities multiplied by the number of domains. If you want to reduce the memory usage, reducing the number of priories from 256 to something more sane is the best way of doing it. So if you reduce the number of priorities to 16, you can have 16 domains with the same memory usage as no domains and 256 priorities.

KernelNumDomainSchedules doesn't affect anything else, it uses 8 bytes per entry. I picked 100 as a default to make it clear there that the number is arbitrary and has no special power-of-two properties, while being large enough for most practical purposes. Microkit uses 100 because that is the kernel default.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the info.

@dreamliner787-9 dreamliner787-9 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just nitpicks from me.

Comment thread docs/manual.md Outdated
Comment thread docs/manual.md Outdated
Comment on lines +1188 to +1189
* `index_shift` (optional) the offset of the Microkit domain schedule within
the kernel's domain schedule array.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please elaborate on this a bit more? As someone who isn't familiar with the domain scheduler I don't understand why we need this. If index_shift is optional then how does the offset of the domain schedule within the kernel's data structure get worked out, is it just equals to the ID or something? Is this similar to the "index" in the RFC?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The initialiser adds this shift to all the other numbers. It's something that was added in the initialiser. I don't actually know why this was added.

@midnightveil midnightveil Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, this was why: seL4/capdl#81 (comment)

... which, now that I think about it, means it should default to 1, which the rust-seL4 work doesn't do.

EDIT: no, nevermind it shouldn't

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(done, updated the comment too)

@midnightveil midnightveil Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, unless we give the domain cap to any PD, I think these are both useless from the Microkit side.

(but we should also rename start_index/whatever to the set_start etc)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose I will keep them.

Comment on lines +1235 to +1237
#[cfg(test)]
mod domains {
use super::*;

@dreamliner787-9 dreamliner787-9 Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have time to add tests for valid cases too? Especially cases that are not covered by the example, e.g. specifying ticks instead of us, etc

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

invalid shifts and stuff is tested. I don't have heaps of time right now.

midnightveil added a commit to seL4/capdl that referenced this pull request Jul 20, 2026
In the work on [RFC-20] it was proposed that it would make sense
to remove the (micro)seconds-to-ticks conversions from seL4
and migrate all of (MCS) userspace APIs to be in ticks, to make dealing
with these calculations and their trickiness to instead be done
at user-level - and the tradeoffs one has to make to be no longer
kernel policy.

This PR demonstrates implements this for the capDL initialiser.
For other cases supported by the capDL specification, such
(MCS) budgets and periods currently specified in microseconds,
the capDL initialiser should be able to paper over the change
in kernel APIs without the users needing to deal with this
distinction.

These changes are motivated by the [Domain schedules] PR to the
seL4 Microkit, which creates a capDL specification for the
(Rust version) of the capDL initialiser to use. At the moment,
this PR is unable to be consistent about the values exposed
to the user for the units of the domain schedule: whilst we can
statically determine the us->ticks conversion for ARM/RISC-V
platforms, on x86 this is a runtime-known value only. Since
the spec is packed at system-build-time, we cannot therefore
specify x86 domain schedules in a consistent set of units.

The concrete specification changes from this PR is the addition
of a 'unit' suffix inside the domain schedule syntax. The
initialiser can then support either ticks (the default without
any units) or microseconds, and it performs a runtime conversion
of this value to ticks for the domain scheduler API.

Canonically, the end marker is left without units as (0, 0), but
we permit inputs of (0, 0 ticks) or (0, 0 us) for consistency.

Note: the meaning of a kernel tick is different between MCS and
non-MCS configurations.

One consequence of taking input in 'us' is that there are cases
where the input is unrepresentable in 'ticks'; and so this
introduces the possiblity of failure in the initialiser that
is unchecked at build-time.

For non-MCS, as kernel ticks are an integer multiple of 1 ms,
the conversion from us to ticks will fail if the conversion is
not exact. This is easy to avoid, one has to specify a 'us' value
that is a multiple of (CONFIG_TIMER_TICK_MS * 1000), something
which does not differ per platform.

For MCS, the kernel tick reflects the underlying timer frequency
(for the domain scheduler). Thus, there exists many frequencies
for which it is impossible to be exact in the conversion from
us to ticks. (those frequencies above 1 MHz, which is most
of them on modern 64-bit platforms). We guarantee a conversion
that is accurate to the nearest tick value, or failure otherwise.
This can be ±1 timer period. If you want to specify durations of
length close to the period between timer ticks, please use ticks
exactly. However, depending on kernel WCET domains of this
duration may not be schedulable.

[RFC-20]: seL4/rfcs#33
[Domain schedules]: seL4/microkit#445

Signed-off-by: Julia Vassiliki <julia.vassiliki@unsw.edu.au>
@midnightveil
midnightveil force-pushed the run_time_domains branch 3 times, most recently from 40926c0 to ff567d9 Compare July 20, 2026 07:49
midnightveil added a commit to seL4/capdl that referenced this pull request Jul 20, 2026
In the work on [RFC-20] it was proposed that it would make sense
to remove the (micro)seconds-to-ticks conversions from seL4
and migrate all of (MCS) userspace APIs to be in ticks, to make dealing
with these calculations and their trickiness to instead be done
at user-level - and the tradeoffs one has to make to be no longer
kernel policy.

This PR demonstrates implements this for the capDL initialiser.
For other cases supported by the capDL specification, such
(MCS) budgets and periods currently specified in microseconds,
the capDL initialiser should be able to paper over the change
in kernel APIs without the users needing to deal with this
distinction.

These changes are motivated by the [Domain schedules] PR to the
seL4 Microkit, which creates a capDL specification for the
(Rust version) of the capDL initialiser to use. At the moment,
this PR is unable to be consistent about the values exposed
to the user for the units of the domain schedule: whilst we can
statically determine the us->ticks conversion for ARM/RISC-V
platforms, on x86 this is a runtime-known value only. Since
the spec is packed at system-build-time, we cannot therefore
specify x86 domain schedules in a consistent set of units.

The concrete specification changes from this PR is the addition
of a 'unit' suffix inside the domain schedule syntax. The
initialiser can then support either ticks (the default without
any units) or microseconds, and it performs a runtime conversion
of this value to ticks for the domain scheduler API.

Canonically, the end marker is left without units as (0, 0), but
we permit inputs of (0, 0 ticks) or (0, 0 us) for consistency.

Note: the meaning of a kernel tick is different between MCS and
non-MCS configurations.

One consequence of taking input in 'us' is that there are cases
where the input is unrepresentable in 'ticks'; and so this
introduces the possiblity of failure in the initialiser that
is unchecked at build-time.

For non-MCS, as kernel ticks are an integer multiple of 1 ms,
the conversion from us to ticks will fail if the conversion is
not exact. This is easy to avoid, one has to specify a 'us' value
that is a multiple of (CONFIG_TIMER_TICK_MS * 1000), something
which does not differ per platform.

For MCS, the kernel tick reflects the underlying timer frequency
(for the domain scheduler). Thus, there exists many frequencies
for which it is impossible to be exact in the conversion from
us to ticks. (those frequencies above 1 MHz, which is most
of them on modern 64-bit platforms). We guarantee a conversion
that is accurate to the nearest tick value, or failure otherwise.
This can be ±1 timer period. If you want to specify durations of
length close to the period between timer ticks, please use ticks
exactly. However, depending on kernel WCET domains of this
duration may not be schedulable.

[RFC-20]: seL4/rfcs#33
[Domain schedules]: seL4/microkit#445

Signed-off-by: Julia Vassiliki <julia.vassiliki@unsw.edu.au>
midnightveil and others added 8 commits July 21, 2026 11:06
This gives us new version of rust-seL4 with the domain
unit changes.

Signed-off-by: Julia Vassiliki <julia.vassiliki@unsw.edu.au>
Having a domain scheduler causes no issues if the domain
scheduler is unused, so we can turn it on on non-smp
configurations. (seL4 does not support SMP domain-scheduler).

Signed-off-by: Julia Vassiliki <julia.vassiliki@unsw.edu.au>
These will be useful later.

Signed-off-by: Julia Vassiliki <julia.vassiliki@unsw.edu.au>
This adds a new syntax which looks like this:

    <domains>
        <domain name="monitor" id="0" />
        <domain name="emitter" id="1" />
        <domain name="collector" />

        <domain_schedule start_index="3" index_shift="0" >
            <schedule_entry domain="monitor" duration="3000 us" />
            <schedule_entry domain="emitter" duration="8000 us" />
            <schedule_entry domain="collector" duration="2000 us" />
            <schedule_entry domain="monitor" duration="3000 us" />
        </domain_schedule>
    </domains>

Co-authored-by: Krishnan Winter <krishnan.winter@unsw.edu.au>
Signed-off-by: Julia Vassiliki <julia.vassiliki@unsw.edu.au>
When we have a domain schedule specified, this is also
required.

Co-authored-by: Krishnan Winter <krishnan.winter@unsw.edu.au>
Signed-off-by: Julia Vassiliki <julia.vassiliki@unsw.edu.au>
This is equivalent to the CAmkES domains example which
has an emitter and collector.

Signed-off-by: Julia Vassiliki <julia.vassiliki@unsw.edu.au>
Mostly just error cases :)

Plus some extra cases in the code.

Co-authored-by: Krishnan Winter <krishnan.winter@unsw.edu.au>
Signed-off-by: Julia Vassiliki <julia.vassiliki@unsw.edu.au>
We describe the syntax and the reference separately.

Co-authored-by: Krishnan Winter <krishnan.winter@unsw.edu.au>
Signed-off-by: Julia Vassiliki <julia.vassiliki@unsw.edu.au>
@midnightveil

Copy link
Copy Markdown
Collaborator

I've tested this works locally on RISCV, AArch64, and x86 so once CI is done I will merge this.

@midnightveil
midnightveil merged commit 5a719bb into seL4:main Jul 21, 2026
12 checks passed
@midnightveil
midnightveil deleted the run_time_domains branch July 21, 2026 01:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants