Repository navigation
honeykrisp: give capture and replay buffers their own address range - #9
joshuaswarren wants to merge 1 commit into
Conversation
A buffer created for capture and replay of its device address takes its address from the same heap as every other allocation. The capture device hands out addresses from the top of the heap. The replay device later asks for the same addresses, but in the meantime it hands out the same kind of addresses to other allocations, for example the memory of the buffers it has already replayed. When such an allocation sits on a captured address, the replay of the buffer that owns that address fails with VK_ERROR_OUT_OF_DEVICE_MEMORY. A trace of three conformance cases showed this: the failing request was the last address allocation in each run, and the allocation in its way came a moment before it. Reserve the lowest 4 GiB of the read-write user address window for capture and replay buffers. The range has its own heap, which ordinary allocations never use, so a captured address is free when it is replayed. The range is a fixed part of the window, so a capture made in one process replays in another on the same machine and kernel. Each buffer keeps its guard page. The allocation stores a flag, and free returns the range to the heap it came from. The read-only shadow of an address stays at the same offset, because the range is inside the read-write half of the window. A capture and replay buffer, with its guard, must now fit in 4 GiB. A larger one fails with VK_ERROR_OUT_OF_DEVICE_MEMORY, as any allocation that does not fit does. Ordinary buffers are not limited by this. An address that cannot be in the range, or that is in use, now returns VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS. A capture request with no address that finds the range full still returns VK_ERROR_OUT_OF_DEVICE_MEMORY. An address captured by an earlier driver lies in the ordinary window, so its replay is refused. Add a test of the allocator that runs without a GPU. It covers the range separation, the guard, replay in a fresh device, requests outside the range and in use, exhaustion, and reuse after free.
|
GREEN at 11ae82d, with two non-blocking notes. I read the full diff against main (7cda785) and ran no build. Checked:
Notes:
Merging is the maintainers' call. |
|
Both notes are open items, and the body already states both. I did not change the branch.
|
|
Maintainer routing request for PR 9. This is a request for a merge contact, not a claim that review is finished. It makes capture and replay buffers replayable in a fresh process. What was run on the head Question: who can merge into this repository's |
|
Second routing request for PR 9, about 14 hours after the first. It is still a request for a merge contact, not a claim that review is finished. Nothing on the head has changed since my first note above. The review notes and the list of what was run are in the description and the two review comments. Question: who can merge into this repository's main branch? If there is a different place to ask, a link is enough. My account has pull access only, so I cannot merge or mark anything ready. |
|
Status, 25 Oct: unchanged since 16 Oct, no review yet. It is in both arms of the #6 whole-suite run (main plus #8, #9, #10), so the |
|
Status, 25 Oct, for the landing order (#10 first, then #8, then #9, then #6). Head MEASURED: this change is in the control arm of the whole-suite CTS of the #6 stack (16-inch M1 Max and 13-inch M1), with no Pass to Fail change. NOT measured: the capture and replay CTS groups with only this change. |
|
The version of this change for That branch already has a capture and replay heap, so this keeps it. The commit records the range, adds MEASURED, native arm64 build of this head on a 16-inch M1 Max:
NOT measured: a GPU run and the capture and replay CTS groups on this head. |
|
Batch 1 for main: #10, then #8, then #9, in that order. All three apply to main at
Stack check, one boot on a 13-inch M1, These are targeted checks, not the full conformance suite. The CTS binary's source commit is not independently verified. #6 is not part of this batch. Its run over the touched test groups at its current head has not finished, and three The author account on these PRs has pull-only access, so a maintainer has to merge. Tell us if you want a different order or a different check first. |
|
@maralcbr could you review this current head for landing after #10 and #8? The exact-head test evidence and remaining scope limits are in the PR body. The requested landing order is #10, then #8, then #9. Our account cannot assign formal reviewers or merge here; please route this to a main-branch maintainer if needed. |
Landing hold (29 October)
Current head
11ae82d8d8c379c2ae895806b4409537c5ceb25bmust wait for #10 and a corrected and qualified #8. Chris identified a retained image-mapping lifetime defect in #8; #8's READY status has been withdrawn. Existing stack measurements below are bound to their recorded source versions and do not qualify a future integration with the #8 fix.Earlier description and evidence
READY TO MERGE after #10 and #8.
Head:
11ae82d8d8c379c2ae895806b4409537c5ceb25b.GitHub reports MERGEABLE against
main: no text conflicts. This is not a test pass.The main-based head built and passed nine allocator checks.
Device result at this exact head, 13-inch M1, one boot, same CTS binary for both arms, dEQP-VK
binding_modelchunk 0 (1,000 cases):7cda7850ed59c25f62c9d)All 122 differences are ResourceError to Pass. No case gets worse. Before the run, each arm was checked to load its own driver file. The table shows the three PRs together, not #9 alone. It is one targeted chunk, not full CTS, and the CTS binary's source commit is not independently verified. An Aurora #18 result does not stand in for this one. Landing order: #10, #8, then #9.
Earlier review, source and test notes are kept below with their original bindings. The line below that says the main-based build was not run on a device is superseded by the result above. They do not change this status otherwise.
What this fixes
A buffer created with
VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BITcannot be replayed. In the first 1000 cases of the conformance groupbinding_model, every capture and replay case fails.vkCreateBufferreturnsVK_ERROR_OUT_OF_DEVICE_MEMORYfor a buffer that the test created with a captured address.The cause is in the address heap. Capture and replay buffers take their addresses from the same heap as every other allocation. In the old code the capture device hands out addresses from the top of the heap. The replay device later asks for the same addresses. In the meantime it gives the same kind of addresses to other allocations. The trace does not say which ones. When one of those sits on a captured address, the replay of the buffer that owns that address fails.
I traced three conformance cases with a throwaway patch that logs every address allocation. In each one the failing request was the last address allocation of the run. It was a fixed-address request on the replay device. It came after 68, 73 and 2 earlier fixed requests had succeeded. The range it asked for overlapped a range that a non-fixed allocation had taken a moment before. No replay buffer was in the way. In the smallest case the request was for 0x8000 bytes at 0x2ffff7c000. The call just before it was a non-fixed allocation of 0x8000 bytes at exactly that address.
What this changes
VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS. The reference page forvkCreateBufferlists this code. A capture request with no address that finds the range full returnsVK_ERROR_OUT_OF_DEVICE_MEMORY.agx_va_allocrefuses a fixed capture request outside the range without calling the heap code, which asserts on a range that wraps.test-agx-va.c.Limits of this change
VK_ERROR_OUT_OF_DEVICE_MEMORYwhere it did not before. Buffers without the flag are not limited. The ordinary heap is 4 GiB smaller.VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS.Test results
The allocator test is plain C and I ran it by hand with assertions on. All 9 cases pass. On a model of the old behavior, with the reservation turned off, 6 of the 9 cases fail.
A small test program, run on the 16-inch M1 Max, the new arm as a native build and the control as the built package, one boot. It captures 100 buffers in one process and replays them in a fresh process, in reverse order, with a memory allocation between replays, as the conformance test does.
VK_ERROR_OUT_OF_DEVICE_MEMORYseed_0,seed_2and one replay compute caseIn the same program this change returns the expected code for an address in the ordinary window, a captured address moved up by 4 GiB, an address in use, a 5 GiB buffer with an address (
VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS), and a second 3 GiB buffer without an address (VK_ERROR_OUT_OF_DEVICE_MEMORY). A buffer destroyed and created again at the same captured address on the same device gets the same address both times, and both memory binds succeed. That test used one size and one recreate. It did not access the recreated buffer from the GPU. I did not read the return value of the scratch bind, which the code ignores.The first 1000 cases of the
binding_modelgroup (chunk 0 of 151), with the library bytes of built packages, one run per arm. The 16-inch M1 Max and the 13-inch M1 give the same table.Exactly 122 of the 1000 rows differ on both chips, all from ResourceError to Pass, and 878 are unchanged. No row gets worse. The 122 rows are the 10
capture_replay_stressseeds and 112buffer_device_addressreplay cases.The control is the package of main with the change from #8 added. The candidate package is built from the commit on top of #8. This branch carries the same change on main.
git patch-idis equal for the two commits, and the main-based commit built natively without errors. I did not run the main-based build on a device.What this does not show