Skip to content

CI only: erikwb GPU PRs #1610 + #1611 + #1752 composed on integration/aurora (do not merge) - #1834

Closed
DjDeveloperr wants to merge 11 commits into
integration/aurorafrom
djdev/ci-erikwb-gpu-20261113
Closed

DjDeveloperr wants to merge 11 commits into
integration/aurorafrom
djdev/ci-erikwb-gpu-20261113

Conversation

@DjDeveloperr

Copy link
Copy Markdown
Contributor

CI only, not for merging. It runs the open erikwb GPU PRs together on integration/aurora, so their merge order gets a whole-kernel build (kunit, DRM_ASAHI=y) and the host tests on the combined source. Neither #1610 nor #1752 touches a path that starts a kernel build in CI on its own.

Composition (d99bda2d5513, tree 173f25a9a0f6):

The one conflict is in include/uapi/drm/asahi_drm.h: #1610's DRM_ASAHI_FEATURE_GEM_RESIDENCY (bit 35) and #1752's DRM_ASAHI_FEATURE_COMPUTE_TIMEOUT (bit 36) are both added right after bit 34. The resolution keeps both, in bit order. Whichever PR lands second needs the same resolution.

The same source is the M5 Pro test kernel for these PRs. This PR will be closed once they have merged.

🤖 Generated with Claude Code

erikwb and others added 11 commits November 11, 2026 18:39
On G17, reclaim may release the backing of VM-private objects of idle
VMs, which the VM's next job then waits to get back. A client that
would rather not pay that for some of its objects (a server holding
model weights between requests, Vulkan's memory priority 1.0) had no
way to say which.

DRM_IOCTL_ASAHI_GEM_RESIDENCY sets or clears a per-object keep flag,
reported by DRM_ASAHI_FEATURE_GEM_RESIDENCY (bit 35) on G17. The flag
is a priority among the objects that reclaim may release, and no more:

- Which reclaim releases GPU memory does not change. The VM shrinkers
  are not memcg-aware, so only reclaim on behalf of the whole system
  calls them (kswapd, direct reclaim, a request to the root memory
  cgroup, drop_caches). Reclaim inside a memory cgroup below the root
  releases no GPU memory, kept or not.
- Which VMs it takes from does not change: an idle one, with no job,
  binding change or other use in progress or in the last two seconds,
  and past the backoff that follows a restoration. The memory of a VM
  in use stays, kept or not.
- Within such a VM, kept objects are released after the others, and
  only when those left the scan target unmet.
- Contents advised DONTNEED are purged as before, kept or not.

The request is therefore a hint. Userspace cannot rely on a kept
object being resident, and a client that stays active, or whose memory
cgroup is the one running out of memory, keeps its GPU memory whether
it asks or not.

A user VM now has two shrinkers. The existing one purges advised
contents and releases the idle objects that are not kept, as before.
The new one counts the kept objects and has a batch no scan target can
reach, so the shrinker core calls its scan only when the target reaches
everything it counted: at the last two of the thirteen reclaim
priorities, or the last three once scans deferred by earlier passes
have built up. Its scan releases what the first shrinker's does and
then, only if that did not reach the target, the kept objects. It is
registered after the first, so a reclaim pass that reaches it has
asked the VM's first shrinker before. Reclaim comes to those
priorities when the earlier ones did not free what it was asked for:
under memory pressure that nothing else relieves, for a request that
names more than the system has to spare, and at once with drop_caches.
Kept objects are not a second pass of the first shrinker's scan
because the shrinker core hides the reclaim priority from a scan:
counted there, they would be released at any priority whenever a VM
had nothing else to give, and left uncounted, a VM with only kept
objects would never be scanned at all.

The flag is changed under the object's reservation, which idle reclaim
holds for the VM it walks, so a request takes effect for every reclaim
pass that starts after the ioctl returns; backing already released
comes back with the next job, as before. Shared, imported and
user-memory objects are never released and accept the flag without
effect on them.

Kept objects count against the memory limits as before. Their sizes are
also totalled for the device (user_kept in the gpu_memory sysfs file)
until the object is freed.

Tested: KUnit in QEMU (arm64, 16K pages), 546 tests, 544 passed, 2
skipped (unrelated configuration); the new asahi_memacct test
kept_bytes_follow_the_object checks that the kept total changes only on
a change of state, is left as it is by a transfer to another client, is
cleared by a free and ignores kernel objects and imports. KUnit does
not reach the shrinkers: what reclaim does with kept objects is covered
by the selftest that follows, which passes on the J716c (M5 Max
MacBook Pro), the case that runs every shrinker at the last priority
included. There, with a Vulkan driver that maps memory priority 1.0 to
this ioctl, the memory priority and pageable allocation cases of the
Vulkan CTS give 430 passed of 430.

Developed with Claude Code.
Signed-off-by: Erik Bourget <erik@ebourget.net>
A kselftest harness program for the render node that exercises
DRM_IOCTL_ASAHI_GEM_RESIDENCY without submitting GPU work:

- a VM-private object: a keep request moves user_kept (gpu_memory
  sysfs file) by the object's size, once for a repeated request, a
  clear request moves it back, and freeing a kept object stops
  counting it;
- unknown flags fail with EINVAL and an unknown handle with ENOENT,
  and neither changes user_kept;
- a shared object takes the flag and is counted the same way;
- light_reclaim_spares_kept_objects: of two idle bound objects,
  reclaim releases the one that is not kept first, never the kept one
  while the other is resident; the kept one stays through 16 further
  reclaim requests; once its request is withdrawn it is released too;
- kept_objects_stay_purgeable: a kept object advised DONTNEED is
  purged by reclaim;
- requests_serialize_with_reclaim: while a thread sets and clears the
  request on one object as fast as it can and reclaim runs, an object
  beside it that is not kept is released, which shows that reclaim got
  to the VM during the race, a kept object beside it is not, the state
  a request that has returned found is the one reclaim leaves, and
  user_kept counts each object once;
- kept_objects_go_under_urgent_pressure: when every shrinker is run at
  the last reclaim priority, a kept object is released as well,
  without being purged.

The four cases that make the kernel reclaim act on the whole system.
They need root and run only when asked for in the environment:
ASAHI_RESIDENCY_RECLAIM=1 for the three that write to memory.reclaim
of the root memory cgroup, ASAHI_RESIDENCY_DROP_CACHES=1 for the last
one, which writes 2 to /proc/sys/vm/drop_caches.

A memory.reclaim request names an amount, not a priority: the kernel
works down its reclaim priorities until it has reclaimed that much,
and where nothing else can be reclaimed it ends at the last ones, at
which kept objects go like everything else. The three cases therefore
bring the memory to reclaim: a sparse temporary file of 2 GiB in the
working directory, read once into the page cache, from which requests
of 4 MiB are usually met within the first priorities. The objects have
2^19 pages (8 GiB with 16K pages), the least a VM must have for the
lightest priority to scan its shrinker. The cases skip without four
times the objects' size of available memory, in a working directory on
tmpfs and in a memory cgroup with memory.min or memory.low protection.

When a kept object was released, a case has to tell whether reclaim
had leave to release it. It does so in one of two ways, and says which:

- From the record of the shrinker passes, where it can have one: the
  events vmscan:mm_shrink_slab_start and mm_shrink_slab_end in a trace
  instance (tracefs at /sys/kernel/tracing). The shrinker core calls a
  VM's shrinker of kept objects only in a pass whose target reaches
  everything that shrinker counted, and the start event shows both
  numbers. The events name a shrinker by its pointer, so a case knows
  its own VM's shrinker by what it counts, the pages of the VM's idle
  kept objects: each case adds a random 0 to 1023 pages to its kept
  object, and takes for its VM's shrinker the one that never counted
  anything else. Leave to release the object is a pass that called that
  shrinker before the case first saw the object gone and had not ended
  when the case last saw it resident; a pass ends with the end event
  of the same shrinker from the same task. With such a pass the case
  is skipped ("as it may"), with their number and priority; without
  one it fails, whatever passes other VMs' shrinkers or other times
  had. If more than one shrinker in the record counts what the test's
  VM counts and a pass called one of them, the case is skipped as
  "inconclusive", which claims nothing about the release. The record
  is used when no event was lost and it shows the driver's other
  shrinker at work, which it knows by the names of the scan functions.
- Otherwise from signs of the environment, a heuristic: whether the
  kernel met every request, whether 512 MiB of the file were still
  cached after each, and whether kswapd or direct reclaim scanned pages
  meanwhile. A kept object released with one of these signs skips the
  case, with the sign and the word that it is a heuristic; without one
  the case fails. The signs do not show which priorities reclaim
  reached: reclaim that comes to the last ones without leaving a sign
  fails a case that the record would skip.

kept_objects_go_under_urgent_pressure also requires, where it has the
record, a pass that called its VM's shrinker of kept objects before
the object went.

The trace instance belongs to the program: main() makes it before the
cases fork, named asahi-residency-<pid>, and removes it when the
program exits or is ended by a signal, so a case that fails, times out
or is killed leaves nothing behind. The signals that end the program
wait while the instance is set up or removed, and the handler makes
only async-signal-safe calls, on paths made beforehand. An instance of
that name that exists already is left alone, and the cases then go by
the heuristic.

The result of every series of reclaim requests is checked for an error
(a failed request, or a client total that could not be read) before it
is taken for a released object.

The program is listed in the selftests' TARGETS. It skips where
renderD128 is not this driver's node or lacks the feature.

Tested on the J716c (M5 Max MacBook Pro), built there with gcc against
this tree's UAPI header:

- unprivileged, without the two variables: pass 3, skip 4;
- as root with both variables: pass 7. The object that is not kept
  went with the first request and the kept one stayed through 16 more;
  the object no longer kept and the advised one went with the next
  request each. In the race, 11 million residency requests ran while
  reclaim released the control object, with its 11th request, and the
  toggled one; the kept object stayed, also through 16 further
  requests. At least 1995 MiB of the cold cache's 2048 were left. The
  record had one shrinker with the count of the test's VM in each case:
  51 and 70 passes over it in these two cases, none of which called
  it, and in the urgent case one that did, at priority 0;
- as root with ASAHI_RESIDENCY_RECLAIM=1, right afterwards and once
  more later: pass 6, skip 1, with similar figures;
- as root with ASAHI_RESIDENCY_RECLAIM=1 and tracefs hidden from the
  program (a private mount over /sys/kernel/tracing): pass 6, skip 1,
  by the heuristic;
- five changed copies of the program, light_reclaim_spares_kept_objects
  only. One that never asks to keep the object fails, with the record
  and without. One that runs every shrinker at the last priority
  between the case's two phases is skipped with "as it may: ... 1,
  down to priority 0 (traced)". One that does not keep the object while
  such a pass calls another client's shrinker of kept objects fails
  (no shrinker with the count of the test's VM), and so does one in
  which such a pass called a shrinker with that count and ended before
  the case first looked. One with a second client whose kept object
  has the same size is skipped with "inconclusive: 2 shrinkers ...".
  Without the record all five fail;
- a case killed with its process group while it records, the program
  ended by SIGTERM, and an instance of its name made beforehand: the
  program removes its instance in the first two and leaves the foreign
  one alone, with the case passing by the heuristic. Sent SIGTERM at a
  random moment of its first 0.3 s, 30 times, it left no instance.

Developed with Claude Code.
Signed-off-by: Erik Bourget <erik@ebourget.net>
…usy time

agx_stats on the M5 reported utilisation only: pstate, the frequencies
and busy_ns read "unsupported", so there was no way to tell whether the
GPU runs at full clock during a workload.

The M5 firmware reports its performance state in its statistics ring:
tag 0x1b names the state now in effect (words 1 and 3, an index into
the device tree's operating points, 1 = 338 MHz to 13 = 1620 MHz), and
tag 0x19 the interval that just ended (start in words 3-4, end in words
1-2, state in word 8). The decoder now takes the state from 0x1b, time
stamped by the end of the preceding 0x19, and the reader turns it into
achieved_frequency_hz through the board's performance state table. A
report whose two state words differ, or that names state 0 or a state
beyond the table, is not used; a gate makes the state stale, as before.

Its utilisation records (tag 0x13, every 8 ms) carry their own timer
timestamps. busy_ns now integrates the device utilisation over the
window each record closes. A record after a gap of more than two
periods (the GPU was gated or records were lost) or with an earlier
timestamp only sets a new baseline, as the legacy integrator does after
a power change, so an idle gap is never credited.

requested_frequency_hz, power_mw and temperature_raw stay unsupported.
Documentation/ABI/testing/sysfs-driver-asahi-agx-stats and
Documentation/gpu/asahi-usage-stats.rst describe the M5 values.

Checked on the J716c (M5 Max MacBook Pro) with a single-thread loop of
dependent integer operations, whose rate is proportional to the clock
(54.0 M iterations/s at 1620 MHz):
- one 1.4 s job that fills the GPU: every agx_stats read during it
  (12, 100 ms apart) gave pstate 13, achieved_frequency_hz 1620000000
  and 100 % device utilisation.
- 60 jobs of 20000 iterations, 30 ms apart: 48 ran in about 1792 us
  (335 MHz against the 338 MHz of state 1) and 12 in under 600 us. Of
  the agx_stats reads during the run (1 ms apart) that found a current
  state, 48 gave pstate 1 with achieved_frequency_hz 338000000 and 11
  gave 13 with 1620000000; the others found the GPU gated.
- three jobs of 300 ms at full clock, 300 ms apart, twice: busy_ns
  advanced by 871 and 863 ms for 905 and 919 ms of work, over 1.8 s
  (one 8 ms window is lost at each power-up).

Tests: the telemetry unit tests (g17-telemetry-tests.rs on the host, 9
passed, and KUnit asahi_g17_telemetry) cover the M5 decode from
firmware records, the busy windows, gates, gaps and other firmwares;
g17-telemetry-integration-tests.py passes and still fails each of its
mutations.

Developed with Claude Code.
Signed-off-by: Erik Bourget <erik@ebourget.net>
Documentation/gpu/drm-usage-stats.rst defines drm-maxfreq-<engine>, the
highest frequency of an engine, and drivers that know it also print the
frequency the engine runs at as drm-curfreq-<engine>. A Rust driver
could only print the busy time.

DriverFile::engine_frequency returns the current frequency, if the
driver knows it, and the highest, in Hz; show_fdinfo prints them for
every engine engine_stats emits. All engines get the same pair, which
suits a device whose engines share one clock. The default, None, prints
nothing, so drivers that do not implement it are unchanged.

Built for arm64; the next commit uses it.

Developed with Claude Code.
Signed-off-by: Erik Bourget <erik@ebourget.net>
Tools that read a client's fdinfo (gputop, nvtop) see its engine busy
time but not the clock the GPU runs at, which agx_stats now reports on
the M5.

Every engine of a client's fdinfo gets drm-curfreq-<engine>, the
achieved frequency of the performance state the firmware reports, while
it reports one (it does not while the GPU is gated), and
drm-maxfreq-<engine>, the highest frequency in the board's table (1620
MHz on the J716c). One clock drives the vertex, fragment and compute
engines. Each read drains the statistics rings under the device mutex,
as an agx_stats read does. GPUs without a board performance table (all
but the M5) print neither key.

Documentation/gpu/asahi-usage-stats.rst said that drm-maxfreq-* was
left out on purpose, because the engine times must not be scaled by a
frequency. That still holds and drm-cycles-* stays absent: the document
now says that the two keys show the device's clock at the time of the
read, and that an fdinfo read on the M5 takes the device mutex for
them.

On the J716c (M5 Max MacBook Pro), the fdinfo of a client running a
1.4 s job that fills the GPU reads, for each of vertex, fragment and
compute, drm-curfreq-<engine>: 1620000000 Hz and drm-maxfreq-<engine>:
1620000000 Hz; that of an idle client with the GPU gated has the
drm-maxfreq lines only.

Developed with Claude Code.
Signed-off-by: Erik Bourget <erik@ebourget.net>
The scheduler's watchdog polls a G17 queue every two seconds and takes a
compute command that a poll finds still running at the head of its
queue to have hung: on the M5 it renews nothing, and the command fails
about two seconds after it got there. A compute job that needs longer,
a long prefill of an inference engine for example, has to be cut into
small command buffers with few in flight, and still fails when other
clients share the GPU: two processes each submitting a 1.1 s compute
job that fills the GPU at the same moment ran side by side, one
finished after 1.57 s and the other was killed 2.0 s after its
submission, with about 0.3 s of its own work left.

drm_asahi_queue_create gets compute_timeout_ms, advertised by
DRM_ASAHI_FEATURE_COMPUTE_TIMEOUT (bit 36): a watchdog allowance for
each compute command of the queue. The driver clamps it (at least the
default of one poll, at most the compute_timeout_max_ms module
parameter, 30000 by default) and returns the allowance in effect. At a
poll, the watchdog now first renews a command that is within its
allowance. Render commands keep the default.

It is an allowance, not a deadline:

- The watchdog still polls every two seconds, so a command may run up
  to one poll longer than its allowance.
- The start of a command is known only approximately. The scheduler
  arms the watchdog when the job ahead completes, and again when a
  later job is pushed, so a job's first expiry comes at least one poll
  after it reached the head of the queue, later if pushes postponed
  it; the time is counted from one poll before that expiry. A job still
  in publication at an expiry (waiting for capacity) has not started:
  its time counts from the first expiry that finds it published.
- Past the allowance, the renewals and deferrals that false_timeout()
  already grants still apply, in the same order as before: the
  execution renewals of ray and acceleration-structure work, the
  shared-work renewals (not on the M5) and the deferral of work behind
  another VM's earlier work.

Under the default allowance the first expiry of a published job is not
renewed by it, as before. The one change there: a job that was still
in publication at an earlier expiry now gets one poll from the expiry
that first finds it published, where before it could fail at that
expiry however recently it had been published.

There is no bound on the sum of the allowances: a queue kept full of
long commands occupies the GPU for as long as it is fed. Nor are the
other clients shielded from such a queue: they share the GPU with it as
the firmware schedules them, and a command of another client can wait
until a long command ends (the numbers are below).

The struct grows at its end: a caller passing the old size gets the
default, and a kernel without the feature ignores the field.

Measured on the J716c (M5 Max MacBook Pro), with compute jobs of a
single-thread loop of known length. Before this change:
- one job of 2.13 s or 2.41 s: killed at 2.04-2.05 s (ETIMEDOUT, the
  client sees a lost device); one of 1.86 s: completes.
- four jobs of 0.93 s submitted back to back on one queue, with and
  without timestamp queries: all complete, the last 3.71 s after its
  submission; two of 1.6 s: the second completes 3.23 s after its
  submission. Jobs queued behind their own queue do not lose time.
- one 1.1 s job on each of two queues of one process: both complete
  (2.24 s for the second), both queues share one firmware queue.
With it, and a Vulkan driver that passes a requested allowance on:
- no request: a 2.13 s job is killed at 2.04 s, as before.
- 30000 requested: jobs of 5 s and 20 s complete; a 40 s job is killed
  30.7 s after its submission, as it is when 600000 is requested.
- three 16 s jobs submitted back to back on one queue: all complete,
  16.0, 32.0 and 48.0 s after the first submission.
- two processes each submitting a 1.1 s job that fills the GPU at the
  same moment: both complete, after 1.93 s and 2.37 s.

Sharing, measured with one client keeping a queue full of compute jobs
that fill the GPU (640 threads), of 1.5 s under the default allowance
and of 5 s and 20 s with 30000 requested, while a second client submits
a short compute job every 100 ms (a single thread of 5 ms, or 5 ms of
work filling the GPU) under the default allowance:
- On the integration/aurora of 2026-11-09 (about 1000 jobs of each kind
  beside the long jobs): the second client's jobs took a median of 6 to
  9 ms and at most 105 ms, whatever the length of the first client's
  jobs. The desktop compositor's render jobs, about 1 ms on an idle
  GPU, took up to 0.7 s beside jobs of any of the three lengths, and 4
  to 5 of them completed per second beside the 5 s and 20 s jobs
  against 9 to 10 beside the 1.5 s jobs and 43 idle; six of its jobs
  waited between 1.4 s and more than 2 s beside the long jobs, none
  beside the 1.5 s jobs.
- On the integration/aurora this change is based on (about 730 jobs of
  the kind that fills the GPU): most took 6 ms, but six waited until
  about the end of the first client's running job: 1.5 s beside a
  1.5 s job, and 3.4, 4.5, 5.7, 8.3 and 18.4 s beside 20 s jobs (the
  4.5, 8.3 and 18.4 s ones with the first client's queue at low
  priority, which did not help). The second client's watchdog deferred
  the ones past two seconds behind the other VM's work; the last one
  ran out of deferrals after 18 s and failed with ETIMEDOUT, 2.3 s
  before the first client's job ended.
So on the current tree a queue with a long allowance can hold up
another client's command for as long as its own command runs, and past
18 s that client's work fails. Under the default allowance the same
wait is bounded by the two seconds a command may run.

Tests: the timeout tests (g17-ray-query-timeout-tests.sh on the host,
23 passed, and KUnit) cover the requested allowances, the default, a
30 s allowance counted from the head of the queue and ended at the
first poll past it, a first expiry delayed by later pushes, and a job
that waited in publication.
The asahi-m4 layout probe compares the frozen queue_create bindings as
a prefix of the grown struct and asserts the new tail.

Developed with Claude Code.
Signed-off-by: Erik Bourget <erik@ebourget.net>
…llowance

With a compute allowance, one client's command may run for up to
compute_timeout_max_ms (30 s by default), and another client's command,
render or compute, can wait on the GPU until it ends. The waiting job's
watchdog defers it behind the other VM's older work at most eight polls
(MAX_RENEWALS, 16 s) and then fails it: a compositor frame that waited
behind a 20 s compute command of another client failed with ETIMEDOUT
after about 18 s, and the compositor lost its device.

A job's watchdog now records when its allowance ends in the packet, and
the cross-VM heads carry whether that job is still inside it. A waiting
job whose blocker is live and inside its allowance is renewed with a new
decision, Excuse, which does not use its eight renewals. Once the
blocker completes, or its allowance has passed, the existing rules apply
again, with what the job has left of its eight renewals for the
blocker's own failure and teardown. Unchanged:

- A blocker that already failed excuses nothing, as before.
- The oldest unfinished work is never excused: it has no blocker.
- A blocker under the default allowance excuses nothing, so clients
  that do not ask for an allowance see the decisions they saw before.

Render and compute watchdogs decide through the same attribution, and
both see compute heads as blockers, so a render job waiting behind a
long compute command is covered too.

The excuse is bounded on the waiting job's side: at most
compute_timeout_max_ms in polls (15 with the default 30 s), however many
blockers take turns. This rule therefore renews a job at most 15 + 8
times by default, 46 s of renewals. A job's own allowance and its other
deferrals are counted separately and come on top, and each renewal
re-arms a timer rather than an absolute deadline, so the time until a
waiting job is failed (spared) can be longer than that.

The watchdog lines for cross-VM decisions now print the excused polls
and whether the blocker was inside its allowance, and are written at the
first excuse, the first counted deferral and the final decision.

Tests: KUnit and the host timeout tests cover a live blocker inside its
allowance past eight renewals (excused), the excuse used up (renewals,
then spared), a blocker past its allowance and a failed one (the
decisions as before), no blocker and the oldest work (own), the bound,
blockers taking turns (the counters are the waiting job's and are not
reset), and when an allowance ends.

Developed with Claude Code.
Signed-off-by: Erik Bourget <erik@ebourget.net>
On the G17 GPUs other than the M5, the watchdog renews a published job
for up to fourteen polls while the GPU keeps completing other work
(renew_shared), which bounds a job under the default allowance at thirty
seconds on a shared GPU. These renewals were tried once a queue's
allowance had passed, so the two added up: with the longest allowance
(30 s by default) a command on a busy GPU could run for about 58 s, and
a 40 s command could complete there instead of failing at about 30 s as
it does on the M5, where the shared renewals do not apply.

A poll that an allowance renews beyond what the default allowance
renews now counts as one of the job's shared renewals. The two run from
the same start, and a job is renewed for the longer of its allowance and
the thirty seconds sharing gives any job, not for their sum. The one
poll every allowance renews after a wait in publication is not counted,
so asking never leaves a job fewer polls than the default.

For an ordinary compute command that never finishes, polled every two
seconds:

- With an allowance of 30 s it is failed one poll after 30 s on a shared
  GPU or alone, on every G17, instead of after about 58 s on a busy G17
  other than the M5.
- With a shorter allowance on a shared GPU it is failed at thirty
  seconds, as a command without an allowance is.
- Under the default allowance nothing changes.

The renewals and deferrals that do not depend on sharing still come on
top, on every G17 as before: the extra polls of ray and acceleration-
structure work, and the deferrals of work waiting behind other work. A
poll that runs late also still moves the estimated start, as the
comment of compute_timeout_ms says; that comment now also says that the
shared renewals run alongside an allowance.

Tests: the timeout unit tests (KUnit and host) apply the allowance and
the shared renewals in the watchdog's order to a job that never
finishes: under the default allowance it fails at 2 s alone and 30 s on
a shared GPU; at 30 s alone and shared with 30000; at 6 s alone and 30 s
shared with 6000; at 30 s shared with 2001 and 20000; at 40 s with 40000;
and after a wait in publication every allowance from the default to
30000 fails at the same poll on a shared GPU. This was not run on a G17
other than the M5 Max, where the shared renewals do not apply.

Developed with Claude Code.
Signed-off-by: Erik Bourget <erik@ebourget.net>
include/uapi/drm/asahi_drm.h: #1610's DRM_ASAHI_FEATURE_GEM_RESIDENCY
(bit 35) and #1752's DRM_ASAHI_FEATURE_COMPUTE_TIMEOUT (bit 36) are both
added after bit 34; keep both, in bit order.

Signed-off-by: Diljit Singh <hey@djdev.me>
@DjDeveloperr

Copy link
Copy Markdown
Contributor Author

Closing: this CI-only composition did its job. #1610 (954a6a7), #1611 (fcf0725) and #1752 (a34d26c) are all merged into integration/aurora.

@DjDeveloperr
DjDeveloperr deleted the djdev/ci-erikwb-gpu-20261113 branch November 13, 2026 15:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants