|Index|Managing I/O and CPU Scheduling with the RT Kernel
SUSE Linux 16.1

Managing I/O and CPU Scheduling with the RT Kernel

Publication Date: 05 Nov 2026

I/O and CPU scheduling using the RT kernel in SUSE Linux Enterprise Server optimizes resource allocation to ensure deterministic execution for latency-sensitive tasks. By configuring block device schedulers, assigning process I/O priorities with ionice and isolating CPU cores using systemd and taskset, administrators prevent disk starvation and latency spikes. This coordination guarantees predictable response times and maximum system throughput for mission-critical real-time applications.

Prerequisites

  • Installed RT kernel on SUSE Linux Enterprise Server

  • Access to a command-line terminal with systemd, taskset, chrt and ionice available

1 How does I/O scheduling work?

Handling I/O requests predictably is critical for deterministic performance in real-time systems. High CPU availability yields limited benefits if tasks stall while awaiting disk access.

The Linux storage subsystem organizes process disk access into three distinct scheduling classes:

Real-time (class 1)

Grants highest priority for disk access. Requests in this class are served before any other class. Within the real-time class, priority is fine-tuned with nice levels from 0 (highest priority) to 7 (lowest priority).

Warning
Warning: Risk of disk bandwidth starvation

Assigning real-time I/O priorities incorrectly can deprive other system processes of disk bandwidth.

Assign real-time I/O class only to thoroughly tested critical tasks.

Best-effort (class 2)

The default scheduling class for standard system processes. Priority is derived from the process CPU nice value (ranging from 0 to 7).

Idle (class 3)

Processes receive disk access only when no other process requests disk I/O. Ideal for background workloads such as logging or maintenance.

2 How to manage process I/O priorities with ionice?

Inspect and adjust process I/O scheduling classes and priority levels using the ionice utility.

Procedure 1: Setting I/O priorities with ionice

To view or modify process I/O scheduling attributes, perform the following steps.

  1. To set the I/O scheduling class of the current shell to idle, run:

    > ionice -c3 -p$$
  2. To print the I/O scheduling class and priority of the current shell, run ionice without additional parameters:

    > ionice
  3. To set the I/O scheduling class of the process with the process ID 42 to real-time and its priority level to 2, run:

    > sudo ionice -c1 -p42 -n2
  4. To start the Bash shell in the idle class, run:

    > ionice -c3 /bin/bash

3 Changing the block device I/O scheduler

Display and change the I/O scheduler of a block device.

The Linux I/O schedulers
none

The none scheduler is a simple scheduler that performs no reordering of I/O requests. This scheduler is mainly used for specialized environments that run their own schedulers optimized for the used hardware, such as fast NVMe storage, storage systems or hardware RAID controllers.

mq-deadline

The main point of deadline scheduling is to try hard to answer a request before a given deadline. This results in good I/O for a random single I/O in real-time environments.

In principle, the mq-deadline scheduler uses two lists with all requests. One is sorted by block sequences to reduce seeking latencies, the other is sorted by expire times for each request. Normally, requests are served according to the block sequence, but if a request reaches its deadline, the scheduler starts to work on this request.

bfq

The Budget Fair Queueing (BFQ) scheduler uses a separate I/O queue for each process and tries to divide the bandwidth fairly between all requesting processes.

Procedure 2: Displaying and changing the I/O scheduler
  1. To print the current scheduler of a block device such as /dev/sda, use the following command:

    > cat /sys/block/sda/queue/scheduler
    none [mq-deadline] bfq

    The scheduler in square brackets is the active one. In this case, the scheduler for /dev/sda is set to mq-deadline.

  2. To change the scheduler, write one of the listed names to /sys/block/DEVICE/queue/scheduler. For example, to set the I/O scheduler of the device /dev/sda to none, use the following command as root:

    # echo "none" > /sys/block/sda/queue/scheduler

To set other variables in the /sys/ file system, use a similar approach.

4 Tuning block device I/O scheduler parameters

Fine-tune operational parameters of active block device schedulers in sysfs to optimize read and write latency for real-time applications.

All schedulers, except for the none scheduler, have several common parameters that may be tuned for each block device. You can access these parameters with sysfs in the /sys/block/DEVICE/queue/iosched/ directory. The following parameters are tunable for the respective scheduler:

The mq-deadline scheduler
read_expire

The main focus of this scheduler is to limit the start latency for a request to a given time. Therefore, for each request, a deadline is calculated from the current time plus the value of read_expire in milliseconds.

write_expire

Similar to read_expire for write requests.

fifo_batch

If a request hits its deadline, it is necessary to move the request from the sorted I/O scheduler list to the dispatch queue. The variable fifo_batch controls how many requests are moved, depending on the cost of each request.

front_merges

The scheduler normally tries to find contiguous I/O requests and merges them. There are two kinds of merges: The new I/O request may be in front of the existing I/O request (front merge), or it may follow behind the existing request (back merge). Most merges are back merges. Therefore, you can disable the front merge functionality by setting front_merges to 0.

writes_starved

If certain read or write requests hit their deadline, the scheduler prefers the read requests by default. To prevent write requests from being postponed forever, the variable writes_starved controls how often read requests are preferred until write requests are preferred over read requests.

The bfq scheduler
back_seek_max and back_seek_penalty

The scheduler normally dispatches requests in a strict ascending order. When needed, it also allows small backward seeks, but it applies a penalty on them. The maximum backward sector seek is defined with back_seek_max, and the multiplier for the penalty is set by back_seek_penalty.

fifo_expire_async and fifo_expire_sync

The fifo_expire_* variables define the timeout in milliseconds for asynchronous and synchronous I/O requests. Set fifo_expire_sync lower than fifo_expire_async to prioritize synchronous requests over asynchronous operations.

slice_idle and slice_idle_us

Defines how long the I/O scheduler idles before servicing the next thread to maintain sequential I/O locality. slice_idle specifies this time in milliseconds, while slice_idle_us specifies it in microseconds.

5 CPU partitioning and affinity

Understand how systemd resource control partitions CPUs to isolate real-time workloads.

SUSE Linux Enterprise Server partitions CPUs with the cpuset controller of the unified cgroups v2 hierarchy, which you manage through systemd. You assign CPUs to slices, services and scopes with the AllowedCPUs= property. The setting can be persistent or transient.

A unit can only use the CPUs that its parent slice allows. Therefore, to dedicate CPUs to real-time workloads, restrict the primary system slices (such as system.slice and user.slice) to the remaining CPUs. The affinity of an individual process, set with taskset, is limited in the same way.

For a complete shielding procedure, refer to the article Shielding Linux Resources on SUSE Linux Enterprise Server.

6 Managing CPU shielding with systemd, taskset and chrt

Isolate real-time workloads on dedicated logical CPUs using systemd resource control, taskset and chrt.

Procedure 3: Configuring CPU isolation for real-time workloads

To restrict general processes to CPUs 0 to 2 and dedicate CPU 3 to a critical task, perform the following steps:

  1. Restrict the system slices to CPUs 0-2. The setting is persistent. To apply it only until the next reboot, add the --runtime option.

    > sudo systemctl set-property system.slice AllowedCPUs=0-2
    > sudo systemctl set-property user.slice AllowedCPUs=0-2
    > sudo systemctl set-property init.scope AllowedCPUs=0-2
  2. To run an application in its own slice and restrict it to CPU 3, use a transient scope:

    > sudo systemd-run --scope --slice=realtime.slice --property=AllowedCPUs=3 APPLICATION
  3. To print the CPU affinity of the application started in the previous step, use taskset with its process ID PID:

    > taskset -c -p PID

    To change the affinity, run the command as root. The CPU list must be within the CPUs that the slice of the process allows. In this example, that is CPU 3:

    > sudo taskset -c -p 3 PID
  4. To assign the SCHED_FIFO real-time scheduling policy with the priority 40 to the process, run:

    > sudo chrt --fifo -p 40 PID

7 Multi-queue I/O schedulers and key parameters

This section provides an overview of the available multi-queue block device I/O schedulers and their sysfs tuning parameters.

7.1 Available multi-queue I/O schedulers

Table 1: Multi-queue I/O schedulers
SchedulerTypical useDescription
noneHigh-performance NVMe SSDsPerforms no reordering of requests and passes them directly to the hardware queues.
mq-deadlineSATA, SAS and traditional HDDsThe system default for most block devices. Tries to serve each request before its deadline to limit latency spikes and prevent write starvation.
bfqDesktop environments and I/O-bound cgroupsDivides storage bandwidth proportionally between requesting processes or cgroups to maintain interactive responsiveness under heavy background loads.

7.2 Key sysfs parameters

Table 2: mq-deadline parameters
ParameterUnitDescription
read_expireMillisecondsDeadline for starting to serve a read request.
write_expireMillisecondsDeadline for starting to serve a write request.
fifo_batchRequestsNumber of requests moved to the dispatch queue at once when a request hits its deadline.
front_mergesFlag (0 or 1)Enables or disables front merges of contiguous requests.
writes_starvedCountHow many times read requests are preferred before a pending write request is served.

8 For more information