Hi Ashutosh, > > * During resize, simply calculate the new size and call ftruncate on each > > segment to adjust memory accordingly, no need to mmap/munmap or modify any > > memory mapping. > > > > > That's same as my understanding. Great, thanks for confirming!
> I thought I had shared a test program upthread, but I don't find it now. > Attached here. Can you please share your test program? Sure, mine is attached here (it’s based on another test program you shared before :-) Regards, Jack Ng
#define _GNU_SOURCE #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/mman.h> #include <linux/memfd.h> #include <string.h> #include <errno.h> #define HUGE_PG_SZ ((size_t)(2048*1024)) unsigned long long MAX_SZ = (100 * ((size_t)(1024 * 1024) * (size_t)(1024 * 1024))); // 100 TB int main(int argc, char *argv[]) { int fd_1 = -1; int fd_2 = -1; size_t old_size = atoi(argv[1]) * HUGE_PG_SZ; size_t new_size = atoi(argv[2]) * HUGE_PG_SZ; // Probe for a valid address that can grow up to MAX_SZ void *probe = mmap(NULL, MAX_SZ, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_NORESERVE | MAP_SHARED | MAP_HUGETLB, -1, 0); if (probe == MAP_FAILED) { perror("mmap"); exit(1); } if (munmap(probe, MAX_SZ) == -1) { perror("munmap"); exit(1); } /* |----- Memory mapping (MAP_NORESERVE) #1 -----|----- Memory mapping (MAP_NORESERVE) #2 -----| |-- In-use region 1 --|-- Reserved region 1 --|-- In-use region 2 --|-- Reserved region 2 --| */ // Create anon mapping #1 that's backed by an anon file fd_1 fd_1 = memfd_create("test_hugepage_map1", MFD_HUGETLB | MFD_HUGE_2MB | MFD_CLOEXEC); if (fd_1 == -1) { perror("memfd_create"); exit(1); } void *map1_addr = mmap(probe, MAX_SZ/2, PROT_READ | PROT_WRITE, MAP_NORESERVE | MAP_SHARED | MAP_HUGETLB, fd_1, 0); if (map1_addr == MAP_FAILED) { perror("mmap"); exit(1); } // Create in-use region 1 if (ftruncate(fd_1, old_size) == -1) { perror("ftruncate"); exit(1); } // The difference between ftruncate and fallocate is that ftruncate does not actually // allocate any memory yet (rather it's allocated on "first touch") but fallocate // does and also initializes the memory with zeros. /*if (fallocate(fd_1, 0, 0, old_size) == -1) { perror("fallocate"); exit(1); }*/ // Optionally, we can protect the reserved region just to be safe if (mprotect(map1_addr + old_size, MAX_SZ/2 - old_size, PROT_NONE) == -1) { perror("mprotect"); exit(1); } // Create anon mapping #2 that's backed by an anon file fd_2 fd_2 = memfd_create("test_hugepage_map2", MFD_HUGETLB | MFD_HUGE_2MB | MFD_CLOEXEC); if (fd_2 == -1) { perror("memfd_create"); exit(1); } void *map2_addr = mmap(probe + MAX_SZ/2, MAX_SZ/2, PROT_READ | PROT_WRITE, MAP_NORESERVE | MAP_SHARED | MAP_HUGETLB, fd_2, 0); if (map2_addr == MAP_FAILED) { perror("mmap"); exit(1); } // Create in-use region 2 if (ftruncate(fd_2, old_size) == -1) { perror("ftruncate"); exit(1); } /*if (fallocate(fd_2, 0, 0, old_size) == -1) { perror("fallocate"); exit(1); }*/ if (mprotect(map2_addr + old_size, MAX_SZ/2 - old_size, PROT_NONE) == -1) { perror("mprotect"); exit(1); } unsigned char *in_use1 = (unsigned char*)map1_addr; unsigned char *in_use2 = (unsigned char*)map2_addr; printf("Press Enter to fill in-use segment 1 memory...\n"); getchar(); memset(map1_addr, 0xee, old_size); printf("Press Enter to fill in-use segment 2 memory...\n"); getchar(); memset(map2_addr, 0xdd, old_size); for (int i=0; i < old_size; i++) { if (in_use1[i] != 0xee) { printf("seg1 content changed!\n"); exit(1); } } for (int i=0; i < old_size; i++) { if (in_use2[i] != 0xdd) { printf("seg2 content changed!\n"); exit(1); } } printf("Press Enter to resize in-use segment 1 memory...\n"); getchar(); // "Unprotect" the old reserved region first if (mprotect(map1_addr + old_size, MAX_SZ/2 - old_size, PROT_READ | PROT_WRITE) == -1) { perror("mprotect"); exit(1); } if (ftruncate(fd_1, new_size) == -1) { perror("fd_1 ftruncate"); exit(1); } // Protect the new reserved region if (mprotect(map1_addr + new_size, MAX_SZ/2 - new_size, PROT_NONE) == -1) { perror("mprotect"); exit(1); } printf("Press Enter to resize in-use segment 2 memory...\n"); getchar(); if (mprotect(map2_addr + old_size, MAX_SZ/2 - old_size, PROT_READ | PROT_WRITE) == -1) { perror("mprotect"); exit(1); } if (ftruncate(fd_2, new_size) == -1) { perror("fd_2 ftruncate"); exit(1); } if (mprotect(map2_addr + new_size, MAX_SZ/2 - new_size, PROT_NONE) == -1) { perror("mprotect"); exit(1); } size_t check_size = (old_size <= new_size ? old_size : new_size); for (int i=0; i < check_size; i++) { if (in_use1[i] != 0xee) { printf("new seg1 content changed!\n"); exit(1); } } for (int i=0; i < check_size; i++) { if (in_use2[i] != 0xdd) { printf("new seg2 content changed!\n"); exit(1); } } memset(map1_addr, 0xaa, new_size); for (int i=0; i < new_size; i++) { if (in_use1[i] != 0xaa) { printf("new seg1 corrupted!\n"); exit(1); } } memset(map2_addr, 0xbb, new_size); for (int i=0; i < new_size; i++) { if (in_use2[i] != 0xbb) { printf("new seg2 corrupted!\n"); exit(1); } } printf("Press Enter to shrink seg 1 to 0...\n"); getchar(); if (ftruncate(fd_1, 0) == -1) { perror("ftruncate"); exit(1); } close(fd_1); printf("Press Enter to shrink seg 2 to 0...\n"); getchar(); if (ftruncate(fd_2, 0) == -1) { perror("ftruncate"); exit(1); } close(fd_2); printf("Press Enter to unmap first mapping...\n"); getchar(); if (munmap(map1_addr, MAX_SZ/2) == -1) { perror("munmap"); exit(1); } printf("Press Enter to unmap first mapping...\n"); getchar(); if (munmap(map2_addr, MAX_SZ/2) == -1) { perror("munmap"); exit(1); } printf("Press Enter to termiante program...\n"); getchar(); return 0; }