mirror of
https://github.com/nestriness/nestri.git
synced 2026-09-26 12:32:25 +03:00
Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
209 lines
11 KiB
Plaintext
209 lines
11 KiB
Plaintext
# Kconfig fragment for the guest kernel.
|
|
#
|
|
# Merged onto the tree's config by scripts/kernel-build.sh with the kernel's own
|
|
# merge_config.sh, then resolved with `make olddefconfig`, then checked: every
|
|
# entry below must hold in the final .config or the build stops. A fragment
|
|
# rather than a saved full config: a full .config pins thousands of symbols and
|
|
# rots the moment the tree is bumped, while this says only what the guest
|
|
# actually requires and lets the version's own defaults handle the rest.
|
|
#
|
|
# Every entry here is load-bearing or a deliberate performance choice. Nothing
|
|
# is speculative.
|
|
|
|
# ── Gaming needs ─────────────────────────────────────────
|
|
# NTSYNC is a great improvement over fsync and esync approaches.
|
|
CONFIG_NTSYNC=y
|
|
|
|
# ── Timers ───────────────────────────────────────────────
|
|
# The one that cost a day of silent audio. The guest has no sound hardware, so
|
|
# PipeWire drives its whole graph off a timerfd at a 2.67ms cycle, which a
|
|
# HZ_PERIODIC/250 kernel cannot express. Symptom: everything reports healthy,
|
|
# neswire sends a steady ~3 kbps of Opus, and every sample is zero.
|
|
CONFIG_HIGH_RES_TIMERS=y
|
|
CONFIG_NO_HZ_IDLE=y
|
|
CONFIG_HZ_1000=y
|
|
|
|
# ── Shared memory ────────────────────────────────────────
|
|
# PipeWire moves audio through memfd, not through its socket. Wine uses POSIX
|
|
# shm and semaphores directly. /dev/shm itself is mounted by nesinit --
|
|
# devtmpfs does not provide it.
|
|
CONFIG_SHMEM=y
|
|
CONFIG_MEMFD_CREATE=y
|
|
CONFIG_TMPFS=y
|
|
CONFIG_DEVTMPFS=y
|
|
CONFIG_DEVTMPFS_MOUNT=y
|
|
|
|
# ── What PipeWire schedules on ───────────────────────────
|
|
# FUTEX_PI is also what Proton's fsync uses.
|
|
CONFIG_TIMERFD=y
|
|
CONFIG_EVENTFD=y
|
|
CONFIG_EPOLL=y
|
|
CONFIG_SIGNALFD=y
|
|
CONFIG_FUTEX=y
|
|
CONFIG_FUTEX_PI=y
|
|
|
|
# ── Clock sources ────────────────────────────────────────
|
|
# Listed because they were correct throughout the silent-audio episode. A good
|
|
# clock *source* says nothing about whether the kernel can schedule a timer
|
|
# *event* at the resolution asked for; only the block above governs that.
|
|
CONFIG_KVM_GUEST=y
|
|
CONFIG_PARAVIRT_CLOCK=y
|
|
CONFIG_X86_TSC=y
|
|
|
|
# ── Steal time ───────────────────────────────────────────
|
|
# Time the host spends running something else on a vCPU is subtracted from
|
|
# whatever task was on it, rather than charged to it. vCPUs share a set of host
|
|
# cores instead of owning one each, so steal happens, and without this the
|
|
# scheduler reads it as the task having run -- a frame thread preempted by the
|
|
# host looks, to the fair class, like one that used its slice.
|
|
CONFIG_PARAVIRT_TIME_ACCOUNTING=y
|
|
|
|
# ── Idle: poll before halting ────────────────────────────
|
|
# An idle vCPU that halts exits to the host, and waking it costs an IPI and a
|
|
# VM entry. The frame pipeline sleeps and wakes every few milliseconds, so that
|
|
# is paid constantly. haltpoll spins for a bounded, adaptive window first
|
|
# (guest_halt_poll_ns, 200us), so a wakeup that comes quickly never leaves the
|
|
# guest. When the host offers it, the guest also switches host-side polling
|
|
# off, so the two never poll at once.
|
|
#
|
|
# Built in but inert: it loads only if the host sets KVM_HINTS_REALTIME or the
|
|
# command line forces it. The hint is the wrong tool -- it also promises the
|
|
# vCPUs are never preempted, and the guest drops paravirt spinlocks on the
|
|
# strength of that, which a shared set of host cores cannot promise. Forcing
|
|
# the idle driver gets the polling and nothing else. Built into the command
|
|
# line, which is prepended to what the host passes, so no caller has to know.
|
|
# The cost is host CPU spent spinning on an idle vCPU.
|
|
#
|
|
# Prepended, not replacing: with OVERRIDE off the host's arguments come after
|
|
# this and the last value wins, so `cpuidle_haltpoll.force=0` from the host
|
|
# still turns it off.
|
|
CONFIG_HALTPOLL_CPUIDLE=y
|
|
CONFIG_CPU_IDLE_GOV_HALTPOLL=y
|
|
CONFIG_CMDLINE_BOOL=y
|
|
CONFIG_CMDLINE="cpuidle_haltpoll.force=1 swiotlb=noforce"
|
|
# CONFIG_CMDLINE_OVERRIDE is not set
|
|
|
|
# ── No bounce buffer ─────────────────────────────────────
|
|
# With more than about 3 GiB of RAM the guest has memory above 4 GiB, and x86
|
|
# then sets aside a 64 MiB SWIOTLB bounce buffer and zeroes it at boot, which
|
|
# makes the host back all of it. Nothing here ever bounces: the virtio devices
|
|
# do not offer ACCESS_PLATFORM, so virtio skips the DMA API entirely, and there
|
|
# is no other DMA-capable device. `swiotlb=noforce` in the command line above
|
|
# skips the allocation. A device that genuinely needs a 32-bit DMA mask would
|
|
# fail to map rather than bounce; there is none.
|
|
|
|
# ── microVM transport ────────────────────────────────────
|
|
CONFIG_VIRTIO=y
|
|
CONFIG_VIRTIO_PCI=y
|
|
CONFIG_VIRTIO_MMIO=y
|
|
CONFIG_VIRTIO_BLK=y # root on /dev/vda
|
|
CONFIG_VIRTIO_CONSOLE=y # hvc0, the only way into a guest that will not boot
|
|
CONFIG_VIRTIO_NET=y
|
|
CONFIG_VIRTIO_FS=y # the shares the boot descriptor names
|
|
CONFIG_FUSE_FS=y # virtiofs needs it
|
|
CONFIG_VSOCKETS=y # neshub's link to the host
|
|
CONFIG_VIRTIO_VSOCKETS=y
|
|
CONFIG_VIRTIO_BALLOON=y
|
|
CONFIG_DRM=y
|
|
CONFIG_DRM_VIRTIO_GPU=y
|
|
CONFIG_VIRTIO_GPU_NV=y # NVIDIA hosts: the ioctl forwarder, from virtio-nvgpu
|
|
|
|
# ── Filesystems ──────────────────────────────────────────
|
|
CONFIG_EXT4_FS=y # output/rootfs.ext4, and a box's writable install layer
|
|
CONFIG_MISC_FILESYSTEMS=y # the menu EROFS lives under; off in base.config, which drops it silently
|
|
CONFIG_EROFS_FS=y # a game's build image, the install overlay's lower layer
|
|
CONFIG_OVERLAY_FS=y # the session overlay over a read-only install
|
|
|
|
# ── Memory typing ────────────────────────────────────────
|
|
# Page Attribute Table, and the MTRR support it depends on.
|
|
#
|
|
# Both are `def_bool y` upstream, promptable only under EXPERT -- so they were
|
|
# switched off deliberately at some point, and the guest booted logging
|
|
# "PAT support disabled because CONFIG_X86_PAT is disabled in the kernel".
|
|
#
|
|
# Without PAT there are no write-combining mappings. This guest exists to push
|
|
# frames through virtio-gpu, and WC on the GPU aperture is precisely what PAT
|
|
# provides; every frame would go through uncached or writeback mappings
|
|
# instead. Not a correctness problem, which is why it went unnoticed.
|
|
CONFIG_MTRR=y
|
|
CONFIG_X86_PAT=y
|
|
|
|
# ── POSIX ACLs ───────────────────────────────────────────
|
|
# Nothing depends on the ACLs themselves; without them, anything that sets one
|
|
# on a device node or a file logs "Operation not supported" at every boot, and
|
|
# a boot log full of failures that do not matter hides the ones that do.
|
|
CONFIG_EXT4_FS_POSIX_ACL=y
|
|
CONFIG_TMPFS_POSIX_ACL=y
|
|
|
|
# ── Performance ──────────────────────────────────────────
|
|
# Chosen, not required. The mainline subset of what CachyOS enables, minus
|
|
# anything needing patches. PREEMPT_LAZY is the tree's own default and is left
|
|
# alone; PREEMPT_DYNAMIC on top allows `preempt=full` from the command line
|
|
# without a rebuild.
|
|
CONFIG_PREEMPT_DYNAMIC=y
|
|
CONFIG_LRU_GEN=y
|
|
CONFIG_LRU_GEN_ENABLED=y
|
|
CONFIG_TRANSPARENT_HUGEPAGE=y
|
|
CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
|
|
CONFIG_PSI=y
|
|
|
|
# ── Undoing the minimal seed ─────────────────────────────
|
|
# base.config descends from a size-minimised config with EXPERT on, and EXPERT
|
|
# is what makes each of these promptable. Every one is `default y` (or the
|
|
# default choice) upstream, and every one was off in the built kernel. They
|
|
# are pinned here because nothing else would stop a reseed from bringing them
|
|
# back.
|
|
#
|
|
# SLUB_TINY drops the per-CPU slab caches, so every kmalloc and kfree takes the
|
|
# locked slow path. Its own help text says it is not for performance. This
|
|
# guest allocates constantly: network buffers, GPU ioctls, io_uring, futexes.
|
|
# CONFIG_SLUB_TINY is not set
|
|
#
|
|
# Without ADVISE_SYSCALLS, madvise() and fadvise() return ENOSYS. THP above is
|
|
# madvise-only, so no user mapping could ever get a huge page, and allocators
|
|
# could never hand memory back with MADV_DONTNEED or MADV_FREE.
|
|
CONFIG_ADVISE_SYSCALLS=y
|
|
#
|
|
# Without MEMBARRIER, Wine and the .NET runtime fall back to an mprotect() trick
|
|
# to flush other threads' write buffers, which forces a TLB shootdown across
|
|
# every vCPU -- an IPI storm, and each IPI is a VM exit. RSEQ gives glibc and
|
|
# per-CPU allocators their fast paths.
|
|
CONFIG_MEMBARRIER=y
|
|
CONFIG_RSEQ=y
|
|
#
|
|
# -O2, not -Os: the whole kernel was being compiled for size, and a guest that
|
|
# runs Wine spends much of its time in syscalls.
|
|
CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y
|
|
# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
|
|
#
|
|
# Without jump labels, every static key is a memory load and a branch instead
|
|
# of a patched no-op, on paths such as steal-time accounting, the scheduler and
|
|
# cgroup checks.
|
|
CONFIG_JUMP_LABEL=y
|
|
|
|
# Autogroup is deliberately OFF, which is a departure from CachyOS.
|
|
#
|
|
# It groups tasks by session id, which is a desktop tuning: it keeps a
|
|
# terminal's `make -j` from starving the browser. This guest is a
|
|
# single-application appliance with no interactive sessions to balance
|
|
# against each other, so there is nothing for it to do but add overhead.
|
|
# CONFIG_SCHED_AUTOGROUP is not set
|
|
|
|
# ── Deliberately absent ──────────────────────────────────
|
|
# No CONFIG_SND of any kind. The guest has no sound hardware and wants none:
|
|
# neswire is a virtual PipeWire sink, and an ALSA stack would only add a second
|
|
# thing that could claim to be the default output.
|
|
|
|
# Speculation mitigations are off, as a decision rather than an oversight.
|
|
#
|
|
# `lscpu` in the guest reports Spectre v1/v2, SSB, SRSO and TSA as Vulnerable,
|
|
# and that is intended. This is a single-tenant sandbox: one player's session,
|
|
# torn down at the end of it, with the VM boundary as the isolation. The
|
|
# attacks these defend against are cross-tenant side channels, and the cost is
|
|
# paid on every syscall and context switch in a workload that is latency
|
|
# bound.
|
|
#
|
|
# What would change this: running two players' sessions on one box at the same
|
|
# time, or anything of value living inside the guest. Neither is true today.
|
|
# CONFIG_CPU_MITIGATIONS is not set
|