fix(agent): collapse duplicate CUDA handle references - #166
galletas1712 wants to merge 1 commit into
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review (1)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Enterprise Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
1f03c19 to
6d1795e
Compare
6d1795e to
a5b9f63
Compare
a5b9f63 to
80334de
Compare
80334de to
0e40729
Compare
0e40729 to
457dc09
Compare
457dc09 to
0e40729
Compare
Signed-off-by: Schwinn Saereesitthipitak <schwinns@nvidia.com>
0e40729 to
1800678
Compare
|
Superseded by #217 (tracking: one driver handle per allocation, aliases collapsed through the tables). The re-cut stack (#212 → #220) supersedes this PR. Its body is quoted verbatim in the replacement's Origin section, and each review thread here has a row in the replacement's "Review threads carried" table with what was done about it. |
Turn the forwarding shim into one that knows which CUDA allocations are shared between processes. Allocations created with exactly the POSIX file-descriptor handle type are tracked behind logical handles; every other handle type passes through untouched. Exports return sealed memfd tickets instead of the driver's descriptor; an import of a ticket asks the creator process for the real descriptor over its control socket. The creator answers from an export cache that holds the one real descriptor per allocation, so its listener never calls into the driver and never takes the shim's main lock, and a creator busy inside a long collective call cannot stall its peers. Bookkeeping lives in hash tables plus a sorted range index that shrink as well as grow; records are freed when no handle and no mapping remains, so a server that maps and unmaps for days does not accumulate dead entries or slow down. One driver handle backs each allocation per process: repeated imports and cuMemRetainAllocationHandle alias it, and the last logical handle releases it. A ticket lives as long as the creator holds a handle or mapping, so freeing memory frees it. cuMemSetAccess keeps the union of access per location so one call per peer GPU survives; a call that partly overlaps a tracked mapping, or would grant more than 32 locations, is refused before the driver sees it. cuMemMap refuses overlapping a tracked mapping and cuMemUnmap may cover several whole mappings but not cut through one. Imports of descriptors that are not tickets are counted so the coordinator can refuse to checkpoint while one is alive. Fork children drop inherited records and register on their first CUDA activity, including through the driver's own symbol resolution, so a child that initializes CUDA without VMM calls is still visible. The multicast wrappers translate tracked member handles for the bind calls and otherwise still forward. The shim does not yet answer the coordinator's lifecycle requests; that is the next change. Compared with the earlier tracking layers (#152, #166): the export happens once at ticket time instead of on demand under the main lock; access sets are merged per location instead of overwritten by the last call; lists that never shrank are replaced by tables that do; the CUDA context is captured at creation rather than only at export; unmap and map are range-checked; the listener retries transient accept errors; the socket path is chmod 0600; NULL and non-zero-flag exports return CUDA_ERROR_INVALID_VALUE as the driver does. Fourteen state tests, table and export-cache unit tests, and the earlier suites run under AddressSanitizer and UndefinedBehaviorSanitizer during the agent image build. Signed-off-by: Schwinn Saereesitthipitak <schwinns@nvidia.com>
Turn the forwarding shim into one that knows which CUDA allocations are shared between processes. Allocations created with exactly the POSIX file-descriptor handle type are tracked behind logical handles; every other handle type passes through untouched. Exports return sealed memfd tickets instead of the driver's descriptor; an import of a ticket asks the creator process for the real descriptor over its control socket. The creator answers from an export cache that holds the one real descriptor per allocation, so its listener never calls into the driver and never takes the shim's main lock, and a creator busy inside a long collective call cannot stall its peers. Bookkeeping lives in hash tables plus a sorted range index that shrink as well as grow; records are freed when no handle and no mapping remains, so a server that maps and unmaps for days does not accumulate dead entries or slow down. One driver handle backs each allocation per process: repeated imports and cuMemRetainAllocationHandle alias it, and the last logical handle releases it. A ticket lives as long as the creator holds a handle or mapping, so freeing memory frees it. cuMemSetAccess keeps the union of access per location so one call per peer GPU survives; a call that partly overlaps a tracked mapping, or would grant more than 32 locations, is refused before the driver sees it. cuMemMap refuses overlapping a tracked mapping and cuMemUnmap may cover several whole mappings but not cut through one. Imports of descriptors that are not tickets are counted so the coordinator can refuse to checkpoint while one is alive. Fork children drop inherited records and register on their first CUDA activity, including through the driver's own symbol resolution, so a child that initializes CUDA without VMM calls is still visible. The multicast wrappers translate tracked member handles for the bind calls and otherwise still forward. The shim does not yet answer the coordinator's lifecycle requests; that is the next change. Compared with the earlier tracking layers (#152, #166): the export happens once at ticket time instead of on demand under the main lock; access sets are merged per location instead of overwritten by the last call; lists that never shrank are replaced by tables that do; the CUDA context is captured at creation rather than only at export; unmap and map are range-checked; the listener retries transient accept errors; the socket path is chmod 0600; NULL and non-zero-flag exports return CUDA_ERROR_INVALID_VALUE as the driver does. Fourteen state tests, table and export-cache unit tests, and the earlier suites run under AddressSanitizer and UndefinedBehaviorSanitizer during the agent image build. Signed-off-by: Schwinn Saereesitthipitak <schwinns@nvidia.com>
Turn the forwarding shim into one that knows which CUDA allocations are shared between processes. Allocations created with exactly the POSIX file-descriptor handle type are tracked behind logical handles; every other handle type passes through untouched. Exports return sealed memfd tickets instead of the driver's descriptor; an import of a ticket asks the creator process for the real descriptor over its control socket. The creator answers from an export cache that holds the one real descriptor per allocation, so its listener never calls into the driver and never takes the shim's main lock, and a creator busy inside a long collective call cannot stall its peers. Bookkeeping lives in hash tables plus a sorted range index that shrink as well as grow; records are freed when no handle and no mapping remains, so a server that maps and unmaps for days does not accumulate dead entries or slow down. One driver handle backs each allocation per process: repeated imports and cuMemRetainAllocationHandle alias it, and the last logical handle releases it. A ticket lives as long as the creator holds a handle or mapping, so freeing memory frees it. cuMemSetAccess keeps the union of access per location so one call per peer GPU survives; a call that partly overlaps a tracked mapping, or would grant more than 32 locations, is refused before the driver sees it. cuMemMap refuses overlapping a tracked mapping and cuMemUnmap may cover several whole mappings but not cut through one. Imports of descriptors that are not tickets are counted so the coordinator can refuse to checkpoint while one is alive. Fork children drop inherited records and register on their first CUDA activity, including through the driver's own symbol resolution, so a child that initializes CUDA without VMM calls is still visible. The multicast wrappers translate tracked member handles for the bind calls and otherwise still forward. The shim does not yet answer the coordinator's lifecycle requests; that is the next change. Compared with the earlier tracking layers (#152, #166): the export happens once at ticket time instead of on demand under the main lock; access sets are merged per location instead of overwritten by the last call; lists that never shrank are replaced by tables that do; the CUDA context is captured at creation rather than only at export; unmap and map are range-checked; the listener retries transient accept errors; the socket path is chmod 0600; NULL and non-zero-flag exports return CUDA_ERROR_INVALID_VALUE as the driver does. Fourteen state tests, table and export-cache unit tests, and the earlier suites run under AddressSanitizer and UndefinedBehaviorSanitizer during the agent image build. Signed-off-by: Schwinn Saereesitthipitak <schwinns@nvidia.com>
Turn the forwarding shim into one that knows which CUDA allocations are shared between processes. Allocations created with exactly the POSIX file-descriptor handle type are tracked behind logical handles; every other handle type passes through untouched. Exports return sealed memfd tickets instead of the driver's descriptor; an import of a ticket asks the creator process for the real descriptor over its control socket. The creator answers from an export cache that holds the one real descriptor per allocation, so its listener never calls into the driver and never takes the shim's main lock, and a creator busy inside a long collective call cannot stall its peers. Bookkeeping lives in hash tables plus a sorted range index that shrink as well as grow; records are freed when no handle and no mapping remains, so a server that maps and unmaps for days does not accumulate dead entries or slow down. One driver handle backs each allocation per process: repeated imports and cuMemRetainAllocationHandle alias it, and the last logical handle releases it. A ticket lives as long as the creator holds a handle or mapping, so freeing memory frees it. cuMemSetAccess keeps the union of access per location so one call per peer GPU survives; a call that partly overlaps a tracked mapping, or would grant more than 32 locations, is refused before the driver sees it. cuMemMap refuses overlapping a tracked mapping and cuMemUnmap may cover several whole mappings but not cut through one. Imports of descriptors that are not tickets are counted so the coordinator can refuse to checkpoint while one is alive. Fork children drop inherited records and register on their first CUDA activity, including through the driver's own symbol resolution, so a child that initializes CUDA without VMM calls is still visible. The multicast wrappers translate tracked member handles for the bind calls and otherwise still forward. The shim does not yet answer the coordinator's lifecycle requests; that is the next change. Compared with the earlier tracking layers (#152, #166): the export happens once at ticket time instead of on demand under the main lock; access sets are merged per location instead of overwritten by the last call; lists that never shrank are replaced by tables that do; the CUDA context is captured at creation rather than only at export; unmap and map are range-checked; the listener retries transient accept errors; the socket path is chmod 0600; NULL and non-zero-flag exports return CUDA_ERROR_INVALID_VALUE as the driver does. Fourteen state tests, table and export-cache unit tests, and the earlier suites run under AddressSanitizer and UndefinedBehaviorSanitizer during the agent image build. Signed-off-by: Schwinn Saereesitthipitak <schwinns@nvidia.com>
Turn the forwarding shim into one that knows which CUDA allocations are shared between processes. Allocations created with exactly the POSIX file-descriptor handle type are tracked behind logical handles; every other handle type passes through untouched. Exports return sealed memfd tickets instead of the driver's descriptor; an import of a ticket asks the creator process for the real descriptor over its control socket. The creator answers from an export cache that holds the one real descriptor per allocation, so its listener never calls into the driver and never takes the shim's main lock, and a creator busy inside a long collective call cannot stall its peers. Bookkeeping lives in hash tables plus a sorted range index that shrink as well as grow; records are freed when no handle and no mapping remains, so a server that maps and unmaps for days does not accumulate dead entries or slow down. One driver handle backs each allocation per process: repeated imports and cuMemRetainAllocationHandle alias it, and the last logical handle releases it. A ticket lives as long as the creator holds a handle or mapping, so freeing memory frees it. cuMemSetAccess keeps the union of access per location so one call per peer GPU survives; a call that partly overlaps a tracked mapping, or would grant more than 32 locations, is refused before the driver sees it. cuMemMap refuses overlapping a tracked mapping and cuMemUnmap may cover several whole mappings but not cut through one. Imports of descriptors that are not tickets are counted so the coordinator can refuse to checkpoint while one is alive. Fork children drop inherited records and register on their first CUDA activity, including through the driver's own symbol resolution, so a child that initializes CUDA without VMM calls is still visible. The multicast wrappers translate tracked member handles for the bind calls and otherwise still forward. The shim does not yet answer the coordinator's lifecycle requests; that is the next change. Compared with the earlier tracking layers (#152, #166): the export happens once at ticket time instead of on demand under the main lock; access sets are merged per location instead of overwritten by the last call; lists that never shrank are replaced by tables that do; the CUDA context is captured at creation rather than only at export; unmap and map are range-checked; the listener retries transient accept errors; the socket path is chmod 0600; NULL and non-zero-flag exports return CUDA_ERROR_INVALID_VALUE as the driver does. Fourteen state tests, table and export-cache unit tests, and the earlier suites run under AddressSanitizer and UndefinedBehaviorSanitizer during the agent image build. Signed-off-by: Schwinn Saereesitthipitak <schwinns@nvidia.com>
Summary
cuMemRetainAllocationHandlewhen a tracked allocation already has a live backing handleValidation
make -C agent/cmd/cuinterpose clean testin the CUDA test imagego test ./agent/internal/criu ./agent/internal/cuda -count=14242