|
|
Log in / Subscribe / Register

Extending the time-slice-extension discussion

Ready to give LWN a try?

With a subscription to LWN, you can stay current with what is happening in the Linux and free-software community and take advantage of subscriber-only site features. We are pleased to offer you a free trial subscription, no credit card required, so that you can see for yourself. Please, join us!

By Jonathan Corbet
September 18, 2025
Time-slice extension is a proposed scheduler feature that would allow a user-space process to request to not be preempted for a short period while it executes a critical section. It is an idea that has been circulating for years, but efforts to implement it became more serious in February of this year. The latest developer to make an attempt at time-slice extension is Thomas Gleixner, who has posted a new patch set with a reworked API. Chances are good that this implementation is close to what will actually be adopted by the kernel.

Imagine a user-space thread that holds a (user-space) spinlock; if that thread is preempted by another thread which subsequently attempts to acquire that same lock, the preempting thread will spin indefinitely, having displaced the thread that could release the lock. That is not a path toward optimal performance. If, though, the lock-holding thread could ask to not be preempted while holding the lock, this scenario could be avoided; time-slice extension is meant to at least give that thread a chance to run long enough to finish its work before being kicked out of the CPU.

The February implementation of this work was by Steve Rostedt; after that work bogged down, an implementation by Prakash Sangappa was considered for a while, but also ran into disagreements over its API and implementation details. Gleixner's work is an attempt to create a version that will pass muster. To get there, he first had to rework the restartable sequences implementation to address a number of problems he found there; a new version of that series was posted on September 8.

In this version of the time-slice-extension API, a process must explicitly enable the feature before attempting to use it. That is done with a new prctl() command named PR_RSEQ_SLICE_EXTENSION_SET. This requirement serves two purposes: it allows the kernel to avoid some overhead for the (presumed) majority of processes that do not use time-slice extension, and a process that can use the feature will get an indication of whether the kernel actually supports it. There is also a new PR_RSEQ_SLICE_EXTENSION_GET command to query whether the feature is currently enabled for the calling process.

Like most of its predecessors, Gleixner's implementation uses the restartable sequences infrastructure, including its block of memory shared between user space and the kernel, in the implementation of the new feature. This time around, though, the rseq structure stored in that block is given a new field:

    __u32 slice_ctrl;

To request a time-slice extension, a thread sets the RSEQ_SLICE_EXT_REQUEST bit in that field; a simple assignment is sufficient, there is no need for any special atomic operations. If that thread is then interrupted, the kernel will handle whatever task caused the interruption; before returning to user space, the kernel checks whether anything has happened that would normally cause the current thread to be scheduled out. If the thread is marked for rescheduling, it would normally lose access to the CPU; if, however, the request bit is set in slice_ctrl, the conditions are right, and the kernel is in a good mood, it will consider giving that thread just a bit more time before the eviction happens.

About that timer: the setting of the 30µs high-resolution timer is one of the most expensive parts of implementing this feature, since it requires reprogramming the hardware. The kernel reduces that overhead by looking at the amount of time left in the thread's time slice; if it exceeds the grant period, the timer will not be set.
Whether the conditions are right depends on a few things, including the reason why rescheduling the process is called for; the kernel is unlikely to grant an extension if a high-priority realtime process needs the CPU, for example. Whether or not the extension is allowed, the kernel will clear the RSEQ_SLICE_EXT_REQUEST bit in slice_ctrl to inform the thread that it was interrupted. If the kernel does decide to grant the extension, it will, before returning to user space, set the RSEQ_SLICE_EXT_GRANTED bit in slice_ctrl and set a timer for (by default) 30µs.

The user-space thread should run its critical section and, at completion, check the request bit with an atomic test-and-clear operation. If that bit had already been cleared by the kernel, the thread will know that an interrupt has happened. In that case, it must check for RSEQ_SLICE_EXT_GRANTED to see whether it is running on borrowed time; if so, it must immediately call the new rseq_slice_yield() system call to tell the kernel that the critical section is complete. That call is likely to result in the rescheduling of the thread.

There are a few things that a potential user of this feature should be aware of. One is that the extension grant can be revoked if the kernel changes its mind; that will happen unconditionally if the thread is interrupted again while running under the extension grant. In that case, the RSEQ_SLICE_EXT_GRANTED bit will be cleared, and the thread does not have to call rseq_slice_yield(). If the thread runs its critical section to completion, though, some care must be taken to not run afoul of the kernel's rules. The rseq_slice_yield() call is not optional; if the kernel's 30µs timer expires before that call is made, the grant will be ended and the thread rescheduled. A more severe fate awaits any thread that makes any system call other than rseq_slice_yield() while running with an extension grant; that thread will be terminated immediately.

Rostedt's February version only worked if the scheduler was running in the lazy preemption mode; that limitation was controversial at the time. Gleixner's implementation does not have that limitation and, in fact, will explicitly not work as well when lazy preemption is in use, especially on realtime kernels, where preemption can happen anywhere in the kernel and not just at the point of return to user space. Implementing time-slice extension in that environment would add to the overhead of the feature, whether it is in use or not. In the earlier discussions, there had been talk of making time-slice extension available to realtime processes as well, but that is not a part of this series.

The API for this feature may not yet be entirely set in stone. Mathieu Desnoyers had a couple of requests, the first of which was to allow the user-space critical sections to be nested, so that a second critical section could be entered while already running within a critical section. That would require turning the RSEQ_SLICE_EXT_REQUEST into a counter informing the kernel of just how many nested critical sections were being executed at any given time. Gleixner pondered the request for a bit before concluding that nesting should be implemented in user space, if at all.

The second request was to allow any system call to signal an end to the critical section, rather than killing the thread. Otherwise, he said, the feature would see far fewer users: "Handling syscall within granted extension by killing the process will likely reserve this feature to the niche use-cases". Gleixner rejected that reasoning, saying: "Having this used only by people who actually know what they are doing is actually the preferred outcome". Even so, he had previously said that he would be willing to consider allowing any system call as a way to end the critical section, but that it would require changing the system-call entry code in a way that he feared might not be acceptable to the scheduler developers (who have not yet expressed an opinion on the subject).

While there may be discussion about the details for a while, it seems likely that the eventual API for time-slice extension will be fairly close to what has been described here. The feature could perhaps be ready to merging within a couple of development cycles. This being the kernel community, though, there is always a chance that somebody will make a request to extend the discussion beyond its natural end.

Index entries for this article
KernelReleases/7.0
KernelScheduler/Time-slice extension


to post comments

prctl

Posted Sep 19, 2025 7:19 UTC (Fri) by magfr (subscriber, #16052) [Link]

Another feature that the mandatory call to prctl adds is that it gives security modules a good place to say no.
Having prctl return EPERM is far more likely to be handled gracefully by the application than never getting the timer extension.

No atomic operations?

Posted Sep 19, 2025 8:24 UTC (Fri) by matthias (subscriber, #94967) [Link] (2 responses)

> To request a time-slice extension, a thread sets the RSEQ_SLICE_EXT_REQUEST bit in that field; a simple assignment is sufficient, there is no need for any special atomic operations.

How can this work with a simple assignment? Without any memory barriers, the CPU as well as the compiler is free to reorder the write (e.g. do the write after the critical section). Maybe you do not need atomic instructions, but at least some kind of memory barrier has to be there. Or am I missing something?

No atomic operations?

Posted Sep 19, 2025 10:26 UTC (Fri) by kkdwivedi (guest, #130744) [Link]

You would just need a compiler barrier at most to ensure the write is done before you do anything that depends on the time slice extension, since the set bit is observed on the same CPU, so program order is guaranteed.

No atomic operations?

Posted Sep 19, 2025 11:31 UTC (Fri) by tglx (subscriber, #31301) [Link]

You need a compiler barrier after the store so the compiler cannot reorder.

On the CPU the ordering is only required against an interrupt. The context switch in the CPU on interrupt acts as a sufficient barrier. That means the outstanding write is drained before the slice extension code can read.

Proxy Execution

Posted Sep 20, 2025 9:50 UTC (Sat) by mmechri (subscriber, #95694) [Link] (2 responses)

Thanks for the write-up.

How does this compare to/interact with the ongoing work on Proxy Execution?

Proxy Execution

Posted Sep 21, 2025 20:25 UTC (Sun) by willy (subscriber, #9762) [Link] (1 responses)

It's totally different. Proxy Execution is "that CPU is holding the lock we need; send this work over there for that CPU to do when it drops the lock" (on the basis that it is cache line hot). This is "I am holding the lock, let me just finish what I'm doing and I'll release it".

I suppose it'll make Proxy Execution less useful, which is probably a good thing.

Proxy Execution

Posted Sep 22, 2025 9:46 UTC (Mon) by mmechri (subscriber, #95694) [Link]

> Proxy Execution is "that CPU is holding the lock we need; send this work over there for that CPU to do when it drops the lock" (on the basis that it is cache line hot).

That is not my understanding of Proxy Execution. Rather, I understand it as “That task holds the lock I need, so I’m going to donate to it some of my CPU time so it hopefully releases the lock sooner”.

if the spin lock is spin_lock_irqsave/spin_lock_irqsave in the thread, how to preempt this thread?

Posted Oct 3, 2025 0:42 UTC (Fri) by yanjun.zhu (guest, #173290) [Link]

Then the snippet should be safe? Honestly I can not get how to preempt if both interrupt and preempt are disabled.


Copyright © 2025, Eklektix, Inc.
This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds