R5b: public-layout base objects (facade 1.0): Linux fence semantics, GEM refcount, drmm, drm_printer, handle walk #13

Merged
toasterson merged 15 commits from r5b-layouts into main 2026-10-05 16:59:32 +00:00
Owner

Anima work item: R5b 01a0f883-25ff-7b12-8b49-25aab381eec5

Public-layout base objects in the Rust DRM core (facade 1.0): the public fields of dma_fence, drm_gem_object and drm_device are now the objects' state, as Linux v7.2 drivers (first target: modern i915, headless) use them, plus foreign fence ops, managed resources, drm_printer and a handle walk.

Base: g8-reimport (#11). The first commit on top is a merge of g7-coexist (#10, 98ea76c, no conflicts), as the work item asked; both pin illumos-ddi 6e65077 (g7-module-busy), unchanged here. Review 91482f3..501c309.

Layouts (Linux v7.2 names and order; MIT reimplementations, D7)

structure public fields size private tail provenance / change in 1.0
drm_device 24 (27 with managed.*) 336 B 128 B include/drm/drm_device.h: managed {resources, final_kfree, lock} after dma_dev, open_count after unique (i915/xe/amdgpu field sets, analysis §2.3)
drm_file 6 88 B 32 B drm_file.h; object_idr/table_lock replaced by drm_illumos_gem_for_each_handle()
drm_gem_object 15 (17 with gpuva.*) 256 B 64 B drm_gem.h; refcount is the object's only count
dma_fence 8 (11 counting union members) 96 B 32 B dma-fence.h; every field is state; flag bits now v7.2's (SEQNO64=2, SIGNALED=3; 0.1 had SIGNALED=2)
dma_fence_ops / dma_fence_cb 7 / 2 56 / 24 B - all ops called; cb_list is a real list
dma_resv / dma_buf 2 / 8 48 / 88 B 16 / 16 B unchanged
drm_printer / va_format 7 (9) / 2 56 / 16 B - drm_print.h, new

The tails are in the version handshake (DRM_FACADE_TAILS, appended after the struct sizes; drm_facade_version keeps its size so an older driver still reads the refusal). gen_facade.py now follows anonymous members (fence lock/list unions, managed.*, gpuva.*): 40 sizes, 256 offsets.

What changed

  • Fences (drm_core::cfence, host-tested): Linux's rules on the structure — the lock (extern or inline) protects cb_list; signalling sets the bit, replaces the list by the timestamp and runs callbacks under the lock; enable_signaling once, signaled polled, wait, set_deadline, names; by-hand signalling (i915 breadcrumbs) works. The core's view: a driver fence has a shadow core fence signalled by a bridge callback on its cb_list (added when the core first takes the fence; holds a reference until it ran); a core fence handed out is a proxy with core ops whose enable_signaling arms a relay (takes a reference only while the count is non-zero; the release removes it, waiting for a running one). Last-reference release runs inline only in thread context, without the fence's lock, and not for a proxy with an armed relay; otherwise it is parked through a node in the tail on the module domain's raw list (no allocation) and run by its task queue — this fixes R5a's same-CPU spin. Unsignalled fences released with callbacks signal them with -EDEADLK (Linux). New: dma_fence_init64, signal_locked, signal_timestamp(_locked), check_and_signal(_locked), enable_sw_signaling, default_wait, set_deadline, driver_name, timeline_name, drm_illumos_fence_put_deferred, drm_illumos_fence_barrier (drains serialised, so the barrier waits for one in progress); inline helpers (dma_fence_spinlock, lock macros, is_later, get_status_locked, ...). A detach runs parked releases before counting the device's fences.
  • GEM: drm_gem_object.refcount is the only count (GemObject::ext_count, drm_core::refcount: every GemRef counts there; no resurrection; upgrades under the offset lock). kref_read/kref_get/kref_get_unless_zero in drm_facade_types.h (lists whose lock keeps the memory; no RCU grace period). drm_illumos_gem_for_each_handle(file, fn, arg) walks a file's handles under the G8 hook lock over a snapshot; drm_gem_object_lookup works from the callback.
  • Managed resources (drm_managed.h, drm_core::managed): drmm_add_action(_or_reset), drmm_release_action, drmm_kmalloc/kzalloc/kmalloc_array/kcalloc, drmm_kstrdup, drmm_kfree; released newest first at the last drm_dev_put(), after unregister and the driver's release, before free. gfp_t bits are the facade's. Not provided: drmm_mutex_init, drmm_alloc_ordered_workqueue (need R5c).
  • Printing (drm_print.h, drm_core::print): v7.2 categories (bit numbers), drm_printer with info/dbg/err printers, drm_printf/vprintf/puts, drm_err/warn/notice/info(_once), drm_dbg_*, DRM_DEV_*, DRM_*; variadic entry points are header statics ending in drm_illumos_vprintk() (vsnprintf into 512 B, cut with "...", dev_err/cmn_err; errors/warnings console+log, notices both, info/debug log only). Knob: set drm_core:drm_debug = 0x6 (R5a's header named the wrong module) or echo 'drm_debug/W 0x6' | mdb -kw.
  • cdrmnull: GPU-like timeline (soft interrupt completes, signals only enabled fences; signaled polls; driver lock at softint priority; second context), FENCE_OPS_SELFTEST (15 checks), HANDLES, PRINT_SELFTEST, drmm order check, _fini barrier. FENCE_SELFTEST RELEASED/QUEUED now check Linux's release rules.
  • Tests: tools/test/drm_r5b_test.c, run-r5b-test.sh; run-r5a-test.sh mismatch probes now cover any major and a same-size drm_device with a smaller tail (and the script is executable).

Evidence

  • Host: cargo test -p drm-core -p drm-uapi -p drm-facade 154 + 2 + 1 (new: 14 cfence tests with a mock driver and threads, relay/release races forced with test hooks, sibling-proxy last put; raw deferral list; refcount incl. upgrade-vs-last-put and underflow; managed order/early release/add_or_reset/misuse/4 threads; print rules; handle walk vs 4 threads closing/creating), virtio_gpu --no-default-features 6; clippy -D warnings, fmt, cargo doc -D warnings, check-generated.sh clean.
  • hephaistos, fresh overlays of golden-20260930 (illumos 7e802e88a3), libdrm 2.4.134 p5p in the overlay, 2 vCPU / 3 GiB, one guest at a time:
    • r5b-run2 at c24a58e (kernel code identical to HEAD): run-r5b-test.sh PASS — run-r5a-test.sh PASS (168 passing checks: R0–R4 on all three drivers, R5a cross-driver ×4, G6, R1 two cdrmnull, mismatch probes incl. tail), drmm reverse order at 7/7 cdrmnull detaches, drm_r5b_test 47/0 (15 fence-ops checks incl. 200 interrupt-race rounds; array of two driver fences, chain out of order, EIO sync file, 3000 walks vs 1592 concurrent handles, refcount 1→2→3→4→3→2), all log paths with drm_debug 0 and 0x6, 0 timeline releases in interrupt context, run-g8-test.sh kernel PASS. Logs logs/hephaistos/illumos-drm/20261003-r5b-run2-*-c24a58e.log.
    • facade-check with guest gcc 14.3.0: 296 lines, 0 differences (20261003-r5b-facade-check.log; committed facade_layout.illumos.out). kernel-check with misc/drm_core (+CTF) loaded: ddi-sys 53/60, 185/185, 984/984; drm-facade-sys sizes 40/45, offsets 244/256 (rest are members of anonymous types, checked via parents), symbols 123/123; private 3/3, 46/46, 15/15 (20261003-r5b-run2-kernel-check-c24a58e.log).
    • r5b-run3 at b3fab03: run-g7-test.sh all 13 phases PASS (81 checks, gfx-drm combo p5p with the reverse guard), then with the new stack selected run-r5a-test.sh LEGACY_GUARD=skip: first run failed the unprivileged opens of cdrmnull's stale 0600 nodes left by the uninstalled package (the known G7 open issue; identical failure set to G7's run), second run PASS (76). Logs 20261003-r5b-run3-g7-*.log.
    • r5b-run1 (earlier iteration at a94a69c/0ca2fae): found the same-context merge test bug (fixed by the second timeline context). No panic in any run; consoles in logs/hephaistos/illumos-drm/r5b-console/.
  • CTF shadow rebuilt on oibldofgate for the new headers (artifacts/illumos-drm-r5b/drm_facade_ctf.o).

Review

Two rounds against the anima-review bar (Opus). Round 1 rejected: relay/release race test did not reach the paths (now forced and asserted), a sibling proxy's callback could make an inline release wait for its own queued relay (armed proxies now always release on the task queue; regression test), GEM external count logic untested (moved to refcount, host-tested), barrier vs concurrent drain (drain lock), kref/RCU doc claims (helpers added, docs limited), drmm log baseline. Round 2 approved; its notes (serialise relay-hook tests, barrier context doc, test count) are fixed in b3fab03.

Deviations / open issues

  • Fence functions are out-of-line; a fence lock must not be a high-level spin mutex; ops kept after signalling; release == NULL frees nothing (dma_fence_free needs R5c's kfree); the core enables signalling when it takes a fence; sync objects return proxies (no pointer identity with the driver's fence).
  • Not yet in the facade: dma_fence_array_create, dma_fence_chain_* walkers, dma_resv iterators and acquire contexts (lock stays a placeholder), drm_dev_enter/exit, drmm_mutex_init, drm_WARN*, line/coredump/seq_file printers; GEM handle_count/name/dma_buf stay in the core; no Rust wrapper for the handle walk (no Rust user).
  • drmm_kmalloc(dev, 0, …) returns NULL; messages logged in interrupt context are cut by illumos at 200 bytes.
  • The packaged drivers' 0600 nodes (G7 open issue) remain.
  • CI not run here (owner's CI); the illumos job's facade-check uses the regenerated facade_layout.illumos.out.
Anima work item: R5b `01a0f883-25ff-7b12-8b49-25aab381eec5` Public-layout base objects in the Rust DRM core (facade 1.0): the public fields of `dma_fence`, `drm_gem_object` and `drm_device` are now the objects' state, as Linux v7.2 drivers (first target: modern i915, headless) use them, plus foreign fence ops, managed resources, `drm_printer` and a handle walk. **Base:** `g8-reimport` (#11). The first commit on top is a merge of `g7-coexist` (#10, `98ea76c`, no conflicts), as the work item asked; both pin illumos-ddi `6e65077` (g7-module-busy), unchanged here. Review `91482f3..501c309`. ## Layouts (Linux v7.2 names and order; MIT reimplementations, D7) | structure | public fields | size | private tail | provenance / change in 1.0 | | --- | --- | --- | --- | --- | | `drm_device` | 24 (27 with `managed.*`) | 336 B | 128 B | `include/drm/drm_device.h`: `managed` {resources, final_kfree, lock} after `dma_dev`, `open_count` after `unique` (i915/xe/amdgpu field sets, analysis §2.3) | | `drm_file` | 6 | 88 B | 32 B | `drm_file.h`; `object_idr`/`table_lock` replaced by `drm_illumos_gem_for_each_handle()` | | `drm_gem_object` | 15 (17 with `gpuva.*`) | 256 B | 64 B | `drm_gem.h`; `refcount` is the object's only count | | `dma_fence` | 8 (11 counting union members) | 96 B | 32 B | `dma-fence.h`; every field is state; flag bits now v7.2's (SEQNO64=2, SIGNALED=3; 0.1 had SIGNALED=2) | | `dma_fence_ops` / `dma_fence_cb` | 7 / 2 | 56 / 24 B | - | all ops called; `cb_list` is a real list | | `dma_resv` / `dma_buf` | 2 / 8 | 48 / 88 B | 16 / 16 B | unchanged | | `drm_printer` / `va_format` | 7 (9) / 2 | 56 / 16 B | - | `drm_print.h`, new | The tails are in the version handshake (`DRM_FACADE_TAILS`, appended after the struct sizes; `drm_facade_version` keeps its size so an older driver still reads the refusal). `gen_facade.py` now follows anonymous members (fence lock/list unions, `managed.*`, `gpuva.*`): 40 sizes, 256 offsets. ## What changed - **Fences** (`drm_core::cfence`, host-tested): Linux's rules on the structure — the lock (extern or inline) protects `cb_list`; signalling sets the bit, replaces the list by the timestamp and runs callbacks under the lock; `enable_signaling` once, `signaled` polled, `wait`, `set_deadline`, names; by-hand signalling (i915 breadcrumbs) works. The core's view: a driver fence has a *shadow* core fence signalled by a *bridge* callback on its `cb_list` (added when the core first takes the fence; holds a reference until it ran); a core fence handed out is a *proxy* with core ops whose `enable_signaling` arms a *relay* (takes a reference only while the count is non-zero; the release removes it, waiting for a running one). Last-reference release runs inline only in thread context, without the fence's lock, and not for a proxy with an armed relay; otherwise it is parked through a node in the tail on the module domain's raw list (no allocation) and run by its task queue — this fixes R5a's same-CPU spin. Unsignalled fences released with callbacks signal them with `-EDEADLK` (Linux). New: `dma_fence_init64`, `signal_locked`, `signal_timestamp(_locked)`, `check_and_signal(_locked)`, `enable_sw_signaling`, `default_wait`, `set_deadline`, `driver_name`, `timeline_name`, `drm_illumos_fence_put_deferred`, `drm_illumos_fence_barrier` (drains serialised, so the barrier waits for one in progress); inline helpers (`dma_fence_spinlock`, lock macros, `is_later`, `get_status_locked`, ...). A detach runs parked releases before counting the device's fences. - **GEM:** `drm_gem_object.refcount` is the only count (`GemObject::ext_count`, `drm_core::refcount`: every `GemRef` counts there; no resurrection; upgrades under the offset lock). `kref_read`/`kref_get`/`kref_get_unless_zero` in `drm_facade_types.h` (lists whose lock keeps the memory; no RCU grace period). `drm_illumos_gem_for_each_handle(file, fn, arg)` walks a file's handles under the G8 hook lock over a snapshot; `drm_gem_object_lookup` works from the callback. - **Managed resources** (`drm_managed.h`, `drm_core::managed`): `drmm_add_action(_or_reset)`, `drmm_release_action`, `drmm_kmalloc/kzalloc/kmalloc_array/kcalloc`, `drmm_kstrdup`, `drmm_kfree`; released newest first at the last `drm_dev_put()`, after unregister and the driver's `release`, before free. `gfp_t` bits are the facade's. Not provided: `drmm_mutex_init`, `drmm_alloc_ordered_workqueue` (need R5c). - **Printing** (`drm_print.h`, `drm_core::print`): v7.2 categories (bit numbers), `drm_printer` with info/dbg/err printers, `drm_printf/vprintf/puts`, `drm_err/warn/notice/info(_once)`, `drm_dbg_*`, `DRM_DEV_*`, `DRM_*`; variadic entry points are header statics ending in `drm_illumos_vprintk()` (vsnprintf into 512 B, cut with "...", `dev_err`/`cmn_err`; errors/warnings console+log, notices both, info/debug log only). Knob: `set drm_core:drm_debug = 0x6` (R5a's header named the wrong module) or `echo 'drm_debug/W 0x6' | mdb -kw`. - **cdrmnull:** GPU-like timeline (soft interrupt completes, signals only enabled fences; `signaled` polls; driver lock at softint priority; second context), `FENCE_OPS_SELFTEST` (15 checks), `HANDLES`, `PRINT_SELFTEST`, drmm order check, `_fini` barrier. `FENCE_SELFTEST` RELEASED/QUEUED now check Linux's release rules. - Tests: `tools/test/drm_r5b_test.c`, `run-r5b-test.sh`; `run-r5a-test.sh` mismatch probes now cover any major and a same-size `drm_device` with a smaller tail (and the script is executable). ## Evidence - Host: `cargo test -p drm-core -p drm-uapi -p drm-facade` 154 + 2 + 1 (new: 14 cfence tests with a mock driver and threads, relay/release races forced with test hooks, sibling-proxy last put; raw deferral list; refcount incl. upgrade-vs-last-put and underflow; managed order/early release/add_or_reset/misuse/4 threads; print rules; handle walk vs 4 threads closing/creating), `virtio_gpu --no-default-features` 6; clippy `-D warnings`, fmt, `cargo doc -D warnings`, `check-generated.sh` clean. - hephaistos, fresh overlays of `golden-20260930` (illumos `7e802e88a3`), libdrm 2.4.134 p5p in the overlay, 2 vCPU / 3 GiB, one guest at a time: - `r5b-run2` at `c24a58e` (kernel code identical to HEAD): `run-r5b-test.sh` PASS — `run-r5a-test.sh` PASS (168 passing checks: R0–R4 on all three drivers, R5a cross-driver ×4, G6, R1 two cdrmnull, mismatch probes incl. tail), drmm reverse order at 7/7 cdrmnull detaches, `drm_r5b_test` 47/0 (15 fence-ops checks incl. 200 interrupt-race rounds; array of two driver fences, chain out of order, EIO sync file, 3000 walks vs 1592 concurrent handles, refcount 1→2→3→4→3→2), all log paths with `drm_debug` 0 and 0x6, 0 timeline releases in interrupt context, `run-g8-test.sh kernel` PASS. Logs `logs/hephaistos/illumos-drm/20261003-r5b-run2-*-c24a58e.log`. - facade-check with guest gcc 14.3.0: 296 lines, 0 differences (`20261003-r5b-facade-check.log`; committed `facade_layout.illumos.out`). kernel-check with `misc/drm_core` (+CTF) loaded: ddi-sys 53/60, 185/185, 984/984; drm-facade-sys sizes 40/45, offsets 244/256 (rest are members of anonymous types, checked via parents), symbols 123/123; private 3/3, 46/46, 15/15 (`20261003-r5b-run2-kernel-check-c24a58e.log`). - `r5b-run3` at `b3fab03`: `run-g7-test.sh` all 13 phases PASS (81 checks, gfx-drm combo p5p with the reverse guard), then with the new stack selected `run-r5a-test.sh LEGACY_GUARD=skip`: first run failed the unprivileged opens of cdrmnull's stale `0600` nodes left by the uninstalled package (the known G7 open issue; identical failure set to G7's run), second run PASS (76). Logs `20261003-r5b-run3-g7-*.log`. - `r5b-run1` (earlier iteration at `a94a69c`/`0ca2fae`): found the same-context merge test bug (fixed by the second timeline context). No panic in any run; consoles in `logs/hephaistos/illumos-drm/r5b-console/`. - CTF shadow rebuilt on oibldofgate for the new headers (`artifacts/illumos-drm-r5b/drm_facade_ctf.o`). ## Review Two rounds against the anima-review bar (Opus). Round 1 rejected: relay/release race test did not reach the paths (now forced and asserted), a sibling proxy's callback could make an inline release wait for its own queued relay (armed proxies now always release on the task queue; regression test), GEM external count logic untested (moved to `refcount`, host-tested), barrier vs concurrent drain (drain lock), kref/RCU doc claims (helpers added, docs limited), drmm log baseline. Round 2 approved; its notes (serialise relay-hook tests, barrier context doc, test count) are fixed in `b3fab03`. ## Deviations / open issues - Fence functions are out-of-line; a fence lock must not be a high-level spin mutex; `ops` kept after signalling; `release == NULL` frees nothing (`dma_fence_free` needs R5c's kfree); the core enables signalling when it takes a fence; sync objects return proxies (no pointer identity with the driver's fence). - Not yet in the facade: `dma_fence_array_create`, `dma_fence_chain_*` walkers, `dma_resv` iterators and acquire contexts (`lock` stays a placeholder), `drm_dev_enter/exit`, `drmm_mutex_init`, `drm_WARN*`, line/coredump/seq_file printers; GEM `handle_count`/`name`/`dma_buf` stay in the core; no Rust wrapper for the handle walk (no Rust user). - `drmm_kmalloc(dev, 0, …)` returns NULL; messages logged in interrupt context are cut by illumos at 200 bytes. - The packaged drivers' `0600` nodes (G7 open issue) remain. - CI not run here (owner's CI); the illumos job's facade-check uses the regenerated `facade_layout.illumos.out`.
gfx-drm's legacy stack (misc/drm, i915) and this repository's
(misc/drm_core and its drivers) install together; each core refuses to
load while the other is loaded. drmadm(8) chooses which one the system
loads at boot:

- drmadm status: the selection (in effect since the boot or from the next
  one), the installed packages of both stacks, their loaded modules, the
  Intel GPU aliases bound to i915 and to i915n (the name reserved for the
  Intel driver of this stack) and every exclude: line naming a module of
  either stack.
- drmadm select legacy|new: refuses while a module of either stack is
  loaded or another drmadm holds its lock; moves the Intel GPU aliases
  (those driver/graphics/drm declares for i915, those bound to i915 or
  i915n, and those recorded before) with update_drv -n, which edits
  /etc/driver_aliases without attaching anything, leaving them unbound
  when the target driver is not installed; writes
  /etc/system.d/driver:graphics:drmadm with the selection, the alias set
  and exclude: lines for the other stack's modules (class/name and bare
  name, so that an explicit-path modload is excluded too); and runs
  bootadm update-archive. -R root works on a mounted boot environment.

The package driver/graphics/drmadm ships it with its manual page;
driver/graphics/drm-core requires it, and it stays when the new stack is
removed, so that its fragment can still be changed.
The guest test of both DRM stacks on one machine, in phases across
reboots: the baseline on OpenIndiana's driver/graphics/drm, the
installation of both stacks into a new boot environment, both guards
(explicit paths, dependent drivers, concurrent loads of both cores),
drmadm select new and legacy with their refusals, the excludes in effect
after each boot, the stale /dev/dri links after a switch, libdrm's view of
the devices and drmadm -R on a mounted boot environment.

run-r5a-test.sh skips its legacy guard check with LEGACY_GUARD=skip, for a
system where the new stack is selected and the legacy core is excluded.
The section describes both guards and why they cannot deadlock, drmadm's
alias and exclude mechanism (update_drv -n, the /etc/system.d fragment,
the boot archive), the IPS consequences, the device links and recovery.
docs/x230.md is the operator note for the ThinkPad X230.
The first G7 guest run found that misc/drm_core's guard counted gfx-drm's
misc/drm as loaded after a refused load of drv/i915: misc/drm, loaded as
i915's dependency, failed its _init and stayed in memory, not installed,
invisible to modinfo and not removable with modunload, so misc/drm_core
refused until misc/drm was loaded again. illumos-ddi 983751f counts a
module as loaded when it is installed, or in memory and busy (during its
_init or _fini). gfx-drm's guard uses the same rule. The README section
describes it.
syslogd folds repeated warnings into "last message repeated", so the
guard checks count messages in ::msgbuf. The guards phase checks that a
core left in memory by a refused dependency (drv/i915 under
misc/drm_core, drv/virtio_gpu under misc/drm) does not keep the other
core out, and the races start the second load after a delay that sweeps
0 to 98 ms, alternating which core starts first, and must be won by both
cores at least once.
Round-1 review findings:

- update_drv -d -i with a list of aliases matches no line of
  driver_aliases (delete_entry compares the whole argument) and still
  exits 0; drmadm now removes one alias per call and reads the file back.
- The fragment, which records the alias set, is written before the
  aliases move and rewritten (with a new time) whenever they move; if a
  move fails, drmadm gives the aliases back and restores the previous
  fragment.
- select says that devfsadm -C after the next boot removes the other
  stack's /dev/dri links; status counts them; the manual page and
  docs/x230.md include the step.
- Only exclude and EXCLUDE count as exclude lines, as in the kernel; -R
  needs an absolute path and is passed to update_drv quoted.
- The manual page refers to i915(7D) and names vgatext without a manual
  page; the README and drmadm.p5m say what happens to a driver action
  with a taken alias (add_drv refuses it, IPS leaves the driver
  unregistered); the guard's exclusion argument rests on mod_lock.

run-g7-test.sh gains the phase i915n (aliases to a stand-in i915n and
back), and illumos-ddi is pinned at 6e65077.
/bin/sh on illumos is ksh93, which predefines the alias r (hist -s), so
$(r etc/driver_aliases) ran the history command and every awk over a root
file read standard input. Found by the G7 guest run.
With gfx-drm's corrected SUNW_drm_link removal rule, devfsadm -C removes
the /dev/dri links and then the empty directory, so the state check after
the test compares the links only and treats a missing directory as an
empty one. Found by the G7 run of run-r5a-test.sh.
- status shows the version for packages of a publisher that is not
  ranked first (pkg list prints NAME (PUBLISHER) VERSION for them).
- rollback first checks whether the aliases went back: only then is the
  previous fragment restored (or the new one removed); otherwise the new
  fragment, which records them, stays and drmadm says so. A HUP, INT or
  TERM while the aliases move rolls back too.
- README and drmadm.p5m: IPS refuses to install a driver action whose
  alias another driver action in the image declares; vgatext binds
  through pciclass,030000; select legacy gives i915 at least its
  package's aliases; after pkg fix or a pkg update of driver/graphics/drm
  that adds i915 aliases, select new again (also in drmadm(8) and
  docs/x230.md).
- run-g7-test.sh: overlap holds one core in modinstall with a DTrace chill
  (after mod_loaded and mod_busy are set, before _init) and loads the
  other, which must refuse; rollback makes every update_drv -d fail on a
  scratch root and checks the exit status, the aliases and the fragment;
  recover-remove and after-recover exercise the hand recovery.
README: G7 results and open issues; run-g7-test.sh: start the overlap's second load once the chill holds
Some checks failed
ci / check-hosted (pull_request) Failing after 19m38s
ci / kmod-illumos (pull_request) Failing after 58m39s
98ea76c50f
The overlap phase waits for DTrace's report that the chill holds the first
core (switch rate 1000 Hz) before it loads the second; with a fixed 150 ms
delay the second load came before the first reached modinstall. docs/x230.md
notes that a selection does not reach a boot environment pkg created
before it.
dma_fence's public fields are now what Linux drivers expect: the lock
(extern or inline) protects a real cb_list of struct dma_fence_cb, the
flags (Linux v7.2's bit numbering, SEQNO64 included; facade 0.1 had the
signalled bit at 2) are changed atomically, signalling replaces the list
by the timestamp and runs the callbacks with the lock held, and the
driver's ops are called: enable_signaling (once), signaled (polled),
wait, release, set_deadline and the names. A driver that signals by
hand, as i915's breadcrumbs do, reaches the core like any signaller.

drm_core::cfence implements this on the bindgen structure and links it
to the core's fence objects: a driver's fence gets a shadow core fence
and, when the core first takes it, a bridge callback on its cb_list that
holds a reference until it ran; a core fence handed out gets a proxy
whose ops are the core's own (enable_signaling arms a relay on the core
fence, signaled asks it). The last reference is released at once only in
thread context by a thread that does not hold the fence's lock; from
interrupt handlers, callbacks, the bridge and the relay it is parked on
the module domain's raw deferral list (a node in the private tail) and
released by its task queue, which removes R5a's same-processor spin.
Unsignalled fences released with callbacks pending signal them with
-EDEADLK, as on Linux.

New facade functions: dma_fence_init64, signal_locked, signal_timestamp
(_locked), check_and_signal(_locked), enable_sw_signaling, default_wait,
set_deadline, driver_name, timeline_name, drm_illumos_fence_put_deferred
and drm_illumos_fence_barrier. cdrmnull's FENCE_SELFTEST now checks the
Linux release rules (RELEASED runs the callback with -EDEADLK, QUEUED
runs the callback behind a last put). Host tests: 13 for cfence with a
mock driver and threads, one for the raw deferral list.
A facade GEM object's refcount is now its only reference count: every
GemRef of an object with GemObject::ext_count (FObj returns the C
structure's kref) counts there, the core's allocation holds the object
with one reference until the count reaches zero, and the count never
comes back from zero (drm_core::refcount, host-tested from six
threads). Drivers can therefore read it and take references with
kref_get_unless_zero() from lists that hold none, as i915's shrinker and
lookups do, and drop them with drm_gem_object_put(). The devmap paths'
upgrades go through the same rule.

drm_illumos_gem_for_each_handle() replaces walking drm_file.object_idr:
it calls the driver for every handle and object of a file with the
file's hook lock (G8) held, so no handle is created or closed during the
walk, over a snapshot of references taken under the handle table's
lock; lookups still work from the callback. objects::Locked::snapshot
is host-tested against four threads closing and creating handles.
drm_gem_is_imported() is added as an inline.
drm_device gains Linux's managed (resources, final_kfree, lock) and
open_count. drm_managed.h provides drmm_add_action(_or_reset),
drmm_release_action, drmm_kmalloc/kzalloc/kmalloc_array/kcalloc,
drmm_kstrdup and drmm_kfree over drm_core::managed: nodes on
managed.resources under managed.lock, released newest first when the
device's last reference goes, after the driver's release callback and
before the structure is freed (host-tested: order, early release and
free, add_or_reset on failure, misuse, four threads). gfp_t and its bits
are the facade's (drm_facade_types.h).

drm_print.h follows Linux v7.2: categories are bit numbers (the bits of
drm_debug are unchanged), struct va_format and struct drm_printer with
the info, debug and error printers, drm_printf/drm_vprintf/drm_puts,
drm_dev_printk, the drm_err/warn/notice/info(_once) and drm_dbg_* family,
DRM_DEV_* and DRM_* macros. The variadic entry points are static
functions of the header; everything ends in drm_illumos_vprintk(), which
formats with vsnprintf(9F) into a 512-byte message and logs it with
dev_err or cmn_err (drm_core::print holds the level, routing, category
and truncation rules, host-tested). The documented knob is
"set drm_core:drm_debug" (R5a's header named the wrong module).

Facade version 1.0: the size list gains drm_printer and the private
tails of drm_device, drm_file, drm_gem_object, dma_fence, dma_resv and
dma_buf (DRM_FACADE_TAILS), checked by drm_facade_check() and
drm_facade::check_facade(); struct drm_facade_version keeps its size, so
an older driver still reads the refusal. gen_facade.py now follows
anonymous members (drm_device.managed.*, the fence's lock and list
unions, gem gpuva.*): 40 structures, 256 offsets. run-r5a-test.sh's
mismatch probes work for any major version and add a same-size,
smaller-tail drm_device.
The test timeline's fences now behave as a GPU driver's do with Linux's
fence ops: the soft interrupt completes a due fence ("hardware" done,
which ops->signaled reports) and signals it only if somebody enabled
signalling (ops->enable_signaling: a callback, a wait, or the core
taking the fence into a sync object, reservation or sync file); the
fences use a driver lock at the soft interrupt's priority. Releases are
counted (none may run in interrupt context), _fini waits for queued
releases (drm_illumos_fence_barrier) and refuses while a timeline fence
lives, and the device's release barriers before its locks go.

New test ioctls: FENCE_OPS_SELFTEST (15 checks: enable once and false,
polling, the wait op, the default wait and polling-only completion on
the timeline, deferred release from the soft interrupt, last puts from a
callback and under the fence's lock, EDEADLK, i915-style signalling by
hand reaching a sync object, names and deadlines, 64-bit sequence
numbers, callbacks under the lock, a relay between two structures, and
an add/remove/signal race against the soft interrupt), HANDLES (the
handle walk with lookups and early stop, the live GEM reference count
and a kref_get_unless_zero round trip) and PRINT_SELFTEST (drm_printer
with a driver printer, the drm_* and DRM_* macros, printers, once, a cut
message). Each device registers managed resources at attach and checks
their release order when it goes.

drm-facade gains gem::for_each_handle, the safe walk for Rust drivers.
drm_r5b_test drives cdrmnull's FENCE_OPS_SELFTEST (one line per check),
puts the driver's timeline fences into a binary sync object, a merged
sync file (an array), a timeline sync object (a chain, signalled out of
order) and an error sync file, checks the core's parked releases, walks
the file's handles (lookups, early stop, 3000 walks against a thread
creating and closing handles), follows a buffer's live reference count
through name, dma-buf and mapping, and checks drm_printf through a
driver printer. run-r5b-test.sh runs run-r5a-test.sh (with the drmm
release order of every cdrmnull detach), then cdrmnull alone with the
system-log checks of every logging path with drm_debug 0 and 0x6, the
timeline's releases (none in interrupt context), the drmm order at
detach and cdrmnull's _fini, then run-g8-test.sh kernel. The stateful
core modules cfence and managed join drm-test-lib.sh's list of symbols
no driver may carry.
Fences of one context merge into the later one (Linux's
dma_fence_unwrap_merge), so drm_r5b_test's merge of two timeline fences
was a single fence and its array check failed on the guest. FENCE's new
CDRMNULL_FENCE_CONTEXT2 puts the fence on the timeline's second context;
the test merges one fence of each and prints FILE_INFO.
- A proxy whose relay is armed is always released by the task queue: a
  callback on another proxy of the same core fence could drop its last
  reference while its relay is queued behind that callback on the same
  thread, and the inline release would wait for the relay for ever
  (host test a_sibling_proxys_callback_drops_the_last_reference).
- proxy_release_races_the_relay forces each path with test hooks (relay
  started but not holding a reference: the release waits and the relay
  finds zero; relay holding the last reference; free races) and asserts
  every path was taken and each callback ran once with the right error.
- FenceDomain drains are serialised by a lock that barrier takes, so a
  barrier no longer returns while another drain still runs a release.
- The external GEM count's KArc logic moves to drm_core::refcount
  (clone_ref, release, upgrade over ExtCounted), host-tested with upgrades
  under a table lock racing clones and the last put, and an underflow;
  GemRef uses it.
- drm_facade_types.h provides kref_read, kref_get and
  kref_get_unless_zero; drm_gem.h and the README limit lockless use to
  driver lists whose lock keeps the memory (no RCU grace period);
  cdrmnull uses the facade's helpers.
- A driver fence that dma_fence_init signalled for lack of memory reaches
  sync objects and sync files as a signalled fence with -ENOMEM.
- Locked::snapshot allocates before taking the handle table's lock.
- run-r5b-test.sh counts the drmm messages of its own run only.
- drm_facade:💎:for_each_handle is gone (no caller, no test).
README: R5b results
Some checks failed
ci / kmod-illumos (pull_request) Failing after 31m19s
ci / check-hosted (pull_request) Failing after 1h27m9s
501c309031
toasterson changed target branch from g8-reimport to main 2026-10-05 16:47:15 +00:00
Merge branch 'main' into r5b-layouts
Some checks failed
ci / check-hosted (pull_request) Successful in 7m56s
ci / kmod-illumos (pull_request) Failing after 21m33s
e75543e304
No conflict. Forgejo reported PR 13 as not mergeable after PRs 10-12
were merged (r5b-layouts already merges g7-coexist, so the branch and
main have more than one merge base); merging main here gives the PR a
single base. The tree equals main with r5b-layouts merged.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
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!13
No description provided.