Linux's own r8169 through LDLS, deep idle, and the GPU plan
The RTL8168 is now driven by Linux's r8169, unmodified, in a process. `make ldls-r8169` stages it from the running kernel together with libphy, mdio-bus, mdio_devres and the Realtek PHY driver, the kernel's BTF and the RTL PHY firmware, and etc/schinit/ldls-r8169.job starts it at boot with its log in /var/log/ldls-r8169.log. The native driver is out of the registry; its code, now with Linux's 8168H setup, stays as the fallback. LDLS grows what that needed: - ldlsd loads a comma-separated module set in dependency order, each linking against the ones before it, with BTF read before any init_module runs; - a driver core (bus match, driver probe), devres, kernel printf, real udelay/msleep/ktime and 1000 Hz jiffies, phylib's bitmaps and link_mode_params, firmware from /lib/firmware, and the symbols whose stub zero was a lie (list validity, try_module_get, dev_err_probe, eth_platform_get_mac_address); - pdev->bus is a real pci_bus, and cmovcc from per-CPU memory is rewritten like the other %gs forms. In QEMU all five modules load, link and initialise, and r8169 registers and finds no device. Probe and traffic are untested until real hardware. The kernel turns ASPM off on Realtek NIC links at boot, as r8169 does through pci_disable_link_state, which a process cannot. Idle CPUs enter ACPI _CST's C2 (CC6 on Zen) by port read or MWAIT when the APIC timer keeps running, instead of only ever halting at C1. docs/plan/gpu.md: amdgpu and nouveau through LDLS, with measurements. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
parent
438e7af141
commit
4ad49ed5f2
18 changed files with 1945 additions and 81 deletions
125
docs/plan/gpu.md
Normal file
125
docs/plan/gpu.md
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
# The GPU, through Linux's own driver
|
||||
|
||||
The laptop this is for (ASUS TUF A15 FA507NU) has a Radeon 680M in the CPU and
|
||||
an RTX 4050 beside it. SchOS draws everything with the CPU on an EFI
|
||||
framebuffer. Rendering a browser page that way keeps every core busy, and the
|
||||
machine runs hot doing it.
|
||||
|
||||
Writing a Radeon driver is not on the table: amdgpu is around four million lines
|
||||
before its register headers. The plan is to **run Linux's amdgpu unmodified**,
|
||||
with its firmware, in a process under LDLS ([../architecture/ldls.md](../architecture/ldls.md)),
|
||||
and to give Mesa the `/dev/dri` it expects. Licensing is not an obstacle:
|
||||
SchOS is GPL-2.0-or-later, and the user has said that Linux code and firmware
|
||||
blobs may be shipped. That supersedes LDLS's rule 1.
|
||||
|
||||
## Why LDLS and not an in-kernel linuxkpi
|
||||
|
||||
FreeBSD's drm-kmod compiles Linux's DRM source into kernel modules against a
|
||||
shim (linuxkpi). SchOS has already chosen the other route, and for good reasons
|
||||
([hybrid.md](hybrid.md)): GPU drivers live outside the kernel, a crashed driver
|
||||
falls back to the framebuffer instead of taking the machine down, and LDLS
|
||||
loads unmodified `.ko` files against the running kernel's BTF instead of
|
||||
needing a source port per Linux release. vgpud already drives a GPU from a
|
||||
process, and LDLS already runs 8139too and e1000 that way.
|
||||
|
||||
## What it costs: the numbers
|
||||
|
||||
Measured against Linux 6.18.54 (the host's kernel and modules), with
|
||||
`modprobe --show-depends`, `nm -u` and the export tables:
|
||||
|
||||
| Driver | Modules in the closure | Symbols needed from the core kernel | LDLS provides today |
|
||||
|---|---|---|---|
|
||||
| r8169 | 12 (libphy, mdio-bus, realtek PHY, ...) | 330 | 70 |
|
||||
| amdgpu | 15 (ttm, gpu-sched, drm_buddy, drm_exec, drm_display_helper, ...) | 1,166 | 76 |
|
||||
|
||||
Of amdgpu's 1,166 symbols, **314 are the DRM core** (`drm_*`). NixOS builds
|
||||
`drm.ko` and `drm_kms_helper.ko` into the kernel, so a module set that has them
|
||||
as modules is needed; see step 0. A further 45 are dma-fence, dma-buf and
|
||||
dma-resv. The rest is ordinary kernel: memory, locks, workqueues, timers, PCI,
|
||||
ACPI, firmware loading, i2c, backlight.
|
||||
|
||||
## What LDLS lacks, and must grow
|
||||
|
||||
LDLS today assumes one thread, no-op locks and a polled interrupt handler
|
||||
(ldls.md, "What it costs"). amdgpu cannot work under that:
|
||||
|
||||
1. **Threads.** `kthread_run`, workqueues with real worker threads,
|
||||
`gpu-sched`'s scheduler threads. Map them to pthreads in the driver process.
|
||||
2. **Locks that lock.** mutex, spinlock, rwsem, `ww_mutex` (TTM and dma-resv
|
||||
depend on wound/wait semantics), completions and wait queues, on futexes.
|
||||
3. **Interrupts as interrupts.** A thread blocked in `read()` on `/dev/pci`
|
||||
calls the handler and any threaded handler, instead of the poll loop.
|
||||
4. **Timers.** `timer_list`, `hrtimer`, `delayed_work` and `jiffies`, from a
|
||||
timer thread.
|
||||
5. **Memory with physical addresses.** TTM allocates system pages for GTT and
|
||||
maps them for the GPU. Without an IOMMU, DMA address equals physical
|
||||
address, so the `PCIIOC_DMA` arena has to grow to hundreds of megabytes,
|
||||
with `struct page` (vmemmap) coverage for all of it.
|
||||
6. **Firmware.** `request_firmware` reads `/lib/firmware/amdgpu/*.bin`, from
|
||||
Alpine's `linux-firmware-amdgpu` package.
|
||||
7. **ACPI.** On an APU the VBIOS comes from the VFCT table, and amdgpu calls
|
||||
ATIF/ATCS. A small `/dev/acpi` (read a table, evaluate a method through
|
||||
uACPI) covers both.
|
||||
|
||||
Each of these is general LDLS work, not amdgpu work. Every later driver needs
|
||||
the same pieces: e1000e, iwlwifi, nouveau.
|
||||
|
||||
## The new kernel piece: `/dev/dri` served by a process
|
||||
|
||||
Mesa and Xorg open `/dev/dri/card0` and `/dev/dri/renderD128`, then use
|
||||
`ioctl`, `mmap` and `poll`, and pass buffers to each other as dma-buf file
|
||||
descriptors. The kernel has to forward each of those to the driver process:
|
||||
|
||||
- **open, ioctl, poll, close** go to the server process with the client's
|
||||
identity attached, the same pattern `/dev/netdev` uses for NICs, extended to
|
||||
ioctls with their argument buffers.
|
||||
- **mmap** is the part the kernel must do itself. The server replies with
|
||||
physical ranges (VRAM through the BAR, or GTT pages from the arena), and the
|
||||
kernel maps those into the client's address space.
|
||||
- **dma-buf** becomes a kernel file object naming a buffer the server owns.
|
||||
`SCM_RIGHTS` passes it between processes (already built, foundations.md
|
||||
item 1), and X's DRI3 needs exactly that.
|
||||
- **sysfs**: `/sys/class/drm/card0`, `/sys/dev/char/226:*`, and the
|
||||
vendor/device files that libdrm and Mesa read to pick a driver.
|
||||
|
||||
## Order
|
||||
|
||||
Each step ends with something you can run.
|
||||
|
||||
0. **One module set.** Build Linux 6.18 from source, once, with `DRM=m`,
|
||||
`DRM_AMDGPU=m` and BTF on. That gives `drm.ko`, `drm_kms_helper.ko` and the
|
||||
rest, plus a matching `vmlinux` BTF. `make ldls-modules` does the build, and
|
||||
packaging it as an apk makes it installable.
|
||||
1. **LDLS concurrency and memory** (lacks 1–5 above), proven on **r8169**.
|
||||
The real Linux r8169 brings PHY tuning, the firmware patch, ASPM handling
|
||||
and every RTL8168 variant. It is the laptop NIC, so this step also fixes the
|
||||
network properly. Done when the laptop's ethernet runs at gigabit speed
|
||||
through `ldls0`.
|
||||
2. **amdgpu probes.** The driver process loads firmware, brings up the IP
|
||||
blocks and takes the display from the EFI framebuffer. The kernel console
|
||||
must hand over the screen and get it back if the process dies. Done when
|
||||
`dmesg` shows amdgpu's "SMU is initialized" and the panel stays lit.
|
||||
3. **KMS.** `/dev/dri/card0` with ioctl forwarding and the mmap path. Done
|
||||
when `modetest` (Alpine `libdrm-tests`) sets a mode and draws a frame.
|
||||
4. **Rendering.** `renderD128`, GEM mmap, dma-buf, syncobj. Done when
|
||||
`eglinfo` names radeonsi and `glmark2-es2-drm` and `vkcube` (RADV) run.
|
||||
5. **The desktop.** Xorg's modesetting driver with glamor, then Epiphany with
|
||||
GL compositing. Done when the fans stay quiet while a page scrolls.
|
||||
|
||||
## The RTX 4050
|
||||
|
||||
Later, and in two parts:
|
||||
|
||||
- **Powered off, now.** Without a driver the dGPU sits in D0 and draws power.
|
||||
Putting it in D3cold through its ACPI power resources (`_PR3` / `_OFF`) is
|
||||
a few dozen lines in the kernel and helps the heat and the battery today.
|
||||
- **Driven, after amdgpu.** nouveau with GSP firmware (Ada needs it), through
|
||||
the same LDLS and `/dev/dri` machinery, for PRIME render offload.
|
||||
|
||||
## Not GPU, but also why the laptop runs hot
|
||||
|
||||
Until now, idle CPUs halted at C1 and never reached the deep states listed in
|
||||
ACPI `_CST`. That kept every core powered at an idle shell prompt. It is fixed
|
||||
alongside this plan: the scheduler's idle path now enters `_CST`'s C2 (CC6 on
|
||||
Zen). P-state control (AMD CPPC) is still missing, so the cores run at the
|
||||
firmware's default clock under load.
|
||||
Loading…
Reference in a new issue