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;
}

Reply via email to