Repository navigation
fix(rocjitsu): Fix local memory fallback for VMID misses #9003
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
977afcf
9188d78
c13acc4
71bc55b
29dda97
0147dc7
d2a9069
fdca487
9acf8a7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ | |
| #include <chrono> | ||
| #include <cstring> | ||
| #include <fcntl.h> | ||
| #include <limits> | ||
| #include <linux/mman.h> | ||
| #ifndef MADV_POPULATE_WRITE | ||
| #define MADV_POPULATE_WRITE 23 | ||
|
|
@@ -698,6 +699,14 @@ int RemoteDriver::send_ioctl(unsigned long request, void *arg) { | |
| void *RemoteDriver::mmap(void *addr, size_t length, int prot, int flags, off_t offset) { | ||
| std::lock_guard<std::mutex> lock(rpc_mutex_); | ||
|
|
||
| constexpr size_t page_size = 4096; | ||
| constexpr size_t page_mask = page_size - 1; | ||
| if (length == 0 || length > std::numeric_limits<size_t>::max() - page_mask) { | ||
| errno = EINVAL; | ||
| return MAP_FAILED; | ||
| } | ||
| const size_t rounded_length = (length + page_mask) & ~page_mask; | ||
|
|
||
| // Send the mmap RPC so the daemon creates its own mapping for GPU simulation. | ||
| int memfd = -1; | ||
| int rc = send_mmap(addr, length, prot, flags, offset, &memfd); | ||
|
|
@@ -717,48 +726,55 @@ void *RemoteDriver::mmap(void *addr, size_t length, int prot, int flags, off_t o | |
| mapping_memfd = it->second; | ||
| } | ||
| if (mapping_memfd >= 0) { | ||
| [[maybe_unused]] auto ft_rc2 = ftruncate(mapping_memfd, static_cast<off_t>(length)); | ||
| struct stat backing_stat {}; | ||
| if (syscall(SYS_fstat, mapping_memfd, &backing_stat) != 0) { | ||
| const int saved_errno = errno; | ||
| if (memfd >= 0) | ||
| syscall(SYS_close, memfd); | ||
| errno = saved_errno; | ||
| return MAP_FAILED; | ||
| } | ||
| if (backing_stat.st_size < 0 || rounded_length > static_cast<uint64_t>(backing_stat.st_size)) { | ||
| if (memfd >= 0) | ||
| syscall(SYS_close, memfd); | ||
| errno = EINVAL; | ||
| return MAP_FAILED; | ||
| } | ||
|
|
||
| // Pre-copy committed pages (code objects) from the existing anonymous | ||
| // reservation into the memfd before MAP_FIXED replaces them. Uses a temp | ||
| // mapping outside the GPUVM range for the copy target. | ||
| if ((flags & MAP_FIXED) && addr != nullptr) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should preserve GPU-authoritative backing on the remote mmap path. The local path stops importing client pages after |
||
| auto prot_rc = syscall(SYS_mprotect, addr, length, PROT_READ | PROT_WRITE); | ||
| auto prot_rc = syscall(SYS_mprotect, addr, rounded_length, PROT_READ | PROT_WRITE); | ||
| if (prot_rc == 0) { | ||
| constexpr size_t page_size = 4096; | ||
| size_t num_pages = (length + page_size - 1) / page_size; | ||
| size_t num_pages = rounded_length / page_size; | ||
| std::vector<uint8_t> page_resident(num_pages); | ||
| auto mc_rc = syscall(SYS_mincore, addr, length, page_resident.data()); | ||
| auto mc_rc = syscall(SYS_mincore, addr, rounded_length, page_resident.data()); | ||
| auto *temp = static_cast<uint8_t *>( | ||
| safe_mmap(nullptr, length, PROT_WRITE, MAP_SHARED, mapping_memfd, 0)); | ||
| safe_mmap(nullptr, rounded_length, PROT_WRITE, MAP_SHARED, mapping_memfd, 0)); | ||
| if (temp != MAP_FAILED) { | ||
| if (mc_rc == 0) { | ||
| auto *src = static_cast<uint8_t *>(addr); | ||
| for (size_t i = 0; i < num_pages; ++i) { | ||
| if (page_resident[i] & 1) { | ||
| size_t off = i * page_size; | ||
| size_t n = std::min(page_size, length - off); | ||
| std::memcpy(temp + off, src + off, n); | ||
| std::memcpy(temp + off, src + off, page_size); | ||
| } | ||
| } | ||
| } | ||
| syscall(SYS_munmap, temp, length); | ||
| syscall(SYS_munmap, temp, rounded_length); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // QEMU vhost-user pattern: per-allocation memfd with F_SEAL_SHRINK (set at | ||
| // creation in alloc_memory_ioctl), MAP_SHARED|MAP_FIXED, then | ||
| // MADV_POPULATE_WRITE to pre-fault pages. This surfaces any shmem ENOSPC | ||
| // as errno rather than deferred SIGBUS on page fault. | ||
| // The allocation owns an already-sized memfd. Keep pages lazy: VRAM | ||
| // reservations can be tens of GiB and must not be physically populated by | ||
| // a CPU mapping alone. | ||
| int mflags = MAP_SHARED; | ||
| if (flags & MAP_FIXED) | ||
| mflags |= MAP_FIXED; | ||
| auto *mapped = safe_mmap(addr, length, PROT_READ | PROT_WRITE, mflags, mapping_memfd, 0); | ||
| if (mapped != MAP_FAILED) | ||
| syscall(SYS_madvise, mapped, length, MADV_POPULATE_WRITE); | ||
|
|
||
| if (memfd >= 0 && memfd != mapping_memfd) | ||
| if (memfd >= 0) | ||
| syscall(SYS_close, memfd); | ||
| return mapped; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should publish the page-rounded backing range here.
create_lazy_vram_backing()and CPU mmap coverbacking_size, but this vector covers onlyalloc.size; for a non-page-multiple VRAM allocation, the tail is classified as unmanaged and localwith_host_ptr()identity-dereferences the client reservation. At this head, a 4097-byte PROT_NONE allocation accepts an in-range write and then faults with SIGSEGV on a tail-page write; with a writable reservation, the tail write lands in client memory instead of the backing. Page-granular overlap checks already reserve the whole backing page, so usingbacking_sizeis safe. Please update the overlap assertion to the same units and add a non-page-multiple regression.