IPC Fastpath

The IPC fastpath is an optimized dispatch path for the two most common IPC patterns: Call (syscall 2) and ReplyRecv (syscall 3). The assembly syscall entry point checks the syscall number and, for Call or ReplyRecv, jumps directly to a Rust fastpath function before the full ABI translation runs. If the fastpath cannot handle the operation, it returns a status code that causes the assembly stub to fall through to the normal slowpath.

Scope

The fastpath handles only simple, short messages between two threads on the same CPU with no capability transfer. All other cases take the slowpath, which supports the full IPC feature set.

Fastpath

Call, ReplyRecv, ReplyRecvAny

Slowpath

All 31 syscalls including Send, Recv, NBSend, timed variants, multi-endpoint variants, and any Call/ReplyRecv that fails fastpath eligibility

Eligibility Conditions

The Call fastpath handles the operation only when all of the following hold:

  1. extra_caps == 0 — no capabilities to transfer.

  2. Message length ⇐ 4 — the entire message fits in registers (MR0-MR3).

  3. The resolved capability is an Endpoint with CALL right.

  4. A receiver is already waiting in RecvBlocked state on the endpoint.

  5. The receiver is locally stable on the current CPU (not migrating, not in another CPU’s ready queue).

  6. The receiver has a valid VSpace root and kernel stack.

If any condition fails, the fastpath returns FastpathResult::slowpath() and the assembly stub falls through to the full dispatch path.

The ReplyRecv fastpath has analogous conditions for the reply and the subsequent receive.

Diagram

Register ABI

On x86_64, the fastpath functions receive arguments directly from the syscall registers:

Register Argument Content

rdi

cap_ptr

Capability address (endpoint slot)

rsi

msg_info

Packed message info word

rdx

mr0

Message register 0

rcx

mr1

Message register 1

r8

mr2

Message register 2

r9

mr3

Message register 3

Return value (FastpathResult):

#[repr(C)]
pub struct FastpathResult {
    pub status: u64,  // 1 = handled, 0 = fall through to slowpath
    pub value: u64,   // badge (for ReplyRecv), 0 (for Call)
}

The assembly stub checks status: if 1, it returns directly to userspace with the result. If 0, it proceeds to the full syscall_handle_rust() entry point.

User RSP is saved on the per-thread kernel stack, not in per-CPU %gs:16. The per-CPU slot is shared state overwritten by other threads' syscalls and is not used for RSP preservation in the fastpath.

Lock Protocol

The fastpath acquires locks in the same order as the slowpath to avoid deadlock:

  1. CAP_LOCK — capability copy-to-stack, then released. The endpoint capability is copied to a stack-local variable under CAP_LOCK; the lock is released immediately after. This prevents torn reads without holding CAP_LOCK across the IPC operation.

  2. SCHED_IPC_LOCK — covers both the IPC operation and scheduler enqueue/context switch. Acquired after CAP_LOCK is released.

  3. scheduler.lock_state — per-CPU scheduler state (acquired under SCHED_IPC_LOCK). Also referred to as sched.lock_cpu in some source comments.

The fastpath never holds CAP_LOCK and SCHED_IPC_LOCK simultaneously.

Message Transfer

In the fastpath, message transfer is register-to-register:

  • MR0-MR3 are written directly from the sender’s registers into the receiver’s register save area.

  • No IPC buffer access is required (since length ⇐ 4 and extra_caps == 0).

  • The badge is delivered through the receiver’s badge register.

This avoids the memory accesses that the slowpath requires for IPC buffer overflow and capability transfer.

Multi-Endpoint Fastpath

ReplyRecvAny has a fastpath variant that handles the common case where only one of the registered endpoints has a ready sender. The same eligibility conditions apply: short message, no cap transfer, receiver locally stable.

Performance Characteristics

The fastpath eliminates:

  • Full ABI register save/restore (only the necessary registers are saved)

  • Message info unpacking and validation (checked inline)

  • IPC buffer resolution and memory copy (registers only)

  • Capability transfer machinery (no caps)

The result is a direct sender-to-receiver thread switch with minimal kernel overhead. This is the critical performance path for microkernel IPC, as every client-server interaction in SaltyOS passes through endpoints.

Bailout Conditions and Edge Cases

Receiver Is Fault-Blocked

If the receiver is in FaultBlocked state (waiting for a fault handler to reply), the fastpath bails to slowpath. A faulted thread cannot act as a ready receiver for an IPC rendezvous.

Slowpath Fallback Is Transparent

When the fastpath bails (returns FastpathResult::slowpath()), the assembly stub falls through to syscall_handle_rust(), which handles the operation identically to a direct slowpath entry. The caller never knows which path executed — the ABI contract is identical.

Capability Lookup Under CAP_LOCK

The fastpath copies the endpoint capability to a stack-local variable under CAP_LOCK, then releases the lock. If another CPU concurrently deletes or modifies the capability between the copy and the endpoint lock acquisition, the stack-local copy remains valid — it was copied by value. The endpoint object itself is refcounted and cannot be freed while the capability exists.

Receiver Migrated Between CPUs

If the receiver’s last_cpu differs from the current CPU, fastpath_waiter_is_local_stable() returns false and the fastpath bails. This prevents the fastpath from switching to a thread whose context was last saved on a different CPU and may not yet be fully flushed.

Receiver Has Pending Enqueue

If the receiver is in a pending_enqueue slot on any CPU (being drained after a prior context switch), the fastpath bails. The slowpath handles this by routing through the normal scheduler enqueue path.

Multi-Level CSpace

The fastpath handles both flat (depth == 0) and multi-level (depth != 0) CSpace lookups via lookup_capability(). No bail is needed for multi-level CSpaces — resolution depth is bounded by MAX_RESOLVE_DEPTH (8).

  • Endpoints — full IPC operations and slowpath semantics

  • Notifications — async signaling (used alongside endpoints)

  • Architecture — syscall entry assembly and lock ordering