pg.ddx.io pgsql-hackers@postgresql.org mailing list archive
help / color / mirror / Atom feedFrom: Jack Ng <Jack.Ng@huawei.com>
To: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
Cc: Dmitry Dolgov <9erthalion6@gmail.com>
Cc: pgsql-hackers@postgresql.org <pgsql-hackers@postgresql.org>
Cc: Robert Haas <robertmhaas@gmail.com>
Cc: Ni Ku <jakkuniku@gmail.com>
Subject: RE: Changing shared_buffers without restart
Date: Tue, 13 May 2025 05:03:03 +0000
Message-ID: <73f05b290b584e0581c4a96a33daa77b@huawei.com> (raw)
In-Reply-To: <CAExHW5sNCQdZsUH8qPZ5c1qJHWzhP5K3LJkC+MOE+jX6vTKMyA@mail.gmail.com>
References: <CAExHW5soDB=08jeyqn-W-=+Y86v-a5f_NJRjAdV9_yFrKpJcCw@mail.gmail.com>
<eqs6v4rsboazl67xz3wxc6xjkgrpfybitpl45y3lmb2br67wbj@o7czebb3rlgd>
<CAExHW5sTB1Ow0a1JtVy4PGCPckM7P1CSngJ4+LX3w+yz7rwQfQ@mail.gmail.com>
<ywyt5gecsfjzxlvxbvingd2dps2dedpwq5l4xhfmbcci6xta2e@h7ygoxvep725>
<CAExHW5s1b-_9GFGGOxSVimrt8oe2Y0a7vRhENrd2BSo+0Tkp-A@mail.gmail.com>
<gzb2oqqabzqidlnumrbau2k2fscb5jo6nlpdjnf6pu5y4bk5mo@xoujqgtiqvgr>
<CAExHW5uGozdiDm_BefS7jpQzaisCs4_nLTMF0+S_Pds49pRG_w@mail.gmail.com>
<CAPuPUJwDa_WEVmkknyYHYub5qvBuwKyLLOOiUpyiq6Wd=9FQ_g@mail.gmail.com>
<a2b6laawu4iya4zu737lhgdbvi5bcspxkxqinzogw6o7e2yhke@ay4qtrdlyzhq>
<fbdc6674ae54420d96368079bf76dd74@huawei.com>
<urny6igvtdqmbumsjmnpvw2vkynbjsxcyunov57gzp4bsikrh4@3pmdg245fkbe>
<dcd396b2e89c4b83a34adaaec5838476@huawei.com>
<CAExHW5sNCQdZsUH8qPZ5c1qJHWzhP5K3LJkC+MOE+jX6vTKMyA@mail.gmail.com>
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;
}
Attachments:
[text/plain] test_hugepage_mappings.c (5.7K, ../73f05b290b584e0581c4a96a33daa77b@huawei.com/3-test_hugepage_mappings.c)
download | inline:
#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;
}
view thread (167+ messages) latest in thread
Message-ID: <73f05b290b584e0581c4a96a33daa77b@huawei.com>
Permalink: ../73f05b290b584e0581c4a96a33daa77b@huawei.com/
Also on: postgresql.org/message-id/73f05b290b584e0581c4a96a33daa77b@huawei.com
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: Jack.Ng@huawei.com, ashutosh.bapat.oss@gmail.com, 9erthalion6@gmail.com, robertmhaas@gmail.com, jakkuniku@gmail.com
Subject: RE: Changing shared_buffers without restart
In-Reply-To: <73f05b290b584e0581c4a96a33daa77b@huawei.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox