R3: KMS object model, atomic commit, vblank events and EDID (WI-455) #5

Merged
toasterson merged 18 commits from r3-kms into main 2026-10-05 16:36:06 +00:00
Contributor

Anima work item: WI-455 01a0e387-b132-7a82-a056-9171bf5f4df4

Milestone R3: kernel mode setting. Adds the KMS object model, atomic commit with the legacy ioctls built on it, vblank counters and events through read(2)/poll(2), EDID parsing, and a virtual display in rdrmnull.

Needs illumos-ddi illumos/illumos-ddi#4 (chpoll(9E) glue, pinned here at 5c792b3229). Stacked on loose-ends-20260928 (illumos-drm PR 4) and illumos-ddi loose-ends-20260928 (PR 3), which are not merged yet, so the diff against main also shows their commits until they are.

What was built

crates/drm/src/kms/:

  • modes holds the mode type and its conversion to and from drm_mode_modeinfo, with Linux's validation. It also has the VESA DMT, established-timing and CTA-861 VIC tables, CVT in integer arithmetic, and the mode list order.
  • edid parses the base block (header repair, checksum, detailed, standard, established timings, CVT codes, the name) and CTA-861 extensions (VIC data block, HDMI VSDB, extension DTDs).
  • formats holds the fourcc codes, the legacy ADDFB formats and the IN_FORMATS blob.
  • props has the standard properties with Linux's names, types and flags.
  • objects has one ID space for all mode objects, the driver Layout, blobs, framebuffers and the ADDFB2 checks.
  • state has the CRTC/plane/connector states, atomic updates that duplicate on first touch (dropping one is the rollback), and the KmsDriver trait with check and commit hooks per CRTC, plane and connector.
  • core covers the registry, getters, probing, and framebuffer and blob lifetimes.
  • atomic does property setting, the core checks and the commit.
  • legacy has SETCRTC, PAGE_FLIP, SETPLANE, CURSOR/CURSOR2, the DPMS property and gamma, all as atomic updates.
  • events has vblank counters, flip and sequence events, and per-file queues; it is allocation-free on the delivery path.
  • ioctl is the table with Linux's drm_ioctls[] flags.
  • dev is the kernel layer: locks, argument copies, GEM lookups, read(9E), chpoll(9E), the kms kstat and the VblankHandle.
  • soft is a software vblank on ddi_periodic(9F).

Shared files:

  • Driver::kms, which defaults to None, so render-only drivers are unaffected.
  • Device routes the KMS table, and handles open, close, read, chpoll and detach.
  • GET_CAP: ADDFB2_MODIFIERS comes from the driver's ModeConfig; CRTC_IN_VBLANK_EVENT and VBLANK_HIGH_CRTC are 1.
  • Plain for integers.

rdrmnull gets one virtual connector with a synthesized EDID (1024x768 preferred; 1280x1024, 800x600, 640x480), one encoder, and one CRTC with primary and cursor planes. It also gets a software vblank at the mode's refresh, a software scanout that latches the primary framebuffer at the next vblank, and a test-only RDRMNULL_IOCTL_SCANOUT that reports the framebuffer on screen and an FNV-1a checksum of its visible pixels.

Tests:

  • crates/drm/tests/edid/: three EDIDs synthesized by gen_edid.py. The provenance is in the script: nothing is captured or copied, and the files are MPL-2.0.
  • tools/test/drm_r3_test.c: 184 checks.
  • tools/test/run-r3-test.sh: runs R0, R1, R3 and modetest.

Decisions

  • Structure. KMS lives in kms/ and the driver side is a separate trait (KmsDriver), as decided. Everything except dev and soft is plain data with host tests.
  • Locks. A mode-setting lock around KmsCore and an event lock around EventCore, taken in that order; GEM locks come after the mode-setting lock. The vblank path takes only the event lock and wakes pollers after dropping it, on a PollHeads array that lives as long as the device. So a wakeup that races with a close finds a valid head.
  • Close. Pending and queued events are unlinked and freed. Framebuffers are removed, which turns off planes that show them and the CRTC of such a primary plane (Linux's atomic_remove_fb). Blobs are destroyed. The clone ID is released only after the file's KMS and GEM state.
  • Flips. A commit arms a flip on each active CRTC it touches after the driver's hooks ran; the next vblank completes it. A non-blocking commit or PAGE_FLIP with a flip pending gets EBUSY. Blocking commits wait for the previous flip and their own (1 s bound each).
  • Events. Linux's 4096-byte per-file budget. read(2) returns whole events only, and readers of one file take turns. O_NONBLOCK gives EAGAIN. poll(2) reports POLLIN.
  • Vblank. VblankHandle::vblank demands the Sleepable token, because the event lock has no interrupt priority. SoftVblank polls every 10 ms (the ddi_periodic resolution) and reports every frame boundary with its exact gethrtime time: 60 Hz on average, each vblank up to 10 ms late.
  • Fences. IN_FENCE_FD and OUT_FENCE_PTR exist so their IDs are known, but no object carries them, and setting them is EINVAL. They are a follow-up after R2.
  • Permissions. Mode-setting ioctls mirror Linux's flags: the setters need the master, and every KMS ioctl is refused on render nodes (EACCES).

Deviations from Linux

Blobs, framebuffers and events are locked kernel memory that any card-node file may create, so they are bounded; going past a bound is ENOMEM:

  • A blob is at most 1 MiB.
  • Per file: at most 256 blobs, 4 MiB of blob data and 256 framebuffers.
  • Per device: at most 4096 blobs, 16 MiB of blob data, 4096 framebuffers and 8192 outstanding events. The current master is exempt from the device bounds.

MODE_ATOMIC is bounded at 1024 objects and 16384 properties (EINVAL).

GETCONNECTOR forces a probe only for the master, as current Linux does. DIRTYFB returns ENOSYS, as Linux does for a driver without damage tracking.

Verification

Host, with illumos-ddi at 5c792b3229:

  • cargo test -p drm -p drm-uapi: 84 tests, including EDID parsing against the three blobs, CVT against the VESA generator, extreme timings, atomic state duplication and rollback, every check errno, lifetimes, event lists and caps, and the ioctl table.
  • clippy -D warnings (also with panic-drill), fmt, rustdoc -D warnings, and uapi-check (0 differences).

Kernel: disposable OpenIndiana guest on hephaistos (illumos cda5b9a49f, fresh overlay from the golden image). run-r3-test.sh gives fail=0:

  • R0: 105 checks.
  • R1: 153 checks.
  • R3: 184 checks.
  • 32-bit probes: 5, 4 and 3.
  • modetest (package system/test/libdrm, gfx-drm's build of the libdrm 2.4.109 tests), four steps:
    • -M rdrmnull lists all objects and properties and decodes IN_FORMATS.
    • -s 25@23:1024x768 sets the mode with a dumb framebuffer.
    • -v page-flips with vblank events at 60.00 Hz.
    • -a -s … -P … sets the mode and plane atomically.
  • The atomic test does TEST_ONLY (nothing changes), then real blocking and NONBLOCK commits with PAGE_FLIP_EVENT, whose events are read through poll(2) + read(2). The scanout checksum matches the written pattern after each flip.
  • Property get/set round trips pass. ADDFB2 with I915_FORMAT_MOD_X_TILED gives EINVAL.
  • Vblank rate is 60.08 Hz measured; timestamps are 16.665 ms apart.
  • The event budget allows 128 events, then ENOMEM.
  • Close with pending events discards them (400-event churn from 8 threads).
  • Four threads query while the master flips.
  • The kms and gem kstats are back at idle before unload (framebuffers 0, blobs 3 device-owned, events outstanding 0).
  • modunload is refused while a file is open and succeeds after it closes.
  • /etc and /devices hashes are unchanged.

Logs (workspace): logs/hephaistos/illumos-drm/20260929-r3-kms-run5.log is the final revision 04a21f9 with the module from 8602beb; 04a21f9 only changes a doc comment. -run1 to -run4 are earlier iterations. -run1 had test-side errno mistakes (libdrm setters return -errno). No panic occurred in any run.

Reviewed locally against the Anima review bar: round 1 was a reject, and every finding was fixed; round 2 approved on the condition that the illumos-ddi repin lands, which is the last commit here.

Open issues and follow-ups

  • Fences (IN_FENCE_FD, OUT_FENCE_PTR) after R2.
  • An interrupt-priority event lock and an Atomic vblank entry point, for the first driver with a hardware vblank interrupt (R4's virtio-gpu uses the software vblank).
  • Not implemented: leases, async and target page flips, atomic colour management (GAMMA_LUT, …; legacy gamma works), VRR_ENABLED, writeback, rotation, zpos, TILE, damage clips.
  • Linux's refusal of ATOMIC to processes named X* needs the process name; no committed DDI interface provides it.
  • If detach is refused after the KMS state was released (GEM objects still referenced), the device stays attached with every CRTC off.
  • IN_FORMATS is attached even for drivers without modifiers. PAGE_FLIP on an inactive CRTC without an event is accepted.
  • R1's GEM device memory limit has the same shared-pool property the KMS device bounds had before the master exemption.

Update: detach checks before teardown

Device::detach now evaluates every refusal condition (open files, GEM objects and mappings, KMS state) before tearing anything down, so a refused detach leaves a fully working device. rdrmnull gained a test-only HOLD ioctl that keeps a GEM object alive; run-r3-test.sh holds one, checks that modunload is refused, then checks that modetest still lists the display and page-flips with vblank events at about 60 Hz, then releases and unloads cleanly. Kernel run on illumos cda5b9a49f: 476 PASS, 0 FAIL.

Anima work item: WI-455 `01a0e387-b132-7a82-a056-9171bf5f4df4` Milestone R3: kernel mode setting. Adds the KMS object model, atomic commit with the legacy ioctls built on it, vblank counters and events through `read(2)`/`poll(2)`, EDID parsing, and a virtual display in `rdrmnull`. Needs illumos-ddi https://code.aopc.cloud/illumos/illumos-ddi/pulls/4 (chpoll(9E) glue, pinned here at `5c792b3229`). Stacked on `loose-ends-20260928` (illumos-drm PR 4) and illumos-ddi `loose-ends-20260928` (PR 3), which are not merged yet, so the diff against `main` also shows their commits until they are. ## What was built `crates/drm/src/kms/`: - `modes` holds the mode type and its conversion to and from `drm_mode_modeinfo`, with Linux's validation. It also has the VESA DMT, established-timing and CTA-861 VIC tables, CVT in integer arithmetic, and the mode list order. - `edid` parses the base block (header repair, checksum, detailed, standard, established timings, CVT codes, the name) and CTA-861 extensions (VIC data block, HDMI VSDB, extension DTDs). - `formats` holds the fourcc codes, the legacy `ADDFB` formats and the `IN_FORMATS` blob. - `props` has the standard properties with Linux's names, types and flags. - `objects` has one ID space for all mode objects, the driver `Layout`, blobs, framebuffers and the `ADDFB2` checks. - `state` has the CRTC/plane/connector states, atomic updates that duplicate on first touch (dropping one is the rollback), and the `KmsDriver` trait with check and commit hooks per CRTC, plane and connector. - `core` covers the registry, getters, probing, and framebuffer and blob lifetimes. - `atomic` does property setting, the core checks and the commit. - `legacy` has `SETCRTC`, `PAGE_FLIP`, `SETPLANE`, `CURSOR`/`CURSOR2`, the `DPMS` property and gamma, all as atomic updates. - `events` has vblank counters, flip and sequence events, and per-file queues; it is allocation-free on the delivery path. - `ioctl` is the table with Linux's `drm_ioctls[]` flags. - `dev` is the kernel layer: locks, argument copies, GEM lookups, `read(9E)`, `chpoll(9E)`, the `kms` kstat and the `VblankHandle`. - `soft` is a software vblank on `ddi_periodic(9F)`. Shared files: - `Driver::kms`, which defaults to `None`, so render-only drivers are unaffected. - `Device` routes the KMS table, and handles open, close, read, chpoll and detach. - `GET_CAP`: `ADDFB2_MODIFIERS` comes from the driver's `ModeConfig`; `CRTC_IN_VBLANK_EVENT` and `VBLANK_HIGH_CRTC` are 1. - `Plain` for integers. `rdrmnull` gets one virtual connector with a synthesized EDID (1024x768 preferred; 1280x1024, 800x600, 640x480), one encoder, and one CRTC with primary and cursor planes. It also gets a software vblank at the mode's refresh, a software scanout that latches the primary framebuffer at the next vblank, and a test-only `RDRMNULL_IOCTL_SCANOUT` that reports the framebuffer on screen and an FNV-1a checksum of its visible pixels. Tests: - `crates/drm/tests/edid/`: three EDIDs synthesized by `gen_edid.py`. The provenance is in the script: nothing is captured or copied, and the files are MPL-2.0. - `tools/test/drm_r3_test.c`: 184 checks. - `tools/test/run-r3-test.sh`: runs R0, R1, R3 and `modetest`. ## Decisions - **Structure.** KMS lives in `kms/` and the driver side is a separate trait (`KmsDriver`), as decided. Everything except `dev` and `soft` is plain data with host tests. - **Locks.** A mode-setting lock around `KmsCore` and an event lock around `EventCore`, taken in that order; GEM locks come after the mode-setting lock. The vblank path takes only the event lock and wakes pollers after dropping it, on a `PollHeads` array that lives as long as the device. So a wakeup that races with a close finds a valid head. - **Close.** Pending and queued events are unlinked and freed. Framebuffers are removed, which turns off planes that show them and the CRTC of such a primary plane (Linux's `atomic_remove_fb`). Blobs are destroyed. The clone ID is released only after the file's KMS and GEM state. - **Flips.** A commit arms a flip on each active CRTC it touches after the driver's hooks ran; the next vblank completes it. A non-blocking commit or `PAGE_FLIP` with a flip pending gets `EBUSY`. Blocking commits wait for the previous flip and their own (1 s bound each). - **Events.** Linux's 4096-byte per-file budget. `read(2)` returns whole events only, and readers of one file take turns. `O_NONBLOCK` gives `EAGAIN`. `poll(2)` reports `POLLIN`. - **Vblank.** `VblankHandle::vblank` demands the `Sleepable` token, because the event lock has no interrupt priority. `SoftVblank` polls every 10 ms (the `ddi_periodic` resolution) and reports every frame boundary with its exact `gethrtime` time: 60 Hz on average, each vblank up to 10 ms late. - **Fences.** `IN_FENCE_FD` and `OUT_FENCE_PTR` exist so their IDs are known, but no object carries them, and setting them is `EINVAL`. They are a follow-up after R2. - **Permissions.** Mode-setting ioctls mirror Linux's flags: the setters need the master, and every KMS ioctl is refused on render nodes (`EACCES`). ## Deviations from Linux Blobs, framebuffers and events are locked kernel memory that any card-node file may create, so they are bounded; going past a bound is `ENOMEM`: - A blob is at most 1 MiB. - Per file: at most 256 blobs, 4 MiB of blob data and 256 framebuffers. - Per device: at most 4096 blobs, 16 MiB of blob data, 4096 framebuffers and 8192 outstanding events. The current master is exempt from the device bounds. `MODE_ATOMIC` is bounded at 1024 objects and 16384 properties (`EINVAL`). `GETCONNECTOR` forces a probe only for the master, as current Linux does. `DIRTYFB` returns `ENOSYS`, as Linux does for a driver without damage tracking. ## Verification Host, with illumos-ddi at `5c792b3229`: - `cargo test -p drm -p drm-uapi`: 84 tests, including EDID parsing against the three blobs, CVT against the VESA generator, extreme timings, atomic state duplication and rollback, every check errno, lifetimes, event lists and caps, and the ioctl table. - clippy `-D warnings` (also with `panic-drill`), fmt, rustdoc `-D warnings`, and `uapi-check` (0 differences). Kernel: disposable OpenIndiana guest on hephaistos (illumos `cda5b9a49f`, fresh overlay from the golden image). `run-r3-test.sh` gives `fail=0`: - R0: 105 checks. - R1: 153 checks. - R3: 184 checks. - 32-bit probes: 5, 4 and 3. - `modetest` (package `system/test/libdrm`, gfx-drm's build of the libdrm 2.4.109 tests), four steps: - `-M rdrmnull` lists all objects and properties and decodes `IN_FORMATS`. - `-s 25@23:1024x768` sets the mode with a dumb framebuffer. - `-v` page-flips with vblank events at 60.00 Hz. - `-a -s … -P …` sets the mode and plane atomically. - The atomic test does `TEST_ONLY` (nothing changes), then real blocking and `NONBLOCK` commits with `PAGE_FLIP_EVENT`, whose events are read through `poll(2)` + `read(2)`. The scanout checksum matches the written pattern after each flip. - Property get/set round trips pass. `ADDFB2` with `I915_FORMAT_MOD_X_TILED` gives `EINVAL`. - Vblank rate is 60.08 Hz measured; timestamps are 16.665 ms apart. - The event budget allows 128 events, then `ENOMEM`. - Close with pending events discards them (400-event churn from 8 threads). - Four threads query while the master flips. - The `kms` and `gem` kstats are back at idle before unload (framebuffers 0, blobs 3 device-owned, events outstanding 0). - `modunload` is refused while a file is open and succeeds after it closes. - `/etc` and `/devices` hashes are unchanged. Logs (workspace): `logs/hephaistos/illumos-drm/20260929-r3-kms-run5.log` is the final revision `04a21f9` with the module from `8602beb`; `04a21f9` only changes a doc comment. `-run1` to `-run4` are earlier iterations. `-run1` had test-side errno mistakes (libdrm setters return -errno). No panic occurred in any run. Reviewed locally against the Anima review bar: round 1 was a reject, and every finding was fixed; round 2 approved on the condition that the illumos-ddi repin lands, which is the last commit here. ## Open issues and follow-ups - Fences (`IN_FENCE_FD`, `OUT_FENCE_PTR`) after R2. - An interrupt-priority event lock and an `Atomic` vblank entry point, for the first driver with a hardware vblank interrupt (R4's virtio-gpu uses the software vblank). - Not implemented: leases, async and target page flips, atomic colour management (`GAMMA_LUT`, …; legacy gamma works), `VRR_ENABLED`, writeback, rotation, `zpos`, `TILE`, damage clips. - Linux's refusal of `ATOMIC` to processes named `X*` needs the process name; no committed DDI interface provides it. - If `detach` is refused after the KMS state was released (GEM objects still referenced), the device stays attached with every CRTC off. - `IN_FORMATS` is attached even for drivers without modifiers. `PAGE_FLIP` on an inactive CRTC without an event is accepted. - R1's GEM device memory limit has the same shared-pool property the KMS device bounds had before the master exemption. ## Update: detach checks before teardown `Device::detach` now evaluates every refusal condition (open files, GEM objects and mappings, KMS state) before tearing anything down, so a refused detach leaves a fully working device. `rdrmnull` gained a test-only HOLD ioctl that keeps a GEM object alive; `run-r3-test.sh` holds one, checks that `modunload` is refused, then checks that modetest still lists the display and page-flips with vblank events at about 60 Hz, then releases and unloads cleanly. Kernel run on illumos cda5b9a49f: 476 PASS, 0 FAIL.
illumos-ddi now takes the driver's OwnNode for minor nodes, property
updates and LDI identifiers. Device::attach creates the card and render
nodes from the node before the GEM state takes it; opens wait until
attach returns, and a later failure drops the nodes again.

The pin is the head of illumos-ddi's loose-ends-20260928 branch
(eb0150cadc); move it to the merge commit on illumos-ddi main once that
branch is merged.
On a patched kernel the root-node drill saw every failed bind reach
userland as EFAULT from CREATE_DUMB: SysMem::bind mapped
DDI_DMA_NOMAPPING to EFAULT, which in a create ioctl reads as a bad user
pointer. drm::dmaerr now maps DMA failures during object creation the
way Linux's DRM reports a failed object allocation: ENOMEM for a
shortage or no mapping (NORESOURCES, TOOBIG, NOMAPPING, PARTIAL_MAP),
EINVAL for attributes that can never be satisfied, ENXIO for the
own-node guard, EIO for faults a fresh handle cannot produce. The handle
allocation, the bind and the drill's raw allocation all use it; host
tests cover the table.

rdrmnull's self drill takes an optional dma-max-transfer property;
tools/test/rdrmnull-dma-self-tiny.conf sets it to one page and
drm_dma_errno_test.c checks that a four-page dumb buffer then fails with
ENOMEM.
Use the core's page size instead of a literal 4096 in rdrmnull
All checks were successful
ci / check-hosted (pull_request) Successful in 4m36s
ci / kmod-illumos (pull_request) Successful in 5m51s
bef21aa92e
The drill's cookie limit and the movable objects' reservation now use
drm::offsets::PAGE_SIZE, as the R1 review advised.
The local decoder knew six of the ten failure codes, so a raw BADLIMITS or
TOOSMALL became EIO although the module documentation promises EINVAL.
illumos-ddi now exports its complete decoder; use it through code_errno and
test all ten codes against the documented errno.
The property is read, applied and logged only for dma-mode="self", and a
bad value reports the actual lookup error. The errno test now checks that a
failed creation leaves the gem kstat unchanged, and the tiny conf names the
rootnex warning that the refusal logs.
kms::modes holds the kernel mode type, its conversion to and from
drm_mode_modeinfo with Linux's validation, the mode list order, the
VESA DMT and established-timing modes, CTA-861 VICs and the CVT
formula in integer arithmetic. kms::edid parses the base block and
CTA-861 extensions into a mode list. The test EDIDs are synthesized by
crates/drm/tests/edid/gen_edid.py.
formats: fourcc codes, legacy ADDFB formats and the IN_FORMATS blob.
props: the standard properties with Linux's names, types and flags.
objects: the mode-object ID space, the driver's pipeline layout, blobs,
framebuffers and ADDFB2 argument checks. state: CRTC, plane and
connector states, atomic updates that duplicate on first touch, the
KmsDriver hook trait and a plane-check helper. events: vblank counters,
flip and sequence events and per-file queues with Linux's 4096-byte
budget, allocation-free on the delivery path.
KmsCore holds a device's mode objects, their IDs and properties, the
current state, the blob and framebuffer registry and per-file
ownership. Atomic updates set properties with Linux's errnos (fence
properties refused with EINVAL until R2), are checked by the core
(routing, CRTC, plane rules) and the driver's hooks, and commit in
hook order. SETCRTC, PAGE_FLIP, SETPLANE, CURSOR, DPMS and gamma are
built on the same path. RMFB and close turn off planes that show a
removed framebuffer. Host tests cover duplication, rollback, the
errnos and the lifetimes with a mock driver.
kms::dev is the kernel layer: the mode-setting lock around KmsCore, the
event lock around EventCore, argument copies for every mode-setting
ioctl with Linux's copy semantics, GEM lookups for framebuffers and
GETFB handles, the commit path with flip stalls, event charging and
arming, blocking waits, WAIT_VBLANK and the CRTC sequence ioctls,
read(9E) of whole events and chpoll(9E) through a PollHeads array, and
the kms kstat. kms::soft is a software vblank source on ddi_periodic.
Driver::kms hands the core a driver's display side; Device routes the
table in kms::ioctl, opens and closes files in KMS and tears it down
before GEM at detach. GET_CAP reports ADDFB2_MODIFIERS from the
driver's ModeConfig and CRTC_IN_VBLANK_EVENT and VBLANK_HIGH_CRTC as 1.
One virtual connector with the synthesized rdrmnull EDID, one encoder,
one CRTC with primary and cursor planes, a software vblank at the
mode's refresh rate, and a software scanout that latches the primary
plane's framebuffer at the vblank after a commit. The test-only ioctl
RDRMNULL_IOCTL_SCANOUT reports the framebuffer on screen, the vblank
and latch counts, and an FNV-1a checksum of the visible pixels.
ADDFB2 modifiers are enabled (linear only).
drm_r3_test.c checks mode objects, properties, blobs, framebuffers
(ADDFB2 with an unsupported modifier is EINVAL), legacy and atomic mode
setting with TEST_ONLY and flip events read through poll(2) and
read(2), the software scanout checksum, vblank rate and timestamps,
CRTC sequence events, the event budget, close semantics and the kms and
gem kstats. run-r3-test.sh runs the R0 and R1 tests as regression, the
R3 test and probe, and libdrm's modetest against rdrmnull.
libdrm's DRM_IOCTL wrappers (drmModeSetCrtc, drmModePageFlip and the
other setters) return -errno instead of -1 with errno set. GETFB gives
a handle to any privileged caller, as Linux does for CAP_SYS_ADMIN, so
the no-handle case is checked from an unprivileged process.
- Close releases the clone id after the file's KMS and GEM state, so a
  concurrent open cannot inherit that state.
- Event lists append in O(1) and waits are sorted by sequence, so a
  vblank touches only the events it completes; the device keeps at most
  8192 events.
- Blobs and framebuffers are bounded per file and per device by count
  and bytes; ID allocation starts from a free-ID hint.
- VblankHandle::vblank demands the Sleepable token, which SoftVblank
  passes from its periodic callback: the event lock has no interrupt
  priority.
- Frame lengths use 128-bit arithmetic; CVT codes of large heights no
  longer overflow; rdrmnull refuses user modes outside its limits.
- chpoll returns a PollRegistration tied to the device (illumos-ddi).
- GETFB2 closes the handles it made when a later one fails; reads are
  serialised; byte views of ioctl arrays need padding-free types; the
  scanout checksum uses volatile loads.
- Tests: blob caps, sorted waits, device event cap, extreme timings,
  large CVT codes, an open/close churn with pending events, a SETCRTC
  mode the driver refuses. The runner prints the source revisions.
Any file that can open card0 could fill the device-wide framebuffer,
blob and event bounds and so make the display server's ADDFB,
CREATEPROPBLOB and page-flip events fail. The current master is exempt
from them (its per-file bounds still apply). Reads of one file take turns
through a flag in the file's event state instead of a device-wide lock
held across the copy-out, so a slow reader holds up only its own file.
Pin illumos-ddi at its r3-kms-glue branch for the chpoll(9E) glue
All checks were successful
ci / check-hosted (pull_request) Successful in 3m1s
ci / kmod-illumos (pull_request) Successful in 5m6s
b1713a83c2
Device::detach tore down the mode-setting state, and rdrmnull's
before_detach stopped its vblank source, before the GEM check; when that
check refused the detach, the device stayed attached without its KMS
state and without vblanks.

Now every check runs first: open files, framebuffers still referenced
(KmsCore::detach_check, which only frees unreferenced orphans), and GEM
objects left. Only when all pass does the new Driver::before_teardown
hook let the driver stop its sources, and the core release the KMS and
GEM state. before_detach is documented as the place for references the
driver holds for itself, which must leave the device working; rdrmnull
moves its vblank shutdown to before_teardown.

Host test: a refused detach_check changes nothing, the device still sets
a mode, and the check passes once the driver lets go.
RDRMNULL_IOCTL_HOLD (test-only, DRM_COMMAND_BASE + 4) keeps a reference
to a GEM object in the driver across the close of every file, and drops
it on request; before_detach leaves it, so detach(9E) is refused.

run-r3-test.sh: drm_r3_test hold, modunload must fail with a logged
"detach refused: GEM state in use", then modetest must list the display
and page-flip with vblank events at about 60 Hz, then drm_r3_test
release; the unload at the end must succeed.
README: the refused-detach run of the R3 test
All checks were successful
ci / check-hosted (pull_request) Successful in 3m14s
ci / kmod-illumos (pull_request) Successful in 3m17s
56b7b4e424
Merge branch 'main' into r3-kms
All checks were successful
ci / check-hosted (pull_request) Successful in 59m46s
ci / kmod-illumos (pull_request) Successful in 1h15m40s
802068709d
Conflict in Cargo.toml/Cargo.lock: main pins illumos-ddi at
449bfe73e9 (loose-ends-20260928), r3-kms at 5c792b3229 (r3-kms-glue),
which contains it. Keep r3-kms's pin; illumos-ddi main now contains
5c792b3229 (illumos-ddi PR 4).
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
illumos/illumos-drm!5
No description provided.