R1 DMA drill: reproduce the root-node DMA panic, fix the /devices snapshot race #3
Loading…
Reference in a new issue
No description provided.
Delete branch "r1-dma-drill"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Stacked on #2 (
r1-gem). This adds the DMA drill, a test aid that reproduces on purpose the kernel panic from R1 trial 2 so that a crash dump could be taken, and fixes a race inrun-r1-test.shthat the runs exposed.Changes
drm::Driver::gem_dmalets a driver choose at attach time how GEM object memory is bound for DMA. The default isDMA_ATTRagainst the driver's own node.GemDma::againstnames another node; it isunsafeand documented as drill-only.rdrmnullreads the string propertydma-modefrom its.conf:none: the default and the current behaviourself: bind against its own node; needs a parent nexus that does DMA (tools/test/rdrmnull-dma-self.conf,parent="/")root-node: allocate the handles againstddi_root_node()from the pseudo node (tools/test/rdrmnull-dma-root-node.conf)run-r1-test.shinstalls the.confnamed byRDRMNULL_CONF. The R1 test expects bound objects when the configuration asks for DMA.run-r1-test.shnow takes the/devicessnapshot afterdevfsadm -C. devfs drops theclone@0nodes of idle drivers (bridge, e1000g, llc1, logindmux, ptm) about 30 s after boot, and thedevfsadm -Cat the end of the script recreates them. The first run on a freshly booted VM therefore failed the/devicescomparison with five entries unrelated to rdrmnull.Runs
The runs used disposable KVM guests: OpenIndiana Hipster 2025.10 updated to osnet 23197, which is
illumos-cda5b9a49f, the same kernel as oibldofgate. Each guest had a 2 GiB dump zvol and a 2 GiB swap zvol.xtask kernel-checkpassed, with mdb answers identical to oibldofgate.r1-gemc4a8512, first run in a fresh VM/devicesrace abovec4a8512, repeat in the same VMc4a8512, second fresh VMc80b75c,noneb2430d0(this head),none, first run in a fresh VMself(root nexus parent)/etc/path_to_instresidueIn the
selfrun, the 1,228,800-byte dumb buffer had 300 cookies, one per page, summing exactly. Four 16 MiB objects had 14,639 cookies. All kstats and vmem arenas returned to zero. The instance entry"/rdrmnull@0,0" 0 "rdrmnull"stays in/etc/path_to_instafterrem_drv -C, afterdevfsadm -Cand across a reboot.With
selfon a pseudo node, everyCREATE_DUMBthat needs memory fails, because the pseudo nexus returnsDDI_DMA_BADATTR. There was no leak and no panic.The panic
The
root-noderun reproduced the panic on the first attempt, at the firstCREATE_DUMBthat allocates memory (100x1, bpp 12):mdb on the dump established the mechanism:
ddi_dma_alloc_handle(root)uses the root's own bus_ops, because the root node is rootnex's node, androotnex_dma_allochdl(root, root)hands out a handle withdmai_rdipset to the root.ddi_dma_addr_bind_handlespecial-cases the root for the nexus hop, but it takes the function fromDEVI(rdip)->devi_bus_dma_bindfunc.ddi_optimize_dtree()fills that field only for nodes with a parent, so for the root node it is 0 and the call goes to address 0.ddi_dma_buf_bind_handleandddi_dma_unbind_handlehave the same gap.ddi_dma_bindhdl()handles the root correctly.Judgements:
ddi_dma_alloc_handle(9F)requires "the device's dev_info structure".unsafe DevInfo::from_rawto reach the root node. The proposal is a runtime guard inDmaHandle::alloc(refuse a node without a parent) and a non-Copyown-node token for DMA.The full analysis, with the mdb transcript and the reproduction recipe, is in the workspace note
notes/illumos-drm-dma-panic-20260928.md.The drill hook (
GemDma::against) exists only to reproduce this. If this PR is merged, consider putting it behind a test-only feature.Update 2026-09-28: the own-node rule in illumos-ddi and the
panic-drillfeatureillumos-ddi now makes this misuse impossible in safe Rust (illumos/illumos-ddi#2, branch
dma-own-node, stacked on #1). This branch pins it atb24c307298. The later commit on that branch,dba0697, changes docs only.API
ddi::Driver::attachreceives addi::OwnNode, the non-Copy, non-Clonecapability for the driver's own node.DmaHandle::newandtry_newtake&OwnNode.DmaError::NotOwnNode(ENXIO) before calling the kernel.This PR (
3a1d1f2,62710d9,51939da)drm::Device::attach(node: OwnNode, driver, ctx)hands the node toGemCore, which binds object memory against it.SysMem::newtakes&OwnNode. DMA errors map throughFrom<DmaError> for Errno.GemDma::againstnow exists only with the test-only cargo featurepanic-drillofdrmandrdrmnull, which is off by default. It allocates with rawddi_dma_alloc_handle(9F), because illumos-ddi's guard would refuse the root node.rdrmnullrefusesdma-mode="root-node"at attach withWARNING: rdrmnull0: dma-mode "root-node" panics the system on purpose and needs rdrmnull built with the panic-drill feature; this build refuses it.ddi_root_node.cargo xtask build rdrmnull --features panic-drill.xtask buildgained--features.--features rdrmnull/panic-drillso that the drill keeps compiling.Tests. Disposable Hephaistos guests from the golden overlay, illumos
cda5b9a49f, 2 vCPUs, 3 GiB. Logs are in the workspace underlogs/hephaistos/illumos-drm/20260928-ddi-own-node-*.dma-modenonefail=0dma-mode="self", root nexus parent/etc/path_to_instresidueroot-noderoot-nodepanic-drillCREATE_DUMB; savecore wrotevmdump.0(118 MB, FMASUNOS-8000-KL)In the
panic-drillrun, the panic string isBAD TRAP: type=e (#pf Page fault) ... addr=0 occurred in module "<unknown>" due to a NULL pointer dereference, withpc=0andrdi=rsi=fffffe04e7db3c28, the root node. The caller frame isSysMem::alloc, the drill path. The dump was deleted with the guest, because the note's dump already covers it.On the host (macOS, Rust 1.97.1),
checkandclippy -D warningspass with and without the feature, and so dofmt,doc -D warnings,cargo test -p drm -p drm-uapi(42 + 1 passed) anduapi-check.