Repository navigation
drm/asahi: G17 (Neo, T8140) driver update for 7.2.11 - #229
Conversation
Bring drivers/gpu/drm/asahi up to the aurora-silicon/linux state at d3cf19b, the base of the G17 performance series that follows, merged three-way on top of integration/aurora with the 7.2.10 merge point 44d5afc as base. integration/aurora's own work is kept: the G15 backend, the M3 T8122 runtime, UAT adoption, deferred-close binding sequences (#214) and the 7.2.11 shared-code adaptations. The G16 (T8132) backend is not carried. This carries the G17 work merged on the aurora side since 7.2.10, including the T6040 (M4 Pro) and 25G83 firmware support and the render replay/recovery series by Jonathan Goyvaerts, the watchdog, replay and backing-population changes by Ryan Murray, the tracked VM lifetime work by Eryk Wieliczko, and the G17 queue, capacity and timestamp changes by DjDeveloperr (aurora-silicon/linux AsahiLinux#382, #391, AsahiLinux#397, AsahiLinux#400). Adaptations to 7.2.11: - module parameters return references; THIS_MODULE replaces kernel::module::this_module; a local raw_try_init replaces the newer pin-init helper; of_device_table! takes the MODULE_OF_TABLE name. - regs.rs keeps G17's sgx window as Devres<IoMem>, as 7.2 has no DevresIoMem. - The shared feature flags keep G17's opt-out of soft faults. - G15 and the M3 runtime use the shared timestamp::Buffer and the reserved-table aware root walk; omacom's G15 vm_closed hook becomes vm_destroyed, next to the G17 vm_closed. - Crate and helper pieces the driver needs: the shmem SG lease and pages_no_reclaim (drm_gem_shmem_helper), DRM file memory stats, fdinfo and is_current_master, and the mm RSS counter. Signed-off-by: DjDeveloperr <hey@djdev.me>
A compute command on a bound queue went through every admission step on each submission. ensure_compute took the device mutex for a replay and witness pass, shared_compute took a preparation lease and the mutex again, and build_compute did both once more to find its queue already bound. compute_packet then took a third lease and the mutex a fourth time, replaying compute again before publishing. Compute also never published from its submit ioctl, so even a command with nothing to wait for went through the scheduler worker first. Its completions never fed the CPU-capacity detector, so the host loop feeding a compute-bound GPU ran at the lowest CPU performance level. Check what ensure_compute establishes under the one device-mutex section that publishes, and run the admission passes only when that check fails. Publish a dependency-free compute command from its submit ioctl when its queue is bound and nothing has to wait, as renders do. Keep a separate backlog per engine so that a command waiting in one engine's scheduler does not stop the other engine's inline publication. Report compute dispatch envelopes to the detector as render passes are. (cherry picked from commit ad691cd) Signed-off-by: DjDeveloperr <hey@djdev.me>
A render whose pair was out on loan slept 200 us before trying again. The event worker lends each pair with completions out for an off-lock observation after every completion event. At queue depths of two and more, completions and submissions interleave, so a submission often found its pair on loan for a few microseconds and then slept the full 200 us. Count the device's change notifications. Every pair return already notifies under the device mutex. Wait for the notification under that mutex, unless one arrived since the attempt started, with the same deadline as before. (cherry picked from commit 1121ae3) Signed-off-by: DjDeveloperr <hey@djdev.me>
Object::initialize already clears the complete descriptor before calling its encoder. Fill that zeroed representation directly instead of clearing the same 16 KiB again. Name the encoder write_zeroed to make its precondition explicit. The firmware bytes and validation remain unchanged. The matched module passed the targeted compute/synchronization gate and checked GPU readbacks. (cherry picked from commit abd8593) Signed-off-by: DjDeveloperr <hey@djdev.me>
A publisher waiting for a physical compute channel reclaimed idle USC backing even though freeing backing cannot create another QID. The normal warm-pool limit could also evict a retired channel while another runnable publisher was waiting to reuse it. Subsequent handoffs repeatedly allocated and cleared fresh pool backing. Distinguish channel pressure from backing allocation pressure. Track unbound compute admission through publication, cancellation and error, and retain backing for the existing compute channels while that demand remains. The last publisher schedules normal idle reclamation. Allocation-pressure reclamation, the idle warm-pool limit and expiry remain in force. Expose the admission count in the capacity diagnostics. On Neo, the matched 16-client probe drops pool allocations from 4888 to 53 and median submission time from 257 to 109 us. The Asahi KUnit suites pass 67/67; the targeted compute/synchronization gate passes 116 with 18 skips. Full mustpass qualification remains pending. (cherry picked from commit 3ef3720) Signed-off-by: DjDeveloperr <hey@djdev.me>
Fence arrays require every member to signal. During syncobj timeline walks, a pending predecessor already proves that the array is incomplete, yet the lazy signaled callback polls every successor too. Return false immediately at that predecessor, keeping callback accounting and the enabled-signaling path unchanged. Add checks for out-of-order completion, avoiding successor polls, and callback installation after a partial lazy poll. Remove the obsolete signal-on-any description from the constructor documentation. (cherry picked from commit f35d7ea) Signed-off-by: DjDeveloperr <hey@djdev.me>
Compute-only contexts that yield their physical channel still take the device mutex in the inline submit path before falling back to admission. Track an advisory binding hint to skip that attempt. Actual publication retains the locked VM, epoch and binding checks, and scheduler admission ordering is unchanged. Mark contexts with validated rendering before any batch member is queued, so mixed-engine queues retain the original inline path. Validated with 69 Asahi KUnit cases, targeted compute/sync CTS (116 pass, 18 skip), mixed device churn and 16-client concurrent GPU readbacks. Matched 16-process quick submission time improves from 117.27 to 89.55 us; paired render follow-ups remain at control performance. (cherry picked from commit 319805f) Signed-off-by: DjDeveloperr <hey@djdev.me>
Only bypass an unbound compute-only inline attempt when multiple publishers are waiting for admission. A lone admission retains the original probe and its latency behavior. Actual publication still uses the existing locked state checks. Matched quick controls retain queue signaling latency (174.1 to 174.5 us) while 16-process submission time improves from 117.3 to 90.7 us. All 20 performance rows complete successfully. Checked mixed/concurrent GPU readbacks, 69 Asahi KUnit cases and targeted compute/sync CTS also pass. (cherry picked from commit 5a4822c) Signed-off-by: DjDeveloperr <hey@djdev.me>
Recognize eligible singleton compute producers through their exported aggregate fences. Order queued consumers after actual producer publication using the existing DRM scheduled fence, then validate the current physical ticket and emit firmware waits. Preserve CPU waits for unsupported dependencies and host outputs, and revalidate recovery epochs before acquiring consumer capacity. Retain source binding loans through consumer retirement, propagate source errors before dependent completion, and release failed submission pins when the last runtime owner retires even if exported fence references survive. Keep bounded member counters in byte atomics so submission state retains its original allocation class. Validated with 78 KUnit tests, the compute/synchronization CTS gate, chained readback, process exits and mixed compute/render pressure. Fixed-core ordinary compute matches the control while the queue-hop path improves. Existing intermittent unrelated-client loss during forced recovery remains unresolved. (cherry picked from commit 026065c) Signed-off-by: DjDeveloperr <hey@djdev.me>
… queued Compute retirement removes the producer from its queue's active list under the device mutex, but signals the producer's fence only after the lock is dropped. A consumer published in between found neither an active producer nor a completed one, fell back to the scheduler, and every later command in the chain then waited for its producer's publication through the scheduler. Mark the singleton firmware source retired as its successful retirement is queued (errors are already recorded there), after rechecking its own inputs. sync.signal queue-to-queue (timeline, 2 queues): scheduler fallbacks for chain consumers 57% -> 0.1%. (cherry picked from commit a3be7f63500c76d117000ee1474713e27889aabe) (cherry picked from commit 89c8873) Signed-off-by: DjDeveloperr <hey@djdev.me>
Early publication was limited to queues with fewer than two outstanding commands, so every deeper queue paid a scheduler worker hop per command and cross-queue chains advanced one scheduler round trip per link. The backend already falls back to the scheduler behind any backlog and on every capacity miss, which preserves submission order, so the depth limit only added latency. sync.signal queue-to-queue (timeline, 2 queues) 94.1 -> 56.2 us/hop together with the preceding change; compute and render depth rows unchanged. (cherry picked from commit 8508510641edcedb5ae1417f560cdff4da646bda) (cherry picked from commit 3ff4f4b) Signed-off-by: DjDeveloperr <hey@djdev.me>
Render descriptors already come from the retained object pool with a firmware-cached mapping; compute descriptors were mapped uncached, so the firmware read every compute descriptor from memory on each command. Allocate the compute descriptor ring from the same pool (fully cached prefix, retained until processor stop) and give the hardware an uncached canonical low alias for the descriptor address it fetches. compute.light d64 27.4 -> 24.3 us/submit, queue-to-queue hop 58.1 -> 54.8 us; compute/sync CTS gate, cross-queue order/data 10k, dependent reads under pressure and mixed churn pass. (cherry picked from commit c85c050ae2ef77cbeebddb70c6602db477675090) (cherry picked from commit a0dfc3c) Signed-off-by: DjDeveloperr <hey@djdev.me>
The compute channel budget was static: compute could only take a QID while every one of the 58 possible execution contexts could still install a render pair, which always left exactly ten compute channels. A client without a channel then made every idle channel of every live client shareable before binding one, so sixteen compute processes evicted each other on nearly every submission (map/unmap and TLB invalidation churn, ComputeReserve refused 52k times in one benchmark pass). Installed QIDs never change engine before processor stop, so a compute channel still permanently narrows render. Size the render reserve by the logical queues that are actually open plus 24 queues of headroom, and refuse a new logical queue only when its dedicated render pair would no longer fit. With every context open this is the previous rule. Rebinding now prefers a channel of a closed owner, then a vacant one to re-arm, then a fresh QID, and only then shares the least recently used idle channel of a single live client. gpubench on Neo (us/submit, matched baseline): compute.light procs d16 92.0 -> 33.4; compute.light queue rows unchanged (d64 27.4 -> 26.5). Stress gate passes. (cherry picked from commit bf7ae5bf04b7d3f14b5ec94ef1f8c1434b4fe494) (cherry picked from commit a36e352) Signed-off-by: DjDeveloperr <hey@djdev.me>
A render whose attachment spans overlapped a live render of another logical queue in the same VM waited in admission, on the host, until that render retired. gpubench render.light queues renders every queue into one image, so all queues ran one render at a time: run_job blocked for (N-1) renders and the 2/4/8-queue rows stayed at one serialized step (174/201/217 us/submit). Order such overlaps at publication instead. The device mutex already orders publications; the later render's fragment kick now waits in firmware for the newest published overlapping fragment kick of every other pair, so its tiling kick stays concurrent and no host wait or scheduler stall is involved. Published QIDs keep their kick rings, so these stamps only advance. An admission bound on other pairs of the VM keeps the barrier count within its fixed room. Cross-VM overlaps through shared objects keep the host wait, so one VM's fault can never hold another VM's kick in firmware. render.light queues d2/d4/d8: 173.8/201.3/216.9 -> 81.4/66.9/62.1 us/submit. Other rows unchanged; the mild stress gate (CTS compute/sync, cross-queue order/data, slow-read pressure, mixed churn) passes. (cherry picked from commit 809d95cfdd685b6f94570cd313b1723c7f01f466) (cherry picked from commit b141ca9) Signed-off-by: DjDeveloperr <hey@djdev.me>
Each compute command writes a queue configuration record, an optional predecessor and completion-wait records, and an announcement; the firmware read all of them through an uncached mapping. Render keeps its equivalent configuration records cached and only its announcements uncached. Move the configuration, predecessor and completion-wait records into a retained pool object with a firmware-cached mapping at the same per-slot offsets; announcements stay uncached. compute.light d1 161.0 -> 152.2 us, d64 24.3 -> 23.1 us/submit, queue-to-queue hop 54.8 -> 51.2 us; compute/sync CTS gate, cross-queue order/data 10k, dependent reads under pressure and mixed churn pass. (cherry picked from commit b3d5b9d6294bb46d63778264205da9d3b750651e) (cherry picked from commit a475f4f) Signed-off-by: DjDeveloperr <hey@djdev.me>
…found Every event pass with an unobserved compute command walked all 2048 records of the compute completion ring under the device mutex to find the few it needed; that walk was half of the event worker's CPU time. Resume each scan after the last record the previous scan needed and stop as soon as every requested identity has an observation. A scan that finds nothing still wraps the whole ring once, as before. scan_compute leaves the top of the 16-process profile; benchmark rows are unchanged within noise (they are not bound by the scan). Compute/sync CTS gate, cross-queue order/data 10k, dependent reads under pressure and mixed churn pass. (cherry picked from commit 67a49c9c40b31b96a7834c8c185dc20e0bac1fa2) (cherry picked from commit ca71e44) Signed-off-by: DjDeveloperr <hey@djdev.me>
…mutex Every compute publication took the device mutex, which the event worker also holds for each service pass, and repeated device-wide work on every submit (replay scan, render-prefix scan, binding checks). Give each physical compute queue its own lock (a cell the registry owns; device-mutex holders lock cells one at a time) and move the device-wide state a publication needs into a per-session port that is reached under a preparation lease: compute work-ring slot reservations, QoS accounting under its own spinlock, the recovery generation, the full-effort flag and the doorbell, whose RTKit borrows are serialized by a mailbox lock. The steady-state path (bound, installed, no input sources, no render prefix) publishes from the submit ioctl or run_job with only the queue's lock and the port's small locks; everything else falls back to the device-mutex path, which now reserves work-ring slots through the same port. A command published while the event worker is scanning is not part of that scan's completion snapshot and is left unobserved until the next one. gpubench on Neo (us/submit, vs matched baseline): compute.light d1 166.3 -> 156.2, d64 27.4 -> 25.8, procs d16 33.4 -> 33.0 (firmware per command time now bounds these rows). Stress gate passes. (cherry picked from commit 8fc91b2117de925e362342a842b0d0befbba5312) (cherry picked from commit 1cdac9b) Signed-off-by: DjDeveloperr <hey@djdev.me>
Render emits its queue configuration record only for an install, a context or QoS update, or when the configuration differs from the last one the firmware consumed in the current recovery epoch. Compute appended the record to every command, so the firmware reapplied unchanged queue state each time. Use the same rule for compute, caching the last consumed configuration with its epoch and forgetting it when the item ring is reset. compute.light d2 61.9 -> 51.5, d8 25.9 -> 24.3, d64 23.9 -> 22.8 us/submit; queue-to-queue hop 51.1 -> 49.6 us. Compute/sync CTS gate, cross-queue order/data 10k, dependent reads under pressure and mixed churn pass. (cherry picked from commit 0e59b1f92effb58c87f705907959de45abe77623) (cherry picked from commit 7242bf8) Signed-off-by: DjDeveloperr <hey@djdev.me>
Waits for shared capacity sampled a condition and slept 1 ms before checking again. Add availability domains: a waiter samples the domain's generation before its failed check and registers a fence against that sample (or retries when a release intervened); the release that provides the resource bumps the generation and signals only that domain's waiters. The fences can serve as scheduler prepare_job dependencies or be waited on directly. The preparation gate can carry a domain that its reopening, failure and removal signal. Logical-queue creation is the first user: a creator refused for want of a queue slot or an execution context held by a closing queue now waits for the returned slot or the reclaimed context instead of sleeping in 1 ms steps, under the same overall deadline. Tested on Neo as part of compute-wait2 (stress gate passes). (cherry picked from commit 79cf4be7732d549b0f5d1f903bed7effdb80a4fb) (cherry picked from commit 014605a) Signed-off-by: DjDeveloperr <hey@djdev.me>
Compute admission and publication retried with sleeps: 1 ms for an unpublished input source, a 20 us..1 ms backoff for a work state, up to 200 x 1 ms for recovery, 1 ms per ring backpressure retry, 200 x 1 ms for a spared drain witness, and 1 ms per growth and channel capacity retry. Use two availability domains for compute admission; the release that provides the resource signals only the waiters of its own domain: - channels: a queue becomes reusable, shareable or re-armable (checked on each transition in the retirement scan and pool service), a scheduler release is consumed, an owner closes, or an unpublished slot and QID are returned; - progress: any compute retirement, finished replays, pool release observation, and the preparation gate reopening after recovery. prepare_job now returns the channel, work-state or recovery fence a compute packet still lacks, so run_job finds its capacity ready, and a prepared packet claims its bound channel so the channel is not shared away before run_job. Retries that remain inside publication block on the same fences; the admission slice only bounds cancellation checks. Each event pass also rechecks the level while waiters exist, which covers capacity that appears through render (QIDs, work states, closed queues). gpubench on Neo (us/submit, matched baseline): unchanged steady state (procs d16 33.1, d64 25.9, d1 162); procs d2 93.2 -> 69.2. With the channel budget forced back to ten channels (diagnostic build), sixteen processes now share them at 51.9 us/submit instead of the old 92.0, with no stall. Stress gate passes, also under the forced shortage. (cherry picked from commit 25fb5800d97db7457f093ff41206047fbd9bddcc) (cherry picked from commit 64f717d) Signed-off-by: DjDeveloperr <hey@djdev.me>
…record A consumer of a firmware-visible compute source carried a completion-wait record before its descriptor and also named the source in its kick entry. The second dependency flagged the command as entry-signalled, so it also appended a predecessor record, and the firmware resolved the same wait twice. Keep only the completion-wait record, which orders the consumer behind its source by itself. Queue-to-queue hop 49.6 -> 46.0 us. Cross-queue order and data checks (10k iterations each), dependent reads under pressure, the compute/sync CTS gate and mixed churn pass. (cherry picked from commit 58ef65a530b707fb77878e9fdc68d577a27759fc) (cherry picked from commit 327e50f) Signed-off-by: DjDeveloperr <hey@djdev.me>
Add tracepoints for each command's publication (doorbell rung, with its out fence and the CPU counter), its retirement (with the firmware's GPU start and end timestamps), physical queue binds and event worker pass boundaries. With the counter and the GPU's system counter timestamps on the same events, a trace places firmware dispatch, GPU execution, firmware completion and every host stage of a command on one timeline. The events are disabled by default and cost a static-key test each. (cherry picked from commit 565dae906223948df97a252cc3274e8db6183363) (cherry picked from commit 6f4d3bb) Signed-off-by: DjDeveloperr <hey@djdev.me>
Compute rang its pipe and priority doorbell (pipe 2 in bits 1:0, class in bits 3:2) only for the first publication of a priority class, then the generic work doorbell for every command. Render rings its own pipe doorbell for every publication, and so does the M5 compute path. Ring the compute pipe doorbell for every publication instead of the generic one. gpubench on Neo (us/submit, on the integration branch): compute.light d2 60.3 -> 53.2, d64 22.8 -> 21.5, d1 163 -> 154..160, procs d16 unchanged (27.2), hop unchanged (48.7). Stress gate passes. (cherry picked from commit 398141cf95c44ac4d0073f3620a99d1235f31309) (cherry picked from commit e24e3ba) Signed-off-by: DjDeveloperr <hey@djdev.me>
Render keeps its queue records in a firmware-cached prefix; the compute queue record, job-list head and item ring were mapped uncached. Allocate the compute graph from the retained object pool with its 32 KiB queue record, job-list head and item ring in a firmware-cached prefix, and move the producer/consumer words to their own uncached page after it, as native does. compute.light d64 22.2 -> 19.7 us/submit, 16-process compute 27.2 -> 25.7, queue-to-queue hop 47.5 -> 42.4 us. Compute/sync CTS gate, cross-queue order/data 10k, dependent reads under pressure and mixed churn pass. (cherry picked from commit 488708add18c39f709d6a9b53736d339428cf5d0) (cherry picked from commit 94c66ec) Signed-off-by: DjDeveloperr <hey@djdev.me>
Render admission and publication still slept or polled: a 20 us..1 ms backoff when every work state of the context was taken, up to 200 x 1 ms while recovery was observed, an unconditional 1 ms sleep for render capacity (through the compute capacity wait), and a 1 ms condvar timeout as the wakeup for a pair on loan, firmware room or a pending teardown. Block on the event that ends each wait instead: - a render availability domain, signalled by every admission-pool return (lease drop, owner close, quarantine change, exit-kill hold release, processor stop), every pair return from a loan, and on each event pass while a render publisher waits, since firmware ring room, consumed releases and teardown steps have no event of their own; - a per-context work-state domain, signalled whenever a work state returns; - the preparation gate: a publisher that observed recovery waits for its closure (close() now wakes the gate's waiters) and then blocks in enter() until recovery reopens it. Render capacity first returns idle backing, then waits on the domain. Waiters sample a domain only after a failed check and retry once before blocking, so steady-state publication takes no extra lock. Deadlines only bound the waits; 10 ms slices only bound cancellation checks. Steady state is unchanged (render d1 135.7, d32 41.4, queues d8 62.6 us/submit); the mild stress gate passes. (cherry picked from commit a3fc054bc52c2bc82350e6073366045a1c3c9489) (cherry picked from commit db365ec) Signed-off-by: DjDeveloperr <hey@djdev.me>
A logical render queue owns exactly one physical pair, whose active ring holds RENDER_DEPTH (16) commands, but its scheduler had 32 credits. Jobs 17 to 32 entered run_job and parked there in the admission pool (or in the 1 ms busy wait) until a retirement freed a ring entry, holding the scheduler worker. With the credit limit equal to the pair depth the scheduler itself waits for the credit that the retirement returns, and run_job never blocks on a full pair. render.light d32 41.15 -> 41.26 us/submit (unchanged; the row is firmware bound), all other rows unchanged, mild stress gate passes. (cherry picked from commit 15de925f91cdb2a875e09fc1c6b64af79fa37ae2) (cherry picked from commit 06bc290) Signed-off-by: DjDeveloperr <hey@djdev.me>
…is busy A render pool whose last render retired sent its final FreeListRelease right away: a control-ring record and a control doorbell for every render of a one-deep queue. The firmware processes each such message serially before it returns to the work rings, so with several queues active the releases stretched every completion burst (one release per render in render.light queues: about 44.6k per run). When another pair still has published work, leave a registration whose references drop to zero lingering instead; the owner's next render reuses it like an overlapping render, with no release and no new generation. A lingering registration ends, and its release is published, when its owner closes or moves to another pool (each event pass checks) or after the 16 ms firmware idle hold, for which the event worker schedules a timed pass. On an otherwise idle device the release is still sent at once: reusing a registration that went idle costs the firmware more than the release does (render d1 147 -> 184 us when every idle registration lingered). render.light queues d2/d4/d8: 80.8/67.3/62.6 -> 76.9/62.4/58.2 us/submit; render d1 148, d32 41.3 (unchanged). Releases per queues-d8 run: 44574 -> 18 (measured with every idle registration lingering). Mild stress gate passes. (cherry picked from commit 3d1f204) Signed-off-by: DjDeveloperr <hey@djdev.me>
A render publication into an owner's installed pair took the device mutex six times from the scheduler (seven from the ioctl): to fetch the admission pool (and, from the ioctl, again to try it), to check the pair's identities, to find the pair already built (constructing a throwaway set of build resources), to look for a sibling pair to warm, to lend the pair out and to publish it. The admission pool lives as long as the firmware session, so the logical queue keeps a reference to it. The identity check also reports whether the owner's pair is installed, idle and backed, in which case the first pass skips graph construction; every retry still goes through it. An owner has exactly one pair (kick.rs asserts it), so there is no sibling to warm. Steady state now takes the device mutex three times from either path. gpubench unchanged within noise (render d1 151.6, d32 42.8, queues d8 58.8 us/submit); the mild stress gate passes. (cherry picked from commit b7fa0d4) Signed-off-by: DjDeveloperr <hey@djdev.me>
Every render publication held the device mutex, which the event worker also needs for each pass, across the whole publication: dependency and hazard collection, kick and descriptor commits, QoS accounting, both work-ring records and producers, and the doorbell. In the steady state (the owner's graph is installed and registered at its priority, last ran this owner's scheduler, needs no growth, has nothing to replay or settle, and the command has no compute prefix) the device mutex now only lends the pair out, together with its free-list preparation and a parameter-buffer reference, and takes it back. Preparation and publication in between take the port: a render publication lock that every render publication holds, with or without the device mutex (the tiling and fragment work rings keep one host producer at a time, and attachment overlaps keep their publication order), QoS accounting, the completion stamps, the doorbell and the full-effort witness. The port gains views of the render work rings and of the stamp page. The free-list lease leaves preparation, or is rolled back with the parameter-buffer reference, when the pair returns; while it is lent the event worker skips the pair, so no retirement can touch its commands or references. A recovery that starts during the publication fails it with EIO, as on the compute path. Anything else, including a first publication, growth and owner changes, still publishes under the device mutex. During gpubench render rows 306k of 320k publications took the new path. render.light d2/d8/d32 68.9/45.2/42.8 -> 65.9/43.5/41.1 us/submit, d1 150.2 (151.6), queues d8 58.7 (58.8); the rows stay firmware bound. Mild stress gate passes. (cherry picked from commit d272a70) Signed-off-by: DjDeveloperr <hey@djdev.me>
After the direct render path, a steady-state publication still took the device mutex three times: once to check the pair's identities and readiness, then to lend the pair and to take it back. The lend itself validates everything the identity check does. The logical queue remembers the pair it last published to directly. A lease on that pair (the ioctl's, or a non-blocking admission from the scheduler) goes straight to the lend; anything else, and any lend that declines, takes the full path with the identity check. gpubench unchanged within noise (render d1 150.8, d32 42.9, queues d8 58.4 us/submit); the mild stress gate passes. (cherry picked from commit 6428f7f) Signed-off-by: DjDeveloperr <hey@djdev.me>
…L generation The M3 runtime admits a VM only for a file that asked GET_PARAMS for at least AGX3_PARAMS_SIZE bytes, and that was the size of drm_asahi_params_global. The G17 update grows the structure from 600 to 616 bytes with two optional tail fields, memory_limit and client_memory_limit, so Mesa built against the 600-byte structure would be refused VM creation with EOPNOTSUPP until it is rebuilt. The check only needs to know that the client knows the AGX3 fields. Pin the minimum at the end of gpu_hal_generation, 600 bytes, and assert that value at build time. GET_PARAMS still copies only the size the client asked for. Signed-off-by: DjDeveloperr <hey@djdev.me>
The main probe configured the GPU memory ledger for every GPU, so the new defaults (mem_limit_pct=67, client_mem_limit_pct=50, oom_attribution=1) also applied to M1, M2 and the M3 runtime. Those GPUs would refuse BO creation with ENOMEM once committed sizes passed the fractions, even with nothing resident, and would count GPU memory in the creator's RSS for the OOM killer. The separate G15 probe never configured the limits, but still got the RSS attribution, because it was read from the module parameter on every object. That is a policy change for every Mac, and it should be proposed on its own. Until then, configure the ledger in the G17 probe only, and make configure() also decide the RSS attribution. Every other GPU keeps the totals, in fdinfo and sysfs, with no limit and no attribution, and reports zero (no driver limit) in the GET_PARAMS tail. The module parameter descriptions now say they apply to G17. Signed-off-by: DjDeveloperr <hey@djdev.me>
|
@DjDeveloperr fair enough on both:
Two things still block, because they change machines we ship today:
With those two fixed, plus a quick boot with a desktop and GL/VK smoke on one M1 or M2 and one M3, I'm happy to approve. |
|
Thanks for the review, and for keeping it one PR. Both blockers are fixed:
#244 stays stacked on this branch. It can be folded in at merge time if you prefer. Testing at 1032928:
On hardware, only the Neo is available to me right now. I'll post a Neo run of this exact head. I can't do the M1/M2 and M3 boots, so those would need you or another tester. |
…0261027 Pick up #229's review fixes (the M3 GET_PARAMS minimum and the G17-only memory limits). No conflicts. Signed-off-by: DjDeveloperr <hey@djdev.me>
|
Neo (J700) hardware run of this exact head (1032928), as promised above. Build and boot: the kernel was built with omarchy-pkgs' linux-aurora PKGBUILD (
This head doesn't carry #235 (J700 display DART handoff), so the internal display path wasn't part of this run; #235 has its own Neo run. A related fix is coming for this stack: aurora-silicon#555 retires a render group on its pass record when the firmware's completion stamps stall. Without it, about every other boot under load one client's render times out after ~30 s, and on an omacom kernel without the G17 timeout fixes the whole GPU runtime stops. That is the J700 first-boot GPU failure seen on a fresh omarchy-pkgs install. Its omacom mirror will stack on #244. |
The aurora-wip sync brings drm_asahi_params_global's optional tail, memory_limit and client_memory_limit, which grows the structure from 600 to 616 bytes. The kernel contract pinned for the ABI check, 7b969982306e, predates it, so check-kernel-abi.py fails on params_size. Pin the contract to omacom/linux-aurora#229's head, 1032928116c1, the kernel change that adds the same tail. With it, every compared size, offset, ioctl number and feature bit matches. This branch therefore merges after #229. At runtime, Mesa still works with a kernel that has the 600-byte structure: it zero-initializes the reply, and such a kernel leaves the optional tail untouched. Signed-off-by: DjDeveloperr <hey@djdev.me>
|
Here's an M2 Pro run (J414, MacBook Pro 14") of this PR's current head, with the rest of the stack merged on top. Kernel: built with omarchy-pkgs' linux-aurora PKGBUILD as
I don't have an M3, so the M3 boot still needs you or another tester. |
maralcbr
left a comment
There was a problem hiding this comment.
@DjDeveloperr thanks, both blockers are fixed, and the Neo and M2 Pro runs cover what I asked for on hardware.
- M3 params size:
AGX3_PARAMS_SIZEis now the end ofgpu_hal_generation, with a build-timeassert!(… == 600)(m3/m3_client.rs:35-38). That's the same minimum the M3 runtime enforced before the struct grew, so the mesa-aurora on edge keeps its VMs. Since GET_PARAMS copies only what the client asked for, the 16-byte tail stays optional. - Memory limits:
LEDGER.configure()moved from the common probe intoprobe_g17()(driver.rs:396-397). It's the only call, and an unconfigured ledger haslimitandclient_limitat 0, which means no limit. RSS attribution now followsATTRIBUTE_RSS, which onlyconfigure()sets (memacct.rs:171-174,:321-327). So on M1/M2/M3 only the counters change. - T6040: the description now says initial and untested. Fine as agreed.
On hardware, the M2 Pro boot with the whole stack (desktop on the GPU, glmark2, vkmark, a clean log) covers the shared driver code on AGX2. The Neo run at this exact head covers G17. Neither tells us about the M3. The M3 runtime also goes through the changed shared MMU code (it adopts the shared UAT, creates shared VMs, binds compute contexts and maps GEM-backed tiler storage), and it picks up the new CPU mapping in timestamp::Buffer. The M4-only paths stay off there (m4_adopted = false). Edge is our qualification channel, so I'll accept that risk and not hold the merge for it. An M3 cold boot (desktop, GL/VK smoke) will be the first edge check once this is pinned.
Approving. #244 can land right after this.
chriskearney
left a comment
There was a problem hiding this comment.
Please restore const MODULE: Option<&'static kernel::ThisModule> = Some(&crate::THIS_MODULE); in AsahiDrmDriver before merging.
At reviewed head 1032928116c13840efaf0ba6ae68fee4e1cd70ea, commit 4b7aa809ff5 removes that override. The unchanged drm::Driver::MODULE default is None; UnregisteredDevice::GEM_FOPS passes it to create_fops, which sets fops.owner = NULL. drm_gem_prime_export also inherits that owner for exported DMA buffers.
For CONFIG_DRM_ASAHI=m, this loses the module reference that open DRM files and exported buffers need to retain their callback code. Unloading with those objects still live can therefore leave callbacks into unloaded code. It affects every Asahi backend, including the M3 configuration, and is inherited by #244. Restoring the existing override addresses the regression.
This finding is from the exact-head source and callback-owner wiring; no live module-unload test was attempted. Please retain regression coverage for the non-null owner in modular builds.
The G17 sync (4b7aa80) took drivers/gpu/drm/asahi/driver.rs from aurora-silicon, whose Rust DRM layer derives the file_operations owner from the driver's OwnerModule type. This tree's layer still takes it from drm::Driver::MODULE, which defaults to None, so the sync dropped the AsahiDrmDriver override that set it to THIS_MODULE. With DRM_ASAHI=m, the GEM file_operations and the dma-bufs that drm_gem_prime_export() creates then have a NULL owner. Open DRM files and exported buffers no longer hold a reference on the module, so it could be unloaded while their callbacks still point into it. Restore the override, and assert at build time that MODULE is set, so a later sync that drops it fails to compile. Signed-off-by: DjDeveloperr <hey@djdev.me>
#229 gained 6c66b41a ("drm/asahi: restore the DRM driver's module owner"),
so its head moved from 1032928116c1. Re-pin the kernel contract to the
new head. The UAPI header is the same in both, so the ABI check is
unchanged. This branch still merges after #229.
Signed-off-by: DjDeveloperr <hey@djdev.me>
|
Thanks, you're right. The G17 sync (4b7aa80) took aurora-silicon's Fixed in 6c66b41:
Testing:
The fix is merged forward into #244, #264, #270 and #271. aurora-silicon/linux needs no change, since its layer sets the owner from |
|
M3 Pro J516S/T6030 combined-kernel A/B/A regression test completed. Boot, GUI, OMT comparison, native GL/Vulkan readbacks and all benchmark/control runs passed. Single-submit CPU latency remains variable; these samples do not establish the initial apparent 21% kernel regression. Tested source: base A1 and A2 booted the original The same pinned Vulkan helper and provider files ran in every arm: one warm-up plus three measured rounds of seven workloads, with clean and deliberately corrupted compute controls first. All 90 runs were valid; the negative controls detected exactly the injected error. Below are median CPU-side submit-and-wait times in milliseconds (lower is better, n=3 per cell).
Single-submit B is +21.4% versus A1 but +4.5% versus A2; the restored original itself shifted +16.1%. Raw ranges overlap: A1 15.36–18.87 ms, B 17.22–19.32 ms, A2 17.58–22.42 ms. GPU timestamp medians were 1.582 / 1.583 / 1.595 ms. CPU placement was not pinned. A small CPU-side difference remains unresolved and needs controlled follow-up; this run cannot attribute the initial 21% difference to the kernel. Single-run OMT scores were glmark2 1949 / 1911 / 1901 and vkmark 6178 / 6362 / 5957. The candidate logged Original packages, module trees, boot files and writer state were restored, both transaction audits were clean, and an additional audit after A2 was clean. The temporary test-agent update was reverted to its exact original binary; audio settings match the initial snapshot and leases are released. The first submission was refused before any test boot for an agent-version mismatch and was fully restored before retry. A2’s local wrapper initially rejected labd’s two implicit identity-test entries; the original rejection is retained, and the saved result was revalidated offline with all six actual tests required and passing. Host/source coverage: 21 non-known host suites passed; two documented preexisting harness failures remain (apple-dart-range mocks and check-backlight include). Two independent shared-code reviews and a focused M3 review identified the module-owner regression reported above. DJ’s successor Coverage is M3/G15 regression testing. Neo/G17 hardware, full conformance, suspend/hotplug and ordinary-login qualification are outside this run. |
chriskearney
left a comment
There was a problem hiding this comment.
The requested module-owner regression is addressed by 6c66b41a: the original MODULE = Some(&crate::THIS_MODULE) override is restored, and the new compile-time assertion rejects its removal. Positive/negative controls passed on Rust 1.85.0 and 1.98.1.
The pre-fix frozen #235+#229+#244 combination passed our M3/G15 boot, correctness and A/B/A regression run with exact restoration. Full results. Single-submit CPU timing remains variable (B +4.5% versus restored A2, overlapping samples); it does not establish the initial apparent 21% regression. This approval covers the shared-code/M3 regression review and owner repair. Updated-head hardware, Neo/G17 hardware and full conformance were not run here; fresh CI and normal merge requirements remain.
drm/asahi: G17 leak fix, render notifications and hop fixes from aurora (stacked on omacom#229)
Brings the G17 driver up to aurora-silicon/linux (AsahiLinux#423 head 8e257af) on integration/aurora:
integration/aurora (base 7.2.10 merge point 44d5afc). Kept: G15, the M3 T8122 runtime, UAT
adoption, M3 Pro GPU crash fix: preserve new mappings during deferred GEM close #214 deferred-close binding sequences and the 7.2.11 shared-code adaptations. Not carried:
the G16 (T8132) backend. 7.2.11 adaptations: module parameter refs, THIS_MODULE, a local
raw_try_init, MODULE_OF_TABLE, Devres for the sgx window, G17's soft-fault opt-out in the
shared feature flags, G15/M3 on the shared timestamp::Buffer and reserved-table root walk.
Crate/helper pieces the driver needs: shmem SG leases and pages_no_reclaim, DRM file memory
stats/fdinfo/is_current_master, mm RSS counter. The message credits the carried authors.
outputs, doorbell batching, liveness watchdog, kill gating, recovery fixes, cumulative signals at bit
29), cherry-picked with -x and signed off.
M4 Pro (T6040): the sync also matches
apple,agx-t6040and probes it through the G17 driver with the 25G83 firmware configuration inhw/t6040.rs. This is initial, untested support: nothing in this PR was run on a T6040, and the installer doesn't offer M4 and later yet.GPU memory limits (
mem_limit_pct,client_mem_limit_pct,oom_attribution) apply only to G17 (1032928). Other GPUs keep the totals in fdinfo and sysfs, with no limit and no RSS attribution.UAPI: include/uapi/drm/asahi_drm.h gains the G17 features (VM status, queue limits, cumulative signals
at bit 29, ...); Mesa: aurora-silicon/mesa #62 (stage rb6) matches.
Testing (Neo J700, 7.2.11-ARCH-neo72 with #226/#227/#228, Mesa rb6):
no GPU errors, recoveries or crashes; oracle 34/34 PASS; desktop alive.
The performance work in the picked commits was measured on 7.3, and the numbers are in aurora-silicon#423. Highlights against macOS on the same Neo: light compute submission matching or beating macOS from depth 1 to 64, the cross-queue hop down from about 31 µs to 23 µs, and multi-client stress that previously collapsed the firmware in under 3 minutes now running clean.
Known differences on 7.2.11
apple-dart 412300000.iommu) fails to probe with -16 ("IOMMU driver was not able to establish FW requested direct mapping"). The display subsystem then never binds, and apple-mcc, which waits for it, never enables the system level cache. That is why light rows were slower here than on 7.3, for example a cross-queue gate pair of 333.6 µs against 227 µs. With iommu/apple-dart: fix the J700 internal display and SLC with handed-off display DARTs #235, the GPU rows match 7.3: hop 22.4 vs 22.7 µs, compute.light d1 106.7 vs 102.2, d64 13.7 vs 13.9, render.light d32 29.0 vs 29.4. Two smaller differences remain. The gate's cross-queue pair is 259 vs 225 µs under schedutil, but 205 vs 204 µs with the performance governor. The cpufreq driver isn't the cause: on 7.3, the same driver as here and the previous one give 229 and 230 µs. So the difference comes from how the 7.2.11 scheduler ramps the CPU under this bursty pattern. mem.image is 7% slower and is still under investigation. On one of three unfixed boots, IRQ 55 also fired with no handler and was disabled; with iommu/apple-dart: fix the J700 internal display and SLC with handed-off display DARTs #235 that no longer happens.Depends on / pairs with